diff --git a/packages/devextreme/js/__internal/data/m_data_helper.ts b/packages/devextreme/js/__internal/data/m_data_helper.ts index b9499ff9641a..01a0c5e0a8e8 100644 --- a/packages/devextreme/js/__internal/data/m_data_helper.ts +++ b/packages/devextreme/js/__internal/data/m_data_helper.ts @@ -11,21 +11,41 @@ const DATA_SOURCE_FROM_URL_LOAD_MODE_METHOD = '_dataSourceFromUrlLoadMode'; const SPECIFIC_DATA_SOURCE_OPTION = '_getSpecificDataSourceOption'; const NORMALIZE_DATA_SOURCE = '_normalizeDataSource'; -export const DataHelperMixin = { - postCtor(): void { +export type Constructor = (new (...args: any[]) => T); +export const DataHelperMixin = >(Base: T) => class DataHelperMixin extends Base { + public _dataSource: any; + + protected _dataController: any; + + protected readyWatcher: any; + + private _proxiedDataSourceChangedHandler: any; + + private _proxiedDataSourceLoadErrorHandler: any; + + private _proxiedDataSourceLoadingChangedHandler: any; + + protected _isSharedDataSource: any; + + private readonly _dataSourceType: any; + + public postCtor() { + // @ts-expect-error ts-error this.on('disposing', () => { this._disposeDataSource(); }); - }, + } - _refreshDataSource(): void { + protected _refreshDataSource() { this._initDataSource(); this._loadDataSource(); - }, + } - _initDataSource(): void { + protected _initDataSource(): void { let dataSourceOptions = SPECIFIC_DATA_SOURCE_OPTION in this + // @ts-expect-error ts-error ? this[SPECIFIC_DATA_SOURCE_OPTION]() + // @ts-expect-error ts-error : this.option('dataSource'); let widgetDataSourceOptions; @@ -39,12 +59,14 @@ export const DataHelperMixin = { this._dataSource = dataSourceOptions; } else { widgetDataSourceOptions = DATA_SOURCE_OPTIONS_METHOD in this + // @ts-expect-error ts-error ? this[DATA_SOURCE_OPTIONS_METHOD]() : {}; dataSourceType = this._dataSourceType ? this._dataSourceType() : DataSource; dataSourceOptions = normalizeDataSourceOptions(dataSourceOptions, { + // @ts-expect-error ts-error fromUrlLoadMode: (DATA_SOURCE_FROM_URL_LOAD_MODE_METHOD in this) && this[DATA_SOURCE_FROM_URL_LOAD_MODE_METHOD](), }); @@ -53,15 +75,17 @@ export const DataHelperMixin = { } if (NORMALIZE_DATA_SOURCE in this) { + // @ts-expect-error ts-error this._dataSource = this[NORMALIZE_DATA_SOURCE](this._dataSource); } this._addDataSourceHandlers(); this._initDataController(); } - }, + } - _initDataController(): void { + protected _initDataController() { + // @ts-expect-error const dataController = this.option?.('_dataController'); const dataSource = this._dataSource; @@ -70,9 +94,9 @@ export const DataHelperMixin = { } else { this._dataController = new DataController(dataSource); } - }, + } - _addDataSourceHandlers(): void { + private _addDataSourceHandlers() { if (DATA_SOURCE_CHANGED_METHOD in this) { this._addDataSourceChangeHandler(); } @@ -86,34 +110,34 @@ export const DataHelperMixin = { } this._addReadyWatcher(); - }, + } - _addReadyWatcher(): void { + private _addReadyWatcher() { this.readyWatcher = function (isLoading) { this._ready && this._ready(!isLoading); }.bind(this); this._dataSource.on('loadingChanged', this.readyWatcher); - }, + } - _addDataSourceChangeHandler(): void { + private _addDataSourceChangeHandler() { const dataSource = this._dataSource; this._proxiedDataSourceChangedHandler = function (e) { this[DATA_SOURCE_CHANGED_METHOD](dataSource.items(), e); }.bind(this); dataSource.on('changed', this._proxiedDataSourceChangedHandler); - }, + } - _addDataSourceLoadErrorHandler(): void { + private _addDataSourceLoadErrorHandler() { this._proxiedDataSourceLoadErrorHandler = this[DATA_SOURCE_LOAD_ERROR_METHOD].bind(this); this._dataSource.on('loadError', this._proxiedDataSourceLoadErrorHandler); - }, + } - _addDataSourceLoadingChangedHandler(): void { + private _addDataSourceLoadingChangedHandler() { this._proxiedDataSourceLoadingChangedHandler = this[DATA_SOURCE_LOADING_CHANGED_METHOD].bind(this); this._dataSource.on('loadingChanged', this._proxiedDataSourceLoadingChangedHandler); - }, + } - _loadDataSource(): void { + protected _loadDataSource() { const dataSource = this._dataSource; if (dataSource) { if (dataSource.isLoaded()) { @@ -122,22 +146,22 @@ export const DataHelperMixin = { dataSource.load(); } } - }, + } - _loadSingle(key, value) { + private _loadSingle(key, value) { key = key === 'this' ? this._dataSource.key() || 'this' : key; return this._dataSource.loadSingle(key, value); - }, + } - _isLastPage(): boolean { + private _isLastPage() { return !this._dataSource || this._dataSource.isLastPage() || !this._dataSource._pageSize; - }, + } - _isDataSourceLoading() { + private _isDataSourceLoading() { return this._dataSource && this._dataSource.isLoading(); - }, + } - _disposeDataSource(): void { + protected _disposeDataSource() { if (this._dataSource) { if (this._isSharedDataSource) { delete this._isSharedDataSource; @@ -159,11 +183,9 @@ export const DataHelperMixin = { delete this._proxiedDataSourceLoadErrorHandler; delete this._proxiedDataSourceLoadingChangedHandler; } - }, + } - getDataSource() { + protected getDataSource() { return this._dataSource || null; - }, + } }; - -export default DataHelperMixin; diff --git a/packages/devextreme/js/__internal/ui/collection/collection_widget.base.ts b/packages/devextreme/js/__internal/ui/collection/collection_widget.base.ts index e3893da4f76b..7157a1a0428c 100644 --- a/packages/devextreme/js/__internal/ui/collection/collection_widget.base.ts +++ b/packages/devextreme/js/__internal/ui/collection/collection_widget.base.ts @@ -26,7 +26,6 @@ import { getOuterHeight, getOuterWidth } from '@js/core/utils/size'; import { findTemplates } from '@js/core/utils/template_manager'; import { isDefined, isFunction, isPlainObject } from '@js/core/utils/type'; import type { DataSourceOptions } from '@js/data/data_source'; -import DataHelperMixin from '@js/data_helper'; import type { Cancelable, DxEvent, EventInfo, ItemInfo, } from '@js/events'; @@ -35,6 +34,7 @@ import { focusable } from '@js/ui/widget/selectors'; import { getPublicElement } from '@ts/core/m_element'; import type { OptionChanged } from '@ts/core/widget/types'; import Widget from '@ts/core/widget/widget'; +import { DataHelperMixin } from '@ts/data/m_data_helper'; import CollectionWidgetItem from '@ts/ui/collection/m_item'; const COLLECTION_CLASS = 'dx-collection'; @@ -91,7 +91,11 @@ export interface CollectionWidgetBaseProperties< focusOnSelectedItem?: boolean; + encodeNoDataText?: boolean; + _itemAttributes?: Record; + + selectOnFocus?: boolean; } class CollectionWidget< @@ -101,7 +105,7 @@ class CollectionWidget< TItem extends ItemLike = any, // eslint-disable-next-line @typescript-eslint/no-explicit-any TKey = any, -> extends Widget { +> extends DataHelperMixin(Widget) { private _focusedItemId?: string; // eslint-disable-next-line no-restricted-globals @@ -121,6 +125,8 @@ class CollectionWidget< _itemFocusHandler?: () => void; + onFocusedItemChanged?: (event?: Partial & ItemInfo>) => void; + _inkRipple?: { showWave: (config: { element: dxElementWrapper; @@ -234,14 +240,12 @@ class CollectionWidget< _init(): void { this._compileDisplayGetter(); - // @ts-expect-error ts-error this._initDataController(); super._init(); this._activeStateUnit = `.${ITEM_CLASS}`; this._cleanRenderedItems(); - // @ts-expect-error ts-error this._refreshDataSource(); } @@ -575,7 +579,7 @@ class CollectionWidget< this._updateFocusedItemState($target, true); // @ts-expect-error ts-error this.onFocusedItemChanged(this.getFocusedItemId()); - // @ts-expect-error ts-error + const { selectOnFocus } = this.option(); const isTargetDisabled = this._isDisabled($target); @@ -683,7 +687,6 @@ class CollectionWidget< this._invalidate(); break; case 'dataSource': - // @ts-expect-error ts-error this._refreshDataSource(); // @ts-expect-error ts-error this._renderEmptyMessage(); @@ -709,7 +712,6 @@ class CollectionWidget< this._attachContextMenuEvent(); break; case 'onFocusedItemChanged': - // @ts-expect-error ts-error this.onFocusedItemChanged = this._createActionByOption('onFocusedItemChanged'); break; case 'selectOnFocus': @@ -742,7 +744,6 @@ class CollectionWidget< _loadNextPage(): Promise { this._expectNextPageLoading(); - // @ts-expect-error ts-error // eslint-disable-next-line @typescript-eslint/no-unsafe-return return this._dataController.loadNextPage(); } @@ -866,7 +867,6 @@ class CollectionWidget< _initMarkup(): void { super._initMarkup(); - // @ts-expect-error ts-error this.onFocusedItemChanged = this._createActionByOption('onFocusedItemChanged'); this.$element().addClass(COLLECTION_CLASS); @@ -1355,8 +1355,8 @@ class CollectionWidget< _renderEmptyMessage(items: TItem[]): void { // eslint-disable-next-line no-param-reassign items = items || this.option('items'); - const noDataText = this.option('noDataText'); - // @ts-expect-error ts-error + const { noDataText } = this.option(); + // eslint-disable-next-line @typescript-eslint/prefer-optional-chain const hideNoData = !noDataText || (items && items.length) || this._dataController.isLoading(); @@ -1371,11 +1371,11 @@ class CollectionWidget< this._$noData = this._$noData ?? $('
').addClass('dx-empty-message'); this._$noData.appendTo(this._emptyMessageContainer()); - if (this.option('encodeNoDataText')) { - // @ts-expect-error ts-error + const { encodeNoDataText } = this.option(); + + if (encodeNoDataText) { this._$noData.text(noDataText); } else { - // @ts-expect-error ts-error this._$noData.html(noDataText); } } @@ -1484,7 +1484,7 @@ class CollectionWidget< } // eslint-disable-next-line @typescript-eslint/no-explicit-any -(CollectionWidget as any).include(DataHelperMixin); +// (CollectionWidget as any).include(DataHelperMixin); // @ts-expect-error ts-error CollectionWidget.ItemClass = CollectionWidgetItem; diff --git a/packages/devextreme/js/__internal/ui/menu/m_menu.ts b/packages/devextreme/js/__internal/ui/menu/m_menu.ts index 57e61d6be877..f5b418ffbceb 100644 --- a/packages/devextreme/js/__internal/ui/menu/m_menu.ts +++ b/packages/devextreme/js/__internal/ui/menu/m_menu.ts @@ -427,7 +427,6 @@ class Menu extends MenuBase { }); return extend(menuOptions, { - // @ts-expect-error dataSource: this.getDataSource(), animationEnabled: !!this.option('animation'), onItemClick: this._treeviewItemClickHandler.bind(this), diff --git a/packages/devextreme/js/common/data.js b/packages/devextreme/js/common/data.js index c497b6c97a6d..8c5cd19312a2 100644 --- a/packages/devextreme/js/common/data.js +++ b/packages/devextreme/js/common/data.js @@ -7,7 +7,7 @@ import { errorHandler, setErrorHandler } from './data/errors'; import LocalStore from './data/local_store'; import query from './data/query'; import { base64_encode, compileGetter, compileSetter } from './data/utils'; -import DataHelperMixin from '../__internal/data/m_data_helper'; +import DataHelperMixin from './data/data_helper'; import ODataContext from './data/odata/context'; import ODataStore from './data/odata/store'; import { EdmLiteral, keyConverters } from './data/odata/utils';