Skip to content

Commit e6e56e6

Browse files
committed
GridCore: add methods to HeaderPanel to add/remove toolbar items
1 parent 937dadd commit e6e56e6

File tree

8 files changed

+156
-78
lines changed

8 files changed

+156
-78
lines changed

packages/devextreme/js/__internal/grids/data_grid/export/m_export.ts

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -731,7 +731,6 @@ const headerPanel = (Base: ModuleType<HeaderPanel>) => class ExportHeaderPanelEx
731731

732732
if (exportButton) {
733733
items.push(exportButton);
734-
this._correctItemsPosition(items);
735734
}
736735

737736
return items;
@@ -840,10 +839,6 @@ const headerPanel = (Base: ModuleType<HeaderPanel>) => class ExportHeaderPanelEx
840839
return items;
841840
}
842841

843-
private _correctItemsPosition(items) {
844-
items.sort((itemA, itemB) => itemA.sortIndex - itemB.sortIndex);
845-
}
846-
847842
private _isExportButtonVisible() {
848843
return this.option('export.enabled');
849844
}

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

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import Menu from '@js/ui/menu';
1313
import Overlay from '@js/ui/overlay/ui.overlay';
1414
import { selectView } from '@js/ui/shared/accessibility';
1515
import type { ColumnsController } from '@ts/grids/grid_core/columns_controller/m_columns_controller';
16+
import type { ToolbarItem } from '@ts/grids/new/grid_core/toolbar/types';
1617
import type MenuInternal from '@ts/ui/menu/menu';
1718

1819
import type { ColumnHeadersView } from '../column_headers/m_column_headers';
@@ -951,16 +952,16 @@ const headerPanel = (Base: ModuleType<HeaderPanel>) => class FilterRowHeaderPane
951952
}
952953
}
953954

954-
protected _getToolbarItems() {
955+
protected _getToolbarItems(): ToolbarItem[] {
955956
const items = super._getToolbarItems();
956957
const filterItem = this._prepareFilterItem();
957958

958959
return filterItem.concat(items);
959960
}
960961

961-
private _prepareFilterItem() {
962+
private _prepareFilterItem(): ToolbarItem[] {
962963
const that = this;
963-
const filterItem: object[] = [];
964+
const filterItem: ToolbarItem[] = [];
964965

965966
if (that._isShowApplyFilterButton()) {
966967
const hintText = that.option('filterRow.applyFilterText');
@@ -972,7 +973,7 @@ const headerPanel = (Base: ModuleType<HeaderPanel>) => class FilterRowHeaderPane
972973
const onClickHandler = function () {
973974
that._applyFilterViewController.applyFilter();
974975
};
975-
const toolbarItem = {
976+
const toolbarItem: ToolbarItem = {
976977
widget: 'dxButton',
977978
options: {
978979
icon: 'apply-filter',

packages/devextreme/js/__internal/grids/grid_core/header_panel/m_header_panel.ts

Lines changed: 48 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,8 @@ import type { Properties as ToolbarProperties } from '@js/ui/toolbar';
77
import Toolbar from '@js/ui/toolbar';
88
import type { EditingController } from '@ts/grids/grid_core/editing/m_editing';
99
import type { HeaderFilterController } from '@ts/grids/grid_core/header_filter/m_header_filter';
10-
import { normalizeToolbarItems } from '@ts/grids/new/grid_core/toolbar/utils';
10+
import type { DefaultToolbarItem, ToolbarItem } from '@ts/grids/new/grid_core/toolbar/types';
11+
import { isDefaultToolbarItem, normalizeToolbarItems } from '@ts/grids/new/grid_core/toolbar/utils';
1112

1213
import type { ModuleType } from '../m_types';
1314
import { ColumnsView } from '../views/m_columns_view';
@@ -25,22 +26,50 @@ export class HeaderPanel extends ColumnsView {
2526

2627
private _toolbarOptions?: ToolbarProperties;
2728

29+
private _registeredToolbarItems: Record<string, ToolbarItem> = {};
30+
2831
protected _editingController!: EditingController;
2932

3033
protected _headerFilterController!: HeaderFilterController;
3134

32-
public init() {
35+
public init(): void {
3336
super.init();
37+
3438
this._editingController = this.getController('editing');
3539
this._headerFilterController = this.getController('headerFilter');
40+
3641
this.createAction('onToolbarPreparing', { excludeValidators: ['disabled', 'readOnly'] });
3742
}
3843

44+
public addToolbarItem(name: string, item: ToolbarItem): void {
45+
this._registeredToolbarItems[name] = item;
46+
47+
if (this._toolbar) {
48+
this._invalidate();
49+
}
50+
}
51+
52+
public removeToolbarItem(name: string): void {
53+
if (this._registeredToolbarItems[name]) {
54+
// eslint-disable-next-line @typescript-eslint/no-dynamic-delete
55+
delete this._registeredToolbarItems[name];
56+
57+
if (this._toolbar) {
58+
this._invalidate();
59+
}
60+
}
61+
}
62+
3963
/**
40-
* @extended: column_chooser, editing, filter_row, search
64+
* @extended: column_chooser, editing, filter_row
4165
*/
42-
protected _getToolbarItems(): any[] {
43-
return [];
66+
protected _getToolbarItems(): ToolbarItem[] {
67+
return Object.values(this._registeredToolbarItems);
68+
}
69+
70+
// eslint-disable-next-line class-methods-use-this
71+
private _sortToolbarItems(items: ToolbarItem[]): ToolbarItem[] {
72+
return [...items].sort((a, b) => (a.sortIndex ?? 0) - (b.sortIndex ?? 0));
4473
}
4574

4675
private _getButtonContainer() {
@@ -53,27 +82,32 @@ export class HeaderPanel extends ColumnsView {
5382
return this.addWidgetPrefix(TOOLBAR_BUTTON_CLASS) + secondClass;
5483
}
5584

56-
private _getToolbarOptions() {
57-
const userToolbarOptions: any = this.option('toolbar');
85+
private _getToolbarOptions(): ToolbarProperties<DefaultToolbarItem | ToolbarItem> {
86+
const { toolbar: userToolbarOptions } = this.option();
87+
const sortedToolbarItems: ToolbarItem[] = this._sortToolbarItems(this._getToolbarItems());
5888

59-
const options = {
89+
const options: { toolbarOptions: ToolbarProperties<DefaultToolbarItem | ToolbarItem> } = {
6090
toolbarOptions: {
61-
items: this._getToolbarItems(),
91+
items: sortedToolbarItems,
6292
visible: userToolbarOptions?.visible,
6393
disabled: userToolbarOptions?.disabled,
6494
onItemRendered(e) {
65-
const itemRenderedCallback = e.itemData.onItemRendered;
95+
const { itemData } = e;
96+
97+
if (itemData && isDefaultToolbarItem(itemData)) {
98+
const itemRenderedCallback = itemData.onItemRendered;
6699

67-
if (itemRenderedCallback) {
68-
itemRenderedCallback(e);
100+
if (itemRenderedCallback) {
101+
itemRenderedCallback(e);
102+
}
69103
}
70104
},
71105
},
72106
};
73107

74108
const userItems = userToolbarOptions?.items;
75109
options.toolbarOptions.items = normalizeToolbarItems(
76-
options.toolbarOptions.items,
110+
sortedToolbarItems as DefaultToolbarItem[],
77111
userItems,
78112
DEFAULT_TOOLBAR_ITEM_NAMES,
79113
);
@@ -179,7 +213,7 @@ export class HeaderPanel extends ColumnsView {
179213
} else if (parts.length === 3) {
180214
// `toolbar.items[i]` case
181215
const normalizedItem = normalizeToolbarItems(
182-
this._getToolbarItems(),
216+
this._getToolbarItems() as DefaultToolbarItem[],
183217
[args.value],
184218
DEFAULT_TOOLBAR_ITEM_NAMES,
185219
)[0];

packages/devextreme/js/__internal/grids/grid_core/keyboard_navigation/m_keyboard_navigation.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1227,8 +1227,7 @@ export class KeyboardNavigationController extends KeyboardNavigationControllerCo
12271227

12281228
private _ctrlFKeyHandler(eventArgs) {
12291229
if (this.option('searchPanel.visible')) {
1230-
// @ts-expect-error
1231-
const searchTextEditor = this._headerPanel.getSearchTextEditor();
1230+
const searchTextEditor = this.getController('searchPanel').getSearchTextEditor();
12321231
if (searchTextEditor) {
12331232
searchTextEditor.focus();
12341233
eventArgs.originalEvent.preventDefault();

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -199,6 +199,7 @@ export interface Controllers {
199199
resizing: import('./views/m_grid_view').ResizingController;
200200
selection: import('./selection/m_selection').SelectionController;
201201
validating: import('./validating/m_validating').ValidatingController;
202+
searchPanel: import('./search/m_search').SearchPanelViewController;
202203
stateStoring: import('./state_storing/m_state_storing_core').StateStoringController;
203204
synchronizeScrolling: import('./views/m_grid_view').SynchronizeScrollingController;
204205
tablePosition: import('./columns_resizing_reordering/m_columns_resizing_reordering').TablePositionViewController;

packages/devextreme/js/__internal/grids/grid_core/search/m_search.ts

Lines changed: 87 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -4,20 +4,25 @@ import messageLocalization from '@js/common/core/localization/message';
44
import type { LangParams } from '@js/common/data';
55
import dataQuery from '@js/common/data/query';
66
import domAdapter from '@js/core/dom_adapter';
7+
import type { dxElementWrapper } from '@js/core/renderer';
78
import $ from '@js/core/renderer';
89
import { compileGetter, toComparable } from '@js/core/utils/data';
10+
import type TextBox from '@js/ui/text_box';
911
import type { Column } from '@ts/grids/grid_core/columns_controller/types';
12+
import type { ToolbarItem } from '@ts/grids/new/grid_core/toolbar/types';
1013

1114
import type { DataController, Filter } from '../data_controller/m_data_controller';
1215
import type { HeaderPanel } from '../header_panel/m_header_panel';
13-
import type { ModuleType } from '../m_types';
16+
import modules from '../m_modules';
17+
import type { ModuleType, OptionChanged } from '../m_types';
1418
import gridCoreUtils from '../m_utils';
1519
import type { RowsView } from '../views/m_rows_view';
1620

1721
const SEARCH_PANEL_CLASS = 'search-panel';
1822
const SEARCH_TEXT_CLASS = 'search-text';
1923
const HEADER_PANEL_CLASS = 'header-panel';
2024
const FILTERING_TIMEOUT = 700;
25+
const SEARCH_PANEL_ITEM_NAME = 'searchPanel';
2126

2227
function allowSearch(column: Column): boolean {
2328
return !!(column.allowSearch ?? column.allowFiltering);
@@ -131,18 +136,27 @@ const dataController = (
131136
}
132137
};
133138

134-
const headerPanel = (
135-
Base: ModuleType<HeaderPanel>,
136-
) => class SearchHeaderPanelExtender extends Base {
137-
public optionChanged(args) {
139+
export class SearchPanelViewController extends modules.ViewController {
140+
private _headerPanel?: HeaderPanel;
141+
142+
private _dataController?: DataController;
143+
144+
public init(): void {
145+
this._headerPanel = this.getView('headerPanel');
146+
this._dataController = this.getController('data');
147+
148+
this._updateSearchPanelItem();
149+
}
150+
151+
public optionChanged(args: OptionChanged): void {
138152
if (args.name === 'searchPanel') {
139153
if (args.fullName === 'searchPanel.text') {
140154
const editor = this.getSearchTextEditor();
141155
if (editor) {
142156
editor.option('value', args.value);
143157
}
144158
} else {
145-
this._invalidate();
159+
this._updateSearchPanelItem();
146160
}
147161

148162
args.handled = true;
@@ -151,68 +165,89 @@ const headerPanel = (
151165
}
152166
}
153167

154-
protected _getToolbarItems() {
155-
const items = super._getToolbarItems();
168+
private _updateSearchPanelItem(): void {
169+
if (!this._headerPanel) {
170+
return;
171+
}
156172

157-
return this._prepareSearchItem(items);
158-
}
159-
160-
private _prepareSearchItem(items) {
161-
const that = this;
162-
const dataController = this._dataController;
163-
const searchPanelOptions = this.option('searchPanel');
173+
const { searchPanel: searchPanelOptions } = this.option();
164174

165175
if (searchPanelOptions && searchPanelOptions.visible) {
166-
const toolbarItem = {
167-
template(data, index, container) {
168-
const $search = $('<div>')
169-
.addClass(that.addWidgetPrefix(SEARCH_PANEL_CLASS))
170-
.appendTo(container);
171-
172-
that._editorFactoryController.createEditor($search, {
173-
width: searchPanelOptions.width,
174-
placeholder: searchPanelOptions.placeholder,
175-
parentType: 'searchPanel',
176-
value: that.option('searchPanel.text'),
177-
updateValueTimeout: FILTERING_TIMEOUT,
178-
setValue(value) {
179-
// @ts-expect-error
180-
dataController.searchByText(value);
181-
},
182-
editorOptions: {
183-
inputAttr: {
184-
'aria-label': messageLocalization.format(`${that.component.NAME}-ariaSearchInGrid`),
176+
const searchPanelToolbarItem = this._getSearchPanelToolbarItem();
177+
178+
if (searchPanelToolbarItem) {
179+
this._headerPanel.addToolbarItem(SEARCH_PANEL_ITEM_NAME, searchPanelToolbarItem);
180+
}
181+
} else {
182+
this._headerPanel.removeToolbarItem(SEARCH_PANEL_ITEM_NAME);
183+
}
184+
}
185+
186+
private _getSearchPanelToolbarItem(): ToolbarItem | null {
187+
const { searchPanel: searchPanelOptions } = this.option();
188+
189+
if (this._headerPanel && searchPanelOptions && searchPanelOptions.visible) {
190+
return {
191+
template: (data, index, container: dxElementWrapper | Element): void => {
192+
if (this._headerPanel) {
193+
const $search = $('<div>')
194+
.addClass(this._headerPanel.addWidgetPrefix(SEARCH_PANEL_CLASS))
195+
.appendTo(container);
196+
197+
this.getController('editorFactory').createEditor($search, {
198+
width: searchPanelOptions.width,
199+
placeholder: searchPanelOptions.placeholder,
200+
parentType: 'searchPanel',
201+
value: this.option('searchPanel.text'),
202+
updateValueTimeout: FILTERING_TIMEOUT,
203+
setValue: (value) => {
204+
// @ts-expect-error
205+
this._dataController.searchByText(value);
185206
},
186-
},
187-
});
207+
editorOptions: {
208+
inputAttr: {
209+
'aria-label': messageLocalization.format(`${this.component.NAME}-ariaSearchInGrid`),
210+
},
211+
},
212+
});
188213

189-
that.resize();
214+
this._headerPanel.resize();
215+
}
190216
},
191-
name: 'searchPanel',
217+
name: SEARCH_PANEL_ITEM_NAME,
192218
location: 'after',
193219
locateInMenu: 'never',
194-
sortIndex: 40,
220+
sortIndex: 50,
195221
};
196-
197-
items.push(toolbarItem);
198222
}
199223

200-
return items;
224+
return null;
201225
}
202226

203-
private getSearchTextEditor() {
204-
const that = this;
205-
const $element = that.element();
206-
const $searchPanel = $element.find(`.${that.addWidgetPrefix(SEARCH_PANEL_CLASS)}`).filter(function () {
207-
return $(this).closest(`.${that.addWidgetPrefix(HEADER_PANEL_CLASS)}`).is($element);
208-
});
227+
public getSearchTextEditor(): TextBox | null {
228+
if (!this._headerPanel) {
229+
return null;
230+
}
231+
232+
const $element = this._headerPanel.element();
233+
234+
if (!$element) {
235+
return null;
236+
}
237+
238+
const headerPanelClass = this._headerPanel.addWidgetPrefix(HEADER_PANEL_CLASS);
239+
const $searchPanel = $element
240+
.find(`.${this._headerPanel.addWidgetPrefix(SEARCH_PANEL_CLASS)}`)
241+
.filter((_, el: HTMLElement) => $(el).closest(`.${headerPanelClass}`).is($element));
209242

210243
if ($searchPanel.length) {
244+
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
211245
return $searchPanel.dxTextBox('instance');
212246
}
247+
213248
return null;
214249
}
215-
};
250+
}
216251

217252
const rowsView = (
218253
Base: ModuleType<RowsView>,
@@ -386,12 +421,14 @@ export const searchModule = {
386421
},
387422
};
388423
},
424+
controllers: {
425+
searchPanel: SearchPanelViewController,
426+
},
389427
extenders: {
390428
controllers: {
391429
data: dataController,
392430
},
393431
views: {
394-
headerPanel,
395432
rowsView,
396433
},
397434
},

0 commit comments

Comments
 (0)