Skip to content

Commit d539a7b

Browse files
List, TreeView: remove the use of SearchBoxMixin (DevExpress#30413)
Co-authored-by: EugeniyKiyashko <[email protected]>
1 parent 52f3602 commit d539a7b

File tree

20 files changed

+590
-338
lines changed

20 files changed

+590
-338
lines changed
Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
1-
export function stubComponent(componentName) {
1+
export function stubComponent<T>(componentName: string): T {
22
// eslint-disable-next-line @typescript-eslint/no-extraneous-class
33
return class NoComponent {
44
constructor() {
55
// TODO: make correct exceptions here and in decorators
66
throw new Error(`Module '${componentName}' not found`);
77
}
88

9-
static getInstance() {}
10-
};
9+
static getInstance(): void {}
10+
} as T;
1111
}

packages/devextreme/js/__internal/core/widget/component.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -38,9 +38,9 @@ export interface ActionConfig {
3838
}
3939

4040
export interface Properties<TComponent> extends ComponentOptions<
41-
EventInfo<TComponent>,
42-
InitializedEventInfo<TComponent>,
43-
OptionChangedEventInfo<TComponent>
41+
EventInfo<TComponent>,
42+
InitializedEventInfo<TComponent>,
43+
OptionChangedEventInfo<TComponent>
4444
> {
4545
onInitializing?: ((e: Record<string, unknown>) => void) | undefined;
4646

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,9 @@ import { extend } from '@js/core/utils/extend';
99
import { getDefaultAlignment } from '@js/core/utils/position';
1010
import { format } from '@js/core/utils/string';
1111
import { isDefined, isFunction } from '@js/core/utils/type';
12-
import List from '@js/ui/list_light';
1312
import errors from '@js/ui/widget/ui.errors';
1413
import { prepareItems } from '@ts/grids/grid_core/m_export';
14+
import List from '@ts/ui/list/m_list.edit.search';
1515

1616
import type { ColumnHeadersView } from '../../grid_core/column_headers/m_column_headers';
1717
import type { ColumnsController } from '../../grid_core/columns_controller/m_columns_controller';

packages/devextreme/js/__internal/grids/grid_core/header_filter/m_header_filter_core.ts

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,11 @@ import { isDefined, isFunction } from '@js/core/utils/type';
1111
import type dxCheckBox from '@js/ui/check_box';
1212
import type { ValueChangedInfo } from '@js/ui/editor/editor';
1313
import type dxList from '@js/ui/list';
14-
import List from '@js/ui/list_light';
1514
import Popup from '@js/ui/popup/ui.popup';
16-
import TreeView from '@js/ui/tree_view';
1715
import Modules from '@ts/grids/grid_core/m_modules';
1816
import type { ModuleType } from '@ts/grids/grid_core/m_types';
19-
import type TextBox from '@ts/ui/text_box/m_text_box';
17+
import List from '@ts/ui/list/m_list.edit.search';
18+
import TreeView from '@ts/ui/tree_view/m_tree_view.search';
2019

2120
import gridCoreUtils from '../m_utils';
2221

@@ -367,7 +366,7 @@ export class HeaderFilterView extends Modules.View {
367366

368367
const onTreeViewOptionChanged = (
369368
event: ChangedOptionInfo & {
370-
component: TreeView & { _searchEditor: TextBox };
369+
component: TreeView;
371370
},
372371
): void => {
373372
switch (true) {
@@ -382,7 +381,7 @@ export class HeaderFilterView extends Modules.View {
382381
// So we should focus the searchEditor only after render will be completed
383382
Promise.resolve()
384383
.then(() => {
385-
event.component._searchEditor.focus();
384+
event.component.getSearchBoxController().focus();
386385
})
387386
.catch(() => {});
388387
break;
Lines changed: 115 additions & 153 deletions
Original file line numberDiff line numberDiff line change
@@ -1,178 +1,140 @@
1+
import type { SearchMode } from '@js/common';
12
import messageLocalization from '@js/common/core/localization/message';
3+
import type { dxElementWrapper } from '@js/core/renderer';
24
import $ from '@js/core/renderer';
5+
import type { DeferredObj } from '@js/core/utils/deferred';
36
import { Deferred } from '@js/core/utils/deferred';
4-
import { extend } from '@js/core/utils/extend';
5-
import { stubComponent } from '@js/core/utils/stubs';
6-
import errors from '@js/ui/widget/ui.errors';
7-
8-
let EditorClass = stubComponent('TextBox');
9-
10-
export default {
11-
_getDefaultOptions() {
12-
return extend(this.callBase(), {
13-
searchMode: '',
14-
searchExpr: null,
15-
searchValue: '',
16-
searchEnabled: false,
17-
searchEditorOptions: {},
18-
});
19-
},
20-
21-
_initMarkup(): void {
22-
this._renderSearch();
23-
this.callBase();
24-
},
7+
import type { ValueChangedEvent } from '@js/ui/text_box';
8+
import type { SearchBoxMixinOptions } from '@js/ui/widget/ui.search_box_mixin';
9+
import { stubComponent } from '@ts/core/utils/m_stubs';
10+
import type TextBox from '@ts/ui/text_box/m_text_box';
11+
import type { TextBoxProperties } from '@ts/ui/text_box/m_text_box';
2512

26-
_renderSearch(): void {
27-
const $element = this.$element();
28-
const searchEnabled = this.option('searchEnabled');
29-
const searchBoxClassName = this._addWidgetPrefix('search');
30-
const rootElementClassName = this._addWidgetPrefix('with-search');
13+
export const getOperationBySearchMode = (searchMode?: SearchMode): string | undefined => (searchMode === 'equals' ? '=' : searchMode);
3114

32-
if (!searchEnabled) {
33-
$element.removeClass(rootElementClassName);
34-
this._removeSearchBox();
35-
return;
36-
}
15+
export type SearchBoxControllerOptions = SearchBoxMixinOptions & {
16+
tabIndex?: number;
17+
onValueChanged?: (value: string) => void;
18+
};
3719

38-
const editorOptions = this._getSearchEditorOptions();
20+
class SearchBoxController {
21+
static EditorClass: (new (...args) => TextBox) = stubComponent('TextBox');
3922

40-
if (this._searchEditor) {
41-
this._searchEditor.option(editorOptions);
42-
} else {
43-
$element.addClass(rootElementClassName);
44-
this._$searchEditorElement = $('<div>').addClass(searchBoxClassName).prependTo($element);
45-
this._searchEditor = this._createComponent(this._$searchEditorElement, EditorClass, editorOptions);
46-
}
47-
},
23+
_editor?: TextBox | null;
4824

49-
_removeSearchBox(): void {
50-
this._$searchEditorElement && this._$searchEditorElement.remove();
51-
delete this._$searchEditorElement;
52-
delete this._searchEditor;
53-
},
25+
_valueChangeDeferred!: DeferredObj<unknown>;
5426

55-
_getSearchEditorOptions() {
56-
const that = this;
57-
const userEditorOptions = that.option('searchEditorOptions');
58-
const searchText = messageLocalization.format('Search');
27+
// eslint-disable-next-line no-restricted-globals
28+
_valueChangeTimeout!: ReturnType<typeof setTimeout>;
5929

60-
return extend({
61-
mode: 'search',
62-
placeholder: searchText,
63-
tabIndex: that.option('tabIndex'),
64-
value: that.option('searchValue'),
65-
valueChangeEvent: 'input',
66-
inputAttr: {
67-
'aria-label': searchText,
68-
},
69-
onValueChanged(e) {
70-
const searchTimeout = that.option('searchTimeout');
71-
that._valueChangeDeferred = Deferred();
72-
clearTimeout(that._valueChangeTimeout);
73-
74-
that._valueChangeDeferred.done(function () {
75-
this.option('searchValue', e.value);
76-
}.bind(that));
77-
78-
if (e.event && e.event.type === 'input' && searchTimeout) {
79-
that._valueChangeTimeout = setTimeout(() => {
80-
that._valueChangeDeferred.resolve();
81-
}, searchTimeout);
82-
} else {
83-
that._valueChangeDeferred.resolve();
84-
}
85-
},
86-
}, userEditorOptions);
87-
},
30+
_onSearchBoxValueChanged?: (value: string) => void;
8831

89-
_getAriaTarget() {
90-
if (this.option('searchEnabled')) {
91-
return this._itemContainer(true);
92-
}
93-
return this.callBase();
94-
},
32+
static setEditorClass(value: new (...args) => TextBox): void {
33+
SearchBoxController.EditorClass = value;
34+
}
9535

96-
_focusTarget() {
97-
if (this.option('searchEnabled')) {
98-
return this._itemContainer(true);
99-
}
36+
render(
37+
widgetPrefix: string,
38+
$container: dxElementWrapper,
39+
options: SearchBoxControllerOptions,
40+
createEditorCallback: (
41+
$element: dxElementWrapper,
42+
component: (new (...args) => TextBox),
43+
options: TextBoxProperties,
44+
) => TextBox,
45+
): void {
46+
const rootElementClassName = `${widgetPrefix}-with-search`;
47+
const searchBoxClassName = `${widgetPrefix}-search`;
48+
const { searchEnabled, onValueChanged } = options;
10049

101-
return this.callBase();
102-
},
50+
this._onSearchBoxValueChanged = onValueChanged;
10351

104-
_updateFocusState(e, isFocused): void {
105-
if (this.option('searchEnabled')) {
106-
this._toggleFocusClass(isFocused, this.$element());
107-
}
108-
this.callBase(e, isFocused);
109-
},
110-
111-
getOperationBySearchMode(searchMode) {
112-
return searchMode === 'equals' ? '=' : searchMode;
113-
},
114-
115-
_optionChanged(args) {
116-
switch (args.name) {
117-
case 'searchEnabled':
118-
case 'searchEditorOptions':
119-
this._invalidate();
120-
break;
121-
case 'searchExpr':
122-
case 'searchMode':
123-
case 'searchValue':
124-
if (!this._dataSource) {
125-
errors.log('W1009');
126-
return;
127-
}
128-
if (args.name === 'searchMode') {
129-
this._dataSource.searchOperation(this.getOperationBySearchMode(args.value));
130-
} else {
131-
this._dataSource[args.name](args.value);
132-
}
133-
this._dataSource.load();
134-
break;
135-
case 'searchTimeout':
136-
break;
137-
default:
138-
this.callBase(args);
52+
if (!searchEnabled) {
53+
$container.removeClass(rootElementClassName);
54+
this.remove();
55+
return;
13956
}
140-
},
14157

142-
focus() {
143-
if (!this.option('focusedElement') && this.option('searchEnabled')) {
144-
this._searchEditor && this._searchEditor.focus();
145-
return;
58+
if (this._editor) {
59+
this.updateEditorOptions(options);
60+
} else {
61+
const editorOptions = this._getEditorOptions(options);
62+
$container.addClass(rootElementClassName);
63+
const $editor = $('<div>')
64+
.addClass(searchBoxClassName)
65+
.prependTo($container);
66+
67+
this._editor = createEditorCallback(
68+
$editor,
69+
SearchBoxController.EditorClass,
70+
editorOptions,
71+
);
14672
}
73+
}
74+
75+
updateEditorOptions(options: SearchBoxControllerOptions): void {
76+
const editorOptions = this._getEditorOptions(options);
77+
this._editor?.option(editorOptions);
78+
}
79+
80+
_getEditorOptions(options: SearchBoxControllerOptions): TextBoxProperties {
81+
const {
82+
tabIndex,
83+
searchValue,
84+
searchEditorOptions,
85+
searchTimeout,
86+
} = options;
87+
const placeholder = messageLocalization.format('Search');
88+
89+
return {
90+
mode: 'search',
91+
placeholder,
92+
tabIndex,
93+
value: searchValue,
94+
valueChangeEvent: 'input',
95+
inputAttr: { 'aria-label': placeholder },
96+
// @ts-expect-error ts-error
97+
onValueChanged: (e: ValueChangedEvent): void => {
98+
this._onValueChanged(e, searchTimeout);
99+
},
100+
...searchEditorOptions,
101+
};
102+
}
147103

148-
this.callBase();
149-
},
104+
_onValueChanged(e: ValueChangedEvent, searchTimeout = 0): void {
105+
this._valueChangeDeferred = Deferred();
106+
clearTimeout(this._valueChangeTimeout);
150107

151-
_cleanAria(): void {
152-
const $element = this.$element();
108+
this._valueChangeDeferred.done((): void => {
109+
this._onSearchBoxValueChanged?.(e.value);
110+
});
153111

154-
this.setAria({
155-
role: null,
156-
activedescendant: null,
157-
}, $element);
112+
if (e.event?.type === 'input' && searchTimeout) {
113+
// eslint-disable-next-line no-restricted-globals
114+
this._valueChangeTimeout = setTimeout((): void => {
115+
this._valueChangeDeferred?.resolve();
116+
}, searchTimeout);
117+
} else {
118+
this._valueChangeDeferred?.resolve();
119+
}
120+
}
158121

159-
$element.attr('tabIndex', null);
160-
},
122+
resolveValueChange(): void {
123+
this._valueChangeDeferred?.resolve();
124+
}
161125

162-
_clean(): void {
163-
this.callBase();
164-
this._cleanAria();
165-
},
126+
remove(): void {
127+
this._editor?.$element().remove();
128+
this._editor = null;
129+
}
166130

167-
_refresh(): void {
168-
if (this._valueChangeDeferred) {
169-
this._valueChangeDeferred.resolve();
170-
}
131+
focus(): void {
132+
this._editor?.focus();
133+
}
171134

172-
this.callBase();
173-
},
135+
dispose(): void {
136+
this.remove();
137+
}
138+
}
174139

175-
setEditorClass(value): void {
176-
EditorClass = value;
177-
},
178-
};
140+
export default SearchBoxController;

packages/devextreme/js/__internal/ui/date_box/m_date_box.strategy.list.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,8 @@ import dateSerialization from '@js/core/utils/date_serialization';
66
import { getHeight, getOuterHeight } from '@js/core/utils/size';
77
import { isDate } from '@js/core/utils/type';
88
import { getWindow } from '@js/core/utils/window';
9-
import List from '@js/ui/list_light';
109
import { getSizeValue } from '@ts/ui/drop_down_editor/m_utils';
10+
import List from '@ts/ui/list/m_list.edit.search';
1111

1212
import DateBoxStrategy from './m_date_box.strategy';
1313
import dateUtils from './m_date_utils';

0 commit comments

Comments
 (0)