From 35482e7a584c16c0c24b07606fd1fe4238764eb2 Mon Sep 17 00:00:00 2001 From: Alyar Date: Mon, 19 Feb 2024 18:39:42 +0400 Subject: [PATCH] GridCore: native classes - dataSourceAdapter (#26701) Co-authored-by: Alyar <> --- .../grids/data_grid/grouping/m_grouping.ts | 271 ++-- .../grids/data_grid/m_data_source_adapter.ts | 7 +- .../module_not_extended/virtual_scrolling.ts | 6 +- .../grids/data_grid/summary/m_summary.ts | 406 ++--- .../m_data_source_adapter.ts | 1409 ++++++++--------- .../m_data_source_adapter_utils.ts | 210 +++ .../js/__internal/grids/grid_core/m_types.ts | 19 +- .../virtual_scrolling/m_virtual_scrolling.ts | 597 ++++--- .../m_data_source_adapter.ts | 1312 +++++++-------- .../grids/tree_list/m_state_storing.ts | 1 - .../grids/tree_list/m_virtual_scrolling.ts | 18 +- 11 files changed, 2272 insertions(+), 1984 deletions(-) create mode 100644 packages/devextreme/js/__internal/grids/grid_core/data_source_adapter/m_data_source_adapter_utils.ts diff --git a/packages/devextreme/js/__internal/grids/data_grid/grouping/m_grouping.ts b/packages/devextreme/js/__internal/grids/data_grid/grouping/m_grouping.ts index bd8587da15f0..f355e2314e08 100644 --- a/packages/devextreme/js/__internal/grids/data_grid/grouping/m_grouping.ts +++ b/packages/devextreme/js/__internal/grids/data_grid/grouping/m_grouping.ts @@ -7,10 +7,12 @@ import { getHeight } from '@js/core/utils/size'; import { isDefined, isString } from '@js/core/utils/type'; import messageLocalization from '@js/localization/message'; import { restoreFocus, setTabIndex } from '@js/ui/shared/accessibility'; +import type DataSourceAdapter from '@ts/grids/grid_core/data_source_adapter/m_data_source_adapter'; import { registerKeyboardAction } from '@ts/grids/grid_core/m_accessibility'; +import type { ModuleType } from '@ts/grids/grid_core/m_types'; import gridCore from '../m_core'; -import dataSourceAdapter from '../m_data_source_adapter'; +import dataSourceAdapterProvider from '../m_data_source_adapter'; import { GroupingHelper as CollapsedGroupingHelper } from './m_grouping_collapsed'; import { GroupingHelper as ExpandedGroupingHelper } from './m_grouping_expanded'; @@ -28,148 +30,165 @@ export interface GroupingDataControllerExtension { changeRowExpand(key, isRowClick?): any; } -const GroupingDataSourceAdapterExtender = (function () { - return { - init() { - this.callBase.apply(this, arguments); - this._initGroupingHelper(); - }, - _initGroupingHelper(options) { - const grouping = this._grouping; - const isAutoExpandAll = this.option('grouping.autoExpandAll'); - const isFocusedRowEnabled = this.option('focusedRowEnabled'); - const remoteOperations = options ? options.remoteOperations : this.remoteOperations(); - const isODataRemoteOperations = remoteOperations.filtering && remoteOperations.sorting && remoteOperations.paging; - - if (isODataRemoteOperations && !remoteOperations.grouping && (isAutoExpandAll || !isFocusedRowEnabled)) { - if (!grouping || grouping instanceof CollapsedGroupingHelper) { - this._grouping = new ExpandedGroupingHelper(this); - } - } else if (!grouping || grouping instanceof ExpandedGroupingHelper) { - this._grouping = new CollapsedGroupingHelper(this); - } - }, - totalItemsCount() { - const that = this; - const totalCount = that.callBase(); +const dataSourceAdapterExtender = (Base: ModuleType) => class GroupingDataSourceAdapterExtender extends Base { + _grouping: any; - return totalCount > 0 && that._dataSource.group() && that._dataSource.requireTotalCount() ? totalCount + that._grouping.totalCountCorrection() : totalCount; - }, - itemsCount() { - return this._dataSource.group() ? this._grouping.itemsCount() || 0 : this.callBase.apply(this, arguments); - }, - allowCollapseAll() { - return this._grouping.allowCollapseAll(); - }, - isGroupItemCountable(item) { - return this._grouping.isGroupItemCountable(item); - }, - isRowExpanded(key) { - const groupInfo = this._grouping.findGroupInfo(key); - return groupInfo ? groupInfo.isExpanded : !this._grouping.allowCollapseAll(); - }, - collapseAll(groupIndex) { - return this._collapseExpandAll(groupIndex, false); - }, - expandAll(groupIndex) { - return this._collapseExpandAll(groupIndex, true); - }, - _collapseExpandAll(groupIndex, isExpand) { - const that = this; - const dataSource = that._dataSource; - const group = dataSource.group(); - const groups = gridCore.normalizeSortingInfo(group || []); - - if (groups.length) { - for (let i = 0; i < groups.length; i++) { - if (groupIndex === undefined || groupIndex === i) { - groups[i].isExpanded = isExpand; - } else if (group && group[i]) { - groups[i].isExpanded = group[i].isExpanded; - } - } - dataSource.group(groups); - that._grouping.foreachGroups((groupInfo, parents) => { - if (groupIndex === undefined || groupIndex === parents.length - 1) { - groupInfo.isExpanded = isExpand; - } - }, false, true); + init() { + super.init.apply(this, arguments as any); + this._initGroupingHelper(); + } - that.resetPagesCache(); + _initGroupingHelper(options?) { + const grouping = this._grouping; + const isAutoExpandAll = this.option('grouping.autoExpandAll'); + const isFocusedRowEnabled = this.option('focusedRowEnabled'); + const remoteOperations = options ? options.remoteOperations : this.remoteOperations(); + const isODataRemoteOperations = remoteOperations.filtering && remoteOperations.sorting && remoteOperations.paging; + + if (isODataRemoteOperations && !remoteOperations.grouping && (isAutoExpandAll || !isFocusedRowEnabled)) { + if (!grouping || grouping instanceof CollapsedGroupingHelper) { + this._grouping = new ExpandedGroupingHelper(this); } - return true; - }, - refresh() { - this.callBase.apply(this, arguments); + } else if (!grouping || grouping instanceof ExpandedGroupingHelper) { + this._grouping = new CollapsedGroupingHelper(this); + } + } - return this._grouping.refresh.apply(this._grouping, arguments); - }, - changeRowExpand(path) { - const that = this; - const dataSource = that._dataSource; + totalItemsCount() { + const totalCount = super.totalItemsCount(); - if (dataSource.group()) { - dataSource.beginLoading(); - if (that._lastLoadOptions) { - that._lastLoadOptions.groupExpand = true; + return totalCount > 0 && this._dataSource.group() && this._dataSource.requireTotalCount() ? totalCount + this._grouping.totalCountCorrection() : totalCount; + } + + itemsCount() { + return this._dataSource.group() ? this._grouping.itemsCount() || 0 : super.itemsCount.apply(this, arguments as any); + } + + allowCollapseAll() { + return this._grouping.allowCollapseAll(); + } + + isGroupItemCountable(item) { + return this._grouping.isGroupItemCountable(item); + } + + isRowExpanded(key) { + const groupInfo = this._grouping.findGroupInfo(key); + return groupInfo ? groupInfo.isExpanded : !this._grouping.allowCollapseAll(); + } + + collapseAll(groupIndex) { + return this._collapseExpandAll(groupIndex, false); + } + + expandAll(groupIndex) { + return this._collapseExpandAll(groupIndex, true); + } + + _collapseExpandAll(groupIndex, isExpand) { + const that = this; + const dataSource = that._dataSource; + const group = dataSource.group(); + const groups = gridCore.normalizeSortingInfo(group || []); + + if (groups.length) { + for (let i = 0; i < groups.length; i++) { + if (groupIndex === undefined || groupIndex === i) { + groups[i].isExpanded = isExpand; + } else if (group && group[i]) { + groups[i].isExpanded = group[i].isExpanded; } - return that._changeRowExpandCore(path).always(() => { - dataSource.endLoading(); - }); } - }, - _changeRowExpandCore(path) { - return this._grouping.changeRowExpand(path); - }, - /// #DEBUG - getGroupsInfo() { - return this._grouping._groupsInfo; - }, - /// #ENDDEBUG - // @ts-expect-error - _hasGroupLevelsExpandState(group, isExpanded) { - if (group && Array.isArray(group)) { - for (let i = 0; i < group.length; i++) { - if (group[i].isExpanded === isExpanded) { - return true; - } + dataSource.group(groups); + that._grouping.foreachGroups((groupInfo, parents) => { + if (groupIndex === undefined || groupIndex === parents.length - 1) { + groupInfo.isExpanded = isExpand; } + }, false, true); + + that.resetPagesCache(); + } + return true; + } + + refresh() { + super.refresh.apply(this, arguments as any); + + return this._grouping.refresh.apply(this._grouping, arguments); + } + + changeRowExpand(path) { + const that = this; + const dataSource = that._dataSource; + + if (dataSource.group()) { + dataSource.beginLoading(); + if (that._lastLoadOptions) { + that._lastLoadOptions.groupExpand = true; } - }, - _customizeRemoteOperations(options, operationTypes) { - const { remoteOperations } = options; + return that._changeRowExpandCore(path).always(() => { + dataSource.endLoading(); + }); + } + } - if (options.storeLoadOptions.group) { - if (remoteOperations.grouping && !options.isCustomLoading) { - if (!remoteOperations.groupPaging || this._hasGroupLevelsExpandState(options.storeLoadOptions.group, true)) { - remoteOperations.paging = false; - } + _changeRowExpandCore(path) { + return this._grouping.changeRowExpand(path); + } + + /// #DEBUG + getGroupsInfo() { + return this._grouping._groupsInfo; + } + + /// #ENDDEBUG + // @ts-expect-error + _hasGroupLevelsExpandState(group, isExpanded) { + if (group && Array.isArray(group)) { + for (let i = 0; i < group.length; i++) { + if (group[i].isExpanded === isExpanded) { + return true; } + } + } + } - if (!remoteOperations.grouping && (!remoteOperations.sorting || !remoteOperations.filtering || options.isCustomLoading || this._hasGroupLevelsExpandState(options.storeLoadOptions.group, false))) { + _customizeRemoteOperations(options, operationTypes) { + const { remoteOperations } = options; + + if (options.storeLoadOptions.group) { + if (remoteOperations.grouping && !options.isCustomLoading) { + if (!remoteOperations.groupPaging || this._hasGroupLevelsExpandState(options.storeLoadOptions.group, true)) { remoteOperations.paging = false; } - } else if (!options.isCustomLoading && remoteOperations.paging && operationTypes.grouping) { - this.resetCache(); } - this.callBase.apply(this, arguments); - }, - _handleDataLoading(options) { - this.callBase(options); - this._initGroupingHelper(options); - return this._grouping.handleDataLoading(options); - }, - _handleDataLoaded(options) { - return this._grouping.handleDataLoaded(options, this.callBase.bind(this)); - }, - _handleDataLoadedCore(options) { - return this._grouping.handleDataLoadedCore(options, this.callBase.bind(this)); - }, - }; -}()); + if (!remoteOperations.grouping && (!remoteOperations.sorting || !remoteOperations.filtering || options.isCustomLoading || this._hasGroupLevelsExpandState(options.storeLoadOptions.group, false))) { + remoteOperations.paging = false; + } + } else if (!options.isCustomLoading && remoteOperations.paging && operationTypes.grouping) { + this.resetCache(); + } + + super._customizeRemoteOperations.apply(this, arguments as any); + } + + _handleDataLoading(options) { + super._handleDataLoading(options); + this._initGroupingHelper(options); + return this._grouping.handleDataLoading(options); + } + + _handleDataLoaded(options) { + return this._grouping.handleDataLoaded(options, super._handleDataLoaded.bind(this)); + } + + _handleDataLoadedCore(options) { + return this._grouping.handleDataLoadedCore(options, super._handleDataLoadedCore.bind(this)); + } +}; -dataSourceAdapter.extend(GroupingDataSourceAdapterExtender); +dataSourceAdapterProvider.extend(dataSourceAdapterExtender); const GroupingDataControllerExtender = (function () { return { diff --git a/packages/devextreme/js/__internal/grids/data_grid/m_data_source_adapter.ts b/packages/devextreme/js/__internal/grids/data_grid/m_data_source_adapter.ts index 8d7d21414aa4..9eca57f9c1d0 100644 --- a/packages/devextreme/js/__internal/grids/data_grid/m_data_source_adapter.ts +++ b/packages/devextreme/js/__internal/grids/data_grid/m_data_source_adapter.ts @@ -1,13 +1,12 @@ import DataSourceAdapter from '@ts/grids/grid_core/data_source_adapter/m_data_source_adapter'; -let dataSourceAdapterType: any = DataSourceAdapter; +let DataSourceAdapterType: any = DataSourceAdapter; export default { extend(extender) { - dataSourceAdapterType = dataSourceAdapterType.inherit(extender); + DataSourceAdapterType = extender(DataSourceAdapterType); }, create(component) { - // eslint-disable-next-line new-cap - return new dataSourceAdapterType(component); + return new DataSourceAdapterType(component); }, }; diff --git a/packages/devextreme/js/__internal/grids/data_grid/module_not_extended/virtual_scrolling.ts b/packages/devextreme/js/__internal/grids/data_grid/module_not_extended/virtual_scrolling.ts index 33e089ed8e1b..8079ccc4351f 100644 --- a/packages/devextreme/js/__internal/grids/data_grid/module_not_extended/virtual_scrolling.ts +++ b/packages/devextreme/js/__internal/grids/data_grid/module_not_extended/virtual_scrolling.ts @@ -1,8 +1,8 @@ -import { virtualScrollingModule } from '@ts/grids/grid_core/virtual_scrolling/m_virtual_scrolling'; +import { dataSourceAdapterExtender, virtualScrollingModule } from '@ts/grids/grid_core/virtual_scrolling/m_virtual_scrolling'; import gridCore from '../m_core'; -import dataSourceAdapter from '../m_data_source_adapter'; +import dataSourceAdapterProvider from '../m_data_source_adapter'; gridCore.registerModule('virtualScrolling', virtualScrollingModule); -dataSourceAdapter.extend(virtualScrollingModule.extenders.dataSourceAdapter); +dataSourceAdapterProvider.extend(dataSourceAdapterExtender); diff --git a/packages/devextreme/js/__internal/grids/data_grid/summary/m_summary.ts b/packages/devextreme/js/__internal/grids/data_grid/summary/m_summary.ts index 59968149dd02..b2cb2551e9cf 100644 --- a/packages/devextreme/js/__internal/grids/data_grid/summary/m_summary.ts +++ b/packages/devextreme/js/__internal/grids/data_grid/summary/m_summary.ts @@ -13,11 +13,13 @@ import storeHelper from '@js/data/store_helper'; import { normalizeSortingInfo } from '@js/data/utils'; import messageLocalization from '@js/localization/message'; import errors from '@js/ui/widget/ui.errors'; +import type DataSourceAdapter from '@ts/grids/grid_core/data_source_adapter/m_data_source_adapter'; +import type { ModuleType } from '@ts/grids/grid_core/m_types'; import { ColumnsView } from '@ts/grids/grid_core/views/m_columns_view'; import AggregateCalculator from '../m_aggregate_calculator'; import gridCore from '../m_core'; -import dataSourceAdapter from '../m_data_source_adapter'; +import dataSourceAdapterProvider from '../m_data_source_adapter'; const DATAGRID_TOTAL_FOOTER_CLASS = 'dx-datagrid-total-footer'; const DATAGRID_SUMMARY_ITEM_CLASS = 'dx-datagrid-summary-item'; @@ -73,6 +75,119 @@ const recalculateWhileEditing = function (that) { return that.option('summary.recalculateWhileEditing'); }; +const forEachGroup = function (groups, groupCount, callback, path?) { + path = path || []; + for (let i = 0; i < groups.length; i++) { + path.push(groups[i].key); + if (groupCount === 1) { + callback(path, groups[i].items); + } else { + forEachGroup(groups[i].items, groupCount - 1, callback, path); + } + path.pop(); + } +}; + +const applyAddedData = function (data, insertedData, groupLevel?) { + if (groupLevel) { + return applyAddedData(data, insertedData.map((item) => ({ items: [item] }), groupLevel - 1)); + } + + return data.concat(insertedData); +}; + +const applyRemovedData = function (data, removedData, groupLevel) { + if (groupLevel) { + return data.map((data) => { + const updatedData = {}; + const updatedItems = applyRemovedData(data.items || [], removedData, groupLevel - 1); + + Object.defineProperty(updatedData, 'aggregates', { + get: () => data.aggregates, + set: (value) => { + data.aggregates = value; + }, + }); + + return extend(updatedData, data, { items: updatedItems }); + }); + } + + return data.filter((data) => removedData.indexOf(data) < 0); +}; + +const sortGroupsBySummaryCore = function (items, groups, sortByGroups) { + if (!items || !groups.length) return items; + + const group = groups[0]; + const sorts = sortByGroups[0]; + let query; + + if (group && sorts && sorts.length) { + query = dataQuery(items); + each(sorts, function (index) { + if (index === 0) { + query = query.sortBy(this.selector, this.desc); + } else { + query = query.thenBy(this.selector, this.desc); + } + }); + query.enumerate().done((sortedItems) => { + items = sortedItems; + }); + } + + groups = groups.slice(1); + sortByGroups = sortByGroups.slice(1); + if (groups.length && sortByGroups.length) { + each(items, function () { + this.items = sortGroupsBySummaryCore(this.items, groups, sortByGroups); + }); + } + + return items; +}; + +const sortGroupsBySummary = function (data, group, summary) { + const sortByGroups = summary && summary.sortByGroups && summary.sortByGroups(); + + if (sortByGroups && sortByGroups.length) { + return sortGroupsBySummaryCore(data, group, sortByGroups); + } + return data; +}; + +const calculateAggregates = function (that, summary, data, groupLevel) { + let calculator; + + if (recalculateWhileEditing(that)) { + const editingController = that.getController('editing'); + if (editingController) { + const insertedData = editingController.getInsertedData(); + if (insertedData.length) { + data = applyAddedData(data, insertedData, groupLevel); + } + + const removedData = editingController.getRemovedData(); + if (removedData.length) { + data = applyRemovedData(data, removedData, groupLevel); + } + } + } + + if (summary) { + calculator = new AggregateCalculator({ + totalAggregates: summary.totalAggregates, + groupAggregates: summary.groupAggregates, + data, + groupLevel, + }); + + calculator.calculate(); + } + return calculator ? calculator.totalAggregates() : []; +}; + export const FooterView = ColumnsView.inherit((function () { return { _getRows() { @@ -170,237 +285,126 @@ export const FooterView = ColumnsView.inherit((function () { }; })()); -const SummaryDataSourceAdapterExtender = (function () { - function forEachGroup(groups, groupCount, callback, path?) { - path = path || []; - for (let i = 0; i < groups.length; i++) { - path.push(groups[i].key); - if (groupCount === 1) { - callback(path, groups[i].items); - } else { - forEachGroup(groups[i].items, groupCount - 1, callback, path); - } - path.pop(); - } - } - - return { - init() { - this.callBase.apply(this, arguments); - this._totalAggregates = []; - this._summaryGetter = noop; - }, - summaryGetter(summaryGetter) { - if (!arguments.length) { - return this._summaryGetter; - } +const dataSourceAdapterExtender = (Base: ModuleType) => class SummaryDataSourceAdapterExtender extends Base { + _totalAggregates: any; - if (isFunction(summaryGetter)) { - this._summaryGetter = summaryGetter; - } - }, - summary(summary) { - if (!arguments.length) { - return this._summaryGetter(); - } - - this._summaryGetter = function () { return summary; }; - }, - totalAggregates() { - return this._totalAggregates; - }, - isLastLevelGroupItemsPagingLocal() { - const summary = this.summary(); - const sortByGroupsInfo = summary && summary.sortByGroups(); - - return sortByGroupsInfo && sortByGroupsInfo.length; - }, - sortLastLevelGroupItems(items, groups, paths) { - const groupedItems = storeHelper.multiLevelGroup(dataQuery(items), groups).toArray(); - let result = []; - - paths.forEach((path) => { - forEachGroup(groupedItems, groups.length, (itemsPath, items) => { - if (path.toString() === itemsPath.toString()) { - result = result.concat(items); - } - }); - }); + _summaryGetter: any; - return result; - }, - }; -}()); + init() { + super.init.apply(this, arguments as any); + this._totalAggregates = []; + this._summaryGetter = noop; + } -const SummaryDataSourceAdapterClientExtender = (function () { - const applyAddedData = function (data, insertedData, groupLevel?) { - if (groupLevel) { - return applyAddedData(data, insertedData.map((item) => ({ items: [item] }), groupLevel - 1)); + summaryGetter(summaryGetter?) { + if (!arguments.length) { + return this._summaryGetter; } - return data.concat(insertedData); - }; - - const applyRemovedData = function (data, removedData, groupLevel) { - if (groupLevel) { - return data.map((data) => { - const updatedData = {}; - const updatedItems = applyRemovedData(data.items || [], removedData, groupLevel - 1); - - Object.defineProperty(updatedData, 'aggregates', { - get: () => data.aggregates, - set: (value) => { - data.aggregates = value; - }, - }); - - return extend(updatedData, data, { items: updatedItems }); - }); + if (isFunction(summaryGetter)) { + this._summaryGetter = summaryGetter; } + } - return data.filter((data) => removedData.indexOf(data) < 0); - }; - - const calculateAggregates = function (that, summary, data, groupLevel) { - let calculator; - - if (recalculateWhileEditing(that)) { - const editingController = that.getController('editing'); - if (editingController) { - const insertedData = editingController.getInsertedData(); - if (insertedData.length) { - data = applyAddedData(data, insertedData, groupLevel); - } - - const removedData = editingController.getRemovedData(); - if (removedData.length) { - data = applyRemovedData(data, removedData, groupLevel); - } - } + summary(summary?) { + if (!arguments.length) { + return this._summaryGetter(); } - if (summary) { - calculator = new AggregateCalculator({ - totalAggregates: summary.totalAggregates, - groupAggregates: summary.groupAggregates, - data, - groupLevel, - }); + this._summaryGetter = function () { return summary; }; + } - calculator.calculate(); - } - return calculator ? calculator.totalAggregates() : []; - }; + totalAggregates() { + return this._totalAggregates; + } - const sortGroupsBySummaryCore = function (items, groups, sortByGroups) { - if (!items || !groups.length) return items; + isLastLevelGroupItemsPagingLocal() { + const summary = this.summary(); + const sortByGroupsInfo = summary?.sortByGroups(); - const group = groups[0]; - const sorts = sortByGroups[0]; - let query; + return sortByGroupsInfo?.length; + } - if (group && sorts && sorts.length) { - query = dataQuery(items); - each(sorts, function (index) { - if (index === 0) { - query = query.sortBy(this.selector, this.desc); - } else { - query = query.thenBy(this.selector, this.desc); - } - }); - query.enumerate().done((sortedItems) => { - items = sortedItems; - }); - } + sortLastLevelGroupItems(items, groups, paths) { + const groupedItems = storeHelper.multiLevelGroup(dataQuery(items), groups).toArray(); + let result = []; - groups = groups.slice(1); - sortByGroups = sortByGroups.slice(1); - if (groups.length && sortByGroups.length) { - each(items, function () { - this.items = sortGroupsBySummaryCore(this.items, groups, sortByGroups); + paths.forEach((path) => { + forEachGroup(groupedItems, groups.length, (itemsPath, items) => { + if (path.toString() === itemsPath.toString()) { + result = result.concat(items); + } }); - } - - return items; - }; + }); - const sortGroupsBySummary = function (data, group, summary) { - const sortByGroups = summary && summary.sortByGroups && summary.sortByGroups(); + return result; + } - if (sortByGroups && sortByGroups.length) { - return sortGroupsBySummaryCore(data, group, sortByGroups); - } - return data; - }; + _customizeRemoteOperations(options) { + const summary = this.summary(); - return { - _customizeRemoteOperations(options) { - const summary = this.summary(); - - if (summary) { - if (options.remoteOperations.summary) { - if (!options.isCustomLoading || options.storeLoadOptions.isLoadingAll) { - if (options.storeLoadOptions.group) { - if (options.remoteOperations.grouping) { - options.storeLoadOptions.groupSummary = summary.groupAggregates; - } else if (summary.groupAggregates.length) { - options.remoteOperations.paging = false; - } + if (summary) { + if (options.remoteOperations.summary) { + if (!options.isCustomLoading || options.storeLoadOptions.isLoadingAll) { + if (options.storeLoadOptions.group) { + if (options.remoteOperations.grouping) { + options.storeLoadOptions.groupSummary = summary.groupAggregates; + } else if (summary.groupAggregates.length) { + options.remoteOperations.paging = false; } - options.storeLoadOptions.totalSummary = summary.totalAggregates; } - } else if (summary.totalAggregates.length || (summary.groupAggregates.length && options.storeLoadOptions.group)) { - options.remoteOperations.paging = false; + options.storeLoadOptions.totalSummary = summary.totalAggregates; } + } else if (summary.totalAggregates.length || (summary.groupAggregates.length && options.storeLoadOptions.group)) { + options.remoteOperations.paging = false; } - this.callBase.apply(this, arguments); + } + super._customizeRemoteOperations.apply(this, arguments as any); - const cachedExtra = options.cachedData.extra; + const cachedExtra = options.cachedData.extra; - if (cachedExtra && cachedExtra.summary && !options.isCustomLoading) { - options.storeLoadOptions.totalSummary = undefined; - } - }, - _handleDataLoadedCore(options) { - const that = this; - const groups = normalizeSortingInfo(options.storeLoadOptions.group || options.loadOptions.group || []); - const remoteOperations = options.remoteOperations || {}; - const summary = that.summaryGetter()(remoteOperations); - - if (!options.isCustomLoading || options.storeLoadOptions.isLoadingAll) { - if (remoteOperations.summary) { - if (!remoteOperations.paging && groups.length && summary) { - if (!remoteOperations.grouping) { - calculateAggregates(that, { groupAggregates: summary.groupAggregates }, options.data, groups.length); - } - options.data = sortGroupsBySummary(options.data, groups, summary); - } - } else if (!remoteOperations.paging && summary) { - const operationTypes = options.operationTypes || {}; - const hasOperations = Object.keys(operationTypes).some((type) => operationTypes[type]); - if (!hasOperations || !options.cachedData?.extra?.summary || groups.length && summary.groupAggregates.length) { - const totalAggregates = calculateAggregates(that, summary, options.data, groups.length); - options.extra = isPlainObject(options.extra) ? options.extra : {}; - options.extra.summary = totalAggregates; - if (options.cachedData) { - options.cachedData.extra = options.extra; - } + if (cachedExtra?.summary && !options.isCustomLoading) { + options.storeLoadOptions.totalSummary = undefined; + } + } + + _handleDataLoadedCore(options) { + const groups = normalizeSortingInfo(options.storeLoadOptions.group || options.loadOptions.group || []); + const remoteOperations = options.remoteOperations || {}; + const summary = this.summaryGetter()(remoteOperations); + + if (!options.isCustomLoading || options.storeLoadOptions.isLoadingAll) { + if (remoteOperations.summary) { + if (!remoteOperations.paging && groups.length && summary) { + if (!remoteOperations.grouping) { + calculateAggregates(this, { groupAggregates: summary.groupAggregates }, options.data, groups.length); } options.data = sortGroupsBySummary(options.data, groups, summary); } + } else if (!remoteOperations.paging && summary) { + const operationTypes = options.operationTypes || {}; + const hasOperations = Object.keys(operationTypes).some((type) => operationTypes[type]); + if (!hasOperations || !options.cachedData?.extra?.summary || groups.length && summary.groupAggregates.length) { + const totalAggregates = calculateAggregates(this, summary, options.data, groups.length); + options.extra = isPlainObject(options.extra) ? options.extra : {}; + options.extra.summary = totalAggregates; + if (options.cachedData) { + options.cachedData.extra = options.extra; + } + } + options.data = sortGroupsBySummary(options.data, groups, summary); } + } - if (!options.isCustomLoading) { - that._totalAggregates = options.extra && options.extra.summary || that._totalAggregates; - } + if (!options.isCustomLoading) { + this._totalAggregates = options.extra && options.extra.summary || this._totalAggregates; + } - that.callBase(options); - }, - }; -}()); + super._handleDataLoadedCore(options); + } +}; -dataSourceAdapter.extend(SummaryDataSourceAdapterExtender); -dataSourceAdapter.extend(SummaryDataSourceAdapterClientExtender); +dataSourceAdapterProvider.extend(dataSourceAdapterExtender); gridCore.registerModule('summary', { defaultOptions() { diff --git a/packages/devextreme/js/__internal/grids/grid_core/data_source_adapter/m_data_source_adapter.ts b/packages/devextreme/js/__internal/grids/grid_core/data_source_adapter/m_data_source_adapter.ts index bd85671f1dc8..4810b3257205 100644 --- a/packages/devextreme/js/__internal/grids/grid_core/data_source_adapter/m_data_source_adapter.ts +++ b/packages/devextreme/js/__internal/grids/grid_core/data_source_adapter/m_data_source_adapter.ts @@ -1,7 +1,7 @@ /* eslint-disable @typescript-eslint/no-dynamic-delete */ import Callbacks from '@js/core/utils/callbacks'; // @ts-expect-error -import { executeAsync, getKeyHash } from '@js/core/utils/common'; +import { getKeyHash } from '@js/core/utils/common'; import { Deferred, when } from '@js/core/utils/deferred'; import { extend } from '@js/core/utils/extend'; import { each } from '@js/core/utils/iterator'; @@ -11,856 +11,797 @@ import { applyBatch } from '@js/data/array_utils'; import modules from '../m_modules'; import gridCoreUtils from '../m_utils'; +import { + calculateOperationTypes, + cloneItems, + createEmptyCachedData, + executeTask, + getPageDataFromCache, + setPageDataToCache, +} from './m_data_source_adapter_utils'; -export default modules.Controller.inherit((function () { - function cloneItems(items, groupCount) { - if (items) { - items = items.slice(0); - if (groupCount) { - for (let i = 0; i < items.length; i++) { - items[i] = extend({ key: items[i].key }, items[i]); - items[i].items = cloneItems(items[i].items, groupCount - 1); - } +export default class DataSourceAdapter extends modules.Controller { + _dataSource: any; + + _remoteOperations: any; + + _isLastPage!: boolean; + + _hasLastPage!: boolean; + + _currentTotalCount: any; + + _items: any; + + _cachedData: any; + + _cachedStoreData: any; + + _cachedPagingData: any; + + _lastOperationTypes: any; + + _eventsStrategy: any; + + _totalCountCorrection: any; + + _isLoadingAll: any; + + _lastLoadOptions: any; + + _dataIndexGetter: any; + + _isRefreshing: any; + + _loadingOperationTypes: any; + + _isRefreshed: any; + + _lastOperationId: any; + + _operationTypes: any; + + _isCustomLoading: any; + + changed: any; + + loadingChanged: any; + + loadError: any; + + customizeStoreLoadOptions: any; + + changing: any; + + pushed: any; + + _dataChangedHandler!: (e: any) => any; + + _customizeStoreLoadOptionsHandler!: (e: any) => any; + + _dataLoadedHandler!: (e: any) => any; + + _loadingChangedHandler!: (e: any) => any; + + _loadErrorHandler!: (e: any) => any; + + _pushHandler!: (e: any) => any; + + _changingHandler!: (e: any) => any; + + store!: () => any; + + group!: (args?: any) => any; + + init(dataSource?, remoteOperations?) { + const that = this; + + that._dataSource = dataSource; + that._remoteOperations = remoteOperations || {}; + + that._isLastPage = !dataSource.isLastPage(); + that._hasLastPage = false; + that._currentTotalCount = 0; + that._cachedData = createEmptyCachedData(); + that._lastOperationTypes = {}; + that._eventsStrategy = dataSource._eventsStrategy; + that._totalCountCorrection = 0; + that._isLoadingAll = false; + + that.changed = Callbacks(); + that.loadingChanged = Callbacks(); + that.loadError = Callbacks(); + that.customizeStoreLoadOptions = Callbacks(); + that.changing = Callbacks(); + that.pushed = Callbacks(); + + that._dataChangedHandler = that._handleDataChanged.bind(that); + that._customizeStoreLoadOptionsHandler = that._handleCustomizeStoreLoadOptions.bind(that); + that._dataLoadedHandler = that._handleDataLoaded.bind(that); + that._loadingChangedHandler = that._handleLoadingChanged.bind(that); + that._loadErrorHandler = that._handleLoadError.bind(that); + that._pushHandler = that._handlePush.bind(that); + that._changingHandler = that._handleChanging.bind(that); + + dataSource.on('changed', that._dataChangedHandler); + dataSource.on('customizeStoreLoadOptions', that._customizeStoreLoadOptionsHandler); + dataSource.on('customizeLoadResult', that._dataLoadedHandler); + dataSource.on('loadingChanged', that._loadingChangedHandler); + dataSource.on('loadError', that._loadErrorHandler); + dataSource.on('changing', that._changingHandler); + dataSource.store().on('beforePush', that._pushHandler); + + each(dataSource, (memberName, member) => { + if (!that[memberName] && isFunction(member)) { + that[memberName] = function () { + return this._dataSource[memberName].apply(this._dataSource, arguments); + }; } + }); + } + + remoteOperations() { + return this._remoteOperations; + } + + dispose(isSharedDataSource?) { + const that = this; + const dataSource = that._dataSource; + const store = dataSource.store(); + + dataSource.off('changed', that._dataChangedHandler); + dataSource.off('customizeStoreLoadOptions', that._customizeStoreLoadOptionsHandler); + dataSource.off('customizeLoadResult', that._dataLoadedHandler); + dataSource.off('loadingChanged', that._loadingChangedHandler); + dataSource.off('loadError', that._loadErrorHandler); + dataSource.off('changing', that._changingHandler); + store && store.off('beforePush', that._pushHandler); + + if (!isSharedDataSource) { + dataSource.dispose(); } - return items; - } - - function calculateOperationTypes(loadOptions, lastLoadOptions, isFullReload?) { - let operationTypes: any = { reload: true, fullReload: true }; - - if (lastLoadOptions) { - operationTypes = { - sorting: !gridCoreUtils.equalSortParameters(loadOptions.sort, lastLoadOptions.sort), - grouping: !gridCoreUtils.equalSortParameters(loadOptions.group, lastLoadOptions.group, true), - groupExpanding: !gridCoreUtils.equalSortParameters(loadOptions.group, lastLoadOptions.group) || lastLoadOptions.groupExpand, - filtering: !gridCoreUtils.equalFilterParameters(loadOptions.filter, lastLoadOptions.filter), - pageIndex: loadOptions.pageIndex !== lastLoadOptions.pageIndex, - skip: loadOptions.skip !== lastLoadOptions.skip, - take: loadOptions.take !== lastLoadOptions.take, - pageSize: loadOptions.pageSize !== lastLoadOptions.pageSize, - fullReload: isFullReload, - reload: false, - paging: false, - }; + } - operationTypes.reload = isFullReload || operationTypes.sorting || operationTypes.grouping || operationTypes.filtering; - operationTypes.paging = operationTypes.pageIndex || operationTypes.pageSize || operationTypes.take; + refresh(options, operationTypes) { + const that = this; + const dataSource = that._dataSource; + + if (operationTypes.reload) { + that.resetCurrentTotalCount(); + that._isLastPage = !dataSource.paginate(); + that._hasLastPage = that._isLastPage; } + } - return operationTypes; + resetCurrentTotalCount() { + this._currentTotalCount = 0; + this._totalCountCorrection = 0; } - function executeTask(action, timeout) { - if (isDefined(timeout)) { - executeAsync(action, timeout); - } else { - action(); - } + resetCache() { + this._cachedStoreData = undefined; + this._cachedPagingData = undefined; } - function createEmptyCachedData() { - return { items: {} }; + // eslint-disable-next-line + resetPagesCache(isLiveUpdate?) { + this._cachedData = createEmptyCachedData(); } - function getPageDataFromCache(options, updatePaging?): any { - const groupCount = gridCoreUtils.normalizeSortingInfo(options.group || options.storeLoadOptions.group || options.loadOptions.group).length; - const items = []; - if (fillItemsFromCache(items, options, groupCount)) { - return items; - } if (updatePaging) { - updatePagingOptionsByCache(items, options, groupCount); - } + _needClearStoreDataCache() { + const remoteOperations = this.remoteOperations(); + const operationTypes = calculateOperationTypes(this._lastLoadOptions || {}, {}); + const isLocalOperations = Object.keys(remoteOperations).every((operationName) => !operationTypes[operationName] || !remoteOperations[operationName]); + + return !isLocalOperations; } - function fillItemsFromCache(items, options, groupCount, fromEnd?) { - const { storeLoadOptions } = options; - const take = options.take ?? storeLoadOptions.take ?? 0; - const cachedItems = options.cachedData?.items; - - if (take && cachedItems) { - const skip = options.skip ?? storeLoadOptions.skip ?? 0; - for (let i = 0; i < take; i++) { - const localIndex = fromEnd ? take - 1 - i : i; - const cacheItemIndex = localIndex + skip; - const cacheItem = cachedItems[cacheItemIndex]; - - if (cacheItem === undefined && cacheItemIndex in cachedItems) { - return true; - } + push(changes, fromStore) { + const store = this.store(); - const item = getItemFromCache(options, cacheItem, groupCount, localIndex, take); + if (this._needClearStoreDataCache()) { + this._cachedStoreData = undefined; + } - if (item) { - items.push(item); - } else { - return false; - } - } - return true; + this._cachedPagingData = undefined; + + this.resetPagesCache(true); + + if (this._cachedStoreData) { + // @ts-expect-error + applyBatch({ + keyInfo: store, + data: this._cachedStoreData, + changes, + }); } - return false; - } - function getItemFromCache(options, cacheItem, groupCount, index, take) { - if (groupCount && cacheItem) { - const skips = index === 0 && options.skips || []; - const takes = index === take - 1 && options.takes || []; - return getGroupItemFromCache(cacheItem, groupCount, skips, takes); + if (!fromStore) { + this._applyBatch(changes); } - return cacheItem; + + this.pushed.fire(changes); } - function getGroupItemFromCache(cacheItem, groupCount, skips, takes) { - if (groupCount && cacheItem) { - const result = { ...cacheItem }; - const skip = skips[0] || 0; - const take = takes[0]; - const { items } = cacheItem; + getDataIndexGetter() { + if (!this._dataIndexGetter) { + let indexByKey; + let storeData; + const store = this.store(); - if (items) { - if (take === undefined && !items[skip]) { - return; - } - result.items = []; - if (skips.length) { - result.isContinuation = true; + this._dataIndexGetter = (data) => { + const isCacheUpdated = storeData && storeData !== this._cachedStoreData; + if (!indexByKey || isCacheUpdated) { + storeData = this._cachedStoreData || []; + indexByKey = {}; + for (let i = 0; i < storeData.length; i++) { + indexByKey[getKeyHash(store.keyOf(storeData[i]))] = i; + } } + return indexByKey[getKeyHash(store.keyOf(data))]; + }; + } - if (take) { - result.isContinuationOnNextPage = cacheItem.count > take; - } + return this._dataIndexGetter; + } - for (let i = 0; take === undefined ? items[i + skip] : i < take; i++) { - const childCacheItem = items[i + skip]; - const isLast = i + 1 === take; - const item = getGroupItemFromCache(childCacheItem, groupCount - 1, i === 0 ? skips.slice(1) : [], isLast ? takes.slice(1) : []); + _getKeyInfo() { + return this.store(); + } - if (item !== undefined) { - result.items.push(item); - } else { - return; - } - } - } + _needToCopyDataObject() { + return true; + } - return result; + _applyBatch(changes, fromStore?) { + const keyInfo = this._getKeyInfo(); + const dataSource = this._dataSource; + const groupCount = gridCoreUtils.normalizeSortingInfo(this.group()).length; + const isReshapeMode = this.option('editing.refreshMode') === 'reshape'; + const isVirtualMode = this.option('scrolling.mode') === 'virtual'; + + changes = changes.filter((change) => !dataSource.paginate() || change.type !== 'insert' || change.index !== undefined); + + const getItemCount = () => (groupCount ? this.itemsCount() : this.items().length); + const oldItemCount = getItemCount(); + + // @ts-expect-error + applyBatch({ + keyInfo, + data: this._items, + changes, + groupCount, + useInsertIndex: true, + skipCopying: !this._needToCopyDataObject(), + }); + // @ts-expect-error + applyBatch({ + keyInfo, + data: dataSource.items(), + changes, + groupCount, + useInsertIndex: true, + skipCopying: !this._needToCopyDataObject(), + }); + + const needUpdateTotalCountCorrection = this._currentTotalCount > 0 || ( + (fromStore || !isReshapeMode) + && isVirtualMode + ); + + if (needUpdateTotalCountCorrection) { + this._totalCountCorrection += getItemCount() - oldItemCount; } - return cacheItem; + changes.splice(0, changes.length); } - function updatePagingOptionsByCache(cacheItemsFromBegin, options, groupCount) { - const cacheItemBeginCount = cacheItemsFromBegin.length; - const { storeLoadOptions } = options; - if (storeLoadOptions.skip !== undefined && storeLoadOptions.take && !groupCount) { - const cacheItemsFromEnd = []; - fillItemsFromCache(cacheItemsFromEnd, options, groupCount, true); - const cacheItemEndCount = cacheItemsFromEnd.length; - - if (cacheItemBeginCount || cacheItemEndCount) { - options.skip = options.skip ?? storeLoadOptions.skip; - options.take = options.take ?? storeLoadOptions.take; - } + _handlePush({ changes }) { + this.push(changes, true); + } - if (cacheItemBeginCount) { - storeLoadOptions.skip += cacheItemBeginCount; - storeLoadOptions.take -= cacheItemBeginCount; - options.cachedDataPartBegin = cacheItemsFromBegin; - } + _handleChanging(e) { + this.changing.fire(e); + this._applyBatch(e.changes, true); + } - if (cacheItemEndCount) { - storeLoadOptions.take -= cacheItemEndCount; - options.cachedDataPartEnd = cacheItemsFromEnd.reverse(); + _needCleanCacheByOperation(operationType, remoteOperations) { + const operationTypesByOrder = ['filtering', 'sorting', 'paging']; + const operationTypeIndex = operationTypesByOrder.indexOf(operationType); + const currentOperationTypes = operationTypeIndex >= 0 ? operationTypesByOrder.slice(operationTypeIndex) : [operationType]; + + return currentOperationTypes.some((operationType) => remoteOperations[operationType]); + } + + _customizeRemoteOperations(options, operationTypes) { + let cachedStoreData = this._cachedStoreData; + let cachedPagingData = this._cachedPagingData; + let cachedData = this._cachedData; + + if ((options.storeLoadOptions.filter && !options.remoteOperations.filtering) || (options.storeLoadOptions.sort && !options.remoteOperations.sorting)) { + options.remoteOperations = { + filtering: options.remoteOperations.filtering, + summary: options.remoteOperations.summary, + }; + } + + if (operationTypes.fullReload) { + cachedStoreData = undefined; + cachedPagingData = undefined; + cachedData = createEmptyCachedData(); + } else { + if (operationTypes.reload) { + cachedPagingData = undefined; + cachedData = createEmptyCachedData(); + } else if (operationTypes.groupExpanding) { + cachedData = createEmptyCachedData(); } + + each(operationTypes, (operationType, value) => { + if (value && this._needCleanCacheByOperation(operationType, options.remoteOperations)) { + cachedStoreData = undefined; + cachedPagingData = undefined; + } + }); + } + + if (cachedPagingData) { + options.remoteOperations.paging = false; + } + + options.cachedStoreData = cachedStoreData; + options.cachedPagingData = cachedPagingData; + options.cachedData = cachedData; + + if (!options.isCustomLoading) { + this._cachedStoreData = cachedStoreData; + this._cachedPagingData = cachedPagingData; + this._cachedData = cachedData; } } - function setPageDataToCache(options, data, groupCount) { - const { storeLoadOptions } = options; - const skip = options.skip ?? storeLoadOptions.skip ?? 0; - const take = options.take ?? storeLoadOptions.take ?? 0; - - for (let i = 0; i < take; i++) { - const globalIndex = i + skip; - const cacheItems = options.cachedData.items; - const skips = i === 0 && options.skips || []; - cacheItems[globalIndex] = getCacheItem(cacheItems[globalIndex], data[i], groupCount, skips); + _handleCustomizeStoreLoadOptions(options) { + this._handleDataLoading(options); + if (!(options.data?.length === 0)) { + // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing + options.data = getPageDataFromCache(options, true) || options.cachedStoreData; } } - function getCacheItem(cacheItem, loadedItem, groupCount, skips) { - if (groupCount && loadedItem) { - const result = { ...loadedItem }; - delete result.isContinuation; - delete result.isContinuationOnNextPage; - const skip = skips[0] || 0; - if (loadedItem.items) { - result.items = cacheItem?.items || {}; - loadedItem.items.forEach((item, index) => { - const globalIndex = index + skip; - const childSkips = index === 0 ? skips.slice(1) : []; - result.items[globalIndex] = getCacheItem( - result.items[globalIndex], - item, - groupCount - 1, - childSkips, - ); - }); - } + _handleDataLoading(options) { + const dataSource = this._dataSource; + const lastLoadOptions = this._lastLoadOptions; - return result; + this.customizeStoreLoadOptions.fire(options); + + options.delay = this.option('loadingTimeout'); + options.originalStoreLoadOptions = options.storeLoadOptions; + options.remoteOperations = extend({}, this.remoteOperations()); + + const isFullReload = !this.isLoaded() && !this._isRefreshing; + + if (this.option('integrationOptions.renderedOnServer') && !this.isLoaded()) { + options.delay = undefined; } - return loadedItem; - } - - const members = { - init(dataSource, remoteOperations) { - const that = this; - - that._dataSource = dataSource; - that._remoteOperations = remoteOperations || {}; - - that._isLastPage = !dataSource.isLastPage(); - that._hasLastPage = false; - that._currentTotalCount = 0; - that._cachedData = createEmptyCachedData(); - that._lastOperationTypes = {}; - that._eventsStrategy = dataSource._eventsStrategy; - that._totalCountCorrection = 0; - that._isLoadingAll = false; - - that.changed = Callbacks(); - that.loadingChanged = Callbacks(); - that.loadError = Callbacks(); - that.customizeStoreLoadOptions = Callbacks(); - that.changing = Callbacks(); - that.pushed = Callbacks(); - - that._dataChangedHandler = that._handleDataChanged.bind(that); - that._customizeStoreLoadOptionsHandler = that._handleCustomizeStoreLoadOptions.bind(that); - that._dataLoadedHandler = that._handleDataLoaded.bind(that); - that._loadingChangedHandler = that._handleLoadingChanged.bind(that); - that._loadErrorHandler = that._handleLoadError.bind(that); - that._pushHandler = that._handlePush.bind(that); - that._changingHandler = that._handleChanging.bind(that); - - dataSource.on('changed', that._dataChangedHandler); - dataSource.on('customizeStoreLoadOptions', that._customizeStoreLoadOptionsHandler); - dataSource.on('customizeLoadResult', that._dataLoadedHandler); - dataSource.on('loadingChanged', that._loadingChangedHandler); - dataSource.on('loadError', that._loadErrorHandler); - dataSource.on('changing', that._changingHandler); - dataSource.store().on('beforePush', that._pushHandler); - - each(dataSource, (memberName, member) => { - if (!that[memberName] && isFunction(member)) { - that[memberName] = function () { - return this._dataSource[memberName].apply(this._dataSource, arguments); - }; + const loadOptions = extend({ pageIndex: this.pageIndex(), pageSize: this.pageSize() }, options.storeLoadOptions); + + const operationTypes = calculateOperationTypes(loadOptions, lastLoadOptions, isFullReload); + + this._customizeRemoteOperations(options, operationTypes); + + if (!options.isCustomLoading) { + const isRefreshing = this._isRefreshing; + + options.pageIndex = dataSource.pageIndex(); + options.lastLoadOptions = loadOptions; + options.operationTypes = operationTypes; + this._loadingOperationTypes = operationTypes; + this._isRefreshing = true; + + when(isRefreshing || this._isRefreshed || this.refresh(options, operationTypes)).done(() => { + if (this._lastOperationId === options.operationId) { + this._isRefreshed = true; + this.load().always(() => { + this._isRefreshed = false; + }); } + }).fail(() => { + dataSource.cancel(options.operationId); + }).always(() => { + this._isRefreshing = false; }); - }, - remoteOperations() { - return this._remoteOperations; - }, - dispose(isSharedDataSource) { - const that = this; - const dataSource = that._dataSource; - const store = dataSource.store(); - dataSource.off('changed', that._dataChangedHandler); - dataSource.off('customizeStoreLoadOptions', that._customizeStoreLoadOptionsHandler); - dataSource.off('customizeLoadResult', that._dataLoadedHandler); - dataSource.off('loadingChanged', that._loadingChangedHandler); - dataSource.off('loadError', that._loadErrorHandler); - dataSource.off('changing', that._changingHandler); - store && store.off('beforePush', that._pushHandler); + dataSource.cancel(this._lastOperationId); + this._lastOperationId = options.operationId; - if (!isSharedDataSource) { - dataSource.dispose(); + if (this._isRefreshing) { + dataSource.cancel(this._lastOperationId); } - }, - refresh(options, operationTypes) { - const that = this; - const dataSource = that._dataSource; + } - if (operationTypes.reload) { - that.resetCurrentTotalCount(); - that._isLastPage = !dataSource.paginate(); - that._hasLastPage = that._isLastPage; - } - }, - resetCurrentTotalCount() { - this._currentTotalCount = 0; - this._totalCountCorrection = 0; - }, - resetCache() { - this._cachedStoreData = undefined; - this._cachedPagingData = undefined; - }, - resetPagesCache() { - this._cachedData = createEmptyCachedData(); - }, - _needClearStoreDataCache() { - const remoteOperations = this.remoteOperations(); - const operationTypes = calculateOperationTypes(this._lastLoadOptions || {}, {}); - const isLocalOperations = Object.keys(remoteOperations).every((operationName) => !operationTypes[operationName] || !remoteOperations[operationName]); - - return !isLocalOperations; - }, - push(changes, fromStore) { - const store = this.store(); + this._handleDataLoadingCore(options); + } - if (this._needClearStoreDataCache()) { - this._cachedStoreData = undefined; - } + _handleDataLoadingCore(options) { + const { remoteOperations } = options; - this._cachedPagingData = undefined; + options.loadOptions = {}; - this.resetPagesCache(true); + const cachedExtra = options.cachedData.extra; + const localLoadOptionNames = { + filter: !remoteOperations.filtering, + sort: !remoteOperations.sorting, + group: !remoteOperations.grouping, + summary: !remoteOperations.summary, + skip: !remoteOperations.paging, + take: !remoteOperations.paging, + requireTotalCount: cachedExtra && 'totalCount' in cachedExtra || !remoteOperations.paging, + langParams: !remoteOperations.filtering || !remoteOperations.sorting, + }; - if (this._cachedStoreData) { - // @ts-expect-error - applyBatch({ - keyInfo: store, - data: this._cachedStoreData, - changes, - }); + each(options.storeLoadOptions, (optionName, optionValue) => { + if (localLoadOptionNames[optionName]) { + options.loadOptions[optionName] = optionValue; + delete options.storeLoadOptions[optionName]; } + }); - if (!fromStore) { - this._applyBatch(changes); - } + if (cachedExtra) { + options.extra = cachedExtra; + } + } - this.pushed.fire(changes); - }, - getDataIndexGetter() { - if (!this._dataIndexGetter) { - let indexByKey; - let storeData; - const store = this.store(); - - this._dataIndexGetter = (data) => { - const isCacheUpdated = storeData && storeData !== this._cachedStoreData; - if (!indexByKey || isCacheUpdated) { - storeData = this._cachedStoreData || []; - indexByKey = {}; - for (let i = 0; i < storeData.length; i++) { - indexByKey[getKeyHash(store.keyOf(storeData[i]))] = i; - } - } - return indexByKey[getKeyHash(store.keyOf(data))]; - }; - } + _handleDataLoaded(options) { + const { loadOptions } = options; + const localPaging = options.remoteOperations && !options.remoteOperations.paging; + const { cachedData } = options; + const { storeLoadOptions } = options; + const needCache = this.option('cacheEnabled') !== false && storeLoadOptions; + const needPageCache = needCache && !options.isCustomLoading && cachedData && (!localPaging || storeLoadOptions.group); + const needPagingCache = needCache && localPaging; + const needStoreCache = needPagingCache && !options.isCustomLoading; + + if (!loadOptions) { + this._dataSource.cancel(options.operationId); + return; + } - return this._dataIndexGetter; - }, - _getKeyInfo() { - return this.store(); - }, - _needToCopyDataObject() { - return true; - }, - _applyBatch(changes, fromStore) { - const keyInfo = this._getKeyInfo(); - const dataSource = this._dataSource; - const groupCount = gridCoreUtils.normalizeSortingInfo(this.group()).length; - const isReshapeMode = this.option('editing.refreshMode') === 'reshape'; - const isVirtualMode = this.option('scrolling.mode') === 'virtual'; - - changes = changes.filter((change) => !dataSource.paginate() || change.type !== 'insert' || change.index !== undefined); - - const getItemCount = () => (groupCount ? this.itemsCount() : this.items().length); - const oldItemCount = getItemCount(); + if (localPaging) { + options.skip = loadOptions.skip; + options.take = loadOptions.take; - // @ts-expect-error - applyBatch({ - keyInfo, - data: this._items, - changes, - groupCount, - useInsertIndex: true, - skipCopying: !this._needToCopyDataObject(), - }); - // @ts-expect-error - applyBatch({ - keyInfo, - data: dataSource.items(), - changes, - groupCount, - useInsertIndex: true, - skipCopying: !this._needToCopyDataObject(), - }); + delete loadOptions.skip; + delete loadOptions.take; + } - const needUpdateTotalCountCorrection = this._currentTotalCount > 0 || ( - (fromStore || !isReshapeMode) - && isVirtualMode - ); + if (loadOptions.group) { + loadOptions.group = options.group || loadOptions.group; + } - if (needUpdateTotalCountCorrection) { - this._totalCountCorrection += getItemCount() - oldItemCount; - } + const groupCount = gridCoreUtils.normalizeSortingInfo(options.group || storeLoadOptions.group || loadOptions.group).length; - changes.splice(0, changes.length); - }, - _handlePush({ changes }) { - this.push(changes, true); - }, - _handleChanging(e) { - this.changing.fire(e); - this._applyBatch(e.changes, true); - }, - _needCleanCacheByOperation(operationType, remoteOperations) { - const operationTypesByOrder = ['filtering', 'sorting', 'paging']; - const operationTypeIndex = operationTypesByOrder.indexOf(operationType); - const currentOperationTypes = operationTypeIndex >= 0 ? operationTypesByOrder.slice(operationTypeIndex) : [operationType]; - - return currentOperationTypes.some((operationType) => remoteOperations[operationType]); - }, - _customizeRemoteOperations(options, operationTypes) { - let cachedStoreData = this._cachedStoreData; - let cachedPagingData = this._cachedPagingData; - let cachedData = this._cachedData; - - if ((options.storeLoadOptions.filter && !options.remoteOperations.filtering) || (options.storeLoadOptions.sort && !options.remoteOperations.sorting)) { - options.remoteOperations = { - filtering: options.remoteOperations.filtering, - summary: options.remoteOperations.summary, - }; - } + if (options.cachedDataPartBegin) { + options.data = options.cachedDataPartBegin.concat(options.data); + } - if (operationTypes.fullReload) { - cachedStoreData = undefined; - cachedPagingData = undefined; - cachedData = createEmptyCachedData(); + if (options.cachedDataPartEnd) { + options.data = options.data.concat(options.cachedDataPartEnd); + } + + if (!needPageCache || !getPageDataFromCache(options)) { + if (needPagingCache && options.cachedPagingData) { + options.data = cloneItems(options.cachedPagingData, groupCount); } else { - if (operationTypes.reload) { - cachedPagingData = undefined; - cachedData = createEmptyCachedData(); - } else if (operationTypes.groupExpanding) { - cachedData = createEmptyCachedData(); + if (needStoreCache) { + if (!this._cachedStoreData) { + this._cachedStoreData = cloneItems(options.data, gridCoreUtils.normalizeSortingInfo(storeLoadOptions.group).length); + } else if (options.mergeStoreLoadData) { + options.data = this._cachedStoreData = this._cachedStoreData.concat(options.data); + } } - - each(operationTypes, (operationType, value) => { - if (value && this._needCleanCacheByOperation(operationType, options.remoteOperations)) { - cachedStoreData = undefined; - cachedPagingData = undefined; + // @ts-expect-error + new ArrayStore(options.data).load(loadOptions).done((data) => { + options.data = data; + if (needStoreCache) { + this._cachedPagingData = cloneItems(options.data, groupCount); } + }).fail((error) => { + // @ts-expect-error + options.data = new Deferred().reject(error); }); } - if (cachedPagingData) { - options.remoteOperations.paging = false; + if (loadOptions.requireTotalCount && localPaging) { + options.extra = isPlainObject(options.extra) ? options.extra : {}; + options.extra.totalCount = options.data.length; } - options.cachedStoreData = cachedStoreData; - options.cachedPagingData = cachedPagingData; - options.cachedData = cachedData; - - if (!options.isCustomLoading) { - this._cachedStoreData = cachedStoreData; - this._cachedPagingData = cachedPagingData; - this._cachedData = cachedData; + if (options.extra && options.extra.totalCount >= 0 && (storeLoadOptions.requireTotalCount === false || loadOptions.requireTotalCount === false)) { + options.extra.totalCount = -1; } - }, - _handleCustomizeStoreLoadOptions(options) { - this._handleDataLoading(options); - if (!(options.data?.length === 0)) { - // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing - options.data = getPageDataFromCache(options, true) || options.cachedStoreData; + + if (!loadOptions.data && (storeLoadOptions.requireTotalCount || (options.extra?.totalCount ?? -1) >= 0)) { + this._totalCountCorrection = 0; } - }, - _handleDataLoading(options) { - const dataSource = this._dataSource; - const lastLoadOptions = this._lastLoadOptions; - this.customizeStoreLoadOptions.fire(options); + this._handleDataLoadedCore(options); - options.delay = this.option('loadingTimeout'); - options.originalStoreLoadOptions = options.storeLoadOptions; - options.remoteOperations = extend({}, this.remoteOperations()); + if (needPageCache) { + cachedData.extra = cachedData.extra || extend({}, options.extra); + when(options.data).done((data) => { + setPageDataToCache(options, data, groupCount); + }); + } + } - const isFullReload = !this.isLoaded() && !this._isRefreshing; + when(options.data).done(() => { + if (options.lastLoadOptions) { + this._lastLoadOptions = options.lastLoadOptions; + Object.keys(options.operationTypes).forEach((operationType) => { + this._lastOperationTypes[operationType] = this._lastOperationTypes[operationType] || options.operationTypes[operationType]; + }); + } + }); + options.storeLoadOptions = options.originalStoreLoadOptions; + } - if (this.option('integrationOptions.renderedOnServer') && !this.isLoaded()) { - options.delay = undefined; + _handleDataLoadedCore(options) { + if (options.remoteOperations && !options.remoteOperations.paging && Array.isArray(options.data)) { + if (options.skip !== undefined) { + options.data = options.data.slice(options.skip); } + if (options.take !== undefined) { + options.data = options.data.slice(0, options.take); + } + } + } - const loadOptions = extend({ pageIndex: this.pageIndex(), pageSize: this.pageSize() }, options.storeLoadOptions); + _handleLoadingChanged(isLoading) { + this.loadingChanged.fire(isLoading); + } - const operationTypes = calculateOperationTypes(loadOptions, lastLoadOptions, isFullReload); + _handleLoadError(error) { + this.loadError.fire(error); + this.changed.fire({ + changeType: 'loadError', + error, + }); + } - this._customizeRemoteOperations(options, operationTypes); + _loadPageSize() { + return this.pageSize(); + } - if (!options.isCustomLoading) { - const isRefreshing = this._isRefreshing; + _handleDataChanged(args) { + let currentTotalCount; + const dataSource = this._dataSource; + let isLoading = false; + const isDataLoading = !args || isDefined(args.changeType); - options.pageIndex = dataSource.pageIndex(); - options.lastLoadOptions = loadOptions; - options.operationTypes = operationTypes; - this._loadingOperationTypes = operationTypes; - this._isRefreshing = true; + const itemsCount = this.itemsCount(); - when(isRefreshing || this._isRefreshed || this.refresh(options, operationTypes)).done(() => { - if (this._lastOperationId === options.operationId) { - this._isRefreshed = true; - this.load().always(() => { - this._isRefreshed = false; - }); - } - }).fail(() => { - dataSource.cancel(options.operationId); - }).always(() => { - this._isRefreshing = false; - }); + if (isDataLoading) { + this._isLastPage = !itemsCount || !this._loadPageSize() || itemsCount < this._loadPageSize(); - dataSource.cancel(this._lastOperationId); - this._lastOperationId = options.operationId; + if (this._isLastPage) { + this._hasLastPage = true; + } + } - if (this._isRefreshing) { - dataSource.cancel(this._lastOperationId); + if (dataSource.totalCount() >= 0) { + if (dataSource.pageIndex() >= this.pageCount()) { + dataSource.pageIndex(this.pageCount() - 1); + this.pageIndex(dataSource.pageIndex()); + this.resetPagesCache(); + dataSource.load(); + isLoading = true; + } + } else if (isDataLoading) { + currentTotalCount = dataSource.pageIndex() * this.pageSize() + itemsCount; + if (currentTotalCount > this._currentTotalCount) { + this._currentTotalCount = currentTotalCount; + if (dataSource.pageIndex() === 0 || !this.option('scrolling.legacyMode')) { + this._totalCountCorrection = 0; + } + } + if (itemsCount === 0 && dataSource.pageIndex() >= this.pageCount()) { + dataSource.pageIndex(this.pageCount() - 1); + if (this.option('scrolling.mode') !== 'infinite') { + dataSource.load(); + isLoading = true; } } + } - this._handleDataLoadingCore(options); - }, - _handleDataLoadingCore(options) { - const { remoteOperations } = options; - - options.loadOptions = {}; - - const cachedExtra = options.cachedData.extra; - const localLoadOptionNames = { - filter: !remoteOperations.filtering, - sort: !remoteOperations.sorting, - group: !remoteOperations.grouping, - summary: !remoteOperations.summary, - skip: !remoteOperations.paging, - take: !remoteOperations.paging, - requireTotalCount: cachedExtra && 'totalCount' in cachedExtra || !remoteOperations.paging, - langParams: !remoteOperations.filtering || !remoteOperations.sorting, - }; + if (!isLoading) { + this._operationTypes = this._lastOperationTypes; + this._lastOperationTypes = {}; - each(options.storeLoadOptions, (optionName, optionValue) => { - if (localLoadOptionNames[optionName]) { - options.loadOptions[optionName] = optionValue; - delete options.storeLoadOptions[optionName]; - } - }); + this.component._optionCache = {}; + this.changed.fire(args); + this.component._optionCache = undefined; + } + } - if (cachedExtra) { - options.extra = cachedExtra; - } - }, - _handleDataLoaded(options) { - const { loadOptions } = options; - const localPaging = options.remoteOperations && !options.remoteOperations.paging; - const { cachedData } = options; - const { storeLoadOptions } = options; - const needCache = this.option('cacheEnabled') !== false && storeLoadOptions; - const needPageCache = needCache && !options.isCustomLoading && cachedData && (!localPaging || storeLoadOptions.group); - const needPagingCache = needCache && localPaging; - const needStoreCache = needPagingCache && !options.isCustomLoading; - - if (!loadOptions) { - this._dataSource.cancel(options.operationId); - return; - } + _scheduleCustomLoadCallbacks(deferred) { + const that = this; - if (localPaging) { - options.skip = loadOptions.skip; - options.take = loadOptions.take; + that._isCustomLoading = true; + deferred.always(() => { + that._isCustomLoading = false; + }); + } - delete loadOptions.skip; - delete loadOptions.take; - } + loadingOperationTypes() { + return this._loadingOperationTypes; + } - if (loadOptions.group) { - loadOptions.group = options.group || loadOptions.group; - } + operationTypes() { + return this._operationTypes; + } - const groupCount = gridCoreUtils.normalizeSortingInfo(options.group || storeLoadOptions.group || loadOptions.group).length; + lastLoadOptions() { + return this._lastLoadOptions || {}; + } - if (options.cachedDataPartBegin) { - options.data = options.cachedDataPartBegin.concat(options.data); - } + isLastPage() { + return this._isLastPage; + } - if (options.cachedDataPartEnd) { - options.data = options.data.concat(options.cachedDataPartEnd); - } + _dataSourceTotalCount() { + return this._dataSource.totalCount(); + } - if (!needPageCache || !getPageDataFromCache(options)) { - if (needPagingCache && options.cachedPagingData) { - options.data = cloneItems(options.cachedPagingData, groupCount); - } else { - if (needStoreCache) { - if (!this._cachedStoreData) { - this._cachedStoreData = cloneItems(options.data, gridCoreUtils.normalizeSortingInfo(storeLoadOptions.group).length); - } else if (options.mergeStoreLoadData) { - options.data = this._cachedStoreData = this._cachedStoreData.concat(options.data); - } - } - // @ts-expect-error - new ArrayStore(options.data).load(loadOptions).done((data) => { - options.data = data; - if (needStoreCache) { - this._cachedPagingData = cloneItems(options.data, groupCount); - } - }).fail((error) => { - // @ts-expect-error - options.data = new Deferred().reject(error); - }); - } + // eslint-disable-next-line @typescript-eslint/no-unused-vars + _changeRowExpandCore(path?: any) { - if (loadOptions.requireTotalCount && localPaging) { - options.extra = isPlainObject(options.extra) ? options.extra : {}; - options.extra.totalCount = options.data.length; - } + } - if (options.extra && options.extra.totalCount >= 0 && (storeLoadOptions.requireTotalCount === false || loadOptions.requireTotalCount === false)) { - options.extra.totalCount = -1; - } + // eslint-disable-next-line @typescript-eslint/no-unused-vars + changeRowExpand(path?: any): any { - if (!loadOptions.data && (storeLoadOptions.requireTotalCount || (options.extra?.totalCount ?? -1) >= 0)) { - this._totalCountCorrection = 0; - } + } - this._handleDataLoadedCore(options); + totalCount() { + // eslint-disable-next-line radix + return parseInt((this._currentTotalCount || this._dataSourceTotalCount()) + this._totalCountCorrection); + } - if (needPageCache) { - cachedData.extra = cachedData.extra || extend({}, options.extra); - when(options.data).done((data) => { - setPageDataToCache(options, data, groupCount); - }); - } - } + totalCountCorrection() { + return this._totalCountCorrection; + } - when(options.data).done(() => { - if (options.lastLoadOptions) { - this._lastLoadOptions = options.lastLoadOptions; - Object.keys(options.operationTypes).forEach((operationType) => { - this._lastOperationTypes[operationType] = this._lastOperationTypes[operationType] || options.operationTypes[operationType]; - }); - } - }); - options.storeLoadOptions = options.originalStoreLoadOptions; - }, - _handleDataLoadedCore(options) { - if (options.remoteOperations && !options.remoteOperations.paging && Array.isArray(options.data)) { - if (options.skip !== undefined) { - options.data = options.data.slice(options.skip); - } - if (options.take !== undefined) { - options.data = options.data.slice(0, options.take); - } - } - }, - _handleLoadingChanged(isLoading) { - this.loadingChanged.fire(isLoading); - }, - _handleLoadError(error) { - this.loadError.fire(error); - this.changed.fire({ - changeType: 'loadError', - error, - }); - }, - _loadPageSize() { - return this.pageSize(); - }, - _handleDataChanged(args) { - let currentTotalCount; - const dataSource = this._dataSource; - let isLoading = false; - const isDataLoading = !args || isDefined(args.changeType); - - const itemsCount = this.itemsCount(); - - if (isDataLoading) { - this._isLastPage = !itemsCount || !this._loadPageSize() || itemsCount < this._loadPageSize(); - - if (this._isLastPage) { - this._hasLastPage = true; - } - } + items(): any { - if (dataSource.totalCount() >= 0) { - if (dataSource.pageIndex() >= this.pageCount()) { - dataSource.pageIndex(this.pageCount() - 1); - this.pageIndex(dataSource.pageIndex()); - this.resetPagesCache(); - dataSource.load(); - isLoading = true; - } - } else if (isDataLoading) { - currentTotalCount = dataSource.pageIndex() * this.pageSize() + itemsCount; - if (currentTotalCount > this._currentTotalCount) { - this._currentTotalCount = currentTotalCount; - if (dataSource.pageIndex() === 0 || !this.option('scrolling.legacyMode')) { - this._totalCountCorrection = 0; - } - } - if (itemsCount === 0 && dataSource.pageIndex() >= this.pageCount()) { - dataSource.pageIndex(this.pageCount() - 1); - if (this.option('scrolling.mode') !== 'infinite') { - dataSource.load(); - isLoading = true; - } - } - } + } + + itemsCount() { + return this._dataSource.items().length; + } + + totalItemsCount() { + return this.totalCount(); + } + + pageSize() { + const dataSource = this._dataSource; + + if (!arguments.length && !dataSource.paginate()) { + return 0; + } + return dataSource.pageSize.apply(dataSource, arguments); + } - if (!isLoading) { - this._operationTypes = this._lastOperationTypes; - this._lastOperationTypes = {}; + pageCount() { + const that = this; + const count = that.totalItemsCount() - that._totalCountCorrection; + const pageSize = that.pageSize(); - this.component._optionCache = {}; - this.changed.fire(args); - this.component._optionCache = undefined; + if (pageSize && count > 0) { + return Math.max(1, Math.ceil(count / pageSize)); + } + return 1; + } + + hasKnownLastPage() { + return this._hasLastPage || this._dataSource.totalCount() >= 0; + } + + loadFromStore(loadOptions, store?) { + const dataSource = this._dataSource; + // @ts-expect-error + const d = new Deferred(); + + if (!dataSource) return; + + store = store || dataSource.store(); + + store.load(loadOptions).done((data, extra) => { + if (data && !Array.isArray(data) && Array.isArray(data.data)) { + extra = data; + data = data.data; } - }, - _scheduleCustomLoadCallbacks(deferred) { - const that = this; + d.resolve(data, extra); + }).fail(d.reject); + + return d; + } + + isCustomLoading() { + return !!this._isCustomLoading; + } - that._isCustomLoading = true; - deferred.always(() => { - that._isCustomLoading = false; + load(options?) { + const that = this; + const dataSource = that._dataSource; + // @ts-expect-error + const d = new Deferred(); + + if (options) { + const store = dataSource.store(); + const dataSourceLoadOptions = dataSource.loadOptions(); + const loadResult: any = { + storeLoadOptions: extend({}, options, { langParams: dataSourceLoadOptions?.langParams }), + isCustomLoading: true, + }; + + each(store._customLoadOptions() || [], (_, optionName) => { + if (!(optionName in loadResult.storeLoadOptions)) { + loadResult.storeLoadOptions[optionName] = dataSourceLoadOptions[optionName]; + } }); - }, - loadingOperationTypes() { - return this._loadingOperationTypes; - }, - operationTypes() { - return this._operationTypes; - }, - lastLoadOptions() { - return this._lastLoadOptions || {}; - }, - isLastPage() { - return this._isLastPage; - }, - _dataSourceTotalCount() { - return this._dataSource.totalCount(); - }, - totalCount() { - // eslint-disable-next-line radix - return parseInt((this._currentTotalCount || this._dataSourceTotalCount()) + this._totalCountCorrection); - }, - totalCountCorrection() { - return this._totalCountCorrection; - }, - itemsCount() { - return this._dataSource.items().length; - }, - totalItemsCount() { - return this.totalCount(); - }, - pageSize() { - const dataSource = this._dataSource; - - if (!arguments.length && !dataSource.paginate()) { - return 0; - } - return dataSource.pageSize.apply(dataSource, arguments); - }, - pageCount() { - const that = this; - const count = that.totalItemsCount() - that._totalCountCorrection; - const pageSize = that.pageSize(); - - if (pageSize && count > 0) { - return Math.max(1, Math.ceil(count / pageSize)); - } - return 1; - }, - hasKnownLastPage() { - return this._hasLastPage || this._dataSource.totalCount() >= 0; - }, - loadFromStore(loadOptions, store) { - const dataSource = this._dataSource; - // @ts-expect-error - const d = new Deferred(); - if (!dataSource) return; + this._isLoadingAll = options.isLoadingAll; - store = store || dataSource.store(); + that._scheduleCustomLoadCallbacks(d); + dataSource._scheduleLoadCallbacks(d); - store.load(loadOptions).done((data, extra) => { - if (data && !Array.isArray(data) && Array.isArray(data.data)) { - extra = data; - data = data.data; + that._handleCustomizeStoreLoadOptions(loadResult); + executeTask(() => { + if (!dataSource.store()) { + return d.reject('canceled'); } - d.resolve(data, extra); - }).fail(d.reject); - - return d; - }, - isCustomLoading() { - return !!this._isCustomLoading; - }, - load(options) { - const that = this; - const dataSource = that._dataSource; - // @ts-expect-error - const d = new Deferred(); - - if (options) { - const store = dataSource.store(); - const dataSourceLoadOptions = dataSource.loadOptions(); - const loadResult: any = { - storeLoadOptions: extend({}, options, { langParams: dataSourceLoadOptions?.langParams }), - isCustomLoading: true, - }; - each(store._customLoadOptions() || [], (_, optionName) => { - if (!(optionName in loadResult.storeLoadOptions)) { - loadResult.storeLoadOptions[optionName] = dataSourceLoadOptions[optionName]; + when(loadResult.data || that.loadFromStore(loadResult.storeLoadOptions)).done((data, extra) => { + loadResult.data = data; + loadResult.extra = extra || {}; + that._handleDataLoaded(loadResult); + + if (options.requireTotalCount && loadResult.extra.totalCount === undefined) { + loadResult.extra.totalCount = store.totalCount(loadResult.storeLoadOptions); } - }); + // TODO map function?? + when(loadResult.data, loadResult.extra.totalCount).done((data, totalCount) => { + loadResult.extra.totalCount = totalCount; + d.resolve(data, loadResult.extra); + }).fail(d.reject); + }).fail(d.reject); + }, that.option('loadingTimeout')); + + return d.fail(function () { + that._eventsStrategy.fireEvent('loadError', arguments); + }).always(() => { + this._isLoadingAll = false; + }).promise(); + } + return dataSource.load(); + } - this._isLoadingAll = options.isLoadingAll; + reload(full) { + return full ? this._dataSource.reload() : this._dataSource.load(); + } - that._scheduleCustomLoadCallbacks(d); - dataSource._scheduleLoadCallbacks(d); + getCachedStoreData() { + return this._cachedStoreData; + } - that._handleCustomizeStoreLoadOptions(loadResult); - executeTask(() => { - if (!dataSource.store()) { - return d.reject('canceled'); - } + isLoaded(): any { - when(loadResult.data || that.loadFromStore(loadResult.storeLoadOptions)).done((data, extra) => { - loadResult.data = data; - loadResult.extra = extra || {}; - that._handleDataLoaded(loadResult); - - if (options.requireTotalCount && loadResult.extra.totalCount === undefined) { - loadResult.extra.totalCount = store.totalCount(loadResult.storeLoadOptions); - } - // TODO map function?? - when(loadResult.data, loadResult.extra.totalCount).done((data, totalCount) => { - loadResult.extra.totalCount = totalCount; - d.resolve(data, loadResult.extra); - }).fail(d.reject); - }).fail(d.reject); - }, that.option('loadingTimeout')); + } - return d.fail(function () { - that._eventsStrategy.fireEvent('loadError', arguments); - }).always(() => { - this._isLoadingAll = false; - }).promise(); - } - return dataSource.load(); - }, - reload(full) { - return full ? this._dataSource.reload() : this._dataSource.load(); - }, - getCachedStoreData() { - return this._cachedStoreData; - }, - }; - - return members; -})()); + // eslint-disable-next-line @typescript-eslint/no-unused-vars + pageIndex(pageIndex?) { + + } +} diff --git a/packages/devextreme/js/__internal/grids/grid_core/data_source_adapter/m_data_source_adapter_utils.ts b/packages/devextreme/js/__internal/grids/grid_core/data_source_adapter/m_data_source_adapter_utils.ts new file mode 100644 index 000000000000..0067be9aa318 --- /dev/null +++ b/packages/devextreme/js/__internal/grids/grid_core/data_source_adapter/m_data_source_adapter_utils.ts @@ -0,0 +1,210 @@ +// @ts-expect-error +import { executeAsync } from '@js/core/utils/common'; +import { extend } from '@js/core/utils/extend'; +import { isDefined } from '@js/core/utils/type'; + +import gridCoreUtils from '../m_utils'; + +export const cloneItems = function (items, groupCount) { + if (items) { + items = items.slice(0); + if (groupCount) { + for (let i = 0; i < items.length; i++) { + items[i] = extend({ key: items[i].key }, items[i]); + items[i].items = cloneItems(items[i].items, groupCount - 1); + } + } + } + return items; +}; + +export const calculateOperationTypes = function (loadOptions, lastLoadOptions, isFullReload?) { + let operationTypes: any = { reload: true, fullReload: true }; + + if (lastLoadOptions) { + operationTypes = { + sorting: !gridCoreUtils.equalSortParameters(loadOptions.sort, lastLoadOptions.sort), + grouping: !gridCoreUtils.equalSortParameters(loadOptions.group, lastLoadOptions.group, true), + groupExpanding: !gridCoreUtils.equalSortParameters(loadOptions.group, lastLoadOptions.group) || lastLoadOptions.groupExpand, + filtering: !gridCoreUtils.equalFilterParameters(loadOptions.filter, lastLoadOptions.filter), + pageIndex: loadOptions.pageIndex !== lastLoadOptions.pageIndex, + skip: loadOptions.skip !== lastLoadOptions.skip, + take: loadOptions.take !== lastLoadOptions.take, + pageSize: loadOptions.pageSize !== lastLoadOptions.pageSize, + fullReload: isFullReload, + reload: false, + paging: false, + }; + + operationTypes.reload = isFullReload || operationTypes.sorting || operationTypes.grouping || operationTypes.filtering; + operationTypes.paging = operationTypes.pageIndex || operationTypes.pageSize || operationTypes.take; + } + + return operationTypes; +}; + +export const executeTask = function (action, timeout) { + if (isDefined(timeout)) { + executeAsync(action, timeout); + } else { + action(); + } +}; + +export const createEmptyCachedData = function () { + return { items: {} }; +}; + +export const getPageDataFromCache = function (options, updatePaging?): any { + const groupCount = gridCoreUtils.normalizeSortingInfo(options.group || options.storeLoadOptions.group || options.loadOptions.group).length; + const items = []; + if (fillItemsFromCache(items, options, groupCount)) { + return items; + } if (updatePaging) { + updatePagingOptionsByCache(items, options, groupCount); + } +}; + +export const fillItemsFromCache = function (items, options, groupCount, fromEnd?) { + const { storeLoadOptions } = options; + const take = options.take ?? storeLoadOptions.take ?? 0; + const cachedItems = options.cachedData?.items; + + if (take && cachedItems) { + const skip: number = options.skip ?? storeLoadOptions.skip ?? 0; + for (let i = 0; i < take; i += 1) { + const localIndex = fromEnd ? take - 1 - i : i; + const cacheItemIndex = localIndex + skip; + const cacheItem = cachedItems[cacheItemIndex]; + + if (cacheItem === undefined && cacheItemIndex in cachedItems) { + return true; + } + + const item = getItemFromCache(options, cacheItem, groupCount, localIndex, take); + + if (item) { + items.push(item); + } else { + return false; + } + } + return true; + } + return false; +}; + +export const getItemFromCache = function (options, cacheItem, groupCount, index, take) { + if (groupCount && cacheItem) { + const skips = (index === 0 && options.skips) || []; + const takes = (index === take - 1 && options.takes) || []; + + return getGroupItemFromCache(cacheItem, groupCount, skips, takes); + } + return cacheItem; +}; + +export const getGroupItemFromCache = function (cacheItem, groupCount, skips, takes) { + if (groupCount && cacheItem) { + const result = { ...cacheItem }; + const skip: number = skips[0] || 0; + const take = takes[0]; + const { items } = cacheItem; + + if (items) { + if (take === undefined && !items[skip]) { + return; + } + result.items = []; + if (skips.length) { + result.isContinuation = true; + } + + if (take) { + result.isContinuationOnNextPage = cacheItem.count > take; + } + + for (let i = 0; take === undefined ? items[i + skip] : i < take; i += 1) { + const childCacheItem = items[i + skip]; + const isLast = i + 1 === take; + const item = getGroupItemFromCache(childCacheItem, groupCount - 1, i === 0 ? skips.slice(1) : [], isLast ? takes.slice(1) : []); + + if (item !== undefined) { + result.items.push(item); + } else { + return; + } + } + } + + return result; + } + + return cacheItem; +}; + +export const updatePagingOptionsByCache = function (cacheItemsFromBegin, options, groupCount) { + const cacheItemBeginCount = cacheItemsFromBegin.length; + const { storeLoadOptions } = options; + if (storeLoadOptions.skip !== undefined && storeLoadOptions.take && !groupCount) { + const cacheItemsFromEnd = []; + fillItemsFromCache(cacheItemsFromEnd, options, groupCount, true); + const cacheItemEndCount = cacheItemsFromEnd.length; + + if (cacheItemBeginCount || cacheItemEndCount) { + options.skip = options.skip ?? storeLoadOptions.skip; + options.take = options.take ?? storeLoadOptions.take; + } + + if (cacheItemBeginCount) { + storeLoadOptions.skip += cacheItemBeginCount; + storeLoadOptions.take -= cacheItemBeginCount; + options.cachedDataPartBegin = cacheItemsFromBegin; + } + + if (cacheItemEndCount) { + storeLoadOptions.take -= cacheItemEndCount; + options.cachedDataPartEnd = cacheItemsFromEnd.reverse(); + } + } +}; + +export const setPageDataToCache = function (options, data, groupCount) { + const { storeLoadOptions } = options; + const skip: number = options.skip ?? storeLoadOptions.skip ?? 0; + const take: number = options.take ?? storeLoadOptions.take ?? 0; + + for (let i = 0; i < take; i += 1) { + const globalIndex = i + skip; + const cacheItems = options.cachedData.items; + const skips = (i === 0 && options.skips) || []; + cacheItems[globalIndex] = getCacheItem(cacheItems[globalIndex], data[i], groupCount, skips); + } +}; + +export const getCacheItem = function (cacheItem, loadedItem, groupCount, skips) { + if (groupCount && loadedItem) { + const result = { ...loadedItem }; + delete result.isContinuation; + delete result.isContinuationOnNextPage; + const skip: number = skips[0] || 0; + + if (loadedItem.items) { + result.items = cacheItem?.items || {}; + loadedItem.items.forEach((item, index: number) => { + const globalIndex = index + skip; + const childSkips = index === 0 ? skips.slice(1) : []; + result.items[globalIndex] = getCacheItem( + result.items[globalIndex], + item, + groupCount - 1, + childSkips, + ); + }); + } + + return result; + } + + return loadedItem; +}; diff --git a/packages/devextreme/js/__internal/grids/grid_core/m_types.ts b/packages/devextreme/js/__internal/grids/grid_core/m_types.ts index 88838039be37..4a381a6e630c 100644 --- a/packages/devextreme/js/__internal/grids/grid_core/m_types.ts +++ b/packages/devextreme/js/__internal/grids/grid_core/m_types.ts @@ -1,11 +1,12 @@ /* eslint-disable spellcheck/spell-checker */ /* eslint-disable max-classes-per-file */ -import { GridBase, GridBaseOptions, SelectionBase } from '@js/common/grids'; -import { Component } from '@js/core/component'; -import { PropertyType } from '@js/core/index'; -import { dxElementWrapper } from '@js/core/renderer'; -import { Properties as DataGridOptions } from '@js/ui/data_grid'; -import Widget from '@js/ui/widget/ui.widget'; +import type { GridBase, GridBaseOptions, SelectionBase } from '@js/common/grids'; +import type { Component } from '@js/core/component'; +import type { PropertyType } from '@js/core/index'; +import type { dxElementWrapper } from '@js/core/renderer'; +import type { Properties as DataGridOptions } from '@js/ui/data_grid'; +import type { Properties as TreeListdOptions } from '@js/ui/tree_list'; +import type Widget from '@js/ui/widget/ui.widget'; type GridPropertyType = PropertyType extends never ? never : PropertyType | undefined; @@ -81,11 +82,15 @@ type TemporarlyOptionsTakenFromDataGrid = Pick; +type TemporarlyOptionsTakenFromTreeList = Pick; interface InternalSelection extends SelectionBase { alwaysSelectByShift?: boolean; } -export interface InternalGridOptions extends GridBaseOptions, TemporarlyOptionsTakenFromDataGrid { +export interface InternalGridOptions extends GridBaseOptions, TemporarlyOptionsTakenFromDataGrid, TemporarlyOptionsTakenFromTreeList { dataRowTemplate?: any; loadingTimeout?: number; diff --git a/packages/devextreme/js/__internal/grids/grid_core/virtual_scrolling/m_virtual_scrolling.ts b/packages/devextreme/js/__internal/grids/grid_core/virtual_scrolling/m_virtual_scrolling.ts index 3196f07abf26..57c75d6fb59f 100644 --- a/packages/devextreme/js/__internal/grids/grid_core/virtual_scrolling/m_virtual_scrolling.ts +++ b/packages/devextreme/js/__internal/grids/grid_core/virtual_scrolling/m_virtual_scrolling.ts @@ -9,6 +9,8 @@ import { isDefined } from '@js/core/utils/type'; import { getWindow } from '@js/core/utils/window'; import LoadIndicator from '@js/ui/load_indicator'; import errors from '@js/ui/widget/ui.errors'; +import type DataSourceAdapter from '@ts/grids/grid_core/data_source_adapter/m_data_source_adapter'; +import type { ModuleType } from '@ts/grids/grid_core/m_types'; import gridCoreUtils from '../m_utils'; import { subscribeToExternalScrollers, VirtualScrollController } from './m_virtual_scrolling_core'; @@ -25,6 +27,11 @@ const SCROLLING_MODE_VIRTUAL = 'virtual'; const LOAD_TIMEOUT = 300; const LEGACY_SCROLLING_MODE = 'scrolling.legacyMode'; const VISIBLE_PAGE_INDEX = 'paging.pageIndex'; +const PAGING_METHOD_NAMES = [ + 'beginPageIndex', + 'endPageIndex', + 'pageIndex', +]; const isVirtualMode = function (that) { return that.option('scrolling.mode') === SCROLLING_MODE_VIRTUAL; @@ -60,291 +67,366 @@ const updateItemIndices = function (items) { return items; }; -const VirtualScrollingDataSourceAdapterExtender = (function () { - const updateLoading = function (that) { - const beginPageIndex = that._virtualScrollController.beginPageIndex(-1); +const updateLoading = function (that) { + const beginPageIndex = that._virtualScrollController.beginPageIndex(-1); - if (isVirtualMode(that)) { - if (beginPageIndex < 0 || (that.viewportSize() >= 0 && that.getViewportItemIndex() >= 0 && (beginPageIndex * that.pageSize() > that.getViewportItemIndex() - || beginPageIndex * that.pageSize() + that.itemsCount() < that.getViewportItemIndex() + that.viewportSize())) && that._dataSource.isLoading()) { - if (!that._isLoading) { - that._isLoading = true; - that.loadingChanged.fire(true); - } - } else if (that._isLoading) { - that._isLoading = false; - that.loadingChanged.fire(false); + if (isVirtualMode(that)) { + if (beginPageIndex < 0 || (that.viewportSize() >= 0 && that.getViewportItemIndex() >= 0 && (beginPageIndex * that.pageSize() > that.getViewportItemIndex() + || beginPageIndex * that.pageSize() + that.itemsCount() < that.getViewportItemIndex() + that.viewportSize())) && that._dataSource.isLoading()) { + if (!that._isLoading) { + that._isLoading = true; + that.loadingChanged.fire(true); } + } else if (that._isLoading) { + that._isLoading = false; + that.loadingChanged.fire(false); } - }; + } +}; - const result = { - init() { - this.callBase.apply(this, arguments); - this._items = []; - this._totalCount = -1; - this._isLoaded = true; - this._loadPageCount = 1; +const proxyDataSourceAdapterMethod = function (that, methodName, args) { + if (that.option(LEGACY_SCROLLING_MODE) === false && PAGING_METHOD_NAMES.includes(methodName)) { + const dataSource = that._dataSource; - this._virtualScrollController = new VirtualScrollController(this.component, this._getVirtualScrollDataOptions()); - }, - _getVirtualScrollDataOptions() { - const that = this; - return { - pageSize() { - return that.pageSize(); - }, - totalItemsCount() { - return that.totalItemsCount(); - }, - hasKnownLastPage() { - return that.hasKnownLastPage(); - }, - pageIndex(index) { - return that._dataSource.pageIndex(index); - }, - isLoading() { - return that._dataSource.isLoading() && !that.isCustomLoading(); - }, - pageCount() { - return that.pageCount(); - }, - load() { - return that._dataSource.load(); - }, - updateLoading() { - updateLoading(that); - }, - itemsCount() { - return that.itemsCount(true); - }, - items() { - return that._dataSource.items(); - }, - viewportItems(items) { - if (items) { - that._items = items; - } - return that._items; - }, - onChanged(e) { - that.changed.fire(e); - }, - changingDuration() { - if (that.isLoading()) { - return LOAD_TIMEOUT; - } + return dataSource.pageIndex.apply(dataSource, args); + } - return that._renderTime || 0; - }, - }; - }, - _handleLoadingChanged(isLoading) { - if (this.option(LEGACY_SCROLLING_MODE) === false) { - this.callBase.apply(this, arguments); - return; - } + const virtualScrollController = that._virtualScrollController; + return virtualScrollController[methodName].apply(virtualScrollController, args); +}; - if (!isVirtualMode(this) || this._isLoadingAll) { - this._isLoading = isLoading; - this.callBase.apply(this, arguments); - } +export const dataSourceAdapterExtender = (Base: ModuleType) => class VirtualScrollingCoreDataSourceAdapterExtender extends Base { + _totalCount: any; - if (isLoading) { - this._startLoadTime = new Date(); - } else { - this._startLoadTime = undefined; - } - }, - _handleLoadError() { - if (this.option(LEGACY_SCROLLING_MODE) !== false) { - this._isLoading = false; - this.loadingChanged.fire(false); - } + _isLoaded: any; - this.callBase.apply(this, arguments); - }, - _handleDataChanged(e) { - if (this.option(LEGACY_SCROLLING_MODE) === false) { - this._items = this._dataSource.items().slice(); - this._totalCount = this._dataSourceTotalCount(true); - this.callBase.apply(this, arguments); - return; - } + _loadPageCount: any; - const callBase = this.callBase.bind(this); + _virtualScrollController: any; - this._virtualScrollController.handleDataChanged(callBase, e); - }, - _customizeRemoteOperations(options, operationTypes) { - const newMode = this.option(LEGACY_SCROLLING_MODE) === false; - let renderAsync = this.option('scrolling.renderAsync'); + _renderTime: any; - if (!isDefined(renderAsync)) { - renderAsync = this._renderTime >= this.option('scrolling.renderingThreshold'); - } + _isLoading: any; - if ((isVirtualMode(this) || (isAppendMode(this) && newMode)) && !operationTypes.reload && (operationTypes.skip || newMode) && !renderAsync) { - options.delay = undefined; - } + _startLoadTime: any; - this.callBase.apply(this, arguments); - }, - items() { - return this._items; - }, - _dataSourceTotalCount(isBase) { - return this.option(LEGACY_SCROLLING_MODE) === false && isVirtualMode(this) && !isBase ? this._totalCount : this.callBase(); - }, - itemsCount(isBase) { - if (isBase || this.option(LEGACY_SCROLLING_MODE) === false) { - return this.callBase(); - } - return this._virtualScrollController.itemsCount(); - }, - load(loadOptions) { - if (this.option(LEGACY_SCROLLING_MODE) === false || loadOptions) { - return this.callBase(loadOptions); - } - return this._virtualScrollController.load(); - }, - isLoading() { - return this.option(LEGACY_SCROLLING_MODE) === false ? this._dataSource.isLoading() : this._isLoading; - }, - isLoaded() { - return this._dataSource.isLoaded() && this._isLoaded; - }, - resetPagesCache(isLiveUpdate) { - if (!isLiveUpdate) { - this._virtualScrollController.reset(true); - } - this.callBase.apply(this, arguments); - }, - _changeRowExpandCore() { - const result = this.callBase.apply(this, arguments); + init() { + super.init.apply(this, arguments as any); + this._items = []; + this._totalCount = -1; + this._isLoaded = true; + this._loadPageCount = 1; - if (this.option(LEGACY_SCROLLING_MODE) === false) { - return result; - } + this._virtualScrollController = new VirtualScrollController(this.component, this._getVirtualScrollDataOptions()); + } + + _getVirtualScrollDataOptions() { + const that = this; + return { + pageSize() { + return that.pageSize(); + }, + totalItemsCount() { + return that.totalItemsCount(); + }, + hasKnownLastPage() { + return that.hasKnownLastPage(); + }, + pageIndex(index) { + return that._dataSource.pageIndex(index); + }, + isLoading() { + return that._dataSource.isLoading() && !that.isCustomLoading(); + }, + pageCount() { + return that.pageCount(); + }, + load() { + return that._dataSource.load(); + }, + updateLoading() { + updateLoading(that); + }, + itemsCount() { + return that.itemsCount(true); + }, + items() { + return that._dataSource.items(); + }, + viewportItems(items) { + if (items) { + that._items = items; + } + return that._items; + }, + onChanged(e) { + that.changed.fire(e); + }, + changingDuration() { + if (that.isLoading()) { + return LOAD_TIMEOUT; + } - this.resetPagesCache(); - updateLoading(this); + return that._renderTime || 0; + }, + }; + } + + _handleLoadingChanged(isLoading) { + if (this.option(LEGACY_SCROLLING_MODE) === false) { + super._handleLoadingChanged.apply(this, arguments as any); + return; + } + + if (!isVirtualMode(this) || this._isLoadingAll) { + this._isLoading = isLoading; + super._handleLoadingChanged.apply(this, arguments as any); + } + if (isLoading) { + this._startLoadTime = new Date(); + } else { + this._startLoadTime = undefined; + } + } + + _handleLoadError() { + if (this.option(LEGACY_SCROLLING_MODE) !== false) { + this._isLoading = false; + this.loadingChanged.fire(false); + } + + super._handleLoadError.apply(this, arguments as any); + } + + _handleDataChanged(e) { + if (this.option(LEGACY_SCROLLING_MODE) === false) { + this._items = this._dataSource.items().slice(); + this._totalCount = this._dataSourceTotalCount(true); + super._handleDataChanged.apply(this, arguments as any); + return; + } + + const callBase = super._handleDataChanged.bind(this); + + this._virtualScrollController.handleDataChanged(callBase, e); + } + + _customizeRemoteOperations(options, operationTypes) { + const newMode = this.option(LEGACY_SCROLLING_MODE) === false; + let renderAsync = this.option('scrolling.renderAsync'); + + if (!isDefined(renderAsync)) { + renderAsync = this._renderTime >= this.option('scrolling.renderingThreshold'); + } + + if ((isVirtualMode(this) || (isAppendMode(this) && newMode)) && !operationTypes.reload && (operationTypes.skip || newMode) && !renderAsync) { + options.delay = undefined; + } + + super._customizeRemoteOperations.apply(this, arguments as any); + } + + items() { + return this._items; + } + + _dataSourceTotalCount(isBase?) { + return this.option(LEGACY_SCROLLING_MODE) === false && isVirtualMode(this) && !isBase ? this._totalCount : super._dataSourceTotalCount(); + } + + itemsCount(isBase?) { + if (isBase || this.option(LEGACY_SCROLLING_MODE) === false) { + return super.itemsCount(); + } + return this._virtualScrollController.itemsCount(); + } + + load(loadOptions) { + if (this.option(LEGACY_SCROLLING_MODE) === false || loadOptions) { + return super.load(loadOptions); + } + return this._virtualScrollController.load(); + } + + isLoading() { + return this.option(LEGACY_SCROLLING_MODE) === false ? this._dataSource.isLoading() : this._isLoading; + } + + isLoaded() { + return this._dataSource.isLoaded() && this._isLoaded; + } + + resetPagesCache(isLiveUpdate?) { + if (!isLiveUpdate) { + this._virtualScrollController.reset(true); + } + super.resetPagesCache.apply(this, arguments as any); + } + + _changeRowExpandCore() { + const result = super._changeRowExpandCore.apply(this, arguments as any); + + if (this.option(LEGACY_SCROLLING_MODE) === false) { return result; - }, - reload() { - this._dataSource.pageIndex(this.pageIndex()); - const virtualScrollController = this._virtualScrollController; + } - if (this.option(LEGACY_SCROLLING_MODE) !== false && virtualScrollController) { - // @ts-expect-error - const d = new Deferred(); - this.callBase.apply(this, arguments).done((r) => { - const delayDeferred = virtualScrollController.getDelayDeferred(); - if (delayDeferred) { - delayDeferred.done(d.resolve).fail(d.reject); - } else { - d.resolve(r); - } - }).fail(d.reject); - return d; - } - return this.callBase.apply(this, arguments); - }, - refresh(options, operationTypes) { - if (this.option(LEGACY_SCROLLING_MODE) !== false) { - const { storeLoadOptions } = options; - const dataSource = this._dataSource; - - if (operationTypes.reload) { - this._virtualScrollController.reset(); - dataSource.items().length = 0; - this._isLoaded = false; - - updateLoading(this); - this._isLoaded = true; - - if (isAppendMode(this)) { - this.pageIndex(0); - dataSource.pageIndex(0); - storeLoadOptions.pageIndex = 0; - options.pageIndex = 0; - storeLoadOptions.skip = 0; - } else { - dataSource.pageIndex(this.pageIndex()); - if (dataSource.paginate()) { - options.pageIndex = this.pageIndex(); - storeLoadOptions.skip = this.pageIndex() * this.pageSize(); - } + this.resetPagesCache(); + updateLoading(this); + + return result; + } + + reload() { + this._dataSource.pageIndex(this.pageIndex()); + const virtualScrollController = this._virtualScrollController; + + if (this.option(LEGACY_SCROLLING_MODE) !== false && virtualScrollController) { + // @ts-expect-error + const d = new Deferred(); + super.reload.apply(this, arguments as any).done((r) => { + const delayDeferred = virtualScrollController.getDelayDeferred(); + if (delayDeferred) { + delayDeferred.done(d.resolve).fail(d.reject); + } else { + d.resolve(r); + } + }).fail(d.reject); + return d; + } + return super.reload.apply(this, arguments as any); + } + + refresh(options, operationTypes) { + if (this.option(LEGACY_SCROLLING_MODE) !== false) { + const { storeLoadOptions } = options; + const dataSource = this._dataSource; + + if (operationTypes.reload) { + this._virtualScrollController.reset(); + dataSource.items().length = 0; + this._isLoaded = false; + + updateLoading(this); + this._isLoaded = true; + + if (isAppendMode(this)) { + this.pageIndex(0); + dataSource.pageIndex(0); + storeLoadOptions.pageIndex = 0; + options.pageIndex = 0; + storeLoadOptions.skip = 0; + } else { + dataSource.pageIndex(this.pageIndex()); + if (dataSource.paginate()) { + options.pageIndex = this.pageIndex(); + storeLoadOptions.skip = this.pageIndex() * this.pageSize(); } - } else if (isAppendMode(this) && storeLoadOptions.skip && this._totalCountCorrection < 0) { - storeLoadOptions.skip += this._totalCountCorrection; } + } else if (isAppendMode(this) && storeLoadOptions.skip && this._totalCountCorrection < 0) { + storeLoadOptions.skip += this._totalCountCorrection; } + } - return this.callBase.apply(this, arguments); - }, - dispose() { - this._virtualScrollController.dispose(); - this.callBase.apply(this, arguments); - }, - loadPageCount(count) { - if (!isDefined(count)) { - return this._loadPageCount; - } - this._loadPageCount = count; - }, - _handleDataLoading(options) { - const loadPageCount = this.loadPageCount(); - const pageSize = this.pageSize(); - const newMode = this.option(LEGACY_SCROLLING_MODE) === false; - const { storeLoadOptions } = options; - const takeIsDefined = isDefined(storeLoadOptions.take); + return super.refresh.apply(this, arguments as any); + } - options.loadPageCount = loadPageCount; - if (!options.isCustomLoading && newMode && takeIsDefined && loadPageCount > 1 && pageSize > 0) { - storeLoadOptions.take = loadPageCount * pageSize; - } - this.callBase.apply(this, arguments); - }, - _loadPageSize() { - return this.callBase.apply(this, arguments) * this.loadPageCount(); - }, - }; + dispose() { + this._virtualScrollController.dispose(); + super.dispose.apply(this, arguments as any); + } - [ - 'beginPageIndex', - 'endPageIndex', - 'pageIndex', - ].forEach((name) => { - result[name] = function () { - if (this.option(LEGACY_SCROLLING_MODE) === false) { - const dataSource = this._dataSource; - return dataSource.pageIndex.apply(dataSource, arguments); - } + loadPageCount(count?) { + if (!isDefined(count)) { + return this._loadPageCount; + } + this._loadPageCount = count; + } - const virtualScrollController = this._virtualScrollController; - return virtualScrollController[name].apply(virtualScrollController, arguments); - }; - }); + _handleDataLoading(options) { + const loadPageCount = this.loadPageCount(); + const pageSize = this.pageSize(); + const newMode = this.option(LEGACY_SCROLLING_MODE) === false; + const { storeLoadOptions } = options; + const takeIsDefined = isDefined(storeLoadOptions.take); - [ - 'virtualItemsCount', - 'getContentOffset', - 'getVirtualContentSize', - 'setContentItemSizes', 'setViewportPosition', - 'getViewportItemIndex', 'setViewportItemIndex', 'getItemIndexByPosition', - 'viewportSize', 'viewportItemSize', 'getItemSize', 'getItemSizes', - 'loadIfNeed', - ].forEach((name) => { - result[name] = function () { - const virtualScrollController = this._virtualScrollController; - return virtualScrollController[name].apply(virtualScrollController, arguments); - }; - }); + options.loadPageCount = loadPageCount; + if (!options.isCustomLoading && newMode && takeIsDefined && loadPageCount > 1 && pageSize > 0) { + storeLoadOptions.take = loadPageCount * pageSize; + } + super._handleDataLoading.apply(this, arguments as any); + } - return result; -}()); + _loadPageSize() { + return super._loadPageSize.apply(this, arguments as any) * this.loadPageCount(); + } + + beginPageIndex(): any { + return proxyDataSourceAdapterMethod(this, 'beginPageIndex', [...arguments]); + } + + endPageIndex(): any { + return proxyDataSourceAdapterMethod(this, 'endPageIndex', [...arguments]); + } + + // eslint-disable-next-line @typescript-eslint/no-unused-vars + pageIndex(pageIndex?): any { + return proxyDataSourceAdapterMethod(this, 'pageIndex', [...arguments]); + } + + virtualItemsCount(): any { + return proxyDataSourceAdapterMethod(this, 'virtualItemsCount', [...arguments]); + } + + getContentOffset(): any { + return proxyDataSourceAdapterMethod(this, 'getContentOffset', [...arguments]); + } + + getVirtualContentSize(): any { + return proxyDataSourceAdapterMethod(this, 'getVirtualContentSize', [...arguments]); + } + + setContentItemSizes(): any { + return proxyDataSourceAdapterMethod(this, 'setContentItemSizes', [...arguments]); + } + + setViewportPosition(): any { + return proxyDataSourceAdapterMethod(this, 'setViewportPosition', [...arguments]); + } + + getViewportItemIndex(): any { + return proxyDataSourceAdapterMethod(this, 'getViewportItemIndex', [...arguments]); + } + + // eslint-disable-next-line @typescript-eslint/no-unused-vars + setViewportItemIndex(viewportItemIndex?): any { + return proxyDataSourceAdapterMethod(this, 'setViewportItemIndex', [...arguments]); + } + + getItemIndexByPosition(): any { + return proxyDataSourceAdapterMethod(this, 'getItemIndexByPosition', [...arguments]); + } + + viewportSize(): any { + return proxyDataSourceAdapterMethod(this, 'viewportSize', [...arguments]); + } + + viewportItemSize(): any { + return proxyDataSourceAdapterMethod(this, 'viewportItemSize', [...arguments]); + } + + getItemSize(): any { + return proxyDataSourceAdapterMethod(this, 'getItemSize', [...arguments]); + } + + getItemSizes(): any { + return proxyDataSourceAdapterMethod(this, 'getItemSizes', [...arguments]); + } + + loadIfNeed(): any { + return proxyDataSourceAdapterMethod(this, 'loadIfNeed', [...arguments]); + } +}; const VirtualScrollingRowsViewExtender = (function () { const removeEmptyRows = function ($emptyRows, className) { @@ -828,7 +910,6 @@ export const virtualScrollingModule = { }; }, extenders: { - dataSourceAdapter: VirtualScrollingDataSourceAdapterExtender, controllers: { data: (function () { const members = { diff --git a/packages/devextreme/js/__internal/grids/tree_list/data_source_adapter/m_data_source_adapter.ts b/packages/devextreme/js/__internal/grids/tree_list/data_source_adapter/m_data_source_adapter.ts index e9ceab65be22..6b1bedbe5527 100644 --- a/packages/devextreme/js/__internal/grids/tree_list/data_source_adapter/m_data_source_adapter.ts +++ b/packages/devextreme/js/__internal/grids/tree_list/data_source_adapter/m_data_source_adapter.ts @@ -20,846 +20,876 @@ const DEFAULT_KEY_EXPRESSION = 'id'; const isFullBranchFilterMode = (that) => that.option('filterMode') === 'fullBranch'; -let DataSourceAdapterTreeList = DataSourceAdapter.inherit((function () { - const getChildKeys = function (that, keys) { - const childKeys: any[] = []; +const getChildKeys = function (that, keys) { + const childKeys: any[] = []; - keys.forEach((key) => { - const node = that.getNodeByKey(key); + keys.forEach((key) => { + const node = that.getNodeByKey(key); - node && node.children.forEach((child) => { - childKeys.push(child.key); - }); + node && node.children.forEach((child) => { + childKeys.push(child.key); }); + }); - return childKeys; - }; + return childKeys; +}; - const applySorting = (data: any[], sort: any): any => queryByOptions( - query(data), - { - sort, - }, - ).toArray(); +const applySorting = (data: any[], sort: any): any => queryByOptions( + query(data), + { + sort, + }, +).toArray(); - return { - _createKeyGetter() { - const keyExpr = this.getKeyExpr(); +class DataSourceAdapterTreeList extends DataSourceAdapter { + _indexByKey: any; - return compileGetter(keyExpr); - }, + _keyGetter: any; - _createKeySetter() { - const keyExpr = this.getKeyExpr(); + _parentIdGetter: any; - if (isFunction(keyExpr)) { - return keyExpr; - } + _hasItemsGetter: any; - return compileSetter(keyExpr); - }, + _itemsGetter: any; - createParentIdGetter() { - return compileGetter(this.option('parentIdExpr')); - }, + _keySetter: any; - createParentIdSetter() { - const parentIdExpr = this.option('parentIdExpr'); + _parentIdSetter: any; - if (isFunction(parentIdExpr)) { - return parentIdExpr; - } + _hasItemsSetter: any; - return compileSetter(parentIdExpr); - }, + _isChildrenLoaded: any; - _createItemsGetter() { - return compileGetter(this.option('itemsExpr')); - }, + _nodeByKey: any; - _createHasItemsGetter() { - const hasItemsExpr = this.option('hasItemsExpr'); + _isReload: any; - return hasItemsExpr && compileGetter(hasItemsExpr); - }, + _rootNode: any; - _createHasItemsSetter() { - const hasItemsExpr = this.option('hasItemsExpr'); + _isNodesInitializing: any; - if (isFunction(hasItemsExpr)) { - return hasItemsExpr; - } + _totalItemsCount: any; - return hasItemsExpr && compileSetter(hasItemsExpr); - }, + _createKeyGetter() { + const keyExpr = this.getKeyExpr(); - _updateIndexByKeyObject(items) { - const that = this; + return compileGetter(keyExpr); + } - that._indexByKey = {}; + _createKeySetter() { + const keyExpr = this.getKeyExpr(); - each(items, (index, item) => { - that._indexByKey[item.key] = index; - }); - }, + if (isFunction(keyExpr)) { + return keyExpr; + } - _calculateHasItems(node, options) { - const that = this; - const { parentIds } = options.storeLoadOptions; - let hasItems; - const isFullBranch = isFullBranchFilterMode(that); + return compileSetter(keyExpr); + } - if (that._hasItemsGetter && (parentIds || !options.storeLoadOptions.filter || isFullBranch)) { - hasItems = that._hasItemsGetter(node.data); - } + createParentIdGetter() { + return compileGetter(this.option('parentIdExpr')); + } - if (hasItems === undefined) { - if (!that._isChildrenLoaded[node.key] && options.remoteOperations.filtering && (parentIds || isFullBranch)) { - hasItems = true; - } else if (options.loadOptions.filter && !options.remoteOperations.filtering && isFullBranch) { - hasItems = node.children.length; - } else { - hasItems = node.hasChildren; - } + createParentIdSetter() { + const parentIdExpr = this.option('parentIdExpr'); + + if (isFunction(parentIdExpr)) { + return parentIdExpr; + } + + return compileSetter(parentIdExpr); + } + + _createItemsGetter() { + return compileGetter(this.option('itemsExpr')); + } + + _createHasItemsGetter() { + const hasItemsExpr = this.option('hasItemsExpr'); + + return hasItemsExpr && compileGetter(hasItemsExpr); + } + + _createHasItemsSetter() { + const hasItemsExpr = this.option('hasItemsExpr'); + + if (isFunction(hasItemsExpr)) { + return hasItemsExpr; + } + + return hasItemsExpr && compileSetter(hasItemsExpr); + } + + _updateIndexByKeyObject(items) { + const that = this; + + that._indexByKey = {}; + + each(items, (index, item) => { + that._indexByKey[item.key] = index; + }); + } + + _calculateHasItems(node, options) { + const that = this; + const { parentIds } = options.storeLoadOptions; + let hasItems; + const isFullBranch = isFullBranchFilterMode(that); + + if (that._hasItemsGetter && (parentIds || !options.storeLoadOptions.filter || isFullBranch)) { + hasItems = that._hasItemsGetter(node.data); + } + + if (hasItems === undefined) { + if (!that._isChildrenLoaded[node.key] && options.remoteOperations.filtering && (parentIds || isFullBranch)) { + hasItems = true; + } else if (options.loadOptions.filter && !options.remoteOperations.filtering && isFullBranch) { + hasItems = node.children.length; + } else { + hasItems = node.hasChildren; } - return !!hasItems; - }, + } + return !!hasItems; + } - _fillVisibleItemsByNodes(nodes, options, result) { - for (let i = 0; i < nodes.length; i++) { - if (nodes[i].visible) { - result.push(nodes[i]); - } + _fillVisibleItemsByNodes(nodes, options, result) { + for (let i = 0; i < nodes.length; i++) { + if (nodes[i].visible) { + result.push(nodes[i]); + } - if ((this.isRowExpanded(nodes[i].key, options) || !nodes[i].visible) && nodes[i].hasChildren && nodes[i].children.length) { - this._fillVisibleItemsByNodes(nodes[i].children, options, result); - } + if ((this.isRowExpanded(nodes[i].key, options) || !nodes[i].visible) && nodes[i].hasChildren && nodes[i].children.length) { + this._fillVisibleItemsByNodes(nodes[i].children, options, result); } - }, + } + } - _convertItemToNode(item, rootValue, nodeByKey) { - const key = this._keyGetter(item); - let parentId = this._parentIdGetter(item); + _convertItemToNode(item, rootValue, nodeByKey) { + const key = this._keyGetter(item); + let parentId = this._parentIdGetter(item); - parentId = isDefined(parentId) ? parentId : rootValue; - const parentNode = nodeByKey[parentId] = nodeByKey[parentId] || { key: parentId, children: [] }; + parentId = isDefined(parentId) ? parentId : rootValue; + const parentNode = nodeByKey[parentId] = nodeByKey[parentId] || { key: parentId, children: [] }; - const node = nodeByKey[key] = nodeByKey[key] || { key, children: [] }; - node.data = item; - node.parent = parentNode; + const node = nodeByKey[key] = nodeByKey[key] || { key, children: [] }; + node.data = item; + node.parent = parentNode; - return node; - }, + return node; + } - _createNodesByItems(items, visibleItems) { - const that = this; - const rootValue = that.option('rootValue'); - const visibleByKey = {}; - const nodeByKey = that._nodeByKey = {}; - let i; + _createNodesByItems(items, visibleItems) { + const that = this; + const rootValue: any = that.option('rootValue'); + const visibleByKey = {}; + const nodeByKey = that._nodeByKey = {}; + let i; - if (visibleItems) { - for (i = 0; i < visibleItems.length; i++) { - visibleByKey[this._keyGetter(visibleItems[i])] = true; - } + if (visibleItems) { + for (i = 0; i < visibleItems.length; i++) { + visibleByKey[this._keyGetter(visibleItems[i])] = true; } + } - for (i = 0; i < items.length; i++) { - const node = that._convertItemToNode(items[i], rootValue, nodeByKey); + for (i = 0; i < items.length; i++) { + const node = that._convertItemToNode(items[i], rootValue, nodeByKey); - if (node.key === undefined) { - return; - } + if (node.key === undefined) { + return; + } - node.visible = !visibleItems || !!visibleByKey[node.key]; - if (node.parent) { - node.parent.children.push(node); - } + node.visible = !visibleItems || !!visibleByKey[node.key]; + if (node.parent) { + node.parent.children.push(node); } + } - const rootNode = nodeByKey[rootValue] || { key: rootValue, children: [] }; + const rootNode = nodeByKey[rootValue] || { key: rootValue, children: [] }; - rootNode.level = -1; + rootNode.level = -1; - return rootNode; - }, + return rootNode; + } - _convertDataToPlainStructure(data, parentId, result) { - let key; + _convertDataToPlainStructure(data, parentId?, result?) { + let key; - if (this._itemsGetter && !data.isConverted) { - result = result || []; + if (this._itemsGetter && !data.isConverted) { + result = result || []; - for (let i = 0; i < data.length; i++) { - const item = createObjectWithChanges(data[i]); + for (let i = 0; i < data.length; i++) { + const item = createObjectWithChanges(data[i]); - key = this._keyGetter(item); - if (key === undefined) { - key = result.length + 1; - this._keySetter(item, key); - } + key = this._keyGetter(item); + if (key === undefined) { + key = result.length + 1; + this._keySetter(item, key); + } - this._parentIdSetter(item, parentId === undefined ? this.option('rootValue') : parentId); + this._parentIdSetter(item, parentId === undefined ? this.option('rootValue') : parentId); - result.push(item); + result.push(item); - const childItems = this._itemsGetter(item); - if (childItems && childItems.length) { - this._convertDataToPlainStructure(childItems, key, result); + const childItems = this._itemsGetter(item); + if (childItems && childItems.length) { + this._convertDataToPlainStructure(childItems, key, result); - const itemsExpr = this.option('itemsExpr'); - if (!isFunction(itemsExpr)) { - // eslint-disable-next-line @typescript-eslint/no-dynamic-delete - delete item[itemsExpr]; - } + const itemsExpr = this.option('itemsExpr'); + if (!isFunction(itemsExpr)) { + // eslint-disable-next-line @typescript-eslint/no-dynamic-delete + delete item[itemsExpr]; } } + } - result.isConverted = true; + result.isConverted = true; - return result; - } + return result; + } - return data; - }, + return data; + } - _createIdFilter(field, keys) { - const parentIdFilters: any[] = []; + _createIdFilter(field, keys) { + const parentIdFilters: any[] = []; - for (let i = 0; i < keys.length; i++) { - parentIdFilters.push([field, '=', keys[i]]); - } - return gridCoreUtils.combineFilters(parentIdFilters, 'or'); - }, + for (let i = 0; i < keys.length; i++) { + parentIdFilters.push([field, '=', keys[i]]); + } + return gridCoreUtils.combineFilters(parentIdFilters, 'or'); + } - _customizeRemoteOperations(options, operationTypes) { - this.callBase.apply(this, arguments); + _customizeRemoteOperations(options, operationTypes) { + super._customizeRemoteOperations.apply(this, arguments as any); - options.remoteOperations.paging = false; + options.remoteOperations.paging = false; - let expandVisibleNodes = false; + let expandVisibleNodes = false; - if (this.option('autoExpandAll')) { - options.remoteOperations.sorting = false; - options.remoteOperations.filtering = false; - if ((!this._lastLoadOptions || operationTypes.filtering && !options.storeLoadOptions.filter) && !options.isCustomLoading) { - expandVisibleNodes = true; - } + if (this.option('autoExpandAll')) { + options.remoteOperations.sorting = false; + options.remoteOperations.filtering = false; + if ((!this._lastLoadOptions || operationTypes.filtering && !options.storeLoadOptions.filter) && !options.isCustomLoading) { + expandVisibleNodes = true; } + } - if (!options.isCustomLoading) { - this._isReload = this._isReload || operationTypes.reload; + if (!options.isCustomLoading) { + this._isReload = this._isReload || operationTypes.reload; - if (!options.cachedStoreData) { - this._isChildrenLoaded = {}; + if (!options.cachedStoreData) { + this._isChildrenLoaded = {}; - if (this._isReload) { - this._nodeByKey = {}; - } + if (this._isReload) { + this._nodeByKey = {}; } + } - if (this.option('expandNodesOnFiltering') && (operationTypes.filtering || this._isReload && options.storeLoadOptions.filter)) { - if (options.storeLoadOptions.filter) { - expandVisibleNodes = true; - } else { - options.collapseVisibleNodes = true; - } + if (this.option('expandNodesOnFiltering') && (operationTypes.filtering || this._isReload && options.storeLoadOptions.filter)) { + if (options.storeLoadOptions.filter) { + expandVisibleNodes = true; + } else { + options.collapseVisibleNodes = true; } } + } - options.expandVisibleNodes = expandVisibleNodes; - }, + options.expandVisibleNodes = expandVisibleNodes; + } - _getParentIdsToLoad(parentIds) { - const parentIdsToLoad: any[] = []; + _getParentIdsToLoad(parentIds) { + const parentIdsToLoad: any[] = []; - for (let i = 0; i < parentIds.length; i++) { - const node = this.getNodeByKey(parentIds[i]); + for (let i = 0; i < parentIds.length; i++) { + const node = this.getNodeByKey(parentIds[i]); - if (!node || node.hasChildren && !node.children.length) { - parentIdsToLoad.push(parentIds[i]); - } + if (!node || node.hasChildren && !node.children.length) { + parentIdsToLoad.push(parentIds[i]); } + } - return parentIdsToLoad; - }, + return parentIdsToLoad; + } - _handleCustomizeStoreLoadOptions(options) { - const rootValue = this.option('rootValue'); - const parentIdExpr = this.option('parentIdExpr'); - let { parentIds } = options.storeLoadOptions; + _handleCustomizeStoreLoadOptions(options) { + const rootValue: any = this.option('rootValue'); + const parentIdExpr = this.option('parentIdExpr'); + let { parentIds } = options.storeLoadOptions; - if (parentIds) { - options.isCustomLoading = false; - } + if (parentIds) { + options.isCustomLoading = false; + } - this.callBase.apply(this, arguments); + super._handleCustomizeStoreLoadOptions.apply(this, arguments as any); - if (options.remoteOperations.filtering && !options.isCustomLoading) { - if (isFullBranchFilterMode(this) && options.cachedStoreData || !options.storeLoadOptions.filter) { - const expandedRowKeys = options.collapseVisibleNodes ? [] : this.option('expandedRowKeys'); - parentIds = [rootValue].concat(expandedRowKeys).concat(parentIds || []); - const parentIdsToLoad = options.data ? this._getParentIdsToLoad(parentIds) : parentIds; + if (options.remoteOperations.filtering && !options.isCustomLoading) { + if (isFullBranchFilterMode(this) && options.cachedStoreData || !options.storeLoadOptions.filter) { + const expandedRowKeys = options.collapseVisibleNodes ? [] : this.option('expandedRowKeys'); + parentIds = [rootValue].concat(expandedRowKeys).concat(parentIds || []); + const parentIdsToLoad = options.data ? this._getParentIdsToLoad(parentIds) : parentIds; - if (parentIdsToLoad.length) { - options.cachedPagingData = undefined; - options.data = undefined; - options.mergeStoreLoadData = true; - options.delay = this.option('loadingTimeout'); // T991320 - } - - options.storeLoadOptions.parentIds = parentIdsToLoad; - options.storeLoadOptions.filter = this._createIdFilter(parentIdExpr, parentIdsToLoad); + if (parentIdsToLoad.length) { + options.cachedPagingData = undefined; + options.data = undefined; + options.mergeStoreLoadData = true; + options.delay = this.option('loadingTimeout'); // T991320 } - } - }, - - _generateInfoToLoad(data, needChildren) { - const that = this; - let key; - const keyMap = {}; - const resultKeyMap = {}; - const resultKeys: any[] = []; - const rootValue = that.option('rootValue'); - let i; - for (i = 0; i < data.length; i++) { - key = needChildren ? that._parentIdGetter(data[i]) : that._keyGetter(data[i]); - keyMap[key] = true; + options.storeLoadOptions.parentIds = parentIdsToLoad; + options.storeLoadOptions.filter = this._createIdFilter(parentIdExpr, parentIdsToLoad); + } + } + } + + _generateInfoToLoad(data, needChildren) { + const that = this; + let key; + const keyMap = {}; + const resultKeyMap = {}; + const resultKeys: any[] = []; + const rootValue = that.option('rootValue'); + let i; + + for (i = 0; i < data.length; i++) { + key = needChildren ? that._parentIdGetter(data[i]) : that._keyGetter(data[i]); + keyMap[key] = true; + } + + for (i = 0; i < data.length; i++) { + key = needChildren ? that._keyGetter(data[i]) : that._parentIdGetter(data[i]); + const needToLoad = needChildren ? that.isRowExpanded(key) : key !== rootValue; + + if (!keyMap[key] && !resultKeyMap[key] && needToLoad) { + resultKeyMap[key] = true; + resultKeys.push(key); + } + } + + return { + keyMap: resultKeyMap, + keys: resultKeys, + }; + } + + _loadParentsOrChildren(data, options, needChildren?) { + let filter; + let needLocalFiltering; + const { keys, keyMap } = this._generateInfoToLoad(data, needChildren); + // @ts-expect-error + const d = new Deferred(); + const isRemoteFiltering = options.remoteOperations.filtering; + const maxFilterLengthInRequest = this.option('maxFilterLengthInRequest'); + const sort = options.storeLoadOptions?.sort ?? options.loadOptions?.sort; + let loadOptions = isRemoteFiltering ? options.storeLoadOptions : options.loadOptions; + + const concatLoadedData = (loadedData): any => { + if (isRemoteFiltering) { + this._cachedStoreData = applySorting( + this._cachedStoreData.concat(loadedData), + sort, + ); } - for (i = 0; i < data.length; i++) { - key = needChildren ? that._keyGetter(data[i]) : that._parentIdGetter(data[i]); - const needToLoad = needChildren ? that.isRowExpanded(key) : key !== rootValue; + return applySorting( + data.concat(loadedData), + sort, + ); + }; - if (!keyMap[key] && !resultKeyMap[key] && needToLoad) { - resultKeyMap[key] = true; - resultKeys.push(key); - } - } + if (!keys.length) { + return d.resolve(data); + } - return { - keyMap: resultKeyMap, - keys: resultKeys, - }; - }, - - _loadParentsOrChildren(data, options, needChildren) { - let filter; - let needLocalFiltering; - const { keys, keyMap } = this._generateInfoToLoad(data, needChildren); - // @ts-expect-error - const d = new Deferred(); - const isRemoteFiltering = options.remoteOperations.filtering; - const maxFilterLengthInRequest = this.option('maxFilterLengthInRequest'); - const sort = options.storeLoadOptions?.sort ?? options.loadOptions?.sort; - let loadOptions = isRemoteFiltering ? options.storeLoadOptions : options.loadOptions; - - const concatLoadedData = (loadedData): any => { - if (isRemoteFiltering) { - this._cachedStoreData = applySorting( - this._cachedStoreData.concat(loadedData), - sort, - ); - } + let cachedNodes = keys.map((id) => this.getNodeByKey(id)).filter((node) => node && node.data); - return applySorting( - data.concat(loadedData), - sort, - ); - }; + if (cachedNodes.length === keys.length) { + if (needChildren) { + cachedNodes = cachedNodes.reduce((result, node) => result.concat(node.children), []); + } - if (!keys.length) { - return d.resolve(data); + if (cachedNodes.length) { + return this._loadParentsOrChildren(concatLoadedData(cachedNodes.map((node) => node.data)), options, needChildren); } + } - let cachedNodes = keys.map((id) => this.getNodeByKey(id)).filter((node) => node && node.data); + const keyExpr = needChildren ? this.option('parentIdExpr') : this.getKeyExpr(); + filter = this._createIdFilter(keyExpr, keys); + const filterLength = encodeURI(JSON.stringify(filter)).length; - if (cachedNodes.length === keys.length) { - if (needChildren) { - cachedNodes = cachedNodes.reduce((result, node) => result.concat(node.children), []); - } + if (filterLength > maxFilterLengthInRequest) { + filter = (itemData) => keyMap[needChildren ? this._parentIdGetter(itemData) : this._keyGetter(itemData)]; - if (cachedNodes.length) { - return this._loadParentsOrChildren(concatLoadedData(cachedNodes.map((node) => node.data)), options, needChildren); - } - } + needLocalFiltering = isRemoteFiltering; + } - const keyExpr = needChildren ? this.option('parentIdExpr') : this.getKeyExpr(); - filter = this._createIdFilter(keyExpr, keys); - const filterLength = encodeURI(JSON.stringify(filter)).length; + loadOptions = extend({}, loadOptions, { + filter: !needLocalFiltering ? filter : null, + }); - if (filterLength > maxFilterLengthInRequest) { - filter = (itemData) => keyMap[needChildren ? this._parentIdGetter(itemData) : this._keyGetter(itemData)]; + const store = options.fullData ? new ArrayStore(options.fullData) : this._dataSource.store(); - needLocalFiltering = isRemoteFiltering; + this.loadFromStore(loadOptions, store).done((loadedData) => { + if (loadedData.length) { + if (needLocalFiltering) { + loadedData = query(loadedData).filter(filter).toArray(); + } + this._loadParentsOrChildren(concatLoadedData(loadedData), options, needChildren).done(d.resolve).fail(d.reject); + } else { + d.resolve(data); } + }).fail(d.reject); - loadOptions = extend({}, loadOptions, { - filter: !needLocalFiltering ? filter : null, - }); + return d; + } - const store = options.fullData ? new ArrayStore(options.fullData) : this._dataSource.store(); + _loadParents(data, options) { + return this._loadParentsOrChildren(data, options); + } - this.loadFromStore(loadOptions, store).done((loadedData) => { - if (loadedData.length) { - if (needLocalFiltering) { - loadedData = query(loadedData).filter(filter).toArray(); - } - this._loadParentsOrChildren(concatLoadedData(loadedData), options, needChildren).done(d.resolve).fail(d.reject); - } else { - d.resolve(data); - } - }).fail(d.reject); + _loadChildrenIfNeed(data, options) { + if (isFullBranchFilterMode(this)) { + return this._loadParentsOrChildren(data, options, true); + } - return d; - }, + return when(data); + } - _loadParents(data, options) { - return this._loadParentsOrChildren(data, options); - }, + _updateHasItemsMap(options) { + const { parentIds } = options.storeLoadOptions; - _loadChildrenIfNeed(data, options) { - if (isFullBranchFilterMode(this)) { - return this._loadParentsOrChildren(data, options, true); + if (parentIds) { + for (let i = 0; i < parentIds.length; i++) { + this._isChildrenLoaded[parentIds[i]] = true; } + } + } - return when(data); - }, + _getKeyInfo() { + return { + key: () => 'key', + keyOf: (data) => data.key, + }; + } - _updateHasItemsMap(options) { - const { parentIds } = options.storeLoadOptions; + _processChanges(changes) { + let processedChanges: any[] = []; - if (parentIds) { - for (let i = 0; i < parentIds.length; i++) { - this._isChildrenLoaded[parentIds[i]] = true; - } + changes.forEach((change) => { + if (change.type === 'insert') { + processedChanges = processedChanges.concat(this._applyInsert(change)); + } else if (change.type === 'remove') { + processedChanges = processedChanges.concat(this._applyRemove(change)); + } else if (change.type === 'update') { + processedChanges.push({ type: change.type, key: change.key, data: { data: change.data } }); } - }, - - _getKeyInfo() { - return { - key: () => 'key', - keyOf: (data) => data.key, - }; - }, - - _processChanges(changes) { - let processedChanges: any[] = []; - - changes.forEach((change) => { - if (change.type === 'insert') { - processedChanges = processedChanges.concat(this._applyInsert(change)); - } else if (change.type === 'remove') { - processedChanges = processedChanges.concat(this._applyRemove(change)); - } else if (change.type === 'update') { - processedChanges.push({ type: change.type, key: change.key, data: { data: change.data } }); - } - }); + }); - return processedChanges; - }, + return processedChanges; + } - _handleChanging(e) { - this.callBase.apply(this, arguments); + _handleChanging(e) { + super._handleChanging.apply(this, arguments as any); - const processChanges = (changes) => { - const changesToProcess = changes.filter((item) => item.type === 'update'); - return this._processChanges(changesToProcess); - }; + const processChanges = (changes) => { + const changesToProcess = changes.filter((item) => item.type === 'update'); + return this._processChanges(changesToProcess); + }; - e.postProcessChanges = processChanges; - }, + e.postProcessChanges = processChanges; + } - _applyBatch(changes) { - const processedChanges = this._processChanges(changes); + _applyBatch(changes) { + const processedChanges = this._processChanges(changes); - this.callBase(processedChanges); - }, + super._applyBatch(processedChanges); + } - _setHasItems(node, value) { - const hasItemsSetter = this._hasItemsSetter; - node.hasChildren = value; - if (hasItemsSetter && node.data) { - hasItemsSetter(node.data, value); - } - }, + _setHasItems(node, value) { + const hasItemsSetter = this._hasItemsSetter; + node.hasChildren = value; + if (hasItemsSetter && node.data) { + hasItemsSetter(node.data, value); + } + } - _applyInsert(change) { - const that = this; - const baseChanges: any[] = []; - const parentId = that.parentKeyOf(change.data); - const parentNode = that.getNodeByKey(parentId); + _applyInsert(change) { + const that = this; + const baseChanges: any[] = []; + const parentId = that.parentKeyOf(change.data); + const parentNode = that.getNodeByKey(parentId); - if (parentNode) { - const rootValue = that.option('rootValue'); - const node = that._convertItemToNode(change.data, rootValue, that._nodeByKey); + if (parentNode) { + const rootValue = that.option('rootValue'); + const node = that._convertItemToNode(change.data, rootValue, that._nodeByKey); - node.hasChildren = false; - node.level = parentNode.level + 1; - node.visible = true; + node.hasChildren = false; + node.level = parentNode.level + 1; + node.visible = true; - parentNode.children.push(node); + parentNode.children.push(node); - that._isChildrenLoaded[node.key] = true; + that._isChildrenLoaded[node.key] = true; - that._setHasItems(parentNode, true); + that._setHasItems(parentNode, true); - if ((!parentNode.parent || that.isRowExpanded(parentNode.key)) && change.index !== undefined) { - let index = that.items().indexOf(parentNode) + 1; + if ((!parentNode.parent || that.isRowExpanded(parentNode.key)) && change.index !== undefined) { + let index = that.items().indexOf(parentNode) + 1; - index += change.index >= 0 ? Math.min(change.index, parentNode.children.length) : parentNode.children.length; + index += change.index >= 0 ? Math.min(change.index, parentNode.children.length) : parentNode.children.length; - baseChanges.push({ type: change.type, data: node, index }); - } + baseChanges.push({ type: change.type, data: node, index }); } + } - return baseChanges; - }, + return baseChanges; + } - _needToCopyDataObject() { - return false; - }, + _needToCopyDataObject() { + return false; + } - _applyRemove(change) { - let baseChanges: any[] = []; - const node = this.getNodeByKey(change.key); - const parentNode = node && node.parent; + _applyRemove(change) { + let baseChanges: any[] = []; + const node = this.getNodeByKey(change.key); + const parentNode = node && node.parent; - if (parentNode) { - const index = parentNode.children.indexOf(node); - if (index >= 0) { - parentNode.children.splice(index, 1); + if (parentNode) { + const index = parentNode.children.indexOf(node); + if (index >= 0) { + parentNode.children.splice(index, 1); - if (!parentNode.children.length) { - this._setHasItems(parentNode, false); - } - - baseChanges.push(change); - baseChanges = baseChanges.concat(this.getChildNodeKeys(change.key).map((key) => ({ type: change.type, key }))); + if (!parentNode.children.length) { + this._setHasItems(parentNode, false); } + + baseChanges.push(change); + baseChanges = baseChanges.concat(this.getChildNodeKeys(change.key).map((key) => ({ type: change.type, key }))); } + } - return baseChanges; - }, + return baseChanges; + } - _handleDataLoaded(options) { - const data = options.data = this._convertDataToPlainStructure(options.data); - if (!options.remoteOperations.filtering && options.loadOptions.filter) { - options.fullData = queryByOptions(query(options.data), { sort: options.loadOptions && options.loadOptions.sort }).toArray(); - } - this._updateHasItemsMap(options); - this.callBase(options); + _handleDataLoaded(options) { + const data = options.data = this._convertDataToPlainStructure(options.data); + if (!options.remoteOperations.filtering && options.loadOptions.filter) { + options.fullData = queryByOptions(query(options.data), { sort: options.loadOptions && options.loadOptions.sort }).toArray(); + } + this._updateHasItemsMap(options); + super._handleDataLoaded(options); - if (data.isConverted && this._cachedStoreData) { - this._cachedStoreData.isConverted = true; - } - }, - - _fillNodes(nodes, options, expandedRowKeys, level) { - const isFullBranch = isFullBranchFilterMode(this); - - level = level || 0; - for (let i = 0; i < nodes.length; i++) { - const node = nodes[i]; - let needToExpand = false; - - // node.hasChildren = false; - this._fillNodes(nodes[i].children, options, expandedRowKeys, level + 1); - - node.level = level; - node.hasChildren = this._calculateHasItems(node, options); - - if (node.visible && node.hasChildren) { - if (isFullBranch) { - if (node.children.filter((node) => node.visible).length) { - needToExpand = true; - } else if (node.children.length) { - treeListCore.foreachNodes(node.children, (node) => { - node.visible = true; - }); - } - } else { + if (data.isConverted && this._cachedStoreData) { + this._cachedStoreData.isConverted = true; + } + } + + _fillNodes(nodes, options, expandedRowKeys, level?) { + const isFullBranch = isFullBranchFilterMode(this); + + level = level || 0; + for (let i = 0; i < nodes.length; i++) { + const node = nodes[i]; + let needToExpand = false; + + // node.hasChildren = false; + this._fillNodes(nodes[i].children, options, expandedRowKeys, level + 1); + + node.level = level; + node.hasChildren = this._calculateHasItems(node, options); + + if (node.visible && node.hasChildren) { + if (isFullBranch) { + if (node.children.filter((node) => node.visible).length) { needToExpand = true; + } else if (node.children.length) { + treeListCore.foreachNodes(node.children, (node) => { + node.visible = true; + }); } - if (options.expandVisibleNodes && needToExpand) { - expandedRowKeys.push(node.key); - } + } else { + needToExpand = true; } - - if (node.visible || node.hasChildren) { - node.parent.hasChildren = true; + if (options.expandVisibleNodes && needToExpand) { + expandedRowKeys.push(node.key); } } - }, - _processTreeStructure(options, visibleItems) { - let { data } = options; - const { parentIds } = options.storeLoadOptions; - const expandedRowKeys = []; + if (node.visible || node.hasChildren) { + node.parent.hasChildren = true; + } + } + } - if (parentIds && parentIds.length || this._isReload) { - if (options.fullData && options.fullData.length > options.data.length) { - data = options.fullData; - visibleItems = visibleItems || options.data; - } + _processTreeStructure(options, visibleItems?) { + let { data } = options; + const { parentIds } = options.storeLoadOptions; + const expandedRowKeys = []; - this._rootNode = this._createNodesByItems(data, visibleItems); - if (!this._rootNode) { - // @ts-expect-error - options.data = new Deferred().reject(errors.Error('E1046', this.getKeyExpr())); - return; - } - this._fillNodes(this._rootNode.children, options, expandedRowKeys); + if (parentIds && parentIds.length || this._isReload) { + if (options.fullData && options.fullData.length > options.data.length) { + data = options.fullData; + visibleItems = visibleItems || options.data; + } - this._isNodesInitializing = true; - if (options.collapseVisibleNodes || expandedRowKeys.length) { - this.option('expandedRowKeys', expandedRowKeys); - } - this._isReload = false; - this.executeAction('onNodesInitialized', { root: this._rootNode }); - this._isNodesInitializing = false; + this._rootNode = this._createNodesByItems(data, visibleItems); + if (!this._rootNode) { + // @ts-expect-error + options.data = new Deferred().reject(errors.Error('E1046', this.getKeyExpr())); + return; + } + this._fillNodes(this._rootNode.children, options, expandedRowKeys); + + this._isNodesInitializing = true; + if (options.collapseVisibleNodes || expandedRowKeys.length) { + this.option('expandedRowKeys', expandedRowKeys); } + this._isReload = false; + this.executeAction('onNodesInitialized', { root: this._rootNode }); + this._isNodesInitializing = false; + } - const resultData = []; + const resultData = []; - this._fillVisibleItemsByNodes(this._rootNode.children, options, resultData); + this._fillVisibleItemsByNodes(this._rootNode.children, options, resultData); - options.data = resultData; - this._totalItemsCount = resultData.length; - }, + options.data = resultData; + this._totalItemsCount = resultData.length; + } - _handleDataLoadedCore(options) { - const that = this; - const { data } = options; - const { callBase } = that; - const filter = options.storeLoadOptions.filter || options.loadOptions.filter; - const filterMode = that.option('filterMode'); - let visibleItems; - const { parentIds } = options.storeLoadOptions; - const needLoadParents = filter && (!parentIds || !parentIds.length) && filterMode !== 'standard'; + _handleDataLoadedCore(options) { + const that = this; + const { data } = options; + const filter = options.storeLoadOptions.filter || options.loadOptions.filter; + const filterMode = that.option('filterMode'); + let visibleItems; + const { parentIds } = options.storeLoadOptions; + const needLoadParents = filter && (!parentIds || !parentIds.length) && filterMode !== 'standard'; - if (!options.isCustomLoading) { - if (needLoadParents) { - // @ts-expect-error - const d = options.data = new Deferred(); + if (!options.isCustomLoading) { + if (needLoadParents) { + // @ts-expect-error + const d = options.data = new Deferred(); - if (filterMode === 'matchOnly') { - visibleItems = data; - } - return that._loadParents(data, options).done((data) => { - that._loadChildrenIfNeed(data, options).done((data) => { - options.data = data; - that._processTreeStructure(options, visibleItems); - callBase.call(that, options); - d.resolve(options.data); - }); - }).fail(d.reject); + if (filterMode === 'matchOnly') { + visibleItems = data; } - that._processTreeStructure(options); + return that._loadParents(data, options).done((data) => { + that._loadChildrenIfNeed(data, options).done((data) => { + options.data = data; + that._processTreeStructure(options, visibleItems); + super._handleDataLoadedCore.call(that, options); + d.resolve(options.data); + }); + }).fail(d.reject); } + that._processTreeStructure(options); + } - that.callBase(options); - }, + super._handleDataLoadedCore(options); + } - _handlePush({ changes }) { - const reshapeOnPush = this._dataSource._reshapeOnPush; - const isNeedReshape = reshapeOnPush && !!changes.length; + _handlePush({ changes }) { + const reshapeOnPush = this._dataSource._reshapeOnPush; + const isNeedReshape = reshapeOnPush && !!changes.length; - if (isNeedReshape) { - this._isReload = true; - } - changes.forEach((change) => { change.index ??= -1; }); - this.callBase.apply(this, arguments); - }, + if (isNeedReshape) { + this._isReload = true; + } + changes.forEach((change) => { change.index ??= -1; }); + super._handlePush.apply(this, arguments as any); + } - // eslint-disable-next-line @typescript-eslint/no-unused-vars - init(dataSource, remoteOperations) { - this.callBase.apply(this, arguments); + // eslint-disable-next-line @typescript-eslint/no-unused-vars + init(dataSource, remoteOperations) { + super.init.apply(this, arguments as any); - const dataStructure = this.option('dataStructure'); + const dataStructure = this.option('dataStructure'); - this._keyGetter = this._createKeyGetter(); - this._parentIdGetter = this.createParentIdGetter(); - this._hasItemsGetter = this._createHasItemsGetter(); - this._hasItemsSetter = this._createHasItemsSetter(); + this._keyGetter = this._createKeyGetter(); + this._parentIdGetter = this.createParentIdGetter(); + this._hasItemsGetter = this._createHasItemsGetter(); + this._hasItemsSetter = this._createHasItemsSetter(); - if (dataStructure === 'tree') { - this._itemsGetter = this._createItemsGetter(); - this._keySetter = this._createKeySetter(); - this._parentIdSetter = this.createParentIdSetter(); - } + if (dataStructure === 'tree') { + this._itemsGetter = this._createItemsGetter(); + this._keySetter = this._createKeySetter(); + this._parentIdSetter = this.createParentIdSetter(); + } - this._nodeByKey = {}; - this._isChildrenLoaded = {}; - this._totalItemsCount = 0; - this.createAction('onNodesInitialized'); - }, + this._nodeByKey = {}; + this._isChildrenLoaded = {}; + this._totalItemsCount = 0; + this.createAction('onNodesInitialized'); + } - getKeyExpr() { - const store = this.store(); - const key = store && store.key(); - const keyExpr = this.option('keyExpr'); + getKeyExpr() { + const store = this.store(); + const key = store && store.key(); + const keyExpr = this.option('keyExpr'); - if (isDefined(key) && isDefined(keyExpr)) { - if (!equalByValue(key, keyExpr)) { - throw errors.Error('E1044'); - } + if (isDefined(key) && isDefined(keyExpr)) { + if (!equalByValue(key, keyExpr)) { + throw errors.Error('E1044'); } + } - return key || keyExpr || DEFAULT_KEY_EXPRESSION; - }, + return key || keyExpr || DEFAULT_KEY_EXPRESSION; + } - keyOf(data) { - return this._keyGetter && this._keyGetter(data); - }, + keyOf(data) { + return this._keyGetter && this._keyGetter(data); + } - parentKeyOf(data) { - return this._parentIdGetter && this._parentIdGetter(data); - }, + parentKeyOf(data) { + return this._parentIdGetter && this._parentIdGetter(data); + } - getRootNode() { - return this._rootNode; - }, + getRootNode() { + return this._rootNode; + } - totalItemsCount() { - return this._totalItemsCount + this._totalCountCorrection; - }, + totalItemsCount() { + return this._totalItemsCount + this._totalCountCorrection; + } - isRowExpanded(key, cache) { - if (cache) { - let { isExpandedByKey } = cache; - if (!isExpandedByKey) { - isExpandedByKey = cache.isExpandedByKey = {}; - this.option('expandedRowKeys').forEach((key) => { - isExpandedByKey[key] = true; - }); - } - return !!isExpandedByKey[key]; + isRowExpanded(key, cache?) { + if (cache) { + let { isExpandedByKey } = cache; + if (!isExpandedByKey) { + const expandedRowKeys = this.option('expandedRowKeys') ?? []; + + isExpandedByKey = cache.isExpandedByKey = {}; + + expandedRowKeys.forEach((key) => { + isExpandedByKey[key] = true; + }); } + return !!isExpandedByKey[key]; + } - const indexExpandedNodeKey = gridCoreUtils.getIndexByKey(key, this.option('expandedRowKeys'), null); + const indexExpandedNodeKey = gridCoreUtils.getIndexByKey(key, this.option('expandedRowKeys'), null); - return indexExpandedNodeKey >= 0; - }, + return indexExpandedNodeKey >= 0; + } - _changeRowExpandCore(key) { - const expandedRowKeys = this.option('expandedRowKeys').slice(); - const indexExpandedNodeKey = gridCoreUtils.getIndexByKey(key, expandedRowKeys, null); + _changeRowExpandCore(key) { + const expandedRowKeys = (this.option('expandedRowKeys') as any[]).slice(); + const indexExpandedNodeKey = gridCoreUtils.getIndexByKey(key, expandedRowKeys, null); - if (indexExpandedNodeKey < 0) { - expandedRowKeys.push(key); - } else { - expandedRowKeys.splice(indexExpandedNodeKey, 1); - } + if (indexExpandedNodeKey < 0) { + expandedRowKeys.push(key); + } else { + expandedRowKeys.splice(indexExpandedNodeKey, 1); + } - this.option('expandedRowKeys', expandedRowKeys); - }, + this.option('expandedRowKeys', expandedRowKeys); + } - changeRowExpand(key) { - this._changeRowExpandCore(key); - // @ts-expect-error - return this._isNodesInitializing ? new Deferred().resolve() : this.load(); - }, + changeRowExpand(key) { + this._changeRowExpandCore(key); + // @ts-expect-error + return this._isNodesInitializing ? new Deferred().resolve() : this.load(); + } - getNodeByKey(key) { - if (this._nodeByKey) { - return this._nodeByKey[key]; - } - }, + getNodeByKey(key) { + if (this._nodeByKey) { + return this._nodeByKey[key]; + } + } - getNodeLeafKeys() { - const that = this; - const result: any[] = []; - const keys = that._rootNode ? [that._rootNode.key] : []; + getNodeLeafKeys() { + const that = this; + const result: any[] = []; + const keys = that._rootNode ? [that._rootNode.key] : []; - keys.forEach((key) => { - const node = that.getNodeByKey(key); + keys.forEach((key) => { + const node = that.getNodeByKey(key); - node && treeListCore.foreachNodes([node], (childNode) => { - !childNode.children.length && result.push(childNode.key); - }); + node && treeListCore.foreachNodes([node], (childNode) => { + !childNode.children.length && result.push(childNode.key); }); + }); - return result; - }, + return result; + } - getChildNodeKeys(parentKey) { - const node = this.getNodeByKey(parentKey); - const childrenKeys: any[] = []; + getChildNodeKeys(parentKey) { + const node = this.getNodeByKey(parentKey); + const childrenKeys: any[] = []; - node && treeListCore.foreachNodes(node.children, (childNode) => { - childrenKeys.push(childNode.key); - }); + node && treeListCore.foreachNodes(node.children, (childNode) => { + childrenKeys.push(childNode.key); + }); - return childrenKeys; - }, + return childrenKeys; + } - loadDescendants(keys, childrenOnly) { - const that = this; - // @ts-expect-error - const d = new Deferred(); - const remoteOperations = that.remoteOperations(); + loadDescendants(keys, childrenOnly) { + const that = this; + // @ts-expect-error + const d = new Deferred(); + const remoteOperations = that.remoteOperations(); - if (isDefined(keys)) { - keys = Array.isArray(keys) ? keys : [keys]; - } else { - keys = that.getNodeLeafKeys(); - } + if (isDefined(keys)) { + keys = Array.isArray(keys) ? keys : [keys]; + } else { + keys = that.getNodeLeafKeys(); + } - if (!remoteOperations.filtering || !keys.length) { - return d.resolve(); - } + if (!remoteOperations.filtering || !keys.length) { + return d.resolve(); + } - const loadOptions = that._dataSource._createStoreLoadOptions(); - loadOptions.parentIds = keys; + const loadOptions = that._dataSource._createStoreLoadOptions(); + loadOptions.parentIds = keys; - that.load(loadOptions) - .done(() => { - if (!childrenOnly) { - const childKeys = getChildKeys(that, keys); + that.load(loadOptions) + .done(() => { + if (!childrenOnly) { + const childKeys = getChildKeys(that, keys); - if (childKeys.length) { - that.loadDescendants(childKeys, childrenOnly).done(d.resolve).fail(d.reject); - return; - } + if (childKeys.length) { + that.loadDescendants(childKeys, childrenOnly).done(d.resolve).fail(d.reject); + return; } - d.resolve(); - }) - .fail(d.reject); - - return d.promise(); - }, - - forEachNode() { - let nodes = []; - let callback; - - if (arguments.length === 1) { - // eslint-disable-next-line prefer-destructuring - callback = arguments[0]; - - const rootNode = this.getRootNode(); - nodes = rootNode && rootNode.children || []; - } else if (arguments.length === 2) { - // eslint-disable-next-line prefer-destructuring - callback = arguments[1]; - - // eslint-disable-next-line prefer-destructuring - nodes = arguments[0]; - nodes = Array.isArray(nodes) ? nodes : [nodes]; - } + } + d.resolve(); + }) + .fail(d.reject); + + return d.promise(); + } + + forEachNode() { + let nodes = []; + let callback; + + if (arguments.length === 1) { + // eslint-disable-next-line prefer-destructuring + callback = arguments[0]; + + const rootNode = this.getRootNode(); + nodes = rootNode && rootNode.children || []; + } else if (arguments.length === 2) { + // eslint-disable-next-line prefer-destructuring + callback = arguments[1]; + + // eslint-disable-next-line prefer-destructuring + nodes = arguments[0]; + nodes = Array.isArray(nodes) ? nodes : [nodes]; + } + + treeListCore.foreachNodes(nodes, callback); + } +} - treeListCore.foreachNodes(nodes, callback); - }, - }; -})()); +let DataSourceAdapterTreeListType: any = DataSourceAdapterTreeList; export default { extend(extender) { - DataSourceAdapterTreeList = DataSourceAdapterTreeList.inherit(extender); + DataSourceAdapterTreeListType = extender(DataSourceAdapterTreeListType); }, create(component) { - return new DataSourceAdapterTreeList(component); + return new DataSourceAdapterTreeListType(component); }, }; diff --git a/packages/devextreme/js/__internal/grids/tree_list/m_state_storing.ts b/packages/devextreme/js/__internal/grids/tree_list/m_state_storing.ts index 53c7cf29945c..bff9aa7a41e2 100644 --- a/packages/devextreme/js/__internal/grids/tree_list/m_state_storing.ts +++ b/packages/devextreme/js/__internal/grids/tree_list/m_state_storing.ts @@ -11,7 +11,6 @@ const stateStoring = ( ) => class TreeListStateStoringExtender extends stateStoringModule.extenders.controllers.stateStoring(Base) { applyState(state) { super.applyState(state); - // @ts-expect-error this.option('expandedRowKeys', state.expandedRowKeys ? state.expandedRowKeys.slice() : []); } }; diff --git a/packages/devextreme/js/__internal/grids/tree_list/m_virtual_scrolling.ts b/packages/devextreme/js/__internal/grids/tree_list/m_virtual_scrolling.ts index d6f5fac4d9e3..28f3fbc4c16e 100644 --- a/packages/devextreme/js/__internal/grids/tree_list/m_virtual_scrolling.ts +++ b/packages/devextreme/js/__internal/grids/tree_list/m_virtual_scrolling.ts @@ -1,12 +1,13 @@ import { extend } from '@js/core/utils/extend'; -import { virtualScrollingModule } from '@ts/grids/grid_core/virtual_scrolling/m_virtual_scrolling'; +import type DataSourceAdapter from '@ts/grids/grid_core/data_source_adapter/m_data_source_adapter'; +import type { ModuleType } from '@ts/grids/grid_core/m_types'; +import { dataSourceAdapterExtender as virtualScrollingDataSourceAdapterExtender, virtualScrollingModule } from '@ts/grids/grid_core/virtual_scrolling/m_virtual_scrolling'; -import dataSourceAdapter from './data_source_adapter/m_data_source_adapter'; +import dataSourceAdapterProvider from './data_source_adapter/m_data_source_adapter'; import gridCore from './m_core'; const oldDefaultOptions = virtualScrollingModule.defaultOptions; const originalDataControllerExtender = virtualScrollingModule.extenders.controllers.data; -const originalDataSourceAdapterExtender = virtualScrollingModule.extenders.dataSourceAdapter; virtualScrollingModule.extenders.controllers.data = extend({}, originalDataControllerExtender, { _loadOnOptionChange() { @@ -16,16 +17,15 @@ virtualScrollingModule.extenders.controllers.data = extend({}, originalDataContr this.callBase(); }, }); - -virtualScrollingModule.extenders.dataSourceAdapter = extend({}, originalDataSourceAdapterExtender, { +const dataSourceAdapterExtender = (Base: ModuleType) => class VirtualScrollingDataSourceAdapterExtender extends virtualScrollingDataSourceAdapterExtender(Base) { changeRowExpand() { - return this.callBase.apply(this, arguments).done(() => { + return super.changeRowExpand.apply(this, arguments as any).done(() => { const viewportItemIndex = this.getViewportItemIndex(); viewportItemIndex >= 0 && this.setViewportItemIndex(viewportItemIndex); }); - }, -}); + } +}; gridCore.registerModule('virtualScrolling', extend({}, virtualScrollingModule, { defaultOptions() { @@ -37,4 +37,4 @@ gridCore.registerModule('virtualScrolling', extend({}, virtualScrollingModule, { }, })); -dataSourceAdapter.extend(virtualScrollingModule.extenders.dataSourceAdapter); +dataSourceAdapterProvider.extend(dataSourceAdapterExtender);