From 5bd5018e1c3c566f968560a07e9c90dcf851e857 Mon Sep 17 00:00:00 2001 From: EugeniyKiyashko Date: Thu, 13 Jun 2024 17:18:17 +0400 Subject: [PATCH] Selection: ignore errors after move to TS --- .../grids/grid_core/selection/m_selection.ts | 5 +- .../grids/tree_list/selection/m_selection.ts | 4 +- .../m_selection.strategy.deferred.ts | 539 ++++++------- .../m_selection.strategy.standard.ts | 733 +++++++++--------- .../ui/selection/m_selection.strategy.ts | 311 ++++---- .../js/__internal/ui/selection/m_selection.ts | 594 +++++++------- .../collection/ui.collection_widget.edit.js | 2 +- ..._manager.items_list.thumbnails.list_box.js | 2 +- .../DevExpress.ui.widgets/selection.test.js | 2 +- 9 files changed, 1119 insertions(+), 1073 deletions(-) diff --git a/packages/devextreme/js/__internal/grids/grid_core/selection/m_selection.ts b/packages/devextreme/js/__internal/grids/grid_core/selection/m_selection.ts index 113658324a15..cf2ca7aefc46 100644 --- a/packages/devextreme/js/__internal/grids/grid_core/selection/m_selection.ts +++ b/packages/devextreme/js/__internal/grids/grid_core/selection/m_selection.ts @@ -1,6 +1,7 @@ /* eslint-disable max-classes-per-file */ import $ from '@js/core/renderer'; import { equalByValue } from '@js/core/utils/common'; +import type { DeferredObj } from '@js/core/utils/deferred'; import { Deferred } from '@js/core/utils/deferred'; import { extend } from '@js/core/utils/extend'; import { each } from '@js/core/utils/iterator'; @@ -12,7 +13,6 @@ import eventsEngine from '@js/events/core/events_engine'; import holdEvent from '@js/events/hold'; import { addNamespace, isCommandKeyPressed } from '@js/events/utils/index'; import messageLocalization from '@js/localization/message'; -import Selection from '@js/ui/selection/selection'; import errors from '@js/ui/widget/ui.errors'; import type { ColumnHeadersView } from '@ts/grids/grid_core/column_headers/m_column_headers'; import type { ColumnsController } from '@ts/grids/grid_core/columns_controller/m_columns_controller'; @@ -20,6 +20,7 @@ import type { ContextMenuController } from '@ts/grids/grid_core/context_menu/m_c import type { ModuleType } from '@ts/grids/grid_core/m_types'; import type { StateStoringController } from '@ts/grids/grid_core/state_storing/m_state_storing_core'; import type { RowsView } from '@ts/grids/grid_core/views/m_rows_view'; +import Selection from '@ts/ui/selection/m_selection'; import type { DataController } from '../data_controller/m_data_controller'; import modules from '../m_modules'; @@ -553,7 +554,7 @@ export class SelectionController extends modules.Controller { return this._selection.loadSelectedItemsWithFilter(); } - public changeItemSelection(visibleItemIndex, keys, setFocusOnly?) { + public changeItemSelection(visibleItemIndex, keys, setFocusOnly?: boolean): boolean | DeferredObj | undefined { keys = keys || {}; if (this.isSelectionWithCheckboxes()) { keys.control = true; diff --git a/packages/devextreme/js/__internal/grids/tree_list/selection/m_selection.ts b/packages/devextreme/js/__internal/grids/tree_list/selection/m_selection.ts index 64845f8863f5..0c5e512d1670 100644 --- a/packages/devextreme/js/__internal/grids/tree_list/selection/m_selection.ts +++ b/packages/devextreme/js/__internal/grids/tree_list/selection/m_selection.ts @@ -1,6 +1,7 @@ /* eslint-disable max-classes-per-file */ import $ from '@js/core/renderer'; import { equalByValue, noop } from '@js/core/utils/common'; +import type { DeferredObj } from '@js/core/utils/deferred'; import { extend } from '@js/core/utils/extend'; import { isDefined } from '@js/core/utils/type'; import type { ColumnHeadersView } from '@ts/grids/grid_core/column_headers/m_column_headers'; @@ -202,7 +203,8 @@ const selection = (Base: ModuleType) => class SelectionCont return super.selectedItemKeys(value, preserve, isDeselect, isSelectAll); } - public changeItemSelection(itemIndex, keyboardKeys) { + // eslint-disable-next-line @typescript-eslint/no-unused-vars + public changeItemSelection(itemIndex, keyboardKeys, setFocusOnly?: boolean): boolean | DeferredObj | undefined { const isRecursiveSelection = this.isRecursiveSelection(); const callBase = super.changeItemSelection.bind(this); diff --git a/packages/devextreme/js/__internal/ui/selection/m_selection.strategy.deferred.ts b/packages/devextreme/js/__internal/ui/selection/m_selection.strategy.deferred.ts index ea59253278b8..13cebb543fad 100644 --- a/packages/devextreme/js/__internal/ui/selection/m_selection.strategy.deferred.ts +++ b/packages/devextreme/js/__internal/ui/selection/m_selection.strategy.deferred.ts @@ -1,341 +1,346 @@ -import { isString } from '../../core/utils/type'; -import SelectionStrategy from './selection.strategy'; -import errors from '../widget/ui.errors'; -import dataQuery from '../../data/query'; -import { Deferred } from '../../core/utils/deferred'; +import { Deferred } from '@js/core/utils/deferred'; +import { isString } from '@js/core/utils/type'; +import dataQuery from '@js/data/query'; +import errors from '@js/ui/widget/ui.errors'; + +import SelectionStrategy from './m_selection.strategy'; export default class DeferredStrategy extends SelectionStrategy { - getSelectedItems() { - return this._loadFilteredData(this.options.selectionFilter); + getSelectedItems() { + return this._loadFilteredData(this.options.selectionFilter); + } + + getSelectedItemKeys() { + const d = Deferred(); + const that = this; + const key = this.options.key(); + const select = isString(key) ? [key] : key; + + this._loadFilteredData(this.options.selectionFilter, null, select).done((items) => { + // @ts-expect-error + const keys = items.map((item) => that.options.keyOf(item)); + + d.resolve(keys); + // eslint-disable-next-line @typescript-eslint/no-misused-promises + }).fail(d.reject); + + return d.promise(); + } + + selectedItemKeys(keys, preserve, isDeselect, isSelectAll) { + if (isSelectAll) { + const filter = this.options.filter(); + const needResetSelectionFilter = !filter || JSON.stringify(filter) === JSON.stringify(this.options.selectionFilter) && isDeselect; + + if (needResetSelectionFilter) { + this._setOption('selectionFilter', isDeselect ? [] : null); + } else { + this._addSelectionFilter(isDeselect, filter, isSelectAll); + } + } else { + if (!preserve) { + this._setOption('selectionFilter', []); + } + + for (let i = 0; i < keys.length; i++) { + if (isDeselect) { + this.removeSelectedItem(keys[i]); + } else { + this.addSelectedItem(keys[i], isSelectAll, !preserve); + } + } } - getSelectedItemKeys() { - const d = new Deferred(); - const that = this; - const key = this.options.key(); - const select = isString(key) ? [key] : key; - - this._loadFilteredData(this.options.selectionFilter, null, select).done(function(items) { - const keys = items.map(function(item) { - return that.options.keyOf(item); - }); + this.onSelectionChanged(); - d.resolve(keys); - }).fail(d.reject); + return Deferred().resolve(); + } - return d.promise(); + setSelectedItems(keys) { + this._setOption('selectionFilter', null); + for (let i = 0; i < keys.length; i++) { + this.addSelectedItem(keys[i]); } + } - selectedItemKeys(keys, preserve, isDeselect, isSelectAll) { - if(isSelectAll) { - const filter = this.options.filter(); - const needResetSelectionFilter = !filter || JSON.stringify(filter) === JSON.stringify(this.options.selectionFilter) && isDeselect; - - if(needResetSelectionFilter) { - this._setOption('selectionFilter', isDeselect ? [] : null); - } else { - this._addSelectionFilter(isDeselect, filter, isSelectAll); - } - - } else { - if(!preserve) { - this._setOption('selectionFilter', []); - } - - for(let i = 0; i < keys.length; i++) { - if(isDeselect) { - this.removeSelectedItem(keys[i]); - } else { - this.addSelectedItem(keys[i], isSelectAll, !preserve); - } - } - } + isItemDataSelected(itemData) { + return this.isItemKeySelected(itemData); + } - this.onSelectionChanged(); + isItemKeySelected(itemData) { + const { selectionFilter } = this.options; - return new Deferred().resolve(); + if (!selectionFilter) { + return true; } - setSelectedItems(keys) { - this._setOption('selectionFilter', null); - for(let i = 0; i < keys.length; i++) { - this.addSelectedItem(keys[i]); - } - } + return !!dataQuery([itemData]).filter(selectionFilter).toArray().length; + } - isItemDataSelected(itemData) { - return this.isItemKeySelected(itemData); + _getKeyExpr() { + const keyField = this.options.key(); + if (Array.isArray(keyField) && keyField.length === 1) { + return keyField[0]; } + return keyField; + } - isItemKeySelected(itemData) { - const selectionFilter = this.options.selectionFilter; - - if(!selectionFilter) { - return true; - } - - return !!dataQuery([itemData]).filter(selectionFilter).toArray().length; + _normalizeKey(key) { + const keyExpr = this.options.key(); + if (Array.isArray(keyExpr) && keyExpr.length === 1) { + return key[keyExpr[0]]; } - - _getKeyExpr() { - const keyField = this.options.key(); - if(Array.isArray(keyField) && keyField.length === 1) { - return keyField[0]; + return key; + } + + _getFilterByKey(key) { + const keyField = this._getKeyExpr(); + let filter = [keyField, '=', this._normalizeKey(key)]; + + if (Array.isArray(keyField)) { + filter = []; + for (let i = 0; i < keyField.length; i++) { + filter.push([keyField[i], '=', key[keyField[i]]]); + if (i !== keyField.length - 1) { + filter.push('and'); } - return keyField; + } } - _normalizeKey(key) { - const keyExpr = this.options.key(); - if(Array.isArray(keyExpr) && keyExpr.length === 1) { - return key[keyExpr[0]]; - } - return key; - } + return filter; + } - _getFilterByKey(key) { - const keyField = this._getKeyExpr(); - let filter = [keyField, '=', this._normalizeKey(key)]; - - if(Array.isArray(keyField)) { - filter = []; - for(let i = 0; i < keyField.length; i++) { - filter.push([keyField[i], '=', key[keyField[i]]]); - if(i !== keyField.length - 1) { - filter.push('and'); - } - } - } + addSelectedItem(key, isSelectAll?: boolean, skipFilter?: boolean) { + const filter = this._getFilterByKey(key); - return filter; - } + this._addSelectionFilter(false, filter, isSelectAll, skipFilter); + } - addSelectedItem(key, isSelectAll, skipFilter) { - const filter = this._getFilterByKey(key); + removeSelectedItem(key) { + const filter = this._getFilterByKey(key); - this._addSelectionFilter(false, filter, isSelectAll, skipFilter); - } + this._addSelectionFilter(true, filter); + } - removeSelectedItem(key) { - const filter = this._getFilterByKey(key); + validate() { + const { key } = this.options; - this._addSelectionFilter(true, filter); + if (key && key() === undefined) { + throw errors.Error('E1042', 'Deferred selection'); } + } - validate() { - const key = this.options.key; + _findSubFilter(selectionFilter, filter) { + if (!selectionFilter) return -1; + const filterString = JSON.stringify(filter); - if(key && key() === undefined) { - throw errors.Error('E1042', 'Deferred selection'); - } + for (let index = 0; index < selectionFilter.length; index++) { + const subFilter = selectionFilter[index]; + if (subFilter && JSON.stringify(subFilter) === filterString) { + return index; + } } - _findSubFilter(selectionFilter, filter) { - if(!selectionFilter) return -1; - const filterString = JSON.stringify(filter); - - for(let index = 0; index < selectionFilter.length; index++) { - const subFilter = selectionFilter[index]; - if(subFilter && JSON.stringify(subFilter) === filterString) { - return index; - } - } + return -1; + } - return -1; + _isLastSubFilter(selectionFilter, filter) { + if (selectionFilter && filter) { + return this._findSubFilter(selectionFilter, filter) === selectionFilter.length - 1 || this._findSubFilter([selectionFilter], filter) === 0; } + return false; + } - _isLastSubFilter(selectionFilter, filter) { - if(selectionFilter && filter) { - return this._findSubFilter(selectionFilter, filter) === selectionFilter.length - 1 || this._findSubFilter([selectionFilter], filter) === 0; - } - return false; + _addFilterOperator(selectionFilter, filterOperator) { + if (selectionFilter.length > 1 && isString(selectionFilter[1]) && selectionFilter[1] !== filterOperator) { + selectionFilter = [selectionFilter]; } - - _addFilterOperator(selectionFilter, filterOperator) { - if(selectionFilter.length > 1 && isString(selectionFilter[1]) && selectionFilter[1] !== filterOperator) { - selectionFilter = [selectionFilter]; - } - if(selectionFilter.length) { - selectionFilter.push(filterOperator); - } - return selectionFilter; + if (selectionFilter.length) { + selectionFilter.push(filterOperator); } + return selectionFilter; + } - _denormalizeFilter(filter) { - if(filter && isString(filter[0])) { - filter = [filter]; - } - return filter; + _denormalizeFilter(filter) { + if (filter && isString(filter[0])) { + filter = [filter]; } - - _isOnlyNegativeFiltersLeft(filters) { - return filters.every((filterItem, i) => { - if(i % 2 === 0) { - return Array.isArray(filterItem) && filterItem[0] === '!'; - } else { - return filterItem === 'and'; - } - }); + return filter; + } + + _isOnlyNegativeFiltersLeft(filters) { + return filters.every((filterItem, i) => { + if (i % 2 === 0) { + return Array.isArray(filterItem) && filterItem[0] === '!'; + } + return filterItem === 'and'; + }); + } + + _addSelectionFilter( + isDeselect, + filter, + isSelectAll?: boolean, + skipFilter?: boolean, + ) { + const that = this; + const currentFilter = isDeselect ? ['!', filter] : filter; + const currentOperation = isDeselect ? 'and' : 'or'; + let needAddFilter = true; + let selectionFilter = that.options.selectionFilter || []; + + selectionFilter = that._denormalizeFilter(selectionFilter); + if (selectionFilter?.length && !skipFilter) { + const removedIndex = that._removeSameFilter(selectionFilter, filter, isDeselect, isSelectAll); + const filterIndex = that._removeSameFilter(selectionFilter, filter, !isDeselect); + + const shouldCleanFilter = isDeselect + && (removedIndex !== -1 || filterIndex !== -1) + && this._isOnlyNegativeFiltersLeft(selectionFilter); + + if (shouldCleanFilter) { + selectionFilter = []; + } + + const isKeyOperatorsAfterRemoved = this._isKeyFilter(filter) && this._hasKeyFiltersOnlyStartingFromIndex(selectionFilter, filterIndex); + + needAddFilter = filter.length && !isKeyOperatorsAfterRemoved; } - _addSelectionFilter(isDeselect, filter, isSelectAll, skipFilter) { - const that = this; - const currentFilter = isDeselect ? ['!', filter] : filter; - const currentOperation = isDeselect ? 'and' : 'or'; - let needAddFilter = true; - let selectionFilter = that.options.selectionFilter || []; - - selectionFilter = that._denormalizeFilter(selectionFilter); - if(selectionFilter?.length && !skipFilter) { - - const removedIndex = that._removeSameFilter(selectionFilter, filter, isDeselect, isSelectAll); - const filterIndex = that._removeSameFilter(selectionFilter, filter, !isDeselect); + if (needAddFilter) { + selectionFilter = that._addFilterOperator(selectionFilter, currentOperation); + selectionFilter.push(currentFilter); + } - const shouldCleanFilter = - isDeselect && - (removedIndex !== -1 || filterIndex !== -1) && - this._isOnlyNegativeFiltersLeft(selectionFilter); + selectionFilter = that._normalizeFilter(selectionFilter); - if(shouldCleanFilter) { - selectionFilter = []; - } + that._setOption('selectionFilter', !isDeselect && !selectionFilter.length ? null : selectionFilter); + } + _normalizeFilter(filter) { + if (filter && filter.length === 1) { + // eslint-disable-next-line prefer-destructuring + filter = filter[0]; + } + return filter; + } - const isKeyOperatorsAfterRemoved = this._isKeyFilter(filter) && this._hasKeyFiltersOnlyStartingFromIndex(selectionFilter, filterIndex); + _removeFilterByIndex(filter, filterIndex, isSelectAll) { + const operation = filter[1]; - needAddFilter = filter.length && !isKeyOperatorsAfterRemoved; - } + if (filterIndex > 0) { + filter.splice(filterIndex - 1, 2); + } else { + filter.splice(filterIndex, 2); + } - if(needAddFilter) { - selectionFilter = that._addFilterOperator(selectionFilter, currentOperation); - selectionFilter.push(currentFilter); - } + if (isSelectAll && operation === 'and') { + filter.splice(0, filter.length); + } + } - selectionFilter = that._normalizeFilter(selectionFilter); + _isSimpleKeyFilter(filter, key) { + return filter.length === 3 && filter[0] === key && filter[1] === '='; + } - that._setOption('selectionFilter', !isDeselect && !selectionFilter.length ? null : selectionFilter); + _isKeyFilter(filter) { + if (filter.length === 2 && filter[0] === '!') { + return this._isKeyFilter(filter[1]); } + const keyField = this._getKeyExpr(); - _normalizeFilter(filter) { - if(filter && filter.length === 1) { - filter = filter[0]; + if (Array.isArray(keyField)) { + if (filter.length !== keyField.length * 2 - 1) { + return false; + } + for (let i = 0; i < keyField.length; i++) { + if (i > 0 && filter[i * 2 - 1] !== 'and') { + return false; } - return filter; + if (!this._isSimpleKeyFilter(filter[i * 2], keyField[i])) { + return false; + } + } + return true; } - _removeFilterByIndex(filter, filterIndex, isSelectAll) { - const operation = filter[1]; + return this._isSimpleKeyFilter(filter, keyField); + } - if(filterIndex > 0) { - filter.splice(filterIndex - 1, 2); - } else { - filter.splice(filterIndex, 2); + _hasKeyFiltersOnlyStartingFromIndex(selectionFilter, filterIndex) { + if (filterIndex >= 0) { + for (let i = filterIndex; i < selectionFilter.length; i++) { + if (typeof selectionFilter[i] !== 'string' && !this._isKeyFilter(selectionFilter[i])) { + return false; } + } - if(isSelectAll && operation === 'and') { - filter.splice(0, filter.length); - } - } - _isSimpleKeyFilter(filter, key) { - return filter.length === 3 && filter[0] === key && filter[1] === '='; + return true; } - _isKeyFilter(filter) { - if(filter.length === 2 && filter[0] === '!') { - return this._isKeyFilter(filter[1]); - } - const keyField = this._getKeyExpr(); - - if(Array.isArray(keyField)) { - if(filter.length !== keyField.length * 2 - 1) { - return false; - } - for(let i = 0; i < keyField.length; i++) { - if(i > 0 && filter[i * 2 - 1] !== 'and') { - return false; - } - if(!this._isSimpleKeyFilter(filter[i * 2], keyField[i])) { - return false; - } - } - return true; - } - return this._isSimpleKeyFilter(filter, keyField); - } - _hasKeyFiltersOnlyStartingFromIndex(selectionFilter, filterIndex) { - if(filterIndex >= 0) { - for(let i = filterIndex; i < selectionFilter.length; i++) { - if(typeof selectionFilter[i] !== 'string' && !this._isKeyFilter(selectionFilter[i])) { - return false; - } - } - - return true; - } + return false; + } - return false; - } - _removeSameFilter(selectionFilter, filter, inverted, isSelectAll) { - filter = inverted ? ['!', filter] : filter; + _removeSameFilter(selectionFilter, filter, inverted, isSelectAll?: boolean) { + filter = inverted ? ['!', filter] : filter; - if(JSON.stringify(filter) === JSON.stringify(selectionFilter)) { - selectionFilter.splice(0, selectionFilter.length); - return 0; - } + if (JSON.stringify(filter) === JSON.stringify(selectionFilter)) { + selectionFilter.splice(0, selectionFilter.length); + return 0; + } - const filterIndex = this._findSubFilter(selectionFilter, filter); + const filterIndex = this._findSubFilter(selectionFilter, filter); - if(filterIndex >= 0) { - this._removeFilterByIndex(selectionFilter, filterIndex, isSelectAll); - return filterIndex; - } else { - for(let i = 0; i < selectionFilter.length; i++) { - if(Array.isArray(selectionFilter[i]) && selectionFilter[i].length > 2) { - const filterIndex = this._removeSameFilter(selectionFilter[i], filter, false, isSelectAll); - if(filterIndex >= 0) { - if(!selectionFilter[i].length) { - this._removeFilterByIndex(selectionFilter, i, isSelectAll); - } else if(selectionFilter[i].length === 1) { - selectionFilter[i] = selectionFilter[i][0]; - } - return filterIndex; - } - } - } - return -1; + if (filterIndex >= 0) { + this._removeFilterByIndex(selectionFilter, filterIndex, isSelectAll); + return filterIndex; + } + for (let i = 0; i < selectionFilter.length; i++) { + if (Array.isArray(selectionFilter[i]) && selectionFilter[i].length > 2) { + const filterIndex = this._removeSameFilter(selectionFilter[i], filter, false, isSelectAll); + if (filterIndex >= 0) { + if (!selectionFilter[i].length) { + this._removeFilterByIndex(selectionFilter, i, isSelectAll); + } else if (selectionFilter[i].length === 1) { + // eslint-disable-next-line prefer-destructuring + selectionFilter[i] = selectionFilter[i][0]; + } + return filterIndex; } + } } + return -1; + } - getSelectAllState() { - const filter = this.options.filter(); - let selectionFilter = this.options.selectionFilter; + getSelectAllState() { + const filter = this.options.filter(); + let { selectionFilter } = this.options; - if(!selectionFilter) return true; - if(!selectionFilter.length) return false; - if(!filter || !filter.length) return undefined; + if (!selectionFilter) return true; + if (!selectionFilter.length) return false; + if (!filter || !filter.length) return undefined; - selectionFilter = this._denormalizeFilter(selectionFilter); - - if(this._isLastSubFilter(selectionFilter, filter)) { - return true; - } + selectionFilter = this._denormalizeFilter(selectionFilter); - if(this._isLastSubFilter(selectionFilter, ['!', filter])) { - return false; - } + if (this._isLastSubFilter(selectionFilter, filter)) { + return true; + } - return undefined; + if (this._isLastSubFilter(selectionFilter, ['!', filter])) { + return false; } - loadSelectedItemsWithFilter() { - const componentFilter = this.options.filter(); - const selectionFilter = this.options.selectionFilter; + return undefined; + } - const filter = componentFilter ? - [componentFilter, 'and', selectionFilter] : - selectionFilter; + loadSelectedItemsWithFilter() { + const componentFilter = this.options.filter(); + const { selectionFilter } = this.options; - return this._loadFilteredData(filter); - } + const filter = componentFilter + ? [componentFilter, 'and', selectionFilter] + : selectionFilter; + return this._loadFilteredData(filter); + } } diff --git a/packages/devextreme/js/__internal/ui/selection/m_selection.strategy.standard.ts b/packages/devextreme/js/__internal/ui/selection/m_selection.strategy.standard.ts index 6b5afa6bbd4d..5bca3a34ee6e 100644 --- a/packages/devextreme/js/__internal/ui/selection/m_selection.strategy.standard.ts +++ b/packages/devextreme/js/__internal/ui/selection/m_selection.strategy.standard.ts @@ -1,465 +1,476 @@ -import { getKeyHash } from '../../core/utils/common'; -import { isDefined, isObject } from '../../core/utils/type'; -import { removeDuplicates, getUniqueValues } from '../../core/utils/array'; -import { isKeysEqual } from '../../core/utils/array_compare'; -import dataQuery from '../../data/query'; -import { Deferred, when } from '../../core/utils/deferred'; -import { SelectionFilterCreator } from '../../core/utils/selection_filter'; -import errors from '../widget/ui.errors'; -import SelectionStrategy from './selection.strategy'; +import { getUniqueValues, removeDuplicates } from '@js/core/utils/array'; +import { isKeysEqual } from '@js/core/utils/array_compare'; +// @ts-expect-error +import { getKeyHash } from '@js/core/utils/common'; +import { Deferred, when } from '@js/core/utils/deferred'; +import { SelectionFilterCreator } from '@js/core/utils/selection_filter'; +import { isDefined, isObject } from '@js/core/utils/type'; +import dataQuery from '@js/data/query'; +import errors from '@js/ui/widget/ui.errors'; + +import SelectionStrategy from './m_selection.strategy'; export default class StandardStrategy extends SelectionStrategy { - constructor(options) { - super(options); - this._initSelectedItemKeyHash(); - } + _shouldMergeWithLastRequest?: boolean; - _initSelectedItemKeyHash() { - this._setOption('keyHashIndices', this.options.equalByReference ? null : {}); - } + _lastLoadDeferred?: any; - getSelectedItemKeys() { - return this.options.selectedItemKeys.slice(0); - } + _lastRequestData?: any; - getSelectedItems() { - return this.options.selectedItems.slice(0); - } + constructor(options) { + super(options); + this._initSelectedItemKeyHash(); + } - _preserveSelectionUpdate(items, isDeselect) { - const keyOf = this.options.keyOf; - let keyIndicesToRemoveMap; - let keyIndex; - let i; + _initSelectedItemKeyHash() { + this._setOption('keyHashIndices', this.options.equalByReference ? null : {}); + } - if(!keyOf) return; + getSelectedItemKeys() { + return this.options.selectedItemKeys.slice(0); + } - const isBatchDeselect = isDeselect && items.length > 1 && !this.options.equalByReference; + getSelectedItems() { + return this.options.selectedItems.slice(0); + } - if(isBatchDeselect) { - keyIndicesToRemoveMap = {}; - } + _preserveSelectionUpdate(items, isDeselect) { + const { keyOf } = this.options; + let keyIndicesToRemoveMap; + let keyIndex; + let i; - for(i = 0; i < items.length; i++) { - const item = items[i]; - const key = keyOf(item); - if(isDeselect) { - keyIndex = this.removeSelectedItem(key, keyIndicesToRemoveMap, item?.disabled); - if(keyIndicesToRemoveMap && keyIndex >= 0) { - keyIndicesToRemoveMap[keyIndex] = true; - } - } else { - this.addSelectedItem(key, item); - } - } + if (!keyOf) return; - if(isBatchDeselect) { - this._batchRemoveSelectedItems(keyIndicesToRemoveMap); - } + const isBatchDeselect = isDeselect && items.length > 1 && !this.options.equalByReference; + + if (isBatchDeselect) { + keyIndicesToRemoveMap = {}; } - _batchRemoveSelectedItems(keyIndicesToRemoveMap) { - const selectedItemKeys = this.options.selectedItemKeys.slice(0); - const selectedItems = this.options.selectedItems.slice(0); + for (i = 0; i < items.length; i++) { + const item = items[i]; + const key = keyOf(item); + if (isDeselect) { + keyIndex = this.removeSelectedItem(key, keyIndicesToRemoveMap, item?.disabled); + if (keyIndicesToRemoveMap && keyIndex >= 0) { + keyIndicesToRemoveMap[keyIndex] = true; + } + } else { + this.addSelectedItem(key, item); + } + } - this.options.selectedItemKeys.length = 0; - this.options.selectedItems.length = 0; + if (isBatchDeselect) { + this._batchRemoveSelectedItems(keyIndicesToRemoveMap); + } + } - for(let i = 0; i < selectedItemKeys.length; i++) { - if(!keyIndicesToRemoveMap[i]) { - this.options.selectedItemKeys.push(selectedItemKeys[i]); - this.options.selectedItems.push(selectedItems[i]); - } - } + _batchRemoveSelectedItems(keyIndicesToRemoveMap) { + const selectedItemKeys = this.options.selectedItemKeys.slice(0); + const selectedItems = this.options.selectedItems.slice(0); + + this.options.selectedItemKeys.length = 0; + this.options.selectedItems.length = 0; - this._initSelectedItemKeyHash(); - this.updateSelectedItemKeyHash(this.options.selectedItemKeys); + for (let i = 0; i < selectedItemKeys.length; i++) { + if (!keyIndicesToRemoveMap[i]) { + this.options.selectedItemKeys.push(selectedItemKeys[i]); + this.options.selectedItems.push(selectedItems[i]); + } } - _loadSelectedItemsCore(keys, isDeselect, isSelectAll, filter, forceCombinedFilter = false) { - let deferred = new Deferred(); - const key = this.options.key(); + this._initSelectedItemKeyHash(); + this.updateSelectedItemKeyHash(this.options.selectedItemKeys); + } - if(!keys.length && !isSelectAll) { - deferred.resolve([]); - return deferred; - } + _loadSelectedItemsCore(keys, isDeselect, isSelectAll, filter, forceCombinedFilter = false) { + let deferred = Deferred(); + const key = this.options.key(); - if(isSelectAll && isDeselect && !filter) { - deferred.resolve(this.getSelectedItems()); - return deferred; - } + if (!keys.length && !isSelectAll) { + deferred.resolve([]); + return deferred; + } - const selectionFilterCreator = new SelectionFilterCreator(keys, isSelectAll); - const combinedFilter = selectionFilterCreator.getCombinedFilter(key, filter, forceCombinedFilter); + if (isSelectAll && isDeselect && !filter) { + deferred.resolve(this.getSelectedItems()); + return deferred; + } - let deselectedItems = []; - if(isDeselect) { - const selectedItems = this.options.selectedItems; - deselectedItems = combinedFilter && keys.length !== selectedItems.length - ? dataQuery(selectedItems).filter(combinedFilter).toArray() - : selectedItems.slice(0); - } + const selectionFilterCreator = new SelectionFilterCreator(keys, isSelectAll); + const combinedFilter = selectionFilterCreator.getCombinedFilter(key, filter, forceCombinedFilter); - let filteredItems = deselectedItems.length ? deselectedItems : this.options.plainItems(true).filter(this.options.isSelectableItem).map(this.options.getItemData); + let deselectedItems = []; + if (isDeselect) { + const { selectedItems } = this.options; + deselectedItems = combinedFilter && keys.length !== selectedItems.length + ? dataQuery(selectedItems).filter(combinedFilter).toArray() + : selectedItems.slice(0); + } - const localFilter = selectionFilterCreator.getLocalFilter(this.options.keyOf, this.equalKeys.bind(this), this.options.equalByReference, key); + let filteredItems = deselectedItems.length ? deselectedItems : this.options.plainItems(true).filter(this.options.isSelectableItem).map(this.options.getItemData); - filteredItems = filteredItems.filter(localFilter); + const localFilter = selectionFilterCreator.getLocalFilter(this.options.keyOf, this.equalKeys.bind(this), this.options.equalByReference, key); - if(deselectedItems.length || (!isSelectAll && filteredItems.length === keys.length)) { - deferred.resolve(filteredItems); - } else { - deferred = this._loadFilteredData(combinedFilter, localFilter, null, isSelectAll); - } + filteredItems = filteredItems.filter(localFilter); - return deferred; + if (deselectedItems.length || (!isSelectAll && filteredItems.length === keys.length)) { + deferred.resolve(filteredItems); + } else { + deferred = this._loadFilteredData(combinedFilter, localFilter, null, isSelectAll); } - _replaceSelectionUpdate(items) { - const internalKeys = []; - const keyOf = this.options.keyOf; + return deferred; + } - if(!keyOf) return; + _replaceSelectionUpdate(items) { + const internalKeys = []; + const { keyOf } = this.options; - for(let i = 0; i < items.length; i++) { - const item = items[i]; - const key = keyOf(item); + if (!keyOf) return; - internalKeys.push(key); - } - - this.setSelectedItems(internalKeys, items); + for (let i = 0; i < items.length; i++) { + const item = items[i]; + const key = keyOf(item); + // @ts-expect-error + internalKeys.push(key); } - _warnOnIncorrectKeys(keys) { - const allowNullValue = this.options.allowNullValue; + this.setSelectedItems(internalKeys, items); + } - for(let i = 0; i < keys.length; i++) { - const key = keys[i]; + _warnOnIncorrectKeys(keys) { + const { allowNullValue } = this.options; - if((!allowNullValue || key !== null) && !this.isItemKeySelected(key)) { - errors.log('W1002', key); - } - } - } + for (let i = 0; i < keys.length; i++) { + const key = keys[i]; - _isMultiSelectEnabled() { - const mode = this.options.mode; - return mode === 'all' || mode === 'multiple'; + if ((!allowNullValue || key !== null) && !this.isItemKeySelected(key)) { + errors.log('W1002', key); + } } + } - _requestInProgress() { - return this._lastLoadDeferred?.state() === 'pending'; - } + _isMultiSelectEnabled() { + const { mode } = this.options; + return mode === 'all' || mode === 'multiple'; + } - _concatRequestsItems(keys, isDeselect, oldRequestItems, updatedKeys) { - let selectedItems; - const deselectedItems = isDeselect ? keys : []; + _requestInProgress() { + return this._lastLoadDeferred?.state() === 'pending'; + } - if(updatedKeys) { - selectedItems = updatedKeys; - } else { - selectedItems = removeDuplicates(keys, this.options.selectedItemKeys); - } + _concatRequestsItems(keys, isDeselect, oldRequestItems, updatedKeys) { + let selectedItems; + const deselectedItems = isDeselect ? keys : []; - return { - addedItems: oldRequestItems.added.concat(selectedItems), - removedItems: oldRequestItems.removed.concat(deselectedItems), - keys: keys - }; - } - - _collectLastRequestData(keys, isDeselect, isSelectAll, updatedKeys) { - const isDeselectAll = isDeselect && isSelectAll; - const oldRequestItems = { - added: [], - removed: [] - }; - const multiSelectEnabled = this._isMultiSelectEnabled(); - let lastRequestData = multiSelectEnabled ? this._lastRequestData : {}; - - if(multiSelectEnabled) { - if(this._shouldMergeWithLastRequest) { - if(isDeselectAll) { - this._lastLoadDeferred.reject(); - lastRequestData = {}; - } else if(!isKeysEqual(keys, this.options.selectedItemKeys)) { - oldRequestItems.added = lastRequestData.addedItems; - oldRequestItems.removed = lastRequestData.removedItems; - - if(!isDeselect) { - this._lastLoadDeferred.reject(); - } - } - } - - lastRequestData = this._concatRequestsItems(keys, isDeselect, oldRequestItems, this._shouldMergeWithLastRequest ? undefined : updatedKeys); - } + if (updatedKeys) { + selectedItems = updatedKeys; + } else { + selectedItems = removeDuplicates(keys, this.options.selectedItemKeys); + } - return lastRequestData; + return { + addedItems: oldRequestItems.added.concat(selectedItems), + removedItems: oldRequestItems.removed.concat(deselectedItems), + keys, + }; + } + + _collectLastRequestData(keys, isDeselect, isSelectAll, updatedKeys) { + const isDeselectAll = isDeselect && isSelectAll; + const oldRequestItems = { + added: [], + removed: [], + }; + const multiSelectEnabled = this._isMultiSelectEnabled(); + let lastRequestData = multiSelectEnabled ? this._lastRequestData : {}; + + if (multiSelectEnabled) { + if (this._shouldMergeWithLastRequest) { + if (isDeselectAll) { + this._lastLoadDeferred.reject(); + lastRequestData = {}; + } else if (!isKeysEqual(keys, this.options.selectedItemKeys)) { + oldRequestItems.added = lastRequestData.addedItems; + oldRequestItems.removed = lastRequestData.removedItems; + + if (!isDeselect) { + this._lastLoadDeferred.reject(); + } + } + } + + lastRequestData = this._concatRequestsItems(keys, isDeselect, oldRequestItems, this._shouldMergeWithLastRequest ? undefined : updatedKeys); } - _updateKeysByLastRequestData(keys, isDeselect, isSelectAll) { - let currentKeys = keys; - if(this._isMultiSelectEnabled() && this._shouldMergeWithLastRequest && !isDeselect && !isSelectAll) { - currentKeys = removeDuplicates(keys.concat(this._lastRequestData?.addedItems), this._lastRequestData?.removedItems); - currentKeys = getUniqueValues(currentKeys); - } + return lastRequestData; + } - return currentKeys; + _updateKeysByLastRequestData(keys, isDeselect, isSelectAll) { + let currentKeys = keys; + if (this._isMultiSelectEnabled() && this._shouldMergeWithLastRequest && !isDeselect && !isSelectAll) { + currentKeys = removeDuplicates(keys.concat(this._lastRequestData?.addedItems), this._lastRequestData?.removedItems); + currentKeys = getUniqueValues(currentKeys); } - _loadSelectedItems(keys, isDeselect, isSelectAll, updatedKeys, forceCombinedFilter = false) { - const that = this; - const deferred = new Deferred(); - const filter = that.options.filter(); + return currentKeys; + } + + _loadSelectedItems(keys, isDeselect, isSelectAll, updatedKeys, forceCombinedFilter = false) { + const that = this; + const deferred = Deferred(); + const filter = that.options.filter(); + + this._shouldMergeWithLastRequest = this._requestInProgress(); + + this._lastRequestData = this._collectLastRequestData(keys, isDeselect, isSelectAll, updatedKeys); + + when(that._lastLoadDeferred).always(() => { + const currentKeys = that._updateKeysByLastRequestData(keys, isDeselect, isSelectAll); + + that._shouldMergeWithLastRequest = false; + + that._loadSelectedItemsCore(currentKeys, isDeselect, isSelectAll, filter, forceCombinedFilter) + // eslint-disable-next-line @typescript-eslint/no-misused-promises + .done(deferred.resolve) + // eslint-disable-next-line @typescript-eslint/no-misused-promises + .fail(deferred.reject); + }); + + that._lastLoadDeferred = deferred; + + return deferred; + } + + selectedItemKeys(keys, preserve, isDeselect, isSelectAll, updatedKeys, forceCombinedFilter = false) { + const that = this; + const deferred = that._loadSelectedItems(keys, isDeselect, isSelectAll, updatedKeys, forceCombinedFilter); + + deferred.done((items) => { + if (preserve) { + that._preserveSelectionUpdate(items, isDeselect); + } else { + that._replaceSelectionUpdate(items); + } + /// #DEBUG + if (!isSelectAll && !isDeselect) { + that._warnOnIncorrectKeys(keys); + } + /// #ENDDEBUG + that.onSelectionChanged(); + }); + + return deferred; + } + + addSelectedItem(key, itemData) { + if (isDefined(itemData) && !this.options.ignoreDisabledItems && itemData.disabled) { + if (this.options.disabledItemKeys.indexOf(key) === -1) { + this.options.disabledItemKeys.push(key); + } + return; + } - this._shouldMergeWithLastRequest = this._requestInProgress(); + const keyHash = this._getKeyHash(key); - this._lastRequestData = this._collectLastRequestData(keys, isDeselect, isSelectAll, updatedKeys); + if (this._indexOfSelectedItemKey(keyHash) === -1) { + if (!isObject(keyHash) && this.options.keyHashIndices) { + this.options.keyHashIndices[keyHash] = [this.options.selectedItemKeys.length]; + } - when(that._lastLoadDeferred).always(function() { - const currentKeys = that._updateKeysByLastRequestData(keys, isDeselect, isSelectAll); + this.options.selectedItemKeys.push(key); + this.options.addedItemKeys.push(key); + this.options.addedItems.push(itemData); + this.options.selectedItems.push(itemData); + } + } - that._shouldMergeWithLastRequest = false; + _getSelectedIndexByKey(key, ignoreIndicesMap) { + const { selectedItemKeys } = this.options; - that._loadSelectedItemsCore(currentKeys, isDeselect, isSelectAll, filter, forceCombinedFilter) - .done(deferred.resolve) - .fail(deferred.reject); - }); + for (let index = 0; index < selectedItemKeys.length; index++) { + if ((!ignoreIndicesMap || !ignoreIndicesMap[index]) && this.equalKeys(selectedItemKeys[index], key)) { + return index; + } + } + return -1; + } - that._lastLoadDeferred = deferred; + _getSelectedIndexByHash(key, ignoreIndicesMap) { + let indices = this.options.keyHashIndices[key]; - return deferred; + if (indices && indices.length > 1 && ignoreIndicesMap) { + indices = indices.filter((index) => !ignoreIndicesMap[index]); } - selectedItemKeys(keys, preserve, isDeselect, isSelectAll, updatedKeys, forceCombinedFilter = false) { - const that = this; - const deferred = that._loadSelectedItems(keys, isDeselect, isSelectAll, updatedKeys, forceCombinedFilter); + return indices && indices[0] >= 0 ? indices[0] : -1; + } - deferred.done(function(items) { - if(preserve) { - that._preserveSelectionUpdate(items, isDeselect); - } else { - that._replaceSelectionUpdate(items); - } - ///#DEBUG - if(!isSelectAll && !isDeselect) { - that._warnOnIncorrectKeys(keys); - } - ///#ENDDEBUG - that.onSelectionChanged(); - }); + _indexOfSelectedItemKey(key, ignoreIndicesMap?: any[]) { + let selectedIndex; - return deferred; + if (this.options.equalByReference) { + selectedIndex = this.options.selectedItemKeys.indexOf(key); + } else if (isObject(key)) { + selectedIndex = this._getSelectedIndexByKey(key, ignoreIndicesMap); + } else { + selectedIndex = this._getSelectedIndexByHash(key, ignoreIndicesMap); } - addSelectedItem(key, itemData) { - if(isDefined(itemData) && !this.options.ignoreDisabledItems && itemData.disabled) { - if(this.options.disabledItemKeys.indexOf(key) === -1) { - this.options.disabledItemKeys.push(key); - } - return; - } + return selectedIndex; + } - const keyHash = this._getKeyHash(key); + _shiftSelectedKeyIndices(keyIndex) { + for (let currentKeyIndex = keyIndex; currentKeyIndex < this.options.selectedItemKeys.length; currentKeyIndex++) { + const currentKey = this.options.selectedItemKeys[currentKeyIndex]; + const currentKeyHash = getKeyHash(currentKey); + const currentKeyIndices = this.options.keyHashIndices[currentKeyHash]; - if(this._indexOfSelectedItemKey(keyHash) === -1) { - if(!isObject(keyHash) && this.options.keyHashIndices) { - this.options.keyHashIndices[keyHash] = [this.options.selectedItemKeys.length]; - } + if (!currentKeyIndices) continue; - this.options.selectedItemKeys.push(key); - this.options.addedItemKeys.push(key); - this.options.addedItems.push(itemData); - this.options.selectedItems.push(itemData); + for (let i = 0; i < currentKeyIndices.length; i++) { + if (currentKeyIndices[i] > keyIndex) { + currentKeyIndices[i]--; } + } } - - _getSelectedIndexByKey(key, ignoreIndicesMap) { - const selectedItemKeys = this.options.selectedItemKeys; - - for(let index = 0; index < selectedItemKeys.length; index++) { - if((!ignoreIndicesMap || !ignoreIndicesMap[index]) && this.equalKeys(selectedItemKeys[index], key)) { - return index; - } - } - return -1; + } + + removeSelectedItem( + key, + keyIndicesToRemoveMap?: any[], + isDisabled?: boolean, + ) { + if (!this.options.ignoreDisabledItems && isDisabled) { + return; } - _getSelectedIndexByHash(key, ignoreIndicesMap) { - let indices = this.options.keyHashIndices[key]; - - if(indices && indices.length > 1 && ignoreIndicesMap) { - indices = indices.filter(function(index) { - return !ignoreIndicesMap[index]; - }); - } + const keyHash = this._getKeyHash(key); + const isBatchDeselect = !!keyIndicesToRemoveMap; + const keyIndex = this._indexOfSelectedItemKey(keyHash, keyIndicesToRemoveMap); - return indices && indices[0] >= 0 ? indices[0] : -1; + if (keyIndex < 0) { + return keyIndex; } - _indexOfSelectedItemKey(key, ignoreIndicesMap) { - let selectedIndex; + this.options.removedItemKeys.push(key); + this.options.removedItems.push(this.options.selectedItems[keyIndex]); - if(this.options.equalByReference) { - selectedIndex = this.options.selectedItemKeys.indexOf(key); - } else if(isObject(key)) { - selectedIndex = this._getSelectedIndexByKey(key, ignoreIndicesMap); - } else { - selectedIndex = this._getSelectedIndexByHash(key, ignoreIndicesMap); - } - - return selectedIndex; + if (isBatchDeselect) { + return keyIndex; } - _shiftSelectedKeyIndices(keyIndex) { - for(let currentKeyIndex = keyIndex; currentKeyIndex < this.options.selectedItemKeys.length; currentKeyIndex++) { - const currentKey = this.options.selectedItemKeys[currentKeyIndex]; - const currentKeyHash = getKeyHash(currentKey); - const currentKeyIndices = this.options.keyHashIndices[currentKeyHash]; - - if(!currentKeyIndices) continue; + this.options.selectedItemKeys.splice(keyIndex, 1); + this.options.selectedItems.splice(keyIndex, 1); - for(let i = 0; i < currentKeyIndices.length; i++) { - if(currentKeyIndices[i] > keyIndex) { - currentKeyIndices[i]--; - } - } - } + if (isObject(keyHash) || !this.options.keyHashIndices) { + return keyIndex; } - removeSelectedItem(key, keyIndicesToRemoveMap, isDisabled) { - if(!this.options.ignoreDisabledItems && isDisabled) { - return; - } - - const keyHash = this._getKeyHash(key); - const isBatchDeselect = !!keyIndicesToRemoveMap; - const keyIndex = this._indexOfSelectedItemKey(keyHash, keyIndicesToRemoveMap); - - if(keyIndex < 0) { - return keyIndex; - } - - this.options.removedItemKeys.push(key); - this.options.removedItems.push(this.options.selectedItems[keyIndex]); - - if(isBatchDeselect) { - return keyIndex; - } - - this.options.selectedItemKeys.splice(keyIndex, 1); - this.options.selectedItems.splice(keyIndex, 1); - - if(isObject(keyHash) || !this.options.keyHashIndices) { - return keyIndex; - } - - const keyIndices = this.options.keyHashIndices[keyHash]; - - if(!keyIndices) { - return keyIndex; - } - - keyIndices.shift(); + const keyIndices = this.options.keyHashIndices[keyHash]; - if(!keyIndices.length) { - delete this.options.keyHashIndices[keyHash]; - } + if (!keyIndices) { + return keyIndex; + } - this._shiftSelectedKeyIndices(keyIndex); + keyIndices.shift(); - return keyIndex; + if (!keyIndices.length) { + // eslint-disable-next-line @typescript-eslint/no-dynamic-delete + delete this.options.keyHashIndices[keyHash]; } - _updateAddedItemKeys(keys, items) { - for(let i = 0; i < keys.length; i++) { - if(!this.isItemKeySelected(keys[i])) { - this.options.addedItemKeys.push(keys[i]); - this.options.addedItems.push(items[i]); - } - } - } + this._shiftSelectedKeyIndices(keyIndex); - _updateRemovedItemKeys(keys, oldSelectedKeys, oldSelectedItems) { - for(let i = 0; i < oldSelectedKeys.length; i++) { - if(!this.isItemKeySelected(oldSelectedKeys[i])) { - this.options.removedItemKeys.push(oldSelectedKeys[i]); - this.options.removedItems.push(oldSelectedItems[i]); - } - } - } + return keyIndex; + } - _isItemSelectionInProgress(key, checkPending) { - const shouldCheckPending = checkPending && this._lastRequestData && this._requestInProgress(); - if(shouldCheckPending) { - const addedItems = this._lastRequestData.addedItems ?? []; - return addedItems.includes(key); - } else { - return false; - } + _updateAddedItemKeys(keys, items) { + for (let i = 0; i < keys.length; i++) { + if (!this.isItemKeySelected(keys[i])) { + this.options.addedItemKeys.push(keys[i]); + this.options.addedItems.push(items[i]); + } } - - _getKeyHash(key) { - return this.options.equalByReference ? key : getKeyHash(key); + } + + _updateRemovedItemKeys(keys, oldSelectedKeys, oldSelectedItems) { + for (let i = 0; i < oldSelectedKeys.length; i++) { + if (!this.isItemKeySelected(oldSelectedKeys[i])) { + this.options.removedItemKeys.push(oldSelectedKeys[i]); + this.options.removedItems.push(oldSelectedItems[i]); + } } + } - setSelectedItems(keys, items) { - this._updateAddedItemKeys(keys, items); + _isItemSelectionInProgress(key, checkPending) { + const shouldCheckPending = checkPending && this._lastRequestData && this._requestInProgress(); + if (shouldCheckPending) { + const addedItems = this._lastRequestData.addedItems ?? []; + return addedItems.includes(key); + } + return false; + } - const oldSelectedKeys = this.options.selectedItemKeys; - const oldSelectedItems = this.options.selectedItems; + _getKeyHash(key) { + return this.options.equalByReference ? key : getKeyHash(key); + } - if(!this.options.equalByReference) { - this._initSelectedItemKeyHash(); - this.updateSelectedItemKeyHash(keys); - } + setSelectedItems(keys, items) { + this._updateAddedItemKeys(keys, items); - this._setOption('selectedItemKeys', keys); - this._setOption('selectedItems', items); + const oldSelectedKeys = this.options.selectedItemKeys; + const oldSelectedItems = this.options.selectedItems; - this._updateRemovedItemKeys(keys, oldSelectedKeys, oldSelectedItems); + if (!this.options.equalByReference) { + this._initSelectedItemKeyHash(); + this.updateSelectedItemKeyHash(keys); } - isItemDataSelected(itemData, options = {}) { - const key = this.options.keyOf(itemData); - return this.isItemKeySelected(key, options); - } + this._setOption('selectedItemKeys', keys); + this._setOption('selectedItems', items); - isItemKeySelected(key, options = {}) { - let result = this._isItemSelectionInProgress(key, options.checkPending); + this._updateRemovedItemKeys(keys, oldSelectedKeys, oldSelectedItems); + } - if(!result) { - const keyHash = this._getKeyHash(key); - const index = this._indexOfSelectedItemKey(keyHash); - result = index !== -1; - } + isItemDataSelected(itemData, options = {}) { + const key = this.options.keyOf(itemData); + return this.isItemKeySelected(key, options); + } - return result; - } + isItemKeySelected(key, options: { checkPending?: boolean } = {}) { + let result = this._isItemSelectionInProgress(key, options.checkPending); - getSelectAllState(visibleOnly) { - if(visibleOnly) { - return this._getVisibleSelectAllState(); - } else { - return this._getFullSelectAllState(); - } + if (!result) { + const keyHash = this._getKeyHash(key); + const index = this._indexOfSelectedItemKey(keyHash); + result = index !== -1; } - loadSelectedItemsWithFilter() { - const keyExpr = this.options.key(); - const keys = this.getSelectedItemKeys(); - const filter = this.options.filter(); + return result; + } - if(!keys.length) { - return Deferred().resolve([]); - } + getSelectAllState(visibleOnly) { + if (visibleOnly) { + return this._getVisibleSelectAllState(); + } + return this._getFullSelectAllState(); + } - const selectionFilterCreator = new SelectionFilterCreator(keys); - const combinedFilter = selectionFilterCreator.getCombinedFilter(keyExpr, filter, true); + loadSelectedItemsWithFilter() { + const keyExpr = this.options.key(); + const keys = this.getSelectedItemKeys(); + const filter = this.options.filter(); - return this._loadFilteredData(combinedFilter); + if (!keys.length) { + return Deferred().resolve([]); } + + const selectionFilterCreator = new SelectionFilterCreator(keys); + const combinedFilter = selectionFilterCreator.getCombinedFilter(keyExpr, filter, true); + + return this._loadFilteredData(combinedFilter); + } } diff --git a/packages/devextreme/js/__internal/ui/selection/m_selection.strategy.ts b/packages/devextreme/js/__internal/ui/selection/m_selection.strategy.ts index 4dd7126bbf16..0cf229886451 100644 --- a/packages/devextreme/js/__internal/ui/selection/m_selection.strategy.ts +++ b/packages/devextreme/js/__internal/ui/selection/m_selection.strategy.ts @@ -1,185 +1,190 @@ -import dataQuery from '../../data/query'; -import { getKeyHash, noop, equalByValue } from '../../core/utils/common'; -import { isPlainObject, isObject } from '../../core/utils/type'; -import { Deferred } from '../../core/utils/deferred'; +import { + equalByValue, + // @ts-expect-error + getKeyHash, + noop, +} from '@js/core/utils/common'; +import { Deferred } from '@js/core/utils/deferred'; +import { isObject, isPlainObject } from '@js/core/utils/type'; +import dataQuery from '@js/data/query'; export default class SelectionStrategy { - constructor(options) { - this.options = options; - - this._setOption('disabledItemKeys', []); - this._clearItemKeys(); + options: any; + + constructor(options) { + this.options = options; + + this._setOption('disabledItemKeys', []); + this._clearItemKeys(); + } + + _clearItemKeys() { + this._setOption('addedItemKeys', []); + this._setOption('removedItemKeys', []); + this._setOption('removedItems', []); + this._setOption('addedItems', []); + } + + validate() { + + } + + _setOption(name, value) { + this.options[name] = value; + } + + onSelectionChanged() { + const { addedItemKeys } = this.options; + const { removedItemKeys } = this.options; + const { addedItems } = this.options; + const { removedItems } = this.options; + const { selectedItems } = this.options; + const { selectedItemKeys } = this.options; + const onSelectionChanged = this.options.onSelectionChanged || noop; + + this._clearItemKeys(); + onSelectionChanged({ + selectedItems, + selectedItemKeys, + addedItemKeys, + removedItemKeys, + addedItems, + removedItems, + }); + } + + equalKeys(key1, key2) { + if (this.options.equalByReference) { + if (isObject(key1) && isObject(key2)) { + return key1 === key2; + } } - _clearItemKeys() { - this._setOption('addedItemKeys', []); - this._setOption('removedItemKeys', []); - this._setOption('removedItems', []); - this._setOption('addedItems', []); - } + return equalByValue(key1, key2); + } - validate() { + getSelectableItems(items) { + return items.filter((item) => !item?.disabled); + } - } + _clearSelection(keys, preserve, isDeselect, isSelectAll) { + keys = keys || []; + keys = Array.isArray(keys) ? keys : [keys]; + this.validate(); + // @ts-expect-error + return this.selectedItemKeys(keys, preserve, isDeselect, isSelectAll); + } - _setOption(name, value) { - this.options[name] = value; + _removeTemplateProperty(remoteFilter: { template: any }) { + if (Array.isArray(remoteFilter)) { + return remoteFilter.map((f) => this._removeTemplateProperty(f)); } - onSelectionChanged() { - const addedItemKeys = this.options.addedItemKeys; - const removedItemKeys = this.options.removedItemKeys; - const addedItems = this.options.addedItems; - const removedItems = this.options.removedItems; - const selectedItems = this.options.selectedItems; - const selectedItemKeys = this.options.selectedItemKeys; - const onSelectionChanged = this.options.onSelectionChanged || noop; - - this._clearItemKeys(); - onSelectionChanged({ - selectedItems: selectedItems, - selectedItemKeys: selectedItemKeys, - addedItemKeys: addedItemKeys, - removedItemKeys: removedItemKeys, - addedItems: addedItems, - removedItems: removedItems - }); + if (isObject(remoteFilter)) { + delete remoteFilter.template; } - equalKeys(key1, key2) { - if(this.options.equalByReference) { - if(isObject(key1) && isObject(key2)) { - return key1 === key2; - } - } - - return equalByValue(key1, key2); + return remoteFilter; + } + + _loadFilteredData(remoteFilter, localFilter?: any, select?: any, isSelectAll?: boolean) { + const filterLength = encodeURI(JSON.stringify(this._removeTemplateProperty(remoteFilter))).length; + const needLoadAllData = this.options.maxFilterLengthInRequest && (filterLength > this.options.maxFilterLengthInRequest); + const deferred = Deferred(); + const loadOptions = { + filter: needLoadAllData ? undefined : remoteFilter, + select: needLoadAllData ? this.options.dataFields() : select || this.options.dataFields(), + }; + + if (remoteFilter && remoteFilter.length === 0) { + deferred.resolve([]); + } else { + this.options.load(loadOptions) + .done((items) => { + let filteredItems = isPlainObject(items) ? items.data : items; + + if (localFilter && !isSelectAll) { + filteredItems = filteredItems.filter(localFilter); + } else if (needLoadAllData) { + filteredItems = dataQuery(filteredItems).filter(remoteFilter).toArray(); + } + + deferred.resolve(filteredItems); + }) + .fail(deferred.reject.bind(deferred)); } - getSelectableItems(items) { - return items.filter(function(item) { - return !item?.disabled; - }); - } + return deferred; + } - _clearSelection(keys, preserve, isDeselect, isSelectAll) { - keys = keys || []; - keys = Array.isArray(keys) ? keys : [keys]; - this.validate(); + updateSelectedItemKeyHash(keys) { + for (let i = 0; i < keys.length; i++) { + const keyHash = getKeyHash(keys[i]); - return this.selectedItemKeys(keys, preserve, isDeselect, isSelectAll); - } + if (!isObject(keyHash)) { + this.options.keyHashIndices[keyHash] = this.options.keyHashIndices[keyHash] || []; - _removeTemplateProperty(remoteFilter) { - if(Array.isArray(remoteFilter)) { - return remoteFilter.map((f) => this._removeTemplateProperty(f)); - } - - if(isObject(remoteFilter)) { - delete remoteFilter.template; - } - - return remoteFilter; + const keyIndices = this.options.keyHashIndices[keyHash]; + keyIndices.push(i); + } } + } - _loadFilteredData(remoteFilter, localFilter, select, isSelectAll) { - const filterLength = encodeURI(JSON.stringify(this._removeTemplateProperty(remoteFilter))).length; - const needLoadAllData = this.options.maxFilterLengthInRequest && (filterLength > this.options.maxFilterLengthInRequest); - const deferred = new Deferred(); - const loadOptions = { - filter: needLoadAllData ? undefined : remoteFilter, - select: needLoadAllData ? this.options.dataFields() : select || this.options.dataFields() - }; - - if(remoteFilter && remoteFilter.length === 0) { - deferred.resolve([]); - } else { - this.options.load(loadOptions) - .done(function(items) { - let filteredItems = isPlainObject(items) ? items.data : items; - - if(localFilter && !isSelectAll) { - filteredItems = filteredItems.filter(localFilter); - } else if(needLoadAllData) { - filteredItems = dataQuery(filteredItems).filter(remoteFilter).toArray(); - } - - deferred.resolve(filteredItems); - }) - .fail(deferred.reject.bind(deferred)); - } - - return deferred; + _isAnyItemSelected(items) { + for (let i = 0; i < items.length; i++) { + if (this.options.isItemSelected(items[i])) { + return undefined; + } } - updateSelectedItemKeyHash(keys) { - for(let i = 0; i < keys.length; i++) { - const keyHash = getKeyHash(keys[i]); + return false; + } - if(!isObject(keyHash)) { - this.options.keyHashIndices[keyHash] = this.options.keyHashIndices[keyHash] || []; + _getFullSelectAllState() { + const items = this.options.plainItems(); + const dataFilter = this.options.filter(); + let selectedItems = this.options.ignoreDisabledItems ? this.options.selectedItems : this.options.selectedItems.filter((item) => !item?.disabled); - const keyIndices = this.options.keyHashIndices[keyHash]; - keyIndices.push(i); - } - } + if (dataFilter) { + selectedItems = dataQuery(selectedItems).filter(dataFilter).toArray(); } - _isAnyItemSelected(items) { - for(let i = 0; i < items.length; i++) { - if(this.options.isItemSelected(items[i])) { - return undefined; - } - } + const selectedItemsLength = selectedItems.length; + const disabledItemsLength = items.length - this.getSelectableItems(items).length; - return false; + if (!selectedItemsLength) { + return this._isAnyItemSelected(items); } - _getFullSelectAllState() { - const items = this.options.plainItems(); - const dataFilter = this.options.filter(); - let selectedItems = this.options.ignoreDisabledItems ? this.options.selectedItems : this.options.selectedItems.filter(item => !item?.disabled); - - if(dataFilter) { - selectedItems = dataQuery(selectedItems).filter(dataFilter).toArray(); - } - - const selectedItemsLength = selectedItems.length; - const disabledItemsLength = items.length - this.getSelectableItems(items).length; - - if(!selectedItemsLength) { - return this._isAnyItemSelected(items); - } - - if(selectedItemsLength >= this.options.totalCount() - disabledItemsLength) { - return true; - } - return undefined; + if (selectedItemsLength >= this.options.totalCount() - disabledItemsLength) { + return true; } - - _getVisibleSelectAllState() { - const items = this.getSelectableItems(this.options.plainItems()); - let hasSelectedItems = false; - let hasUnselectedItems = false; - - for(let i = 0; i < items.length; i++) { - const item = items[i]; - const itemData = this.options.getItemData(item); - const key = this.options.keyOf(itemData); - - if(this.options.isSelectableItem(item)) { - if(this.isItemKeySelected(key)) { - hasSelectedItems = true; - } else { - hasUnselectedItems = true; - } - } - } - - if(hasSelectedItems) { - return !hasUnselectedItems ? true : undefined; + return undefined; + } + + _getVisibleSelectAllState() { + const items = this.getSelectableItems(this.options.plainItems()); + let hasSelectedItems = false; + let hasUnselectedItems = false; + + for (let i = 0; i < items.length; i++) { + const item = items[i]; + const itemData = this.options.getItemData(item); + const key = this.options.keyOf(itemData); + + if (this.options.isSelectableItem(item)) { + // @ts-expect-error + if (this.isItemKeySelected(key)) { + hasSelectedItems = true; } else { - return false; + hasUnselectedItems = true; } + } + } + + if (hasSelectedItems) { + return !hasUnselectedItems ? true : undefined; } + return false; + } } diff --git a/packages/devextreme/js/__internal/ui/selection/m_selection.ts b/packages/devextreme/js/__internal/ui/selection/m_selection.ts index f436c62c400b..763271fc10c7 100644 --- a/packages/devextreme/js/__internal/ui/selection/m_selection.ts +++ b/packages/devextreme/js/__internal/ui/selection/m_selection.ts @@ -1,341 +1,363 @@ -import deferredStrategy from './selection.strategy.deferred'; -import standardStrategy from './selection.strategy.standard'; -import { extend } from '../../core/utils/extend'; -import { noop } from '../../core/utils/common'; -import { isDefined } from '../../core/utils/type'; -import { Deferred, when } from '../../core/utils/deferred'; +import { noop } from '@js/core/utils/common'; +import { Deferred, when } from '@js/core/utils/deferred'; +import { extend } from '@js/core/utils/extend'; +import { isDefined } from '@js/core/utils/type'; + +import deferredStrategy from './m_selection.strategy.deferred'; +import standardStrategy from './m_selection.strategy.standard'; export default class Selection { - constructor(options) { - this.options = extend(this._getDefaultOptions(), options, { - selectedItemKeys: options.selectedKeys || [] - }); + options: any; - this._selectionStrategy = this.options.deferred ? new deferredStrategy(this.options) : new standardStrategy(this.options); - this._focusedItemIndex = -1; + _selectionStrategy: deferredStrategy | standardStrategy; - if(!this.options.equalByReference) { - this._selectionStrategy.updateSelectedItemKeyHash(this.options.selectedItemKeys); - } - } + _focusedItemIndex: number; - _getDefaultOptions() { - return { - allowNullValue: false, - deferred: false, - equalByReference: false, - mode: 'multiple', - selectedItems: [], - selectionFilter: [], - maxFilterLengthInRequest: 0, - onSelectionChanged: noop, - key: noop, - keyOf: function(item) { return item; }, - load: function() { return new Deferred().resolve([]); }, - totalCount: function() { return -1; }, - isSelectableItem: function() { return true; }, - isItemSelected: function() { return false; }, - getItemData: function(item) { return item; }, - dataFields: noop, - filter: noop - }; - } + _shiftFocusedItemIndex!: number; - validate() { - this._selectionStrategy.validate(); - } + constructor(options) { + this.options = extend(this._getDefaultOptions(), options, { + selectedItemKeys: options.selectedKeys || [], + }); - getSelectedItemKeys() { - return this._selectionStrategy.getSelectedItemKeys(); - } + this._selectionStrategy = this.options.deferred + // eslint-disable-next-line new-cap + ? new deferredStrategy(this.options) + // eslint-disable-next-line new-cap + : new standardStrategy(this.options); - getSelectedItems() { - return this._selectionStrategy.getSelectedItems(); - } + this._focusedItemIndex = -1; - selectionFilter(value) { - if(value === undefined) { - return this.options.selectionFilter; - } - - const filterIsChanged = this.options.selectionFilter !== value && JSON.stringify(this.options.selectionFilter) !== JSON.stringify(value); - - this.options.selectionFilter = value; - - filterIsChanged && this.onSelectionChanged(); + if (!this.options.equalByReference) { + this._selectionStrategy.updateSelectedItemKeyHash(this.options.selectedItemKeys); } - - setSelection(keys, updatedKeys) { - return this.selectedItemKeys(keys, false, false, false, updatedKeys); + } + + _getDefaultOptions() { + return { + allowNullValue: false, + deferred: false, + equalByReference: false, + mode: 'multiple', + selectedItems: [], + selectionFilter: [], + maxFilterLengthInRequest: 0, + onSelectionChanged: noop, + key: noop, + keyOf(item) { return item; }, + load() { return Deferred().resolve([]); }, + totalCount() { return -1; }, + isSelectableItem() { return true; }, + isItemSelected() { return false; }, + getItemData(item) { return item; }, + dataFields: noop, + filter: noop, + }; + } + + validate() { + this._selectionStrategy.validate(); + } + + getSelectedItemKeys() { + return this._selectionStrategy.getSelectedItemKeys(); + } + + getSelectedItems() { + return this._selectionStrategy.getSelectedItems(); + } + + selectionFilter(value?: any) { + if (value === undefined) { + return this.options.selectionFilter; } - select(keys) { - return this.selectedItemKeys(keys, true); + const filterIsChanged = this.options.selectionFilter !== value && JSON.stringify(this.options.selectionFilter) !== JSON.stringify(value); + + this.options.selectionFilter = value; + + filterIsChanged && this.onSelectionChanged(); + } + + setSelection(keys, updatedKeys) { + return this.selectedItemKeys(keys, false, false, false, updatedKeys); + } + + select(keys) { + return this.selectedItemKeys(keys, true); + } + + deselect(keys) { + return this.selectedItemKeys(keys, true, true); + } + + selectedItemKeys( + keys, + preserve?: boolean, + isDeselect?: boolean, + isSelectAll?: boolean, + updatedKeys?: any[], + ) { + const that = this; + + keys = keys ?? []; + keys = Array.isArray(keys) ? keys : [keys]; + that.validate(); + + return this._selectionStrategy.selectedItemKeys(keys, preserve, isDeselect, isSelectAll, updatedKeys); + } + + clearSelection() { + return this.selectedItemKeys([]); + } + + _addSelectedItem(itemData, key) { + this._selectionStrategy.addSelectedItem(key, itemData); + } + + _removeSelectedItem(key) { + this._selectionStrategy.removeSelectedItem(key); + } + + _setSelectedItems(keys, items) { + this._selectionStrategy.setSelectedItems(keys, items); + } + + onSelectionChanged() { + this._selectionStrategy.onSelectionChanged(); + } + + // @ts-expect-error + changeItemSelection(itemIndex, keys, setFocusOnly) { + let isSelectedItemsChanged; + const items = this.options.plainItems(); + const item = items[itemIndex]; + let deferred; + const { isVirtualPaging } = this.options; + const allowLoadByRange = this.options.allowLoadByRange?.(); + const { alwaysSelectByShift } = this.options; + let indexOffset; + let focusedItemNotInLoadedRange = false; + let shiftFocusedItemNotInLoadedRange = false; + + const itemIsNotInLoadedRange = (index) => index >= 0 && !items.filter((it) => it.loadIndex === index).length; + + if (isVirtualPaging && isDefined(item)) { + if (allowLoadByRange) { + indexOffset = item.loadIndex - itemIndex; + itemIndex = item.loadIndex; + } + focusedItemNotInLoadedRange = itemIsNotInLoadedRange(this._focusedItemIndex); + if (isDefined(this._shiftFocusedItemIndex)) { + shiftFocusedItemNotInLoadedRange = itemIsNotInLoadedRange(this._shiftFocusedItemIndex); + } } - deselect(keys) { - return this.selectedItemKeys(keys, true, true); + if (!this.isSelectable() || !this.isDataItem(item)) { + return false; } - selectedItemKeys(keys, preserve, isDeselect, isSelectAll, updatedKeys) { - const that = this; + const itemData = this.options.getItemData(item); + const itemKey = this.options.keyOf(itemData); - keys = keys ?? []; - keys = Array.isArray(keys) ? keys : [keys]; - that.validate(); + keys = keys || {}; + let allowSelectByShift = keys.shift; - return this._selectionStrategy.selectedItemKeys(keys, preserve, isDeselect, isSelectAll, updatedKeys); + if (alwaysSelectByShift === false && allowSelectByShift) { + allowSelectByShift = allowLoadByRange !== false || (!focusedItemNotInLoadedRange && !shiftFocusedItemNotInLoadedRange); } - clearSelection() { - return this.selectedItemKeys([]); - } - - _addSelectedItem(itemData, key) { - this._selectionStrategy.addSelectedItem(key, itemData); - } + if (allowSelectByShift && this.options.mode === 'multiple' && this._focusedItemIndex >= 0) { + if (allowLoadByRange && (focusedItemNotInLoadedRange || shiftFocusedItemNotInLoadedRange)) { + isSelectedItemsChanged = itemIndex !== this._shiftFocusedItemIndex || this._focusedItemIndex !== this._shiftFocusedItemIndex; - _removeSelectedItem(key) { - this._selectionStrategy.removeSelectedItem(key); - } - - _setSelectedItems(keys, items) { - this._selectionStrategy.setSelectedItems(keys, items); - } - - onSelectionChanged() { - this._selectionStrategy.onSelectionChanged(); - } - - changeItemSelection(itemIndex, keys, setFocusOnly) { - let isSelectedItemsChanged; - const items = this.options.plainItems(); - const item = items[itemIndex]; - let deferred; - const isVirtualPaging = this.options.isVirtualPaging; - const allowLoadByRange = this.options.allowLoadByRange?.(); - const alwaysSelectByShift = this.options.alwaysSelectByShift; - let indexOffset; - let focusedItemNotInLoadedRange = false; - let shiftFocusedItemNotInLoadedRange = false; - - const itemIsNotInLoadedRange = (index) => index >= 0 && !items.filter(it => it.loadIndex === index).length; - - if(isVirtualPaging && isDefined(item)) { - if(allowLoadByRange) { - indexOffset = item.loadIndex - itemIndex; - itemIndex = item.loadIndex; - } - focusedItemNotInLoadedRange = itemIsNotInLoadedRange(this._focusedItemIndex); - if(isDefined(this._shiftFocusedItemIndex)) { - shiftFocusedItemNotInLoadedRange = itemIsNotInLoadedRange(this._shiftFocusedItemIndex); - } - } - - if(!this.isSelectable() || !this.isDataItem(item)) { - return false; + if (isSelectedItemsChanged) { + deferred = this.changeItemSelectionWhenShiftKeyInVirtualPaging(itemIndex); } - - const itemData = this.options.getItemData(item); - const itemKey = this.options.keyOf(itemData); - - keys = keys || {}; - let allowSelectByShift = keys.shift; - - if(alwaysSelectByShift === false && allowSelectByShift) { - allowSelectByShift = (allowLoadByRange !== false || (!focusedItemNotInLoadedRange && !shiftFocusedItemNotInLoadedRange)); + } else { + isSelectedItemsChanged = this.changeItemSelectionWhenShiftKeyPressed(itemIndex, items, indexOffset); + } + } else if (keys.control) { + this._resetItemSelectionWhenShiftKeyPressed(); + if (!setFocusOnly) { + const isSelected = this._selectionStrategy.isItemDataSelected(itemData); + if (this.options.mode === 'single') { + this.clearSelectedItems(); } - - if(allowSelectByShift && this.options.mode === 'multiple' && this._focusedItemIndex >= 0) { - if(allowLoadByRange && (focusedItemNotInLoadedRange || shiftFocusedItemNotInLoadedRange)) { - isSelectedItemsChanged = itemIndex !== this._shiftFocusedItemIndex || this._focusedItemIndex !== this._shiftFocusedItemIndex; - - if(isSelectedItemsChanged) { - deferred = this.changeItemSelectionWhenShiftKeyInVirtualPaging(itemIndex); - } - } else { - isSelectedItemsChanged = this.changeItemSelectionWhenShiftKeyPressed(itemIndex, items, indexOffset); - } - } else if(keys.control) { - this._resetItemSelectionWhenShiftKeyPressed(); - if(!setFocusOnly) { - const isSelected = this._selectionStrategy.isItemDataSelected(itemData); - if(this.options.mode === 'single') { - this.clearSelectedItems(); - } - if(isSelected) { - this._removeSelectedItem(itemKey); - } else { - this._addSelectedItem(itemData, itemKey); - } - } - isSelectedItemsChanged = true; + if (isSelected) { + this._removeSelectedItem(itemKey); } else { - this._resetItemSelectionWhenShiftKeyPressed(); - const isKeysEqual = this._selectionStrategy.equalKeys(this.options.selectedItemKeys[0], itemKey); - if(this.options.selectedItemKeys.length !== 1 || !isKeysEqual) { - this._setSelectedItems([itemKey], [itemData]); - isSelectedItemsChanged = true; - } + this._addSelectedItem(itemData, itemKey); } - - if(isSelectedItemsChanged) { - when(deferred).done(() => { - this._focusedItemIndex = itemIndex; - !setFocusOnly && this.onSelectionChanged(); - }); - return true; - } - } - - isDataItem(item) { - return this.options.isSelectableItem(item); + } + isSelectedItemsChanged = true; + } else { + this._resetItemSelectionWhenShiftKeyPressed(); + const isKeysEqual = this._selectionStrategy.equalKeys(this.options.selectedItemKeys[0], itemKey); + if (this.options.selectedItemKeys.length !== 1 || !isKeysEqual) { + this._setSelectedItems([itemKey], [itemData]); + isSelectedItemsChanged = true; + } } - isSelectable() { - return this.options.mode === 'single' || this.options.mode === 'multiple'; + if (isSelectedItemsChanged) { + when(deferred).done(() => { + this._focusedItemIndex = itemIndex; + !setFocusOnly && this.onSelectionChanged(); + }); + return true; } - - isItemDataSelected(data) { - return this._selectionStrategy.isItemDataSelected(data, { checkPending: true }); + } + + isDataItem(item) { + return this.options.isSelectableItem(item); + } + + isSelectable() { + return this.options.mode === 'single' || this.options.mode === 'multiple'; + } + + isItemDataSelected(data) { + return this._selectionStrategy.isItemDataSelected(data, { checkPending: true }); + } + + isItemSelected(arg, options?: any) { + return this._selectionStrategy.isItemKeySelected(arg, options); + } + + _resetItemSelectionWhenShiftKeyPressed() { + // @ts-expect-error + delete this._shiftFocusedItemIndex; + } + + _resetFocusedItemIndex() { + this._focusedItemIndex = -1; + } + + changeItemSelectionWhenShiftKeyInVirtualPaging(loadIndex) { + const loadOptions = this.options.getLoadOptions(loadIndex, this._focusedItemIndex, this._shiftFocusedItemIndex); + const deferred = Deferred(); + const indexOffset = loadOptions.skip; + + this.options.load(loadOptions).done((items) => { + this.changeItemSelectionWhenShiftKeyPressed(loadIndex, items, indexOffset); + + deferred.resolve(); + }); + + return deferred.promise(); + } + + changeItemSelectionWhenShiftKeyPressed(itemIndex, items, indexOffset) { + let isSelectedItemsChanged = false; + let itemIndexStep; + const indexOffsetDefined = isDefined(indexOffset); + let index = indexOffsetDefined ? this._focusedItemIndex - indexOffset : this._focusedItemIndex; + const { keyOf } = this.options; + const focusedItem = items[index]; + const focusedData = this.options.getItemData(focusedItem); + const focusedKey = keyOf(focusedData); + const isFocusedItemSelected = focusedItem && this.isItemDataSelected(focusedData); + + if (!isDefined(this._shiftFocusedItemIndex)) { + this._shiftFocusedItemIndex = this._focusedItemIndex; } - isItemSelected(arg, options) { - return this._selectionStrategy.isItemKeySelected(arg, options); + let data; + let itemKey; + let startIndex; + let endIndex; + + if (this._shiftFocusedItemIndex !== this._focusedItemIndex) { + itemIndexStep = this._focusedItemIndex < this._shiftFocusedItemIndex ? 1 : -1; + startIndex = indexOffsetDefined ? this._focusedItemIndex - indexOffset : this._focusedItemIndex; + endIndex = indexOffsetDefined ? this._shiftFocusedItemIndex - indexOffset : this._shiftFocusedItemIndex; + for (index = startIndex; index !== endIndex; index += itemIndexStep) { + if (indexOffsetDefined || this.isDataItem(items[index])) { + itemKey = keyOf(this.options.getItemData(items[index])); + this._removeSelectedItem(itemKey); + isSelectedItemsChanged = true; + } + } } - _resetItemSelectionWhenShiftKeyPressed() { - delete this._shiftFocusedItemIndex; + if (itemIndex !== this._shiftFocusedItemIndex) { + itemIndexStep = itemIndex < this._shiftFocusedItemIndex ? 1 : -1; + startIndex = indexOffsetDefined ? itemIndex - indexOffset : itemIndex; + endIndex = indexOffsetDefined + ? this._shiftFocusedItemIndex - indexOffset + : this._shiftFocusedItemIndex; + for (index = startIndex; index !== endIndex; index += itemIndexStep) { + if (indexOffsetDefined || this.isDataItem(items[index])) { + data = this.options.getItemData(items[index]); + itemKey = keyOf(data); + + this._addSelectedItem(data, itemKey); + isSelectedItemsChanged = true; + } + } } - _resetFocusedItemIndex() { - this._focusedItemIndex = -1; + if ((indexOffsetDefined || this.isDataItem(focusedItem)) && !isFocusedItemSelected) { + this._addSelectedItem(focusedData, focusedKey); + isSelectedItemsChanged = true; } - changeItemSelectionWhenShiftKeyInVirtualPaging(loadIndex) { - const loadOptions = this.options.getLoadOptions(loadIndex, this._focusedItemIndex, this._shiftFocusedItemIndex); - const deferred = new Deferred(); - const indexOffset = loadOptions.skip; + return isSelectedItemsChanged; + } - this.options.load(loadOptions).done((items) => { - this.changeItemSelectionWhenShiftKeyPressed(loadIndex, items, indexOffset); + clearSelectedItems() { + this._setSelectedItems([], []); + } - deferred.resolve(); - }); + selectAll(isOnePage) { + this._resetFocusedItemIndex(); - return deferred.promise(); + if (isOnePage) { + return this._onePageSelectAll(false); } + return this.selectedItemKeys([], true, false, true); + } - changeItemSelectionWhenShiftKeyPressed(itemIndex, items, indexOffset) { - let isSelectedItemsChanged = false; - let itemIndexStep; - const indexOffsetDefined = isDefined(indexOffset); - let index = indexOffsetDefined ? this._focusedItemIndex - indexOffset : this._focusedItemIndex; - const keyOf = this.options.keyOf; - const focusedItem = items[index]; - const focusedData = this.options.getItemData(focusedItem); - const focusedKey = keyOf(focusedData); - const isFocusedItemSelected = focusedItem && this.isItemDataSelected(focusedData); - - if(!isDefined(this._shiftFocusedItemIndex)) { - this._shiftFocusedItemIndex = this._focusedItemIndex; - } + deselectAll(isOnePage) { + this._resetFocusedItemIndex(); - let data; - let itemKey; - let startIndex; - let endIndex; - - if(this._shiftFocusedItemIndex !== this._focusedItemIndex) { - itemIndexStep = this._focusedItemIndex < this._shiftFocusedItemIndex ? 1 : -1; - startIndex = indexOffsetDefined ? this._focusedItemIndex - indexOffset : this._focusedItemIndex; - endIndex = indexOffsetDefined ? this._shiftFocusedItemIndex - indexOffset : this._shiftFocusedItemIndex; - for(index = startIndex; index !== endIndex; index += itemIndexStep) { - if(indexOffsetDefined || this.isDataItem(items[index])) { - itemKey = keyOf(this.options.getItemData(items[index])); - this._removeSelectedItem(itemKey); - isSelectedItemsChanged = true; - } - } - } - - if(itemIndex !== this._shiftFocusedItemIndex) { - itemIndexStep = itemIndex < this._shiftFocusedItemIndex ? 1 : -1; - startIndex = indexOffsetDefined ? itemIndex - indexOffset : itemIndex; - endIndex = indexOffsetDefined ? this._shiftFocusedItemIndex - indexOffset : this._shiftFocusedItemIndex; - for(index = startIndex; index !== endIndex; index += itemIndexStep) { - if(indexOffsetDefined || this.isDataItem(items[index])) { - data = this.options.getItemData(items[index]); - itemKey = keyOf(data); - - this._addSelectedItem(data, itemKey); - isSelectedItemsChanged = true; - } - } - } - - if((indexOffsetDefined || this.isDataItem(focusedItem)) && !isFocusedItemSelected) { - this._addSelectedItem(focusedData, focusedKey); - isSelectedItemsChanged = true; - } - - return isSelectedItemsChanged; + if (isOnePage) { + return this._onePageSelectAll(true); } + return this.selectedItemKeys([], true, true, true); + } - clearSelectedItems() { - this._setSelectedItems([], []); - } + _onePageSelectAll(isDeselect) { + const items = this._selectionStrategy.getSelectableItems(this.options.plainItems()); + for (let i = 0; i < items.length; i++) { + const item = items[i]; - selectAll(isOnePage) { - this._resetFocusedItemIndex(); + if (this.isDataItem(item)) { + const itemData = this.options.getItemData(item); + const itemKey = this.options.keyOf(itemData); + const isSelected = this.isItemSelected(itemKey); - if(isOnePage) { - return this._onePageSelectAll(false); - } else { - return this.selectedItemKeys([], true, false, true); + if (!isSelected && !isDeselect) { + this._addSelectedItem(itemData, itemKey); } - } - deselectAll(isOnePage) { - this._resetFocusedItemIndex(); - - if(isOnePage) { - return this._onePageSelectAll(true); - } else { - return this.selectedItemKeys([], true, true, true); + if (isSelected && isDeselect) { + this._removeSelectedItem(itemKey); } + } } - _onePageSelectAll(isDeselect) { - const items = this._selectionStrategy.getSelectableItems(this.options.plainItems()); - for(let i = 0; i < items.length; i++) { - const item = items[i]; - - if(this.isDataItem(item)) { - const itemData = this.options.getItemData(item); - const itemKey = this.options.keyOf(itemData); - const isSelected = this.isItemSelected(itemKey); + this.onSelectionChanged(); - if(!isSelected && !isDeselect) { - this._addSelectedItem(itemData, itemKey); - } + return Deferred().resolve(); + } - if(isSelected && isDeselect) { - this._removeSelectedItem(itemKey); - } - } - } - - this.onSelectionChanged(); - - return new Deferred().resolve(); - } + getSelectAllState(visibleOnly) { + return this._selectionStrategy.getSelectAllState(visibleOnly); + } - getSelectAllState(visibleOnly) { - return this._selectionStrategy.getSelectAllState(visibleOnly); - } - - loadSelectedItemsWithFilter() { - return this._selectionStrategy.loadSelectedItemsWithFilter(); - } + loadSelectedItemsWithFilter() { + return this._selectionStrategy.loadSelectedItemsWithFilter(); + } } diff --git a/packages/devextreme/js/ui/collection/ui.collection_widget.edit.js b/packages/devextreme/js/ui/collection/ui.collection_widget.edit.js index 49e1ad37321c..935f2521fac4 100644 --- a/packages/devextreme/js/ui/collection/ui.collection_widget.edit.js +++ b/packages/devextreme/js/ui/collection/ui.collection_widget.edit.js @@ -10,7 +10,7 @@ import PlainEditStrategy from './ui.collection_widget.edit.strategy.plain'; import { compileGetter } from '../../core/utils/data'; import { DataSource } from '../../data/data_source/data_source'; import { normalizeLoadResult } from '../../data/data_source/utils'; -import Selection from '../selection/selection'; +import Selection from '../../__internal/ui/selection/m_selection'; import { when, Deferred, fromPromise } from '../../core/utils/deferred'; const ITEM_DELETING_DATA_KEY = 'dxItemDeleting'; diff --git a/packages/devextreme/js/ui/file_manager/ui.file_manager.items_list.thumbnails.list_box.js b/packages/devextreme/js/ui/file_manager/ui.file_manager.items_list.thumbnails.list_box.js index cdfc8403c94b..72a2f9ab816f 100644 --- a/packages/devextreme/js/ui/file_manager/ui.file_manager.items_list.thumbnails.list_box.js +++ b/packages/devextreme/js/ui/file_manager/ui.file_manager.items_list.thumbnails.list_box.js @@ -12,7 +12,7 @@ import { BindableTemplate } from '../../core/templates/bindable_template'; import ScrollView from '../scroll_view'; import CollectionWidget from '../collection/ui.collection_widget.edit'; -import Selection from '../selection/selection'; +import Selection from '../../__internal/ui/selection/m_selection'; const FILE_MANAGER_THUMBNAILS_VIEW_PORT_CLASS = 'dx-filemanager-thumbnails-view-port'; const FILE_MANAGER_THUMBNAILS_ITEM_LIST_CONTAINER_CLASS = 'dx-filemanager-thumbnails-container'; diff --git a/packages/devextreme/testing/tests/DevExpress.ui.widgets/selection.test.js b/packages/devextreme/testing/tests/DevExpress.ui.widgets/selection.test.js index 8a8a602fa090..1ae6f4872048 100644 --- a/packages/devextreme/testing/tests/DevExpress.ui.widgets/selection.test.js +++ b/packages/devextreme/testing/tests/DevExpress.ui.widgets/selection.test.js @@ -1,6 +1,6 @@ import $ from 'jquery'; import errors from 'ui/widget/ui.errors'; -import Selection from 'ui/selection/selection'; +import Selection from '__internal/ui/selection/m_selection'; import Guid from 'core/guid'; import { DataSource } from 'data/data_source/data_source'; import CustomStore from 'data/custom_store';