devextreme
Version:
JavaScript/TypeScript Component Suite for Responsive Web Development
501 lines (498 loc) • 19.7 kB
JavaScript
/**
* DevExtreme (cjs/__internal/ui/hierarchical_collection/data_adapter.js)
* Version: 25.2.5
* Build date: Fri Feb 20 2026
*
* Copyright (c) 2012 - 2026 Developer Express Inc. ALL RIGHTS RESERVED
* Read about DevExtreme licensing here: https://js.devexpress.com/Licensing/
*/
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = void 0;
var _query = _interopRequireDefault(require("../../../common/data/query"));
var _store_helper = _interopRequireDefault(require("../../../common/data/store_helper"));
var _common = require("../../../core/utils/common");
var _extend = require("../../../core/utils/extend");
var _iterator = require("../../../core/utils/iterator");
var _type = require("../../../core/utils/type");
var _ui = _interopRequireDefault(require("../../../ui/widget/ui.errors"));
var _search_box_controller = _interopRequireWildcard(require("../../ui/collection/search_box_controller"));
var _m_text_box = _interopRequireDefault(require("../../ui/text_box/m_text_box"));
var _data_converter = _interopRequireDefault(require("./data_converter"));
function _interopRequireWildcard(e, t) {
if ("function" == typeof WeakMap) {
var r = new WeakMap,
n = new WeakMap
}
return (_interopRequireWildcard = function(e, t) {
if (!t && e && e.__esModule) {
return e
}
var o, i, f = {
__proto__: null,
default: e
};
if (null === e || "object" != typeof e && "function" != typeof e) {
return f
}
if (o = t ? n : r) {
if (o.has(e)) {
return o.get(e)
}
o.set(e, f)
}
for (const t in e) {
"default" !== t && {}.hasOwnProperty.call(e, t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, t)) && (i.get || i.set) ? o(f, t, i) : f[t] = e[t])
}
return f
})(e, t)
}
function _interopRequireDefault(e) {
return e && e.__esModule ? e : {
default: e
}
}
const EXPANDED = "expanded";
const SELECTED = "selected";
const DISABLED = "disabled";
_search_box_controller.default.setEditorClass(_m_text_box.default);
class DataAdapter {
constructor(options) {
this.options = {
dataAccessors: {},
items: [],
multipleSelection: true,
recursiveSelection: false,
recursiveExpansion: false,
rootValue: 0,
searchValue: "",
dataType: "tree",
searchMode: "contains",
dataConverter: new _data_converter.default,
onNodeChanged: _common.noop,
sort: null
};
this._selectedNodesKeys = [];
this._expandedNodesKeys = [];
this._dataStructure = [];
this._initialDataStructure = [];
(0, _extend.extend)(this.options, options);
this.options.dataConverter.setDataAccessors(this.options.dataAccessors);
this._createInternalDataStructure();
this.getTreeNodes()
}
setOption(name, value) {
this.options[name] = value;
if ("recursiveSelection" === name) {
this._updateSelection()
}
}
_createInternalDataStructure() {
this._initialDataStructure = this.options.dataConverter.createPlainStructure(this.options.items, this.options.rootValue, this.options.dataType);
this._dataStructure = this.options.searchValue.length ? this.search(this.options.searchValue) : this._initialDataStructure;
this.options.dataConverter._dataStructure = this._dataStructure;
this._updateSelection();
this._updateExpansion()
}
_updateSelection() {
if (this.options.recursiveSelection) {
this._setChildrenSelection();
this._setParentSelection()
}
this._selectedNodesKeys = this._updateNodesKeysArray(SELECTED)
}
_updateExpansion(key) {
if (this.options.recursiveExpansion) {
if (key) {
this._updateOneBranch(key)
} else {
this._setParentExpansion()
}
}
this._expandedNodesKeys = this._updateNodesKeysArray(EXPANDED)
}
_updateNodesKeysArray(property) {
let array = [];
(0, _iterator.each)(this._getDataBySelectionMode(), ((_index, node) => {
if (!this._isNodeVisible(node)) {
return
}
if (node.internalFields[property]) {
if (property === EXPANDED || this.options.multipleSelection) {
array.push(node.internalFields.key)
} else {
if (array.length) {
this.toggleSelection(array[0], false, true)
}
array = [node.internalFields.key]
}
}
}));
return array
}
_getDataBySelectionMode() {
return this.options.multipleSelection ? this.getData() : this.getFullData()
}
_isNodeVisible(node) {
return false !== node.internalFields.item.visible
}
_getByKey(data, key) {
return data === this._dataStructure ? this.options.dataConverter._getByKey(key) : this.options.dataConverter.getByKey(data.filter(Boolean), key)
}
_setChildrenSelection() {
(0, _iterator.each)(this._dataStructure, ((_index, node) => {
if (!(null !== node && void 0 !== node && node.internalFields.childrenKeys.length)) {
return
}
const isSelected = node.internalFields.selected;
if (isSelected) {
this._toggleChildrenSelection(node, isSelected)
}
}))
}
_setParentSelection() {
(0, _iterator.each)(this._dataStructure, ((_index, node) => {
if (!node) {
return
}
const parent = this.options.dataConverter.getParentNode(node);
if (parent && node.internalFields.parentKey !== this.options.rootValue) {
this._iterateParents(node, (parentNode => {
const newParentState = this._calculateSelectedState(parentNode);
this._setFieldState(parentNode, SELECTED, newParentState)
}))
}
}))
}
_setParentExpansion() {
(0, _iterator.each)(this._dataStructure, ((_index, node) => {
if (!(null !== node && void 0 !== node && node.internalFields.expanded)) {
return
}
this._updateOneBranch(node.internalFields.key)
}))
}
_updateOneBranch(key) {
const node = this.getNodeByKey(key);
this._iterateParents(node, (parent => {
this._setFieldState(parent, EXPANDED, true)
}))
}
_iterateChildren(node, recursive, callback, processedKeys) {
if (!(0, _type.isFunction)(callback) || !node) {
return
}
const nodeKey = node.internalFields.key;
const keys = processedKeys ?? [];
if (void 0 !== nodeKey && !keys.includes(nodeKey)) {
keys.push(nodeKey);
(0, _iterator.each)(node.internalFields.childrenKeys, ((_index, key) => {
const child = this.getNodeByKey(key);
callback(child);
if (null !== child && void 0 !== child && child.internalFields.childrenKeys.length && recursive) {
this._iterateChildren(child, recursive, callback, keys)
}
}))
}
}
_iterateParents(node, callback, processedKeys) {
if (!node || node.internalFields.parentKey === this.options.rootValue || !(0, _type.isFunction)(callback)) {
return
}
const keys = processedKeys ?? [];
const {
key: key
} = node.internalFields;
if (!keys.includes(key)) {
keys.push(key);
const parent = this.options.dataConverter.getParentNode(node);
if (parent) {
callback(parent);
if (parent.internalFields.parentKey !== this.options.rootValue) {
this._iterateParents(parent, callback, keys)
}
}
}
}
_calculateSelectedState(node) {
const itemsCount = node.internalFields.childrenKeys.length;
let selectedItemsCount = 0;
let invisibleItemsCount = 0;
let result = false;
for (let i = 0; i <= itemsCount - 1; i += 1) {
const childNode = this.getNodeByKey(node.internalFields.childrenKeys[i]);
const isChildInvisible = false === (null === childNode || void 0 === childNode ? void 0 : childNode.internalFields.item.visible);
const childState = null === childNode || void 0 === childNode ? void 0 : childNode.internalFields.selected;
if (isChildInvisible) {
invisibleItemsCount += 1
} else if (childState) {
selectedItemsCount += 1
} else if (void 0 === childState) {
selectedItemsCount += .5
}
}
if (selectedItemsCount) {
result = selectedItemsCount === itemsCount - invisibleItemsCount ? true : void 0
}
return result
}
_toggleChildrenSelection(node, state) {
this._iterateChildren(node, true, (child => {
if (child && this._isNodeVisible(child)) {
this._setFieldState(child, SELECTED, state)
}
}))
}
_setFieldState(node, field, state) {
if (node.internalFields[field] === state) {
return
}
node.internalFields[field] = state;
if (node.internalFields.publicNode) {
node.internalFields.publicNode[field] = state
}
this.options.dataAccessors.setters[field](node.internalFields.item, state);
this.options.onNodeChanged(node)
}
_markChildren(keys) {
(0, _iterator.each)(keys, ((_index, key) => {
const index = this.getIndexByKey(key);
const node = this.getNodeByKey(key);
this._dataStructure[index] = null;
if (null !== node && void 0 !== node && node.internalFields.childrenKeys.length) {
this._markChildren(node.internalFields.childrenKeys)
}
}))
}
_removeNode(key) {
const node = this.getNodeByKey(key);
this._dataStructure[this.getIndexByKey(key)] = null;
if (null !== node && void 0 !== node && node.internalFields.childrenKeys.length) {
this._markChildren(node.internalFields.childrenKeys)
}
let counter = 0;
const items = (0, _extend.extend)([], this._dataStructure);
(0, _iterator.each)(items, ((index, item) => {
if (!item) {
this._dataStructure.splice(index - counter, 1);
counter += 1
}
}))
}
_addNode(item) {
const {
dataConverter: dataConverter
} = this.options;
const node = dataConverter._convertItemToNode(item, this.options.dataAccessors.getters.parentKey(item));
this._dataStructure = this._dataStructure.concat(node);
this._initialDataStructure = this._initialDataStructure.concat(node);
dataConverter._dataStructure = dataConverter._dataStructure.concat(node)
}
_updateFields() {
this.options.dataConverter.updateChildrenKeys();
this._updateSelection();
this._updateExpansion()
}
getSelectedNodesKeys() {
return this._selectedNodesKeys
}
getExpandedNodesKeys() {
return this._expandedNodesKeys
}
getData() {
return this._dataStructure
}
getFullData() {
return this._initialDataStructure
}
getNodeByItem(item) {
let result = null;
(0, _iterator.each)(this._dataStructure, ((_index, node) => {
if ((null === node || void 0 === node ? void 0 : node.internalFields.item) === item) {
result = node;
return false
}
return true
}));
return result
}
getNodesByItems(items) {
const nodes = [];
(0, _iterator.each)(items, ((_index, item) => {
const node = this.getNodeByItem(item);
if (node) {
nodes.push(node)
}
}));
return nodes
}
getNodeByKey(key, data) {
return this._getByKey(data ?? this._getDataBySelectionMode(), key)
}
getTreeNodes() {
const rootNodes = this.getRootNodes();
const rootKeys = rootNodes.map((node => node.internalFields.key));
return this.options.dataConverter.convertToPublicNodes(rootKeys, null)
}
getItemsCount() {
return this.options.dataConverter.getItemsCount()
}
getVisibleItemsCount() {
return this.options.dataConverter.getVisibleItemsCount()
}
getPublicNode(node) {
return null === node || void 0 === node ? void 0 : node.internalFields.publicNode
}
getRootNodes() {
return this.getChildrenNodes(this.options.rootValue)
}
getChildrenNodes(parentKey) {
return (0, _query.default)(this._dataStructure, {
langParams: this.options.langParams
}).filter(["internalFields.parentKey", parentKey]).toArray()
}
getIndexByKey(key) {
return this.options.dataConverter.getIndexByKey(key)
}
addItem(item) {
this._addNode(item);
this._updateFields()
}
removeItem(key) {
this._removeNode(key);
this._updateFields()
}
toggleSelection(key, state, selectRecursive) {
const isSingleModeUnselect = this._isSingleModeUnselect(state);
const dataArray = selectRecursive || isSingleModeUnselect ? this._initialDataStructure : this._dataStructure;
const node = this._getByKey(dataArray, key);
if (node) {
this._setFieldState(node, SELECTED, state);
if (this.options.recursiveSelection && !selectRecursive) {
if (state) {
this._setChildrenSelection()
} else {
this._toggleChildrenSelection(node, state)
}
this._setParentSelection()
}
}
this._selectedNodesKeys = this._updateNodesKeysArray(SELECTED)
}
_isSingleModeUnselect(selectionState) {
return !this.options.multipleSelection && !selectionState
}
toggleNodeDisabledState(key, state) {
const node = this.getNodeByKey(key);
if (node) {
this._setFieldState(node, DISABLED, state)
}
}
toggleSelectAll(state) {
if (!(0, _type.isDefined)(state)) {
return
}
const lastSelectedKey = this._selectedNodesKeys[this._selectedNodesKeys.length - 1];
const dataStructure = this._isSingleModeUnselect(state) ? this._initialDataStructure : this._dataStructure;
(0, _iterator.each)(dataStructure, ((_index, node) => {
if (node && this._isNodeVisible(node)) {
this._setFieldState(node, SELECTED, state)
}
}));
this._selectedNodesKeys = this._updateNodesKeysArray(SELECTED);
if (!state && this.options.selectionRequired) {
this.toggleSelection(lastSelectedKey, true)
}
}
isAllSelected() {
if (this.getSelectedNodesKeys().length) {
return this.getSelectedNodesKeys().length === this.getVisibleItemsCount() ? true : void 0
}
return false
}
toggleExpansion(key, state) {
const node = this.getNodeByKey(key);
if (node) {
this._setFieldState(node, EXPANDED, state);
if (state) {
this._updateExpansion(key)
}
}
this._expandedNodesKeys = this._updateNodesKeysArray(EXPANDED)
}
isFiltered(item) {
return !this.options.searchValue.length || !!this._filterDataStructure(this.options.searchValue, [item]).length
}
static _createCriteria(selector, value, operation) {
const searchFilter = [];
if (!Array.isArray(selector)) {
return [selector, operation, value]
}(0, _iterator.each)(selector, ((_index, item) => {
searchFilter.push([item, operation, value], "or")
}));
searchFilter.pop();
return searchFilter
}
_filterDataStructure(filterValue, dataStructure) {
const selector = this.options.searchExpr ?? this.options.dataAccessors.getters.display;
const operation = (0, _search_box_controller.getOperationBySearchMode)(this.options.searchMode);
const criteria = DataAdapter._createCriteria(selector, filterValue, operation);
const data = dataStructure ?? this._initialDataStructure;
return (0, _query.default)(data, {
langParams: this.options.langParams
}).filter(criteria).toArray()
}
search(searchValue) {
let matches = this._filterDataStructure(searchValue);
const {
dataConverter: dataConverter
} = this.options;
const lookForParents = (matchesArray, startIndex) => {
const {
length: length
} = matchesArray;
let index = startIndex;
while (index < length) {
const node = matchesArray[index];
if (node.internalFields.parentKey === this.options.rootValue) {
index += 1
} else {
const parent = dataConverter.getParentNode(node);
if (!parent) {
_ui.default.log("W1007", node.internalFields.parentKey, node.internalFields.key);
index += 1
} else {
if (!parent.internalFields.expanded) {
this._setFieldState(parent, EXPANDED, true)
}
if (matchesArray.includes(parent)) {
index += 1
} else {
matchesArray.splice(index, 0, parent);
lookForParents(matchesArray, index);
return
}
}
}
}
};
lookForParents(matches, 0);
if (this.options.sort) {
matches = _store_helper.default.queryByOptions((0, _query.default)(matches), {
sort: this.options.sort,
langParams: this.options.langParams
}).toArray()
}
dataConverter._indexByKey = {};
(0, _iterator.each)(matches, ((index, node) => {
node.internalFields.childrenKeys = [];
dataConverter._indexByKey[node.internalFields.key] = index
}));
dataConverter._dataStructure = matches;
dataConverter.setChildrenKeys();
return dataConverter._dataStructure
}
}
var _default = exports.default = DataAdapter;