diff --git a/packages/devextreme/js/__internal/grids/data_grid/module_not_extended/state_storing.ts b/packages/devextreme/js/__internal/grids/data_grid/module_not_extended/state_storing.ts index 08b4bf5ad167..efd015421dce 100644 --- a/packages/devextreme/js/__internal/grids/data_grid/module_not_extended/state_storing.ts +++ b/packages/devextreme/js/__internal/grids/data_grid/module_not_extended/state_storing.ts @@ -1,4 +1,4 @@ -import { stateStoringModule } from '@ts/grids/grid_core/state_storing/m_state_storing'; +import { stateStoringModule } from '@ts/grids/grid_core/state_storing/state_storing_module'; import gridCore from '../m_core'; diff --git a/packages/devextreme/js/__internal/grids/grid_core/columns_controller/m_columns_controller.ts b/packages/devextreme/js/__internal/grids/grid_core/columns_controller/m_columns_controller.ts index 3fb7d5270680..9ce02b89a247 100644 --- a/packages/devextreme/js/__internal/grids/grid_core/columns_controller/m_columns_controller.ts +++ b/packages/devextreme/js/__internal/grids/grid_core/columns_controller/m_columns_controller.ts @@ -23,7 +23,7 @@ import inflector from '@ts/core/utils/m_inflector'; import type { Column, ColumnsChanges, FilterField } from '@ts/grids/grid_core/columns_controller/types'; import type { DataController } from '@ts/grids/grid_core/data_controller/data_controller'; import type { FocusController } from '@ts/grids/grid_core/focus/m_focus'; -import type { StateStoringController } from '@ts/grids/grid_core/state_storing/m_state_storing_core'; +import type { StateStoringController } from '@ts/grids/grid_core/state_storing/m_state_storing_controller'; import { AI_COLUMN_NAME } from '../ai_column/const'; import modules from '../m_modules'; 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 3d1735dbfdba..e54d3d3c75df 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 @@ -41,7 +41,6 @@ import type { } from '@ts/grids/grid_core/m_types'; import gridCoreUtils from '@ts/grids/grid_core/m_utils'; import type { SelectionController } from '@ts/grids/grid_core/selection/m_selection'; -import type { StateStoringController } from '@ts/grids/grid_core/state_storing/m_state_storing_core'; import type { VirtualScrollController } from '@ts/grids/grid_core/virtual_scrolling/m_virtual_scrolling_core'; import { DataHelperMixin } from './data_helper_mixin'; @@ -151,8 +150,6 @@ export class DataController extends DataHelperMixin(modules.Controller) { protected _selectionController!: SelectionController; - protected _stateStoringController!: StateStoringController; - private loadErrorHandlerProxy!: (e: Error | string) => void; private dataPushedHandlerProxy!: (changes: StoreChange[]) => void; @@ -173,7 +170,6 @@ export class DataController extends DataHelperMixin(modules.Controller) { this._focusController = this.getController('focus'); this._headerFilterController = this.getController('headerFilter'); this._selectionController = this.getController('selection'); - this._stateStoringController = this.getController('stateStoring'); this._isPaging = false; this._currentOperationTypes = null; @@ -1305,7 +1301,7 @@ export class DataController extends DataHelperMixin(modules.Controller) { /** * @extended: state_storing */ - public isLoading() { + public isLoading(): boolean { return this._isLoading || this._isCustomLoading; } diff --git a/packages/devextreme/js/__internal/grids/grid_core/data_controller/data_helper_mixin.ts b/packages/devextreme/js/__internal/grids/grid_core/data_controller/data_helper_mixin.ts index c8fc65d55cb1..d80daa533091 100644 --- a/packages/devextreme/js/__internal/grids/grid_core/data_controller/data_helper_mixin.ts +++ b/packages/devextreme/js/__internal/grids/grid_core/data_controller/data_helper_mixin.ts @@ -1,5 +1,6 @@ import { DataSource } from '@js/common/data/data_source/data_source'; import { normalizeDataSourceOptions } from '@js/common/data/data_source/utils'; +import type { DeferredObj } from '@js/core/utils/deferred'; import { extend } from '@js/core/utils/extend'; import DataController from '@ts/ui/collection/m_data_controller'; @@ -49,9 +50,11 @@ export const DataHelperMixin = >(Base: T) => cl /** * @extended: state_storing, virtual_scrolling */ - protected _refreshDataSource(): void { + protected _refreshDataSource(): DeferredObj | undefined { this._initDataSource(); this._loadDataSource(); + + return undefined; } protected _initDataSource(): void { diff --git a/packages/devextreme/js/__internal/grids/grid_core/m_types.ts b/packages/devextreme/js/__internal/grids/grid_core/m_types.ts index 10b13d15d308..2f94bd160ce7 100644 --- a/packages/devextreme/js/__internal/grids/grid_core/m_types.ts +++ b/packages/devextreme/js/__internal/grids/grid_core/m_types.ts @@ -207,7 +207,7 @@ export interface Controllers { selection: import('./selection/m_selection').SelectionController; validating: import('./validating/m_validating').ValidatingController; searchPanel: import('./search/m_search').SearchPanelViewController; - stateStoring: import('./state_storing/m_state_storing_core').StateStoringController; + stateStoring: import('./state_storing/m_state_storing_controller').StateStoringController; synchronizeScrolling: import('./views/m_grid_view').SynchronizeScrollingController; tablePosition: import('./columns_resizing_reordering/m_columns_resizing_reordering').TablePositionViewController; toastViewController: import('./toast/m_toast_controller').ToastViewController; 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 4c3316208268..85841f7d286f 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 @@ -20,7 +20,7 @@ import type { ColumnsController } from '@ts/grids/grid_core/columns_controller/m import type { Column } from '@ts/grids/grid_core/columns_controller/types'; import type { ContextMenuController } from '@ts/grids/grid_core/context_menu/m_context_menu'; 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 { StateStoringController } from '@ts/grids/grid_core/state_storing/m_state_storing_controller'; import type { RowsView } from '@ts/grids/grid_core/views/m_rows_view'; import Selection from '@ts/ui/selection/selection'; diff --git a/packages/devextreme/js/__internal/grids/grid_core/state_storing/extenders/state_storing_data_controller.ts b/packages/devextreme/js/__internal/grids/grid_core/state_storing/extenders/state_storing_data_controller.ts new file mode 100644 index 000000000000..1439fe03acce --- /dev/null +++ b/packages/devextreme/js/__internal/grids/grid_core/state_storing/extenders/state_storing_data_controller.ts @@ -0,0 +1,85 @@ +import type { Callback } from '@js/core/utils/callbacks'; +import type { DeferredObj } from '@js/core/utils/deferred'; +import { Deferred } from '@js/core/utils/deferred'; +import { isDefined } from '@js/core/utils/type'; +import type { DataController } from '@ts/grids/grid_core/data_controller/data_controller'; +import type { ModuleType } from '@ts/grids/grid_core/m_types'; + +import type { StateStoringController } from '../m_state_storing_controller'; + +export interface StateStoringDataControllerExtension { + stateLoaded: Callback<[]>; +} + +export const stateStoringDataControllerExtender = ( + Base: ModuleType, +): ModuleType< + DataController & StateStoringDataControllerExtension +> => class StateStoringDataExtender extends Base { + public stateLoaded!: Callback<[]>; + + protected _stateStoringController!: StateStoringController; + + private _restoreStateTimeoutID?: ReturnType | null; + + public init(): void { + this._stateStoringController = this.getController('stateStoring'); + super.init(); + } + + public dispose(): void { + clearTimeout(this._restoreStateTimeoutID ?? undefined); + super.dispose(); + } + + protected callbackNames(): string[] { + return super.callbackNames().concat(['stateLoaded']); + } + + protected _refreshDataSource(): DeferredObj | undefined { + if (this._stateStoringController.isEnabled() && !this._stateStoringController.isLoaded()) { + clearTimeout(this._restoreStateTimeoutID ?? undefined); + + const deferred = Deferred(); + // eslint-disable-next-line no-restricted-globals + this._restoreStateTimeoutID = setTimeout(() => { + this._stateStoringController.load() + .always(() => { + this._restoreStateTimeoutID = null; + }) + .done(() => { + super._refreshDataSource(); + + this.stateLoaded.fire(); + deferred.resolve(); + }) + .fail((error: Error) => { + this.stateLoaded.fire(); + this.loadErrorHandler(error ?? 'Unknown error'); + deferred.reject(); + }); + }); + + // @ts-expect-error promise() is typed as Promise but returns a Deferred-like value at runtime + return deferred.promise(); + } + + if (!this.isStateLoading()) { + super._refreshDataSource(); + } + + return undefined; + } + + public isLoading(): boolean { + return super.isLoading() || this._stateStoringController.isLoading(); + } + + private isStateLoading(): boolean { + return isDefined(this._restoreStateTimeoutID); + } + + public isLoaded(): boolean { + return super.isLoaded() && !this.isStateLoading(); + } +}; diff --git a/packages/devextreme/js/__internal/grids/grid_core/state_storing/m_state_storing.ts b/packages/devextreme/js/__internal/grids/grid_core/state_storing/m_state_storing.ts index 54561fb88826..c722f7a0cb2a 100644 --- a/packages/devextreme/js/__internal/grids/grid_core/state_storing/m_state_storing.ts +++ b/packages/devextreme/js/__internal/grids/grid_core/state_storing/m_state_storing.ts @@ -1,6 +1,5 @@ /* eslint-disable max-classes-per-file */ import { equalByValue, getKeyHash } from '@js/core/utils/common'; -import { Deferred } from '@js/core/utils/deferred'; import { extend } from '@js/core/utils/extend'; import { isDefined } from '@js/core/utils/type'; @@ -9,7 +8,8 @@ import type { DataController } from '../data_controller/data_controller'; import type { ModuleType } from '../m_types'; import type { SelectionController } from '../selection/m_selection'; import type { RowsView } from '../views/m_rows_view'; -import { StateStoringController } from './m_state_storing_core'; +import type { StateStoringDataControllerExtension } from './extenders/state_storing_data_controller'; +import type { StateStoringController } from './m_state_storing_controller'; const getDataState = (that) => { // TODO getView @@ -91,11 +91,12 @@ const getFilterValue = (that, state) => { return that._initialFilterValue ?? filterSyncController.getFilterValueFromColumns(columns); }; -const rowsView = (Base: ModuleType) => class StateStoringRowsViewExtender extends Base { +export const rowsView = (Base: ModuleType) => class StateStoringRowsViewExtender extends Base { + protected _dataController!: DataController & StateStoringDataControllerExtension; + public init() { super.init(); - // @ts-expect-error this._dataController.stateLoaded.add(() => { if (this._dataController.isLoaded() && !this._dataController.getDataSource()) { this.setLoading(false); @@ -109,7 +110,7 @@ const rowsView = (Base: ModuleType) => class StateStoringRowsViewExten } }; -const stateStoring = (Base: ModuleType) => class StateStoringExtender extends Base { +export const stateStoring = (Base: ModuleType) => class StateStoringExtender extends Base { private readonly _initialPageSize: any; public init() { @@ -209,7 +210,7 @@ const stateStoring = (Base: ModuleType) => class StateSt } }; -const columns = (Base: ModuleType) => class StateStoringColumnsExtender extends Base { +export const columns = (Base: ModuleType) => class StateStoringColumnsExtender extends Base { protected _shouldReturnVisibleColumns() { // @ts-expect-error const result = super._shouldReturnVisibleColumns.apply(this, arguments); @@ -218,59 +219,7 @@ const columns = (Base: ModuleType) => class StateStoringColum } }; -const data = (Base: ModuleType) => class StateStoringDataExtender extends Base { - private _restoreStateTimeoutID: any; - - public dispose() { - clearTimeout(this._restoreStateTimeoutID); - super.dispose(); - } - - protected callbackNames() { - return super.callbackNames().concat(['stateLoaded']); - } - - protected _refreshDataSource() { - if (this._stateStoringController.isEnabled() && !this._stateStoringController.isLoaded()) { - clearTimeout(this._restoreStateTimeoutID); - - // @ts-expect-error - const deferred = new Deferred(); - this._restoreStateTimeoutID = setTimeout(() => { - this._stateStoringController.load().always(() => { - this._restoreStateTimeoutID = null; - }).done(() => { - super._refreshDataSource(); - // @ts-expect-error - this.stateLoaded.fire(); - deferred.resolve(); - }).fail((error) => { - // @ts-expect-error - this.stateLoaded.fire(); - this.loadErrorHandler(error || 'Unknown error'); - deferred.reject(); - }); - }); - return deferred.promise(); - } if (!this.isStateLoading()) { - super._refreshDataSource(); - } - } - - public isLoading() { - return super.isLoading() || this._stateStoringController.isLoading(); - } - - private isStateLoading() { - return isDefined(this._restoreStateTimeoutID); - } - - public isLoaded() { - return super.isLoaded() && !this.isStateLoading(); - } -}; - -const selection = (Base: ModuleType) => class StateStoringSelectionExtender extends Base { +export const selection = (Base: ModuleType) => class StateStoringSelectionExtender extends Base { // eslint-disable-next-line @typescript-eslint/no-unused-vars protected _fireSelectionChanged(options) { const isDeferredSelection = this.option('selection.deferred'); @@ -281,32 +230,3 @@ const selection = (Base: ModuleType) => class StateStoringS super._fireSelectionChanged.apply(this, arguments); } }; - -export const stateStoringModule = { - defaultOptions() { - return { - stateStoring: { - enabled: false, - storageKey: undefined, - type: 'localStorage', - customLoad: undefined, - customSave: undefined, - savingTimeout: 2000, - }, - }; - }, - controllers: { - stateStoring: StateStoringController, - }, - extenders: { - views: { - rowsView, - }, - controllers: { - stateStoring, - columns, - data, - selection, - }, - }, -}; diff --git a/packages/devextreme/js/__internal/grids/grid_core/state_storing/m_state_storing_core.ts b/packages/devextreme/js/__internal/grids/grid_core/state_storing/m_state_storing_controller.ts similarity index 98% rename from packages/devextreme/js/__internal/grids/grid_core/state_storing/m_state_storing_core.ts rename to packages/devextreme/js/__internal/grids/grid_core/state_storing/m_state_storing_controller.ts index 2ac42580aa0d..a7b9d0dc6b80 100644 --- a/packages/devextreme/js/__internal/grids/grid_core/state_storing/m_state_storing_core.ts +++ b/packages/devextreme/js/__internal/grids/grid_core/state_storing/m_state_storing_controller.ts @@ -48,7 +48,7 @@ export class StateStoringController extends modules.ViewController { private _isLoaded: any; - private _isLoading: any; + private _isLoading!: boolean; private _windowUnloadHandler: any; @@ -145,7 +145,7 @@ export class StateStoringController extends modules.ViewController { return this._isLoaded; } - public isLoading() { + public isLoading(): boolean { return this._isLoading; } diff --git a/packages/devextreme/js/__internal/grids/grid_core/state_storing/state_storing_module.ts b/packages/devextreme/js/__internal/grids/grid_core/state_storing/state_storing_module.ts new file mode 100644 index 000000000000..d03b47138c2b --- /dev/null +++ b/packages/devextreme/js/__internal/grids/grid_core/state_storing/state_storing_module.ts @@ -0,0 +1,39 @@ +import type { StateStoring } from '@js/common/grids'; + +import { stateStoringDataControllerExtender } from './extenders/state_storing_data_controller'; +import { + columns, + rowsView, + selection, + stateStoring, +} from './m_state_storing'; +import { StateStoringController } from './m_state_storing_controller'; + +export const stateStoringModule = { + defaultOptions(): { stateStoring: StateStoring } { + return { + stateStoring: { + enabled: false, + storageKey: undefined, + type: 'localStorage', + customLoad: undefined, + customSave: undefined, + savingTimeout: 2000, + }, + }; + }, + controllers: { + stateStoring: StateStoringController, + }, + extenders: { + views: { + rowsView, + }, + controllers: { + stateStoring, + columns, + data: stateStoringDataControllerExtender, + selection, + }, + }, +}; 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 e505c8d49f74..9f06035646d1 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 @@ -23,6 +23,7 @@ import type { RowsView } from '@ts/grids/grid_core/views/m_rows_view'; import type { ChangedEvent } from '../data_source_adapter/types'; import gridCoreUtils from '../m_utils'; +import type { StateStoringDataControllerExtension } from '../state_storing/extenders/state_storing_data_controller'; import type { RowsViewScrollEvent } from '../views/types'; import { subscribeToExternalScrollers, VirtualScrollController } from './m_virtual_scrolling_core'; import type { ChangedLoadParams } from './types'; @@ -476,9 +477,9 @@ export const data = (Base: ModuleType) => class VirtualScrolling super.dispose.apply(this, arguments as any); } - protected _refreshDataSource() { - // @ts-expect-error - const baseResult = super._refreshDataSource.apply(this, arguments as any) || new Deferred().resolve().promise(); + protected _refreshDataSource(): DeferredObj { + // @ts-expect-error promise() is typed as Promise but returns a Deferred-like value at runtime + const baseResult: DeferredObj = super._refreshDataSource() ?? Deferred().resolve().promise(); baseResult.done(this.initVirtualRows.bind(this)); @@ -1434,6 +1435,8 @@ export const resizing = (Base: ModuleType) => class VirtualS }; export const rowsView = (Base: ModuleType) => class VirtualScrollingRowsViewExtender extends Base { + protected _dataController!: DataController & Partial; + private _isFixedTableRendering: any; private _heightWarningIsThrown: any; @@ -1461,11 +1464,9 @@ export const rowsView = (Base: ModuleType) => class VirtualScrollingRo !this._scrollTop && this._scrollToCurrentPageOnResize(); }); - this._dataController - // @ts-expect-error - .stateLoaded?.add(() => { - this._scrollToCurrentPageOnResize(); - }); + this._dataController.stateLoaded?.add(() => { + this._scrollToCurrentPageOnResize(); + }); this._scrollToCurrentPageOnResize(); } diff --git a/packages/devextreme/js/__internal/grids/pivot_grid/data_controller/m_data_controller.ts b/packages/devextreme/js/__internal/grids/pivot_grid/data_controller/m_data_controller.ts index 6171c3fd7f3c..9d7e4cd84b66 100644 --- a/packages/devextreme/js/__internal/grids/pivot_grid/data_controller/m_data_controller.ts +++ b/packages/devextreme/js/__internal/grids/pivot_grid/data_controller/m_data_controller.ts @@ -5,7 +5,7 @@ import { extend } from '@js/core/utils/extend'; import { each, map } from '@js/core/utils/iterator'; import { format } from '@js/core/utils/string'; import { isDefined, isString } from '@js/core/utils/type'; -import stateStoring from '@ts/grids/grid_core/state_storing/m_state_storing_core'; +import stateStoring from '@ts/grids/grid_core/state_storing/m_state_storing_controller'; import { createColumnsInfo, foreachColumnInfo } from '@ts/grids/grid_core/virtual_columns/m_virtual_columns_core'; import VirtualScrollControllerModule from '@ts/grids/grid_core/virtual_scrolling/m_virtual_scrolling_core'; diff --git a/packages/devextreme/js/__internal/grids/tree_list/m_widget.ts b/packages/devextreme/js/__internal/grids/tree_list/m_widget.ts index 2a89ba216c6f..ec4c9eb74697 100644 --- a/packages/devextreme/js/__internal/grids/tree_list/m_widget.ts +++ b/packages/devextreme/js/__internal/grids/tree_list/m_widget.ts @@ -2,7 +2,7 @@ /* eslint-disable simple-import-sort/imports */ import TreeList from './m_widget_base'; -import './m_state_storing'; +import './state_storing'; import './module_not_extended/ai_column'; import './module_not_extended/column_chooser'; import './m_master_detail'; diff --git a/packages/devextreme/js/__internal/grids/tree_list/m_state_storing.ts b/packages/devextreme/js/__internal/grids/tree_list/state_storing.ts similarity index 59% rename from packages/devextreme/js/__internal/grids/tree_list/m_state_storing.ts rename to packages/devextreme/js/__internal/grids/tree_list/state_storing.ts index 64f5545c6db1..4b237e20db1d 100644 --- a/packages/devextreme/js/__internal/grids/tree_list/m_state_storing.ts +++ b/packages/devextreme/js/__internal/grids/tree_list/state_storing.ts @@ -1,15 +1,20 @@ // eslint-disable-next-line max-classes-per-file -import { stateStoringModule } from '@ts/grids/grid_core/state_storing/m_state_storing'; +import type { UserState } from '@ts/grids/grid_core/data_controller/types'; +import type { + StateStoringDataControllerExtension, +} from '@ts/grids/grid_core/state_storing/extenders/state_storing_data_controller'; +import { stateStoringModule } from '@ts/grids/grid_core/state_storing/state_storing_module'; import type { DataController } from '../grid_core/data_controller/data_controller'; import type { ModuleType } from '../grid_core/m_types'; -import type { StateStoringController } from '../grid_core/state_storing/m_state_storing_core'; +import type { StateStoringController } from '../grid_core/state_storing/m_state_storing_controller'; import treeListCore from './m_core'; const stateStoring = ( Base: ModuleType, -) => class TreeListStateStoringExtender extends stateStoringModule.extenders.controllers.stateStoring(Base) { - protected applyState(state) { + // eslint-disable-next-line @stylistic/max-len +): ModuleType => class TreeListStateStoringExtender extends stateStoringModule.extenders.controllers.stateStoring(Base) { + protected applyState(state): void { super.applyState(state); this.option('expandedRowKeys', state.expandedRowKeys ? state.expandedRowKeys.slice() : []); } @@ -17,8 +22,11 @@ const stateStoring = ( const data = ( Base: ModuleType, -) => class TreeListStateStoringDataExtender extends stateStoringModule.extenders.controllers.data(Base) { - public getUserState() { +): ModuleType< + DataController & StateStoringDataControllerExtension +// eslint-disable-next-line @stylistic/max-len +> => class TreeListStateStoringDataExtender extends stateStoringModule.extenders.controllers.data(Base) { + public getUserState(): UserState { const state = super.getUserState(); if (!this.option('autoExpandAll')) { diff --git a/packages/devextreme/testing/tests/DevExpress.ui.widgets.pivotGrid/dataController.tests.js b/packages/devextreme/testing/tests/DevExpress.ui.widgets.pivotGrid/dataController.tests.js index 162bc0be14f3..07ada5ea3355 100644 --- a/packages/devextreme/testing/tests/DevExpress.ui.widgets.pivotGrid/dataController.tests.js +++ b/packages/devextreme/testing/tests/DevExpress.ui.widgets.pivotGrid/dataController.tests.js @@ -2,7 +2,7 @@ import $ from 'jquery'; import Class from 'core/class'; import { DataController } from '__internal/grids/pivot_grid/data_controller/m_data_controller'; import virtualScrolling from '__internal/grids/grid_core/virtual_scrolling/m_virtual_scrolling_core'; -import stateStoring from '__internal/grids/grid_core/state_storing/m_state_storing_core'; +import stateStoring from '__internal/grids/grid_core/state_storing/m_state_storing_controller'; import pivotGridUtils from '__internal/grids/pivot_grid/m_widget_utils'; import { PivotGridDataSource } from '__internal/grids/pivot_grid/data_source/m_data_source';