ui.data_adapter.js 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425
  1. /**
  2. * DevExtreme (ui/hierarchical_collection/ui.data_adapter.js)
  3. * Version: 19.1.16
  4. * Build date: Tue Oct 18 2022
  5. *
  6. * Copyright (c) 2012 - 2022 Developer Express Inc. ALL RIGHTS RESERVED
  7. * Read about DevExtreme licensing here: https://js.devexpress.com/Licensing/
  8. */
  9. "use strict";
  10. var Class = require("../../core/class");
  11. var commonUtils = require("../../core/utils/common");
  12. var iteratorUtils = require("../../core/utils/iterator");
  13. var each = require("../../core/utils/iterator").each;
  14. var typeUtils = require("../../core/utils/type");
  15. var extend = require("../../core/utils/extend").extend;
  16. var errors = require("../../ui/widget/ui.errors");
  17. var getOperationBySearchMode = require("../../ui/widget/ui.search_box_mixin").getOperationBySearchMode;
  18. var inArray = require("../../core/utils/array").inArray;
  19. var query = require("../../data/query");
  20. var storeHelper = require("../../data/store_helper");
  21. var HierarchicalDataConverter = require("./ui.data_converter");
  22. var EXPANDED = "expanded";
  23. var SELECTED = "selected";
  24. var DISABLED = "disabled";
  25. var DataAdapter = Class.inherit({
  26. ctor: function(options) {
  27. this.options = {};
  28. extend(this.options, this._defaultOptions(), options);
  29. this.options.dataConverter.setDataAccessors(this.options.dataAccessors);
  30. this._selectedNodesKeys = [];
  31. this._expandedNodesKeys = [];
  32. this._dataStructure = [];
  33. this._createInternalDataStructure();
  34. this.getTreeNodes()
  35. },
  36. setOption: function(name, value) {
  37. this.options[name] = value;
  38. if ("recursiveSelection" === name) {
  39. this._updateSelection()
  40. }
  41. },
  42. _defaultOptions: function() {
  43. return {
  44. dataAccessors: void 0,
  45. items: [],
  46. multipleSelection: true,
  47. recursiveSelection: false,
  48. recursiveExpansion: false,
  49. rootValue: 0,
  50. searchValue: "",
  51. dataType: "tree",
  52. searchMode: "contains",
  53. dataConverter: new HierarchicalDataConverter,
  54. onNodeChanged: commonUtils.noop,
  55. sort: null
  56. }
  57. },
  58. _createInternalDataStructure: function() {
  59. this._initialDataStructure = this.options.dataConverter.createPlainStructure(this.options.items, this.options.rootValue, this.options.dataType);
  60. this._dataStructure = this.options.searchValue.length ? this.search(this.options.searchValue) : this._initialDataStructure;
  61. this.options.dataConverter._dataStructure = this._dataStructure;
  62. this._updateSelection();
  63. this._updateExpansion()
  64. },
  65. _updateSelection: function() {
  66. if (this.options.recursiveSelection) {
  67. this._setChildrenSelection();
  68. this._setParentSelection()
  69. }
  70. this._selectedNodesKeys = this._updateNodesKeysArray(SELECTED)
  71. },
  72. _updateExpansion: function(key) {
  73. if (this.options.recursiveExpansion) {
  74. key ? this._updateOneBranch(key) : this._setParentExpansion()
  75. }
  76. this._expandedNodesKeys = this._updateNodesKeysArray(EXPANDED)
  77. },
  78. _updateNodesKeysArray: function(property) {
  79. var that = this;
  80. var array = [];
  81. each(that._getDataBySelectionMode(), function(_, node) {
  82. if (!that._isNodeVisible(node)) {
  83. return
  84. }
  85. if (node.internalFields[property]) {
  86. if (property === EXPANDED || that.options.multipleSelection) {
  87. array.push(node.internalFields.key)
  88. } else {
  89. array.length && that.toggleSelection(array[0], false, true);
  90. array = [node.internalFields.key]
  91. }
  92. }
  93. });
  94. return array
  95. },
  96. _getDataBySelectionMode: function() {
  97. return this.options.multipleSelection ? this.getData() : this.getFullData()
  98. },
  99. _isNodeVisible: function(node) {
  100. return false !== node.internalFields.item.visible
  101. },
  102. _getByKey: function(data, key) {
  103. return data === this._dataStructure ? this.options.dataConverter._getByKey(key) : this.options.dataConverter.getByKey(data, key)
  104. },
  105. _setChildrenSelection: function() {
  106. var that = this;
  107. each(this._dataStructure, function(_, node) {
  108. if (!node.internalFields.childrenKeys.length) {
  109. return
  110. }
  111. var isSelected = node.internalFields.selected;
  112. true === isSelected && that._toggleChildrenSelection(node, isSelected)
  113. })
  114. },
  115. _setParentSelection: function() {
  116. var that = this;
  117. each(this._dataStructure, function(_, node) {
  118. var parent = that.options.dataConverter.getParentNode(node);
  119. if (parent && node.internalFields.parentKey !== that.options.rootValue) {
  120. that._iterateParents(node, function(parent) {
  121. var newParentState = that._calculateSelectedState(parent);
  122. that._setFieldState(parent, SELECTED, newParentState)
  123. })
  124. }
  125. })
  126. },
  127. _setParentExpansion: function() {
  128. var that = this;
  129. each(this._dataStructure, function(_, node) {
  130. if (!node.internalFields.expanded) {
  131. return
  132. }
  133. that._updateOneBranch(node.internalFields.key)
  134. })
  135. },
  136. _updateOneBranch: function(key) {
  137. var that = this;
  138. var node = this.getNodeByKey(key);
  139. that._iterateParents(node, function(parent) {
  140. that._setFieldState(parent, EXPANDED, true)
  141. })
  142. },
  143. _iterateChildren: function(node, recursive, callback) {
  144. var that = this;
  145. each(node.internalFields.childrenKeys, function(_, key) {
  146. var child = that.getNodeByKey(key);
  147. typeUtils.isFunction(callback) && callback(child);
  148. if (child.internalFields.childrenKeys.length && recursive) {
  149. that._iterateChildren(child, recursive, callback)
  150. }
  151. })
  152. },
  153. _iterateParents: function(node, callback) {
  154. if (node.internalFields.parentKey === this.options.rootValue) {
  155. return
  156. }
  157. var parent = this.options.dataConverter.getParentNode(node);
  158. if (parent) {
  159. typeUtils.isFunction(callback) && callback(parent);
  160. if (parent.internalFields.parentKey !== this.options.rootValue) {
  161. this._iterateParents(parent, callback)
  162. }
  163. }
  164. },
  165. _calculateSelectedState: function(node) {
  166. var itemsCount = node.internalFields.childrenKeys.length;
  167. var selectedItemsCount = 0;
  168. var invisibleItemsCount = 0;
  169. var result = false;
  170. for (var i = 0; i <= itemsCount - 1; i++) {
  171. var childNode = this.getNodeByKey(node.internalFields.childrenKeys[i]);
  172. var isChildInvisible = false === childNode.internalFields.item.visible;
  173. var childState = childNode.internalFields.selected;
  174. if (isChildInvisible) {
  175. invisibleItemsCount++;
  176. continue
  177. }
  178. if (childState) {
  179. selectedItemsCount++
  180. } else {
  181. if (void 0 === childState) {
  182. selectedItemsCount += .5
  183. }
  184. }
  185. }
  186. if (selectedItemsCount) {
  187. result = selectedItemsCount === itemsCount - invisibleItemsCount ? true : void 0
  188. }
  189. return result
  190. },
  191. _toggleChildrenSelection: function(node, state) {
  192. var that = this;
  193. this._iterateChildren(node, true, function(child) {
  194. if (that._isNodeVisible(child)) {
  195. that._setFieldState(child, SELECTED, state)
  196. }
  197. })
  198. },
  199. _setFieldState: function(node, field, state) {
  200. if (node.internalFields[field] === state) {
  201. return
  202. }
  203. node.internalFields[field] = state;
  204. if (node.internalFields.publicNode) {
  205. node.internalFields.publicNode[field] = state
  206. }
  207. this.options.dataAccessors.setters[field](node.internalFields.item, state);
  208. this.options.onNodeChanged(node)
  209. },
  210. _markChildren: function(keys) {
  211. var that = this;
  212. each(keys, function(_, key) {
  213. var index = that.getIndexByKey(key);
  214. var node = that.getNodeByKey(key);
  215. that._dataStructure[index] = 0;
  216. node.internalFields.childrenKeys.length && that._markChildren(node.internalFields.childrenKeys)
  217. })
  218. },
  219. _removeNode: function(key) {
  220. var node = this.getNodeByKey(key);
  221. this._dataStructure[this.getIndexByKey(key)] = 0;
  222. this._markChildren(node.internalFields.childrenKeys);
  223. var that = this;
  224. var counter = 0;
  225. var items = extend([], this._dataStructure);
  226. each(items, function(index, item) {
  227. if (!item) {
  228. that._dataStructure.splice(index - counter, 1);
  229. counter++
  230. }
  231. })
  232. },
  233. _addNode: function(item) {
  234. var dataConverter = this.options.dataConverter;
  235. var node = dataConverter._convertItemToNode(item, this.options.dataAccessors.getters.parentKey(item));
  236. this._dataStructure = this._dataStructure.concat(node);
  237. this._initialDataStructure = this._initialDataStructure.concat(node);
  238. dataConverter._dataStructure = dataConverter._dataStructure.concat(node)
  239. },
  240. _updateFields: function() {
  241. this.options.dataConverter.updateChildrenKeys();
  242. this._updateSelection();
  243. this._updateExpansion()
  244. },
  245. getSelectedNodesKeys: function() {
  246. return this._selectedNodesKeys
  247. },
  248. getExpandedNodesKeys: function() {
  249. return this._expandedNodesKeys
  250. },
  251. getData: function() {
  252. return this._dataStructure
  253. },
  254. getFullData: function() {
  255. return this._initialDataStructure
  256. },
  257. getNodeByItem: function(item) {
  258. var result = null;
  259. each(this._dataStructure, function(_, node) {
  260. if (node.internalFields.item === item) {
  261. result = node;
  262. return false
  263. }
  264. });
  265. return result
  266. },
  267. getNodesByItems: function(items) {
  268. var that = this;
  269. var nodes = [];
  270. each(items, function(_, item) {
  271. var node = that.getNodeByItem(item);
  272. node && nodes.push(node)
  273. });
  274. return nodes
  275. },
  276. getNodeByKey: function(key, data) {
  277. return this._getByKey(data || this._getDataBySelectionMode(), key)
  278. },
  279. getTreeNodes: function() {
  280. return this.options.dataConverter.convertToPublicNodes(this.getRootNodes())
  281. },
  282. getItemsCount: function() {
  283. return this.options.dataConverter.getItemsCount()
  284. },
  285. getVisibleItemsCount: function() {
  286. return this.options.dataConverter.getVisibleItemsCount()
  287. },
  288. getPublicNode: function(node) {
  289. return node.internalFields.publicNode
  290. },
  291. getRootNodes: function() {
  292. return this.getChildrenNodes(this.options.rootValue)
  293. },
  294. getChildrenNodes: function(parentKey) {
  295. return query(this._dataStructure).filter(["internalFields.parentKey", parentKey]).toArray()
  296. },
  297. getIndexByKey: function(key) {
  298. return this.options.dataConverter.getIndexByKey(key)
  299. },
  300. addItem: function(item) {
  301. this._addNode(item);
  302. this._updateFields()
  303. },
  304. removeItem: function(key) {
  305. this._removeNode(key);
  306. this._updateFields()
  307. },
  308. toggleSelection: function(key, state, selectRecursive) {
  309. var isSingleModeUnselect = this._isSingleModeUnselect(state);
  310. var node = this._getByKey(selectRecursive || isSingleModeUnselect ? this._initialDataStructure : this._dataStructure, key);
  311. this._setFieldState(node, SELECTED, state);
  312. if (this.options.recursiveSelection && !selectRecursive) {
  313. state ? this._setChildrenSelection() : this._toggleChildrenSelection(node, state);
  314. this._setParentSelection()
  315. }
  316. this._selectedNodesKeys = this._updateNodesKeysArray(SELECTED)
  317. },
  318. _isSingleModeUnselect: function(selectionState) {
  319. return !this.options.multipleSelection && !selectionState
  320. },
  321. toggleNodeDisabledState: function(key, state) {
  322. var node = this.getNodeByKey(key);
  323. this._setFieldState(node, DISABLED, state)
  324. },
  325. toggleSelectAll: function(state) {
  326. if (!typeUtils.isDefined(state)) {
  327. return
  328. }
  329. var that = this;
  330. var lastSelectedKey = that._selectedNodesKeys[that._selectedNodesKeys.length - 1];
  331. var dataStructure = that._isSingleModeUnselect(state) ? this._initialDataStructure : this._dataStructure;
  332. each(dataStructure, function(index, node) {
  333. if (!that._isNodeVisible(node)) {
  334. return
  335. }
  336. that._setFieldState(node, SELECTED, state)
  337. });
  338. that._selectedNodesKeys = that._updateNodesKeysArray(SELECTED);
  339. if (!state && that.options.selectionRequired) {
  340. that.toggleSelection(lastSelectedKey, true)
  341. }
  342. },
  343. isAllSelected: function() {
  344. if (this.getSelectedNodesKeys().length) {
  345. return this.getSelectedNodesKeys().length === this.getVisibleItemsCount() ? true : void 0
  346. } else {
  347. return false
  348. }
  349. },
  350. toggleExpansion: function(key, state) {
  351. var node = this.getNodeByKey(key);
  352. this._setFieldState(node, EXPANDED, state);
  353. if (state) {
  354. this._updateExpansion(key)
  355. }
  356. this._expandedNodesKeys = this._updateNodesKeysArray(EXPANDED)
  357. },
  358. isFiltered: function(item) {
  359. return !this.options.searchValue.length || !!this._filterDataStructure(this.options.searchValue, [item]).length
  360. },
  361. _createCriteria: function(selector, value, operation) {
  362. var searchFilter = [];
  363. if (!Array.isArray(selector)) {
  364. return [selector, operation, value]
  365. }
  366. iteratorUtils.each(selector, function(i, item) {
  367. searchFilter.push([item, operation, value], "or")
  368. });
  369. searchFilter.pop();
  370. return searchFilter
  371. },
  372. _filterDataStructure: function(filterValue, dataStructure) {
  373. var selector = this.options.searchExpr || this.options.dataAccessors.getters.display;
  374. var operation = getOperationBySearchMode(this.options.searchMode);
  375. var criteria = this._createCriteria(selector, filterValue, operation);
  376. dataStructure = dataStructure || this._initialDataStructure;
  377. return query(dataStructure).filter(criteria).toArray()
  378. },
  379. search: function(searchValue) {
  380. var that = this;
  381. var matches = this._filterDataStructure(searchValue);
  382. var dataConverter = this.options.dataConverter;
  383. function lookForParents(matches, index) {
  384. var length = matches.length;
  385. while (index < length) {
  386. var node = matches[index];
  387. if (node.internalFields.parentKey === that.options.rootValue) {
  388. index++;
  389. continue
  390. }
  391. var parent = dataConverter.getParentNode(node);
  392. if (!parent) {
  393. errors.log("W1007", node.internalFields.parentKey, node.internalFields.key);
  394. index++;
  395. continue
  396. }
  397. if (!parent.internalFields.expanded) {
  398. that._setFieldState(parent, EXPANDED, true)
  399. }
  400. if (inArray(parent, matches) > -1) {
  401. index++;
  402. continue
  403. }
  404. matches.splice(index, 0, parent);
  405. lookForParents(matches, index)
  406. }
  407. }
  408. lookForParents(matches, 0);
  409. if (this.options.sort) {
  410. matches = storeHelper.queryByOptions(query(matches), {
  411. sort: this.options.sort
  412. }).toArray()
  413. }
  414. dataConverter._indexByKey = {};
  415. each(matches, function(index, node) {
  416. node.internalFields.childrenKeys = [];
  417. dataConverter._indexByKey[node.internalFields.key] = index
  418. });
  419. dataConverter._dataStructure = matches;
  420. dataConverter.setChildrenKeys();
  421. return dataConverter._dataStructure
  422. }
  423. });
  424. module.exports = DataAdapter;