From 9eadaee52d4c8d07d1d23ec5f936dfb01031b08c Mon Sep 17 00:00:00 2001 From: Mark Allen Ramirez Date: Tue, 19 May 2026 17:25:31 +0800 Subject: [PATCH 1/2] spike --- .../columns_controller/columns_controller.ts | 37 ++++++++++++++++++- .../new/grid_core/columns_controller/const.ts | 6 +++ .../options_controller_base.ts | 17 +++++++++ 3 files changed, 59 insertions(+), 1 deletion(-) create mode 100644 packages/devextreme/js/__internal/grids/new/grid_core/columns_controller/const.ts diff --git a/packages/devextreme/js/__internal/grids/new/grid_core/columns_controller/columns_controller.ts b/packages/devextreme/js/__internal/grids/new/grid_core/columns_controller/columns_controller.ts index 6f6876098824..c6c5fc868c26 100644 --- a/packages/devextreme/js/__internal/grids/new/grid_core/columns_controller/columns_controller.ts +++ b/packages/devextreme/js/__internal/grids/new/grid_core/columns_controller/columns_controller.ts @@ -1,3 +1,5 @@ +import { equalByValue } from '@js/core/utils/common'; +import { getPathParts } from '@js/core/utils/data'; import type { ReadonlySignal, Signal } from '@ts/core/state_manager/index'; import { computed, effect, signal } from '@ts/core/state_manager/index'; import type { DataObject } from '@ts/grids/new/grid_core/data_controller/types'; @@ -6,7 +8,9 @@ import { isColumnFilterable, mergeColumnHeaderFilterOptions } from '@ts/grids/ne import type { OptionWithChanges } from '@ts/grids/new/grid_core/options_controller/types'; import { OptionsController } from '../options_controller/options_controller'; +import { getTreeNodeByPath } from '../utils/tree/index'; import { updateColumnSettings } from './columns_settings/index'; +import { IGNORE_COLUMN_OPTION_NAMES } from './const'; import type { ColumnProperties, ColumnSettings, PreNormalizedColumn } from './options'; import type { Column, ColumnsConfigurationFromData, VisibleColumn } from './types'; import { @@ -121,14 +125,16 @@ export class ColumnsController { } public columnOption( - { name }: Column, + column: Column, // TODO: Fix type -> option may be path with dots in runtime // E.g: 'columnOption('A', 'headerFilter.search.enabled', true) option: TProp, value: ColumnSettings[TProp], ): void { + const { name } = column; const settings = this.columnsSettings.peek(); const columnIdx = getColumnIndexByName(settings, name); + const prevValue = getTreeNodeByPath(column, getPathParts(option)); this.columnsSettings.value = columnOptionUpdate( settings, @@ -136,12 +142,41 @@ export class ColumnsController { option, value, ); + + this.fireOptionChanged(columnIdx, option, value, prevValue); } public updateColumns(func: (columns: PreNormalizedColumn[]) => PreNormalizedColumn[]): void { + const prevColumns = this.columns.peek(); + let newColumnSettings = func(this.columnsSettings.peek()); newColumnSettings = normalizeColumnsVisibleIndexes(newColumnSettings); this.columnsSettings.value = newColumnSettings; + + const newColumns = this.columns.peek(); + newColumns.forEach((newColumn, columnIdx) => { + const prevColumn = prevColumns[columnIdx]; + const options = new Set([...Object.keys(prevColumn), ...Object.keys(newColumn)]); + for (const option of options) { + if (!IGNORE_COLUMN_OPTION_NAMES[option]) { + const prevValue = prevColumn[option]; + const newValue = newColumn[option]; + this.fireOptionChanged(columnIdx, option, newValue, prevValue); + } + } + }); + } + + private fireOptionChanged( + columnIndex: number, + optionName: string, + newValue: unknown, + prevValue: unknown, + ): void { + if (!equalByValue(prevValue, newValue, { maxDepth: 5 })) { + const fullOptionPath = `columns[${columnIndex}].${optionName}`; + this.options.notifyColumnOptionChanged(fullOptionPath, newValue, prevValue); + } } public setColumnOptionsFromDataItem( diff --git a/packages/devextreme/js/__internal/grids/new/grid_core/columns_controller/const.ts b/packages/devextreme/js/__internal/grids/new/grid_core/columns_controller/const.ts new file mode 100644 index 000000000000..3f42777ddec4 --- /dev/null +++ b/packages/devextreme/js/__internal/grids/new/grid_core/columns_controller/const.ts @@ -0,0 +1,6 @@ +export const IGNORE_COLUMN_OPTION_NAMES = { + visibleWidth: true, + bestFitWidth: true, + bufferedFilterValue: true, + selector: true, +}; diff --git a/packages/devextreme/js/__internal/grids/new/grid_core/options_controller/options_controller_base.ts b/packages/devextreme/js/__internal/grids/new/grid_core/options_controller/options_controller_base.ts index 0b41813464a1..509b3943a06b 100644 --- a/packages/devextreme/js/__internal/grids/new/grid_core/options_controller/options_controller_base.ts +++ b/packages/devextreme/js/__internal/grids/new/grid_core/options_controller/options_controller_base.ts @@ -47,6 +47,8 @@ export class OptionsController< private isControlledMode = false; + private _skipProcessingColumnsChange: string | false = false; + private readonly internalOptions: Signal>; // @ts-expect-error Component type doesn't have fields from widget.ts @@ -76,6 +78,10 @@ export class OptionsController< private onOptionChangedHandler(optionChanges: ChangedOptionInfo): void { const { fullName } = optionChanges; + if (this._skipProcessingColumnsChange === fullName) { + return; + } + this.updateIsControlledMode(); this.updateInternalOptionsState(fullName, optionChanges); } @@ -221,4 +227,15 @@ export class OptionsController< ); }); } + + public notifyColumnOptionChanged( + fullOptionPath: string, + newValue: unknown, + prevValue: unknown, + ): void { + this._skipProcessingColumnsChange = fullOptionPath; + // @ts-expect-error + this.component._notifyOptionChanged(fullOptionPath, newValue, prevValue); + this._skipProcessingColumnsChange = false; + } } From 6e348fa65d80de45cc8674715711f34c32eef50b Mon Sep 17 00:00:00 2001 From: Mark Allen Ramirez Date: Tue, 19 May 2026 18:29:31 +0800 Subject: [PATCH 2/2] spike fix --- .../columns_controller/columns_controller.ts | 17 ++++++++++------- .../new/grid_core/columns_controller/const.ts | 3 --- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/packages/devextreme/js/__internal/grids/new/grid_core/columns_controller/columns_controller.ts b/packages/devextreme/js/__internal/grids/new/grid_core/columns_controller/columns_controller.ts index c6c5fc868c26..1f15857289af 100644 --- a/packages/devextreme/js/__internal/grids/new/grid_core/columns_controller/columns_controller.ts +++ b/packages/devextreme/js/__internal/grids/new/grid_core/columns_controller/columns_controller.ts @@ -15,6 +15,7 @@ import type { ColumnProperties, ColumnSettings, PreNormalizedColumn } from './op import type { Column, ColumnsConfigurationFromData, VisibleColumn } from './types'; import { columnOptionUpdate, + getColumnByIndexOrName, getColumnIndexByName, getColumnOptionsFromDataItem, normalizeColumns, @@ -155,13 +156,15 @@ export class ColumnsController { const newColumns = this.columns.peek(); newColumns.forEach((newColumn, columnIdx) => { - const prevColumn = prevColumns[columnIdx]; - const options = new Set([...Object.keys(prevColumn), ...Object.keys(newColumn)]); - for (const option of options) { - if (!IGNORE_COLUMN_OPTION_NAMES[option]) { - const prevValue = prevColumn[option]; - const newValue = newColumn[option]; - this.fireOptionChanged(columnIdx, option, newValue, prevValue); + const prevColumn = getColumnByIndexOrName(prevColumns, newColumn.name); + if (prevColumn) { + const options = new Set([...Object.keys(prevColumn), ...Object.keys(newColumn)]); + for (const option of options) { + if (!IGNORE_COLUMN_OPTION_NAMES[option]) { + const prevValue = prevColumn[option]; + const newValue = newColumn[option]; + this.fireOptionChanged(columnIdx, option, newValue, prevValue); + } } } }); diff --git a/packages/devextreme/js/__internal/grids/new/grid_core/columns_controller/const.ts b/packages/devextreme/js/__internal/grids/new/grid_core/columns_controller/const.ts index 3f42777ddec4..862389a7ea76 100644 --- a/packages/devextreme/js/__internal/grids/new/grid_core/columns_controller/const.ts +++ b/packages/devextreme/js/__internal/grids/new/grid_core/columns_controller/const.ts @@ -1,6 +1,3 @@ export const IGNORE_COLUMN_OPTION_NAMES = { - visibleWidth: true, - bestFitWidth: true, - bufferedFilterValue: true, selector: true, };