From a8113e3aa541ffbb47bce4a2d120da2bce6394b0 Mon Sep 17 00:00:00 2001 From: "anna.shakhova" <68295572+anna-shakhova@users.noreply.github.com> Date: Wed, 19 Aug 2026 16:06:42 +0200 Subject: [PATCH 1/2] Grids: remove kbn dataController extender --- .../data_controller/data_controller.ts | 6 +-- .../m_keyboard_navigation.ts | 44 ++++++++----------- .../m_virtual_data_loader.ts | 4 +- .../grid_core/virtual_data_loader/types.ts | 5 +++ .../virtual_scrolling/m_virtual_scrolling.ts | 20 ++++++--- .../m_virtual_scrolling_core.ts | 6 +-- .../testing/helpers/gridBaseMocks.js | 5 +-- 7 files changed, 48 insertions(+), 42 deletions(-) create mode 100644 packages/devextreme/js/__internal/grids/grid_core/virtual_data_loader/types.ts diff --git a/packages/devextreme/js/__internal/grids/grid_core/data_controller/data_controller.ts b/packages/devextreme/js/__internal/grids/grid_core/data_controller/data_controller.ts index 80e91f805280..50815c23af06 100644 --- a/packages/devextreme/js/__internal/grids/grid_core/data_controller/data_controller.ts +++ b/packages/devextreme/js/__internal/grids/grid_core/data_controller/data_controller.ts @@ -118,7 +118,7 @@ export class DataController extends DataHelperMixin(modules.Controller) { public dataSourceChanged!: Callback<[]>; - public rowIndicesCorrected!: Callback<[RowIndexCorrection]>; + public rowIndicesChanged!: Callback<[RowIndexCorrection]>; protected _lastRenderingPageIndex?: number; @@ -187,7 +187,7 @@ export class DataController extends DataHelperMixin(modules.Controller) { } protected callbackNames(): string[] { - return ['changed', 'loadingChanged', 'dataErrorOccurred', 'pageChanged', 'dataSourceChanged', 'pushed', 'rowIndicesCorrected']; + return ['changed', 'loadingChanged', 'dataErrorOccurred', 'pageChanged', 'dataSourceChanged', 'pushed', 'rowIndicesChanged']; } protected callbackFlags(name?: string): CallbackFlags | undefined { @@ -1148,7 +1148,7 @@ export class DataController extends DataHelperMixin(modules.Controller) { change.isLiveUpdate = true; } - this.rowIndicesCorrected.fire( + this.rowIndicesChanged.fire( (rowIndex: number): number => this.getRowIndexCorrection(rowIndex, oldItems, newIndexByKey), ); } diff --git a/packages/devextreme/js/__internal/grids/grid_core/keyboard_navigation/m_keyboard_navigation.ts b/packages/devextreme/js/__internal/grids/grid_core/keyboard_navigation/m_keyboard_navigation.ts index 1eb6a0db437c..c8bdc1bf95ba 100644 --- a/packages/devextreme/js/__internal/grids/grid_core/keyboard_navigation/m_keyboard_navigation.ts +++ b/packages/devextreme/js/__internal/grids/grid_core/keyboard_navigation/m_keyboard_navigation.ts @@ -31,6 +31,7 @@ import type { RowIndexCorrection } from '@ts/grids/grid_core/data_controller/typ import type { EditingController } from '@ts/grids/grid_core/editing/m_editing'; import type { RowsView } from '@ts/grids/grid_core/views/m_rows_view'; import type { RowsViewScrollEvent } from '@ts/grids/grid_core/views/types'; +import type { VirtualScrollingDataControllerExtension } from '@ts/grids/grid_core/virtual_scrolling/m_virtual_scrolling'; import { memoize } from '@ts/utils/memoize'; import { @@ -128,7 +129,7 @@ export class KeyboardNavigationController extends KeyboardNavigationControllerCo private _testInteractiveElement: any; - protected _dataController!: Controllers['data']; + protected _dataController!: DataController & Partial; private _selectionController!: Controllers['selection']; @@ -184,8 +185,8 @@ export class KeyboardNavigationController extends KeyboardNavigationControllerCo this.initDocumentHandlers(); // init runs again on option changes, so drop the previous subscription first - this._dataController.rowIndicesCorrected.remove(this.handleRowIndicesCorrected); - this._dataController.rowIndicesCorrected.add(this.handleRowIndicesCorrected); + this._dataController.rowIndicesChanged.remove(this.handleRowIndicesChanged); + this._dataController.rowIndicesChanged.add(this.handleRowIndicesChanged); } public dispose(): void { @@ -198,10 +199,10 @@ export class KeyboardNavigationController extends KeyboardNavigationControllerCo ); clearTimeout(this._updateFocusTimeout); accessibility.unsubscribeVisibilityChange(); - this._dataController.rowIndicesCorrected.remove(this.handleRowIndicesCorrected); + this._dataController.rowIndicesChanged.remove(this.handleRowIndicesChanged); } - private readonly handleRowIndicesCorrected = ( + private readonly handleRowIndicesChanged = ( getRowIndexCorrection: RowIndexCorrection, ): void => { const focusedCellPosition = this._focusedCellPosition; @@ -2063,15 +2064,24 @@ export class KeyboardNavigationController extends KeyboardNavigationControllerCo return this._isCellValid($cell); } - private _isLastRow(rowIndex: number): boolean { - const dataController = this._dataController; + private getMaxRowIndex(): number { + const lastLoadedRowIndex = this._dataController.items().length - 1; + const virtualItemsCount = this._dataController.virtualItemsCount?.(); + + if (!virtualItemsCount) { + return lastLoadedRowIndex; + } + return lastLoadedRowIndex + this._dataController.getRowIndexOffset() + virtualItemsCount.end; + } + + private _isLastRow(rowIndex: number): boolean { if (this._isVirtualRowRender()) { - return rowIndex >= (dataController as any).getMaxRowIndex(); + return rowIndex >= this.getMaxRowIndex(); } const lastVisibleIndex = Math.max( - ...dataController.items() + ...this._dataController.items() .map((item, index) => (item.visible !== false ? index : -1)), ); @@ -3195,21 +3205,6 @@ const editing = (Base: ModuleType) => class EditingController } }; -const data = (Base: ModuleType) => class DataControllerKeyboardExtender extends Base { - private getMaxRowIndex() { - let result = this.items().length - 1; - // @ts-expect-error - const virtualItemsCount = this.virtualItemsCount(); - - if (virtualItemsCount) { - const rowIndexOffset = this.getRowIndexOffset(); - result += rowIndexOffset + virtualItemsCount.end; - } - - return result; - } -}; - const adaptiveColumns = (Base: ModuleType) => class AdaptiveColumnsKeyboardExtender extends Base { protected _showHiddenCellsInView({ viewName, $cells, isCommandColumn }) { super._showHiddenCellsInView.apply(this, arguments as any); @@ -3260,7 +3255,6 @@ export const keyboardNavigationModule: import('../m_types').Module = { }, controllers: { editing, - data, adaptiveColumns, keyboardNavigation: keyboardNavigationScrollableA11yExtender, }, diff --git a/packages/devextreme/js/__internal/grids/grid_core/virtual_data_loader/m_virtual_data_loader.ts b/packages/devextreme/js/__internal/grids/grid_core/virtual_data_loader/m_virtual_data_loader.ts index 2d70278a68c9..35658595a566 100644 --- a/packages/devextreme/js/__internal/grids/grid_core/virtual_data_loader/m_virtual_data_loader.ts +++ b/packages/devextreme/js/__internal/grids/grid_core/virtual_data_loader/m_virtual_data_loader.ts @@ -1,6 +1,8 @@ import { Deferred, when } from '@js/core/utils/deferred'; import { isObject, isString } from '@js/core/utils/type'; +import type { VirtualItemsCount } from './types'; + const LEGACY_SCROLLING_MODE = 'scrolling.legacyMode'; const needTwoPagesLoading = (that) => that.option('scrolling.loadTwoPagesOnStart') || that._controller.isVirtual() || that._controller.getViewportItemIndex() > 0; @@ -405,7 +407,7 @@ export class VirtualDataLoader { return count; } - private virtualItemsCount() { + public virtualItemsCount(): VirtualItemsCount { let pageIndex = getBeginPageIndex(this); if (pageIndex < 0) { pageIndex = this._dataOptions.pageIndex(); diff --git a/packages/devextreme/js/__internal/grids/grid_core/virtual_data_loader/types.ts b/packages/devextreme/js/__internal/grids/grid_core/virtual_data_loader/types.ts new file mode 100644 index 000000000000..997d30cf104a --- /dev/null +++ b/packages/devextreme/js/__internal/grids/grid_core/virtual_data_loader/types.ts @@ -0,0 +1,5 @@ +/** How many virtual (not loaded) items sit before and after the loaded window. */ +export interface VirtualItemsCount { + begin: number; + end: 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 2579006c62bb..8a6a29ec0797 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 @@ -21,6 +21,7 @@ import type { ErrorHandlingViewController } from '@ts/grids/grid_core/error_hand import type { ModuleType } from '@ts/grids/grid_core/m_types'; import type { ResizingController } from '@ts/grids/grid_core/views/m_grid_view'; import type { RowsView } from '@ts/grids/grid_core/views/m_rows_view'; +import type { VirtualItemsCount } from '@ts/grids/grid_core/virtual_data_loader/types'; import type { ChangedEvent } from '../data_source_adapter/types'; import gridCoreUtils from '../m_utils'; @@ -456,7 +457,16 @@ export const dataSourceAdapterExtender = (Base: ModuleType) = return proxyDataSourceAdapterMethod(this, 'loadIfNeed', [...arguments]); } }; -export const data = (Base: ModuleType) => class VirtualScrollingDataControllerExtender extends Base { + +export interface VirtualScrollingDataControllerExtension { + virtualItemsCount: () => VirtualItemsCount | undefined; +} + +export const data = ( + Base: ModuleType, +): ModuleType< + DataController & VirtualScrollingDataControllerExtension +> => class VirtualScrollingDataControllerExtender extends Base { private _loadViewportParams: any; private _allItems: any; @@ -1278,16 +1288,14 @@ export const data = (Base: ModuleType) => class VirtualScrolling return viewportParams && viewportParams.skip + viewportParams.take; } - private virtualItemsCount() { + public virtualItemsCount(): VirtualItemsCount | undefined { const rowsScrollController = this._rowsScrollController; if (rowsScrollController) { - // @ts-expect-error - return rowsScrollController.virtualItemsCount.apply(rowsScrollController, arguments); + return rowsScrollController.virtualItemsCount(); } - const dataSource = this._dataSource; - return dataSource?.virtualItemsCount.apply(dataSource, arguments); + return this._dataSource?.virtualItemsCount() as VirtualItemsCount | undefined; } public pageIndex(): number; diff --git a/packages/devextreme/js/__internal/grids/grid_core/virtual_scrolling/m_virtual_scrolling_core.ts b/packages/devextreme/js/__internal/grids/grid_core/virtual_scrolling/m_virtual_scrolling_core.ts index 87397bf88cce..eac3dca33055 100644 --- a/packages/devextreme/js/__internal/grids/grid_core/virtual_scrolling/m_virtual_scrolling_core.ts +++ b/packages/devextreme/js/__internal/grids/grid_core/virtual_scrolling/m_virtual_scrolling_core.ts @@ -7,6 +7,7 @@ import { Deferred } from '@js/core/utils/deferred'; import { each } from '@js/core/utils/iterator'; import { isDefined } from '@js/core/utils/type'; import { getWindow } from '@js/core/utils/window'; +import type { VirtualItemsCount } from '@ts/grids/grid_core/virtual_data_loader/types'; import gridCoreUtils from '../m_utils'; import { VirtualDataLoader } from '../virtual_data_loader/m_virtual_data_loader'; @@ -167,7 +168,7 @@ class VirtualScrollController { return this._isVirtual; } - private virtualItemsCount() { + public virtualItemsCount(): VirtualItemsCount | undefined { if (isVirtualMode(this)) { const dataOptions = this._dataOptions; const totalItemsCount = dataOptions.totalItemsCount(); @@ -186,8 +187,7 @@ class VirtualScrollController { }; } - // @ts-expect-error - return this._dataLoader.virtualItemsCount.apply(this._dataLoader, arguments); + return this._dataLoader.virtualItemsCount(); } return undefined; diff --git a/packages/devextreme/testing/helpers/gridBaseMocks.js b/packages/devextreme/testing/helpers/gridBaseMocks.js index c19378f691dd..029e59537099 100644 --- a/packages/devextreme/testing/helpers/gridBaseMocks.js +++ b/packages/devextreme/testing/helpers/gridBaseMocks.js @@ -255,11 +255,8 @@ module.exports = function($, gridCore, columnResizingReordering, domUtils, commo pageChanged: $.Callbacks(), dataSourceChanged: $.Callbacks(), pushed: $.Callbacks(), - rowIndicesCorrected: $.Callbacks(), + rowIndicesChanged: $.Callbacks(), fireError: function() { }, - getMaxRowIndex: function() { - this.items().length - 1; - }, loadViewport: commonUtils.noop, updateViewport: commonUtils.noop, getScrollingTimeout: function() { From 8d09ca14a4401cb34c786c89651f307eb0e2fe20 Mon Sep 17 00:00:00 2001 From: "anna.shakhova" <68295572+anna-shakhova@users.noreply.github.com> Date: Thu, 20 Aug 2026 09:23:16 +0200 Subject: [PATCH 2/2] rename --- .../keyboard_navigation/m_keyboard_navigation.ts | 8 ++++---- .../grid_core/virtual_scrolling/m_virtual_scrolling.ts | 4 ++-- .../js/__internal/grids/tree_list/m_virtual_scrolling.ts | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/packages/devextreme/js/__internal/grids/grid_core/keyboard_navigation/m_keyboard_navigation.ts b/packages/devextreme/js/__internal/grids/grid_core/keyboard_navigation/m_keyboard_navigation.ts index c8bdc1bf95ba..f905d4ce36a1 100644 --- a/packages/devextreme/js/__internal/grids/grid_core/keyboard_navigation/m_keyboard_navigation.ts +++ b/packages/devextreme/js/__internal/grids/grid_core/keyboard_navigation/m_keyboard_navigation.ts @@ -185,8 +185,8 @@ export class KeyboardNavigationController extends KeyboardNavigationControllerCo this.initDocumentHandlers(); // init runs again on option changes, so drop the previous subscription first - this._dataController.rowIndicesChanged.remove(this.handleRowIndicesChanged); - this._dataController.rowIndicesChanged.add(this.handleRowIndicesChanged); + this._dataController.rowIndicesChanged.remove(this.rowIndicesChangedHandler); + this._dataController.rowIndicesChanged.add(this.rowIndicesChangedHandler); } public dispose(): void { @@ -199,10 +199,10 @@ export class KeyboardNavigationController extends KeyboardNavigationControllerCo ); clearTimeout(this._updateFocusTimeout); accessibility.unsubscribeVisibilityChange(); - this._dataController.rowIndicesChanged.remove(this.handleRowIndicesChanged); + this._dataController.rowIndicesChanged.remove(this.rowIndicesChangedHandler); } - private readonly handleRowIndicesChanged = ( + private readonly rowIndicesChangedHandler = ( getRowIndexCorrection: RowIndexCorrection, ): void => { const focusedCellPosition = this._focusedCellPosition; 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 8a6a29ec0797..0b0c3b459bcf 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 @@ -462,7 +462,7 @@ export interface VirtualScrollingDataControllerExtension { virtualItemsCount: () => VirtualItemsCount | undefined; } -export const data = ( +export const virtualScrollingDataControllerExtender = ( Base: ModuleType, ): ModuleType< DataController & VirtualScrollingDataControllerExtension @@ -1968,7 +1968,7 @@ export const virtualScrollingModule = { }, extenders: { controllers: { - data, + data: virtualScrollingDataControllerExtender, resizing, }, views: { 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 e7c0bedfe337..386125aa5bdd 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 @@ -6,9 +6,9 @@ import type { ModuleType } from '@ts/grids/grid_core/m_types'; import gridCoreUtils from '@ts/grids/grid_core/m_utils'; import type { RowsView } from '@ts/grids/grid_core/views/m_rows_view'; import { - data as virtualScrollingDataControllerExtender, dataSourceAdapterExtender as virtualScrollingDataSourceAdapterExtender, rowsView as virtualScrollingRowsViewExtender, + virtualScrollingDataControllerExtender, virtualScrollingModule, } from '@ts/grids/grid_core/virtual_scrolling/m_virtual_scrolling';