Skip to content

Commit 6cf389c

Browse files
committed
Grids: move _filterSyncController from base data to extender (#34997)
(cherry picked from commit 2fb35c6)
1 parent 9122a23 commit 6cf389c

21 files changed

Lines changed: 283 additions & 196 deletions

File tree

packages/devextreme/js/__internal/grids/data_grid/grouping/m_grouping_core.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ export function createOffsetFilter(path, storeLoadOptions, lastLevelOnly?) {
3535
filter.push(gridCore.combineFilters(filterElement));
3636
}
3737

38+
// @ts-expect-error
3839
filter = gridCore.combineFilters(filter, 'or');
3940

4041
return gridCore.combineFilters([filter, storeLoadOptions.filter]);

packages/devextreme/js/__internal/grids/data_grid/grouping/m_grouping_expanded.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -211,6 +211,7 @@ const createNotGroupFilter = function (path, storeLoadOptions, group) {
211211
}
212212
filter.push(dataGridCore.combineFilters(filterElement));
213213
}
214+
// @ts-expect-error
214215
filter = dataGridCore.combineFilters(filter, 'or');
215216

216217
return dataGridCore.combineFilters([filter, storeLoadOptions.filter]);

packages/devextreme/js/__internal/grids/data_grid/module_not_extended/filter_sync.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { filterSyncModule } from '@ts/grids/grid_core/filter_sync/m_filter_sync';
1+
import { filterSyncModule } from '@ts/grids/grid_core/filter_sync/filter_sync_module';
22

33
import core from '../m_core';
44

packages/devextreme/js/__internal/grids/grid_core/data_controller/data_controller.ts

Lines changed: 9 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@ import type {
2020
ChangedEvent, DataSourceAdapterProvider, LoadOperation, OperationTypes, RawItemData,
2121
} from '@ts/grids/grid_core/data_source_adapter/types';
2222
import { isLocalStore } from '@ts/grids/grid_core/data_source_adapter/utils/store';
23-
import type { FilterSyncController } from '@ts/grids/grid_core/filter_sync/m_filter_sync';
2423
import type { FocusController } from '@ts/grids/grid_core/focus/m_focus';
2524
import modules from '@ts/grids/grid_core/m_modules';
2625
import type {
@@ -134,8 +133,6 @@ export class DataController extends modules.Controller {
134133
// TODO public controller
135134
public _rowsScrollController?: VirtualScrollController | null;
136135

137-
protected _filterSyncController!: FilterSyncController;
138-
139136
private _filterExcludedColumn: Column | null = null;
140137

141138
protected _focusController!: FocusController;
@@ -150,7 +147,6 @@ export class DataController extends modules.Controller {
150147
this._items = [];
151148
this._cachedProcessedItems = null;
152149
this._columnsController = this.getController('columns');
153-
this._filterSyncController = this.getController('filterSync');
154150
this._focusController = this.getController('focus');
155151

156152
this._isPaging = false;
@@ -370,7 +366,7 @@ export class DataController extends modules.Controller {
370366
|| this._columnsController.isAllDataTypesDefined();
371367

372368
if (isColumnsTypesDefined) {
373-
const additionalFilter = this._calculateAdditionalFilter();
369+
const additionalFilter = this.calculateAdditionalFilter();
374370

375371
combined = additionalFilter
376372
? gridCoreUtils.combineFilters([additionalFilter, combined])
@@ -531,7 +527,7 @@ export class DataController extends modules.Controller {
531527
}
532528
} else if (changeTypes.columns) {
533529
if (this.shouldApplyFilter(e)) {
534-
this._applyFilter();
530+
this.applyFilter();
535531
filterApplied = true;
536532
}
537533

@@ -544,7 +540,7 @@ export class DataController extends modules.Controller {
544540
const hasFilterValue = isDefined(column?.filterValue) || isDefined(column?.filterValues);
545541

546542
if (hasFilterValue) {
547-
this._applyFilter();
543+
this.applyFilter();
548544
filterApplied = true;
549545
}
550546
}
@@ -581,7 +577,7 @@ export class DataController extends modules.Controller {
581577
this._isDataSourceApplying = false;
582578

583579
const hasAdditionalFilter = (): boolean => {
584-
const additionalFilter = this._calculateAdditionalFilter();
580+
const additionalFilter = this.calculateAdditionalFilter();
585581
return Boolean(additionalFilter?.length);
586582
};
587583

@@ -590,7 +586,7 @@ export class DataController extends modules.Controller {
590586

591587
if (needApplyFilter && !this._isAllDataTypesDefined && hasAdditionalFilter()) {
592588
errors.log('W1005', this.component.NAME);
593-
this._applyFilter();
589+
this.applyFilter();
594590
} else {
595591
this._currentOperationTypes = dataSource.operationTypes();
596592

@@ -1305,14 +1301,14 @@ export class DataController extends modules.Controller {
13051301
/**
13061302
* @extended: filter_row, filter_sync, header_filter, search
13071303
*/
1308-
protected _calculateAdditionalFilter(): DataFilter {
1304+
protected calculateAdditionalFilter(): DataFilter {
13091305
return null;
13101306
}
13111307

13121308
/**
13131309
* @extended: filter_sync, virtual_scrolling
13141310
*/
1315-
protected _applyFilter(): DeferredObj<unknown> {
1311+
protected applyFilter(): DeferredObj<unknown> {
13161312
const dataSource = this._dataSource;
13171313

13181314
if (dataSource) {
@@ -1354,7 +1350,7 @@ export class DataController extends modules.Controller {
13541350
}
13551351

13561352
this._dataSource?.filter(filterExpr);
1357-
this._applyFilter();
1353+
this.applyFilter();
13581354

13591355
return undefined;
13601356
}
@@ -1374,7 +1370,7 @@ export class DataController extends modules.Controller {
13741370

13751371
this.component.beginUpdate();
13761372

1377-
if (arguments.length > 0) {
1373+
if (filterName !== undefined) {
13781374
switch (filterName) {
13791375
case 'dataSource':
13801376
this.filter(null);

packages/devextreme/js/__internal/grids/grid_core/filter_panel/m_filter_panel.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -330,7 +330,7 @@ const data = (Base: ModuleType<DataController>) => class FilterPanelDataControll
330330
public optionChanged(args) {
331331
switch (args.name) {
332332
case 'filterPanel':
333-
this._applyFilter();
333+
this.applyFilter();
334334
args.handled = true;
335335
break;
336336
default:

packages/devextreme/js/__internal/grids/grid_core/filter_row/m_filter_row.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -816,12 +816,12 @@ const data = (Base: ModuleType<DataController>) => class DataControllerFilterRow
816816
return false;
817817
}
818818

819-
protected _calculateAdditionalFilter() {
819+
protected calculateAdditionalFilter() {
820820
if (this.skipCalculateColumnFilters()) {
821-
return super._calculateAdditionalFilter();
821+
return super.calculateAdditionalFilter();
822822
}
823823

824-
const filters = [super._calculateAdditionalFilter()];
824+
const filters = [super.calculateAdditionalFilter()];
825825
const columns = this._columnsController.getVisibleColumns(null, true);
826826

827827
const excludedColumn = this.getFilterExcludedColumn();
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
export const FILTER_ROW_OPERATIONS = ['=', '<>', '<', '<=', '>', '>=', 'notcontains', 'contains', 'startswith', 'endswith', 'between'];
2+
export const FILTER_TYPES_INCLUDE = 'include';
3+
export const FILTER_TYPES_EXCLUDE = 'exclude';
Lines changed: 163 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,163 @@
1+
import type { DeferredObj } from '@js/core/utils/deferred';
2+
import { Deferred } from '@js/core/utils/deferred';
3+
import { isDefined } from '@js/core/utils/type';
4+
import {
5+
getFilterExpression,
6+
removeFieldConditionsFromFilter,
7+
} from '@ts/filter_builder/m_utils';
8+
import type { Column } from '@ts/grids/grid_core/columns_controller/types';
9+
import type { DataController } from '@ts/grids/grid_core/data_controller/data_controller';
10+
import type { DataFilter } from '@ts/grids/grid_core/data_controller/types';
11+
import { FILTER_TYPES_EXCLUDE } from '@ts/grids/grid_core/filter_sync/const';
12+
import type { FilterSyncController } from '@ts/grids/grid_core/filter_sync/m_filter_sync';
13+
import { getColumnIdentifier } from '@ts/grids/grid_core/filter_sync/utils';
14+
import type { ModuleType, OptionChanged } from '@ts/grids/grid_core/m_types';
15+
import gridCoreUtils from '@ts/grids/grid_core/m_utils';
16+
17+
export const filterSyncDataControllerExtender = (
18+
Base: ModuleType<DataController>,
19+
): ModuleType<DataController> => class FilterSyncDataControllerExtender extends Base {
20+
private filterSyncController!: FilterSyncController;
21+
22+
public init(): void {
23+
this.filterSyncController = this.getController('filterSync');
24+
25+
super.init();
26+
}
27+
28+
public optionChanged(args: OptionChanged): void {
29+
switch (args.name) {
30+
case 'filterValue':
31+
this.applyFilter();
32+
33+
if (this.isFilterSyncActive()) {
34+
this.filterSyncController.syncFilterValue();
35+
}
36+
args.handled = true;
37+
break;
38+
case 'filterSyncEnabled':
39+
args.handled = true;
40+
break;
41+
case 'columns':
42+
if (this.isFilterSyncActive()) {
43+
const column: Column = this._columnsController.getColumnByPath(args.fullName);
44+
45+
if (column && !this.filterSyncController.isSyncingColumnOptions()) {
46+
this.filterSyncController.withColumnOptionsSync(() => {
47+
this.syncColumnOption(
48+
column,
49+
this.parseColumnPropertyName(args.fullName),
50+
args.value,
51+
args.previousValue,
52+
);
53+
});
54+
}
55+
}
56+
super.optionChanged(args);
57+
break;
58+
default:
59+
super.optionChanged(args);
60+
}
61+
}
62+
63+
private isFilterSyncActive(): boolean | undefined {
64+
const filterSyncEnabledValue = this.option('filterSyncEnabled');
65+
return filterSyncEnabledValue === 'auto' ? this.option('filterPanel.visible') : filterSyncEnabledValue;
66+
}
67+
68+
private skipCalculateColumnFilters(): boolean | undefined {
69+
const hasFilterValueOrSyncing = isDefined(this.option('filterValue'))
70+
|| this.filterSyncController.isSyncingColumnOptions();
71+
72+
return hasFilterValueOrSyncing && this.isFilterSyncActive();
73+
}
74+
75+
protected calculateAdditionalFilter(): DataFilter {
76+
const columns = this._columnsController.getFilteringColumns();
77+
const isFilterValueDisabled = this.option('filterPanel.filterEnabled') === false;
78+
79+
if (!columns?.length || isFilterValueDisabled) {
80+
return super.calculateAdditionalFilter();
81+
}
82+
83+
const filters = [super.calculateAdditionalFilter()];
84+
let filterValue = this.option('filterValue');
85+
86+
if (this.isFilterSyncActive()) {
87+
const excludedColumn = this.getFilterExcludedColumn();
88+
89+
if (isDefined(excludedColumn) && filterValue) {
90+
filterValue = removeFieldConditionsFromFilter(
91+
filterValue,
92+
getColumnIdentifier(excludedColumn),
93+
);
94+
}
95+
}
96+
97+
const customOperations = this.filterSyncController.getCustomFilterOperations();
98+
const calculatedFilterValue: DataFilter = getFilterExpression(filterValue, columns, customOperations, 'filterBuilder');
99+
100+
if (calculatedFilterValue) {
101+
filters.push(calculatedFilterValue);
102+
}
103+
104+
return gridCoreUtils.combineFilters(filters);
105+
}
106+
107+
private parseColumnPropertyName(fullName: string): string | null {
108+
const matched = /.*\.(.*)/.exec(fullName);
109+
110+
if (matched) {
111+
return matched[1];
112+
}
113+
114+
return null;
115+
}
116+
117+
private syncColumnOption(
118+
column: Column,
119+
propertyName: string | null,
120+
// `OptionChanged` is discriminated on `name`, and its key list cannot enumerate array
121+
// indices, so `columns[N].<prop>` has no union member and falls back to the `columns`
122+
// array type. `unknown` is the honest type for the leaf value.
123+
value: unknown,
124+
previousValue: unknown,
125+
): void {
126+
const hasExcludeFilterType = value === FILTER_TYPES_EXCLUDE
127+
|| previousValue === FILTER_TYPES_EXCLUDE;
128+
const isExcludeFilterTypeToggled = propertyName === 'filterType' && hasExcludeFilterType;
129+
const needSyncHeaderFilter = isExcludeFilterTypeToggled || propertyName === 'filterValues';
130+
const needSyncFilterRow = propertyName === 'filterValue'
131+
|| propertyName === 'selectedFilterOperation';
132+
133+
if (needSyncHeaderFilter) {
134+
this.filterSyncController.syncHeaderFilter(column);
135+
136+
return;
137+
}
138+
139+
if (needSyncFilterRow) {
140+
this.filterSyncController.syncFilterRow(column, column.filterValue);
141+
}
142+
}
143+
144+
protected clearFilter(filterName?: string): void {
145+
this.component.beginUpdate();
146+
147+
if (filterName === undefined || filterName === 'filterValue') {
148+
this.option('filterValue', null);
149+
}
150+
151+
super.clearFilter(filterName);
152+
153+
this.component.endUpdate();
154+
}
155+
156+
protected applyFilter(): DeferredObj<unknown> {
157+
if (this.filterSyncController.isSyncingColumnOptions()) {
158+
return Deferred().resolve();
159+
}
160+
161+
return super.applyFilter();
162+
}
163+
};
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import type { InternalGridOptions } from '@ts/grids/grid_core/m_types';
2+
3+
import { filterSyncDataControllerExtender } from './extenders/filter_sync_data_controller';
4+
import { columnHeadersView, FilterSyncController } from './m_filter_sync';
5+
6+
export const filterSyncModule = {
7+
defaultOptions(): Pick<InternalGridOptions, 'filterValue' | 'filterSyncEnabled'> {
8+
return {
9+
filterValue: null,
10+
filterSyncEnabled: 'auto',
11+
};
12+
},
13+
controllers: {
14+
filterSync: FilterSyncController,
15+
},
16+
extenders: {
17+
controllers: {
18+
data: filterSyncDataControllerExtender,
19+
},
20+
views: {
21+
columnHeadersView,
22+
},
23+
},
24+
};

0 commit comments

Comments
 (0)