@xh/hoist 80.0.0-SNAPSHOT.1769000829579 → 80.0.0-SNAPSHOT.1769042130982
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +4 -0
- package/build/types/cmp/grid/GridModel.d.ts +1 -1
- package/build/types/cmp/grid/Types.d.ts +7 -3
- package/build/types/cmp/store/StoreFilterField.d.ts +3 -3
- package/build/types/cmp/store/impl/StoreFilterFieldImplModel.d.ts +9 -7
- package/build/types/data/filter/Types.d.ts +7 -0
- package/build/types/desktop/cmp/grid/find/GridFindField.d.ts +3 -2
- package/build/types/desktop/cmp/grid/find/impl/GridFindFieldImplModel.d.ts +5 -3
- package/build/types/desktop/cmp/grid/impl/colchooser/ColChooserModel.d.ts +6 -11
- package/build/types/desktop/cmp/leftrightchooser/LeftRightChooserFilter.d.ts +3 -2
- package/cmp/grid/GridModel.ts +1 -1
- package/cmp/grid/Types.ts +15 -4
- package/cmp/store/StoreFilterField.ts +3 -3
- package/cmp/store/impl/StoreFilterFieldImplModel.ts +32 -26
- package/data/filter/Types.ts +8 -0
- package/desktop/cmp/grid/find/GridFindField.ts +3 -2
- package/desktop/cmp/grid/find/impl/GridFindFieldImplModel.ts +41 -37
- package/desktop/cmp/grid/impl/colchooser/ColChooser.ts +2 -2
- package/desktop/cmp/grid/impl/colchooser/ColChooserModel.ts +9 -5
- package/desktop/cmp/leftrightchooser/LeftRightChooserFilter.ts +26 -11
- package/package.json +1 -1
- package/tsconfig.tsbuildinfo +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -19,6 +19,8 @@
|
|
|
19
19
|
* `Store.isStore`
|
|
20
20
|
* `View.isView`
|
|
21
21
|
* `Filter.isFilter`
|
|
22
|
+
* Replaced `LeftRightChooserFilter.anyMatch` with `matchMode`. Changes are not expected to be
|
|
23
|
+
required as apps typically do not create this component directly.
|
|
22
24
|
|
|
23
25
|
### 🐞 Bug Fixes
|
|
24
26
|
|
|
@@ -32,6 +34,8 @@
|
|
|
32
34
|
hamburger menu. Set to `true` to render the current user's initials instead or provide a function
|
|
33
35
|
to render a custom element for the user.
|
|
34
36
|
* Added `AggregationContext` as an additional argument to `CubeField.canAggregateFn`.
|
|
37
|
+
* Added `filterMatchMode` option to `ColChooserModel`, allowing customizing match to `start`,
|
|
38
|
+
`startWord`, or `any`.
|
|
35
39
|
|
|
36
40
|
### ⚙️ Typescript API Adjustments
|
|
37
41
|
|
|
@@ -29,7 +29,7 @@ export interface GridConfig {
|
|
|
29
29
|
/** Config with which to create a GridFilterModel, or `true` to enable default. Desktop only.*/
|
|
30
30
|
filterModel?: GridFilterModelConfig | boolean;
|
|
31
31
|
/** Config with which to create a ColChooserModel, or boolean `true` to enable default.*/
|
|
32
|
-
colChooserModel?: ColChooserConfig | boolean;
|
|
32
|
+
colChooserModel?: Omit<ColChooserConfig, 'gridModel'> | boolean;
|
|
33
33
|
/**
|
|
34
34
|
* Function to be called when the user triggers GridModel.restoreDefaultsAsync(). This
|
|
35
35
|
* function will be called after the built-in defaults have been restored, and can be
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import { FilterBindTarget, FilterValueSource, Store, StoreRecord } from '@xh/hoist/data';
|
|
1
|
+
import type { HSide, PersistOptions, Some } from '@xh/hoist/core';
|
|
2
|
+
import type { FilterBindTarget, FilterMatchMode, FilterValueSource, Store, StoreRecord } from '@xh/hoist/data';
|
|
4
3
|
import type { CellClassParams, CustomCellEditorProps, HeaderClassParams, HeaderValueGetterParams, ICellRendererParams, IRowNode, ITooltipParams, RowClassParams, ValueSetterParams } from '@xh/hoist/kit/ag-grid';
|
|
5
4
|
import type { ReactElement, ReactNode } from 'react';
|
|
6
5
|
import type { Column, ColumnSpec } from './columns/Column';
|
|
7
6
|
import type { ColumnGroup, ColumnGroupSpec } from './columns/ColumnGroup';
|
|
7
|
+
import type { GridFilterFieldSpecConfig } from './filter/GridFilterFieldSpec';
|
|
8
8
|
import type { GridModel } from './GridModel';
|
|
9
9
|
export interface ColumnState {
|
|
10
10
|
colId: string;
|
|
@@ -89,6 +89,8 @@ export interface GridFilterBindTarget extends FilterBindTarget, FilterValueSourc
|
|
|
89
89
|
*/
|
|
90
90
|
export type GroupRowRenderer = (context: ICellRendererParams) => ReactNode;
|
|
91
91
|
export interface ColChooserConfig {
|
|
92
|
+
/** GridModel to bind to. Not required if creating via `GridModel.colChooserModel` */
|
|
93
|
+
gridModel?: GridModel;
|
|
92
94
|
/**
|
|
93
95
|
* Immediately render changed columns on grid (default true).
|
|
94
96
|
* Set to false to enable Save button for committing changes on save. Desktop only.
|
|
@@ -108,6 +110,8 @@ export interface ColChooserConfig {
|
|
|
108
110
|
width?: string | number;
|
|
109
111
|
/** Chooser height for popover and dialog. Desktop only. */
|
|
110
112
|
height?: string | number;
|
|
113
|
+
/** Mode to use when filtering (default 'startWord'). Desktop only. */
|
|
114
|
+
filterMatchMode?: FilterMatchMode;
|
|
111
115
|
}
|
|
112
116
|
export type ColumnOrGroup = Column | ColumnGroup;
|
|
113
117
|
export type ColumnOrGroupSpec = ColumnSpec | ColumnGroupSpec;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { GridModel } from '@xh/hoist/cmp/grid';
|
|
1
|
+
import type { GridModel } from '@xh/hoist/cmp/grid';
|
|
2
2
|
import { DefaultHoistProps, HoistModel } from '@xh/hoist/core';
|
|
3
|
-
import { FilterTestFn, Store } from '@xh/hoist/data';
|
|
3
|
+
import type { FilterMatchMode, FilterTestFn, Store } from '@xh/hoist/data';
|
|
4
4
|
export interface StoreFilterFieldProps extends DefaultHoistProps {
|
|
5
5
|
/**
|
|
6
6
|
* Automatically apply the filter to bound store (default true). Applications that need to
|
|
@@ -37,7 +37,7 @@ export interface StoreFilterFieldProps extends DefaultHoistProps {
|
|
|
37
37
|
*/
|
|
38
38
|
includeFields?: string[];
|
|
39
39
|
/** Mode to use when filtering (default 'startWord'). */
|
|
40
|
-
matchMode?:
|
|
40
|
+
matchMode?: FilterMatchMode;
|
|
41
41
|
/** Optional model for raw value binding - see comments on the `bind` prop for details. */
|
|
42
42
|
model?: HoistModel;
|
|
43
43
|
/**
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import { HoistModel } from '@xh/hoist/core';
|
|
2
1
|
import { GridModel } from '@xh/hoist/cmp/grid';
|
|
2
|
+
import { HoistModel } from '@xh/hoist/core';
|
|
3
|
+
import type { FilterMatchMode, StoreRecord } from '@xh/hoist/data';
|
|
3
4
|
import { Store } from '@xh/hoist/data';
|
|
4
5
|
/**
|
|
5
6
|
* @internal
|
|
@@ -9,15 +10,16 @@ export declare class StoreFilterFieldImplModel extends HoistModel {
|
|
|
9
10
|
model: any;
|
|
10
11
|
gridModel: GridModel;
|
|
11
12
|
store: Store;
|
|
12
|
-
filter
|
|
13
|
-
bufferedApplyFilter
|
|
13
|
+
private filter;
|
|
14
|
+
private bufferedApplyFilter;
|
|
15
|
+
get matchMode(): FilterMatchMode;
|
|
14
16
|
constructor();
|
|
15
17
|
onLinked(): void;
|
|
16
|
-
get filterText():
|
|
17
|
-
setFilterText(v:
|
|
18
|
+
get filterText(): string;
|
|
19
|
+
setFilterText(v: string): void;
|
|
18
20
|
applyFilter(): void;
|
|
19
21
|
regenerateFilter(): void;
|
|
20
|
-
getRegex(searchTerm:
|
|
22
|
+
getRegex(searchTerm: string): RegExp;
|
|
21
23
|
getActiveFields(): string[];
|
|
22
|
-
getValGetters(fieldName:
|
|
24
|
+
getValGetters(fieldName: string): ((record: StoreRecord) => any)[] | ((rec: StoreRecord) => any);
|
|
23
25
|
}
|
|
@@ -58,3 +58,10 @@ export interface FilterValueSource {
|
|
|
58
58
|
isFilterValueSource: true;
|
|
59
59
|
}
|
|
60
60
|
export declare function isFilterValueSource(v: unknown): v is FilterValueSource;
|
|
61
|
+
/**
|
|
62
|
+
* Option to customize matching behavior for {@link StoreFilterField} and related components.
|
|
63
|
+
* - `start`: match beginning of candidate strings only.
|
|
64
|
+
* - `startWord`: match beginning of words within candidate strings.
|
|
65
|
+
* - `any`: match anywhere within candidate strings.
|
|
66
|
+
*/
|
|
67
|
+
export type FilterMatchMode = 'start' | 'startWord' | 'any';
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import { GridModel } from '@xh/hoist/cmp/grid';
|
|
1
|
+
import type { GridModel } from '@xh/hoist/cmp/grid';
|
|
2
2
|
import { LayoutProps } from '@xh/hoist/core';
|
|
3
|
+
import type { FilterMatchMode } from '@xh/hoist/data';
|
|
3
4
|
import { TextInputProps } from '@xh/hoist/desktop/cmp/input';
|
|
4
5
|
import '@xh/hoist/desktop/register';
|
|
5
6
|
import './GridFindField.scss';
|
|
@@ -10,7 +11,7 @@ export interface GridFindFieldProps extends TextInputProps, LayoutProps {
|
|
|
10
11
|
*/
|
|
11
12
|
gridModel?: GridModel;
|
|
12
13
|
/** Mode to use when searching (default 'startWord'). */
|
|
13
|
-
matchMode?:
|
|
14
|
+
matchMode?: FilterMatchMode;
|
|
14
15
|
/**
|
|
15
16
|
* Delay (in ms) to buffer searching the grid after the value changes from user input.
|
|
16
17
|
* Default 200ms. Set to 0 to filter immediately on each keystroke.
|
|
@@ -1,4 +1,6 @@
|
|
|
1
|
+
import { GridModel } from '@xh/hoist/cmp/grid';
|
|
1
2
|
import { HoistModel } from '@xh/hoist/core';
|
|
3
|
+
import type { FilterMatchMode, StoreRecord } from '@xh/hoist/data';
|
|
2
4
|
import { TextInputModel } from '@xh/hoist/desktop/cmp/input';
|
|
3
5
|
/**
|
|
4
6
|
* @internal
|
|
@@ -6,20 +8,20 @@ import { TextInputModel } from '@xh/hoist/desktop/cmp/input';
|
|
|
6
8
|
export declare class GridFindFieldImplModel extends HoistModel {
|
|
7
9
|
xhImpl: boolean;
|
|
8
10
|
query: string;
|
|
9
|
-
get matchMode():
|
|
11
|
+
get matchMode(): FilterMatchMode;
|
|
10
12
|
get queryBuffer(): number;
|
|
11
13
|
get includeFields(): string[];
|
|
12
14
|
get excludeFields(): string[];
|
|
13
15
|
results: any;
|
|
14
16
|
inputRef: import("react").RefObject<TextInputModel> & import("react").RefCallback<TextInputModel>;
|
|
15
|
-
_records:
|
|
17
|
+
_records: StoreRecord[];
|
|
16
18
|
get count(): number;
|
|
17
19
|
get selectedIdx(): number;
|
|
18
20
|
get countLabel(): string;
|
|
19
21
|
get hasFocus(): boolean;
|
|
20
22
|
get hasQuery(): boolean;
|
|
21
23
|
get hasResults(): boolean;
|
|
22
|
-
get gridModel():
|
|
24
|
+
get gridModel(): GridModel;
|
|
23
25
|
constructor();
|
|
24
26
|
onLinked(): void;
|
|
25
27
|
selectPrev(): void;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import { GridModel } from '@xh/hoist/cmp/grid';
|
|
1
|
+
import { ColChooserConfig, GridModel } from '@xh/hoist/cmp/grid';
|
|
2
2
|
import { HoistModel } from '@xh/hoist/core';
|
|
3
|
+
import type { FilterMatchMode } from '@xh/hoist/data';
|
|
3
4
|
import { LeftRightChooserModel } from '@xh/hoist/desktop/cmp/leftrightchooser';
|
|
4
5
|
/**
|
|
5
6
|
* State management for the ColChooser component.
|
|
@@ -14,16 +15,10 @@ export declare class ColChooserModel extends HoistModel {
|
|
|
14
15
|
commitOnChange: boolean;
|
|
15
16
|
showRestoreDefaults: boolean;
|
|
16
17
|
autosizeOnCommit: boolean;
|
|
17
|
-
width: number;
|
|
18
|
-
height: number;
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
commitOnChange?: boolean;
|
|
22
|
-
showRestoreDefaults?: boolean;
|
|
23
|
-
autosizeOnCommit?: boolean;
|
|
24
|
-
width?: number;
|
|
25
|
-
height?: number;
|
|
26
|
-
});
|
|
18
|
+
width: string | number;
|
|
19
|
+
height: string | number;
|
|
20
|
+
filterMatchMode: FilterMatchMode;
|
|
21
|
+
constructor({ gridModel, commitOnChange, showRestoreDefaults, autosizeOnCommit, width, height, filterMatchMode }: ColChooserConfig);
|
|
27
22
|
open(): void;
|
|
28
23
|
openPopover(): void;
|
|
29
24
|
close(): void;
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import { HoistProps } from '@xh/hoist/core';
|
|
2
|
+
import type { FilterMatchMode } from '@xh/hoist/data';
|
|
2
3
|
import '@xh/hoist/desktop/register';
|
|
3
4
|
import { LeftRightChooserModel } from './LeftRightChooserModel';
|
|
4
5
|
export interface LeftRightChooserFilterProps extends HoistProps<LeftRightChooserModel> {
|
|
5
6
|
/** Names of fields in chooser on which to filter. Defaults to ['text', 'group'] */
|
|
6
7
|
fields?: string[];
|
|
7
|
-
/**
|
|
8
|
-
|
|
8
|
+
/** Mode to use when filtering (default 'startWord'). */
|
|
9
|
+
matchMode?: FilterMatchMode;
|
|
9
10
|
}
|
|
10
11
|
/**
|
|
11
12
|
* A Component that can bind to a LeftRightChooser and filter both lists
|
package/cmp/grid/GridModel.ts
CHANGED
|
@@ -146,7 +146,7 @@ export interface GridConfig {
|
|
|
146
146
|
filterModel?: GridFilterModelConfig | boolean;
|
|
147
147
|
|
|
148
148
|
/** Config with which to create a ColChooserModel, or boolean `true` to enable default.*/
|
|
149
|
-
colChooserModel?: ColChooserConfig | boolean;
|
|
149
|
+
colChooserModel?: Omit<ColChooserConfig, 'gridModel'> | boolean;
|
|
150
150
|
|
|
151
151
|
/**
|
|
152
152
|
* Function to be called when the user triggers GridModel.restoreDefaultsAsync(). This
|
package/cmp/grid/Types.ts
CHANGED
|
@@ -5,10 +5,14 @@
|
|
|
5
5
|
* Copyright © 2026 Extremely Heavy Industries Inc.
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
10
|
-
|
|
11
|
-
|
|
8
|
+
import type {HSide, PersistOptions, Some} from '@xh/hoist/core';
|
|
9
|
+
import type {
|
|
10
|
+
FilterBindTarget,
|
|
11
|
+
FilterMatchMode,
|
|
12
|
+
FilterValueSource,
|
|
13
|
+
Store,
|
|
14
|
+
StoreRecord
|
|
15
|
+
} from '@xh/hoist/data';
|
|
12
16
|
import type {
|
|
13
17
|
CellClassParams,
|
|
14
18
|
CustomCellEditorProps,
|
|
@@ -23,6 +27,7 @@ import type {
|
|
|
23
27
|
import type {ReactElement, ReactNode} from 'react';
|
|
24
28
|
import type {Column, ColumnSpec} from './columns/Column';
|
|
25
29
|
import type {ColumnGroup, ColumnGroupSpec} from './columns/ColumnGroup';
|
|
30
|
+
import type {GridFilterFieldSpecConfig} from './filter/GridFilterFieldSpec';
|
|
26
31
|
import type {GridModel} from './GridModel';
|
|
27
32
|
|
|
28
33
|
export interface ColumnState {
|
|
@@ -124,6 +129,9 @@ export interface GridFilterBindTarget extends FilterBindTarget, FilterValueSourc
|
|
|
124
129
|
export type GroupRowRenderer = (context: ICellRendererParams) => ReactNode;
|
|
125
130
|
|
|
126
131
|
export interface ColChooserConfig {
|
|
132
|
+
/** GridModel to bind to. Not required if creating via `GridModel.colChooserModel` */
|
|
133
|
+
gridModel?: GridModel;
|
|
134
|
+
|
|
127
135
|
/**
|
|
128
136
|
* Immediately render changed columns on grid (default true).
|
|
129
137
|
* Set to false to enable Save button for committing changes on save. Desktop only.
|
|
@@ -147,6 +155,9 @@ export interface ColChooserConfig {
|
|
|
147
155
|
|
|
148
156
|
/** Chooser height for popover and dialog. Desktop only. */
|
|
149
157
|
height?: string | number;
|
|
158
|
+
|
|
159
|
+
/** Mode to use when filtering (default 'startWord'). Desktop only. */
|
|
160
|
+
filterMatchMode?: FilterMatchMode;
|
|
150
161
|
}
|
|
151
162
|
|
|
152
163
|
export type ColumnOrGroup = Column | ColumnGroup;
|
|
@@ -4,9 +4,9 @@
|
|
|
4
4
|
*
|
|
5
5
|
* Copyright © 2026 Extremely Heavy Industries Inc.
|
|
6
6
|
*/
|
|
7
|
-
import {GridModel} from '@xh/hoist/cmp/grid';
|
|
7
|
+
import type {GridModel} from '@xh/hoist/cmp/grid';
|
|
8
8
|
import {DefaultHoistProps, hoistCmp, HoistModel, useLocalModel, XH} from '@xh/hoist/core';
|
|
9
|
-
import {FilterTestFn, Store} from '@xh/hoist/data';
|
|
9
|
+
import type {FilterMatchMode, FilterTestFn, Store} from '@xh/hoist/data';
|
|
10
10
|
import {storeFilterFieldImpl as desktopStoreFilterFieldImpl} from '@xh/hoist/dynamics/desktop';
|
|
11
11
|
import {storeFilterFieldImpl as mobileStoreFilterFieldImpl} from '@xh/hoist/dynamics/mobile';
|
|
12
12
|
import {StoreFilterFieldImplModel} from './impl/StoreFilterFieldImplModel';
|
|
@@ -53,7 +53,7 @@ export interface StoreFilterFieldProps extends DefaultHoistProps {
|
|
|
53
53
|
includeFields?: string[];
|
|
54
54
|
|
|
55
55
|
/** Mode to use when filtering (default 'startWord'). */
|
|
56
|
-
matchMode?:
|
|
56
|
+
matchMode?: FilterMatchMode;
|
|
57
57
|
|
|
58
58
|
/** Optional model for raw value binding - see comments on the `bind` prop for details. */
|
|
59
59
|
model?: HoistModel;
|
|
@@ -4,10 +4,11 @@
|
|
|
4
4
|
*
|
|
5
5
|
* Copyright © 2026 Extremely Heavy Industries Inc.
|
|
6
6
|
*/
|
|
7
|
-
import {HoistModel, XH, lookup} from '@xh/hoist/core';
|
|
8
7
|
import {GridModel} from '@xh/hoist/cmp/grid';
|
|
8
|
+
import {HoistModel, lookup, XH} from '@xh/hoist/core';
|
|
9
|
+
import type {FilterMatchMode, StoreRecord} from '@xh/hoist/data';
|
|
9
10
|
import {CompoundFilter, FilterLike, Store, withFilterByKey} from '@xh/hoist/data';
|
|
10
|
-
import {action,
|
|
11
|
+
import {action, comparer, makeObservable} from '@xh/hoist/mobx';
|
|
11
12
|
import {stripTags, throwIf, warnIf, withDefault} from '@xh/hoist/utils/js';
|
|
12
13
|
import {
|
|
13
14
|
debounce,
|
|
@@ -33,8 +34,12 @@ export class StoreFilterFieldImplModel extends HoistModel {
|
|
|
33
34
|
gridModel: GridModel;
|
|
34
35
|
store: Store;
|
|
35
36
|
|
|
36
|
-
filter;
|
|
37
|
-
bufferedApplyFilter;
|
|
37
|
+
private filter: (rec: StoreRecord) => boolean;
|
|
38
|
+
private bufferedApplyFilter;
|
|
39
|
+
|
|
40
|
+
get matchMode(): FilterMatchMode {
|
|
41
|
+
return this.componentProps.matchMode ?? 'startWord';
|
|
42
|
+
}
|
|
38
43
|
|
|
39
44
|
constructor() {
|
|
40
45
|
super();
|
|
@@ -59,29 +64,30 @@ export class StoreFilterFieldImplModel extends HoistModel {
|
|
|
59
64
|
|
|
60
65
|
this.bufferedApplyFilter = debounce(() => this.applyFilter(), filterBuffer);
|
|
61
66
|
|
|
62
|
-
this.addReaction(
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
67
|
+
this.addReaction(
|
|
68
|
+
{
|
|
69
|
+
track: () => [this.filterText, gridModel?.columns, gridModel?.groupBy],
|
|
70
|
+
run: () => this.regenerateFilter(),
|
|
71
|
+
fireImmediately: true
|
|
72
|
+
},
|
|
73
|
+
{
|
|
74
|
+
track: () => [this.componentProps.includeFields, this.componentProps.excludeFields],
|
|
75
|
+
run: () => this.regenerateFilter(),
|
|
76
|
+
equals: comparer.structural
|
|
77
|
+
}
|
|
78
|
+
);
|
|
73
79
|
}
|
|
74
80
|
|
|
75
81
|
//------------------------------------------------------------------
|
|
76
82
|
// Trampoline value to bindable -- from bound model, or store
|
|
77
83
|
//------------------------------------------------------------------
|
|
78
|
-
get filterText() {
|
|
84
|
+
get filterText(): string {
|
|
79
85
|
const {bind, model} = this.componentProps;
|
|
80
86
|
return bind ? model[bind] : this.store.xhFilterText;
|
|
81
87
|
}
|
|
82
88
|
|
|
83
89
|
@action
|
|
84
|
-
setFilterText(v) {
|
|
90
|
+
setFilterText(v: string) {
|
|
85
91
|
const {bind, model} = this.componentProps;
|
|
86
92
|
if (bind) {
|
|
87
93
|
model.setBindable(bind, v);
|
|
@@ -122,7 +128,7 @@ export class StoreFilterFieldImplModel extends HoistModel {
|
|
|
122
128
|
if (filterText && !isEmpty(activeFields)) {
|
|
123
129
|
const regex = this.getRegex(filterText),
|
|
124
130
|
valGetters = flatMap(activeFields, fieldPath => this.getValGetters(fieldPath));
|
|
125
|
-
newFilter = rec => valGetters.some(fn => regex.test(fn(rec)));
|
|
131
|
+
newFilter = (rec: StoreRecord) => valGetters.some(fn => regex.test(fn(rec)));
|
|
126
132
|
}
|
|
127
133
|
|
|
128
134
|
if (filter === newFilter) return;
|
|
@@ -140,9 +146,9 @@ export class StoreFilterFieldImplModel extends HoistModel {
|
|
|
140
146
|
}
|
|
141
147
|
}
|
|
142
148
|
|
|
143
|
-
getRegex(searchTerm) {
|
|
149
|
+
getRegex(searchTerm: string): RegExp {
|
|
144
150
|
searchTerm = escapeRegExp(searchTerm);
|
|
145
|
-
switch (this.
|
|
151
|
+
switch (this.matchMode) {
|
|
146
152
|
case 'any':
|
|
147
153
|
return new RegExp(searchTerm, 'i');
|
|
148
154
|
case 'start':
|
|
@@ -153,11 +159,11 @@ export class StoreFilterFieldImplModel extends HoistModel {
|
|
|
153
159
|
throw XH.exception('Unknown matchMode in StoreFilterField');
|
|
154
160
|
}
|
|
155
161
|
|
|
156
|
-
getActiveFields() {
|
|
162
|
+
getActiveFields(): string[] {
|
|
157
163
|
const {gridModel, store, componentProps} = this,
|
|
158
164
|
{includeFields, excludeFields} = componentProps;
|
|
159
165
|
|
|
160
|
-
let ret = store ? ['id', ...store.
|
|
166
|
+
let ret = store ? ['id', ...store.fieldNames] : [];
|
|
161
167
|
if (includeFields) ret = store ? intersection(ret, includeFields) : includeFields;
|
|
162
168
|
if (excludeFields) ret = without(ret, ...excludeFields);
|
|
163
169
|
|
|
@@ -196,7 +202,7 @@ export class StoreFilterFieldImplModel extends HoistModel {
|
|
|
196
202
|
return ret;
|
|
197
203
|
}
|
|
198
204
|
|
|
199
|
-
getValGetters(fieldName) {
|
|
205
|
+
getValGetters(fieldName: string) {
|
|
200
206
|
const {gridModel} = this;
|
|
201
207
|
|
|
202
208
|
// If a GridModel has been configured, the user is looking at rendered values in a grid and
|
|
@@ -219,7 +225,7 @@ export class StoreFilterFieldImplModel extends HoistModel {
|
|
|
219
225
|
|
|
220
226
|
return cols.map(column => {
|
|
221
227
|
const {renderer, getValueFn} = column;
|
|
222
|
-
return record => {
|
|
228
|
+
return (record: StoreRecord) => {
|
|
223
229
|
const ctx = {
|
|
224
230
|
record,
|
|
225
231
|
field: field.name,
|
|
@@ -239,7 +245,7 @@ export class StoreFilterFieldImplModel extends HoistModel {
|
|
|
239
245
|
// Otherwise just match raw.
|
|
240
246
|
// Use expensive get() only when needed to support dot-separated paths.
|
|
241
247
|
return fieldName.includes('.')
|
|
242
|
-
? rec => get(rec.data, fieldName)
|
|
243
|
-
: rec => rec.data[fieldName];
|
|
248
|
+
? (rec: StoreRecord) => get(rec.data, fieldName)
|
|
249
|
+
: (rec: StoreRecord) => rec.data[fieldName];
|
|
244
250
|
}
|
|
245
251
|
}
|
package/data/filter/Types.ts
CHANGED
|
@@ -97,3 +97,11 @@ export interface FilterValueSource {
|
|
|
97
97
|
export function isFilterValueSource(v: unknown): v is FilterValueSource {
|
|
98
98
|
return (v as any)?.isFilterValueSource === true;
|
|
99
99
|
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Option to customize matching behavior for {@link StoreFilterField} and related components.
|
|
103
|
+
* - `start`: match beginning of candidate strings only.
|
|
104
|
+
* - `startWord`: match beginning of words within candidate strings.
|
|
105
|
+
* - `any`: match anywhere within candidate strings.
|
|
106
|
+
*/
|
|
107
|
+
export type FilterMatchMode = 'start' | 'startWord' | 'any';
|
|
@@ -5,9 +5,10 @@
|
|
|
5
5
|
* Copyright © 2026 Extremely Heavy Industries Inc.
|
|
6
6
|
*/
|
|
7
7
|
import composeRefs from '@seznam/compose-react-refs/composeRefs';
|
|
8
|
-
import {GridModel} from '@xh/hoist/cmp/grid';
|
|
8
|
+
import type {GridModel} from '@xh/hoist/cmp/grid';
|
|
9
9
|
import {hbox, span, vbox} from '@xh/hoist/cmp/layout';
|
|
10
10
|
import {hoistCmp, LayoutProps, useLocalModel} from '@xh/hoist/core';
|
|
11
|
+
import type {FilterMatchMode} from '@xh/hoist/data';
|
|
11
12
|
import {button} from '@xh/hoist/desktop/cmp/button';
|
|
12
13
|
import {textInput, TextInputProps} from '@xh/hoist/desktop/cmp/input';
|
|
13
14
|
import '@xh/hoist/desktop/register';
|
|
@@ -25,7 +26,7 @@ export interface GridFindFieldProps extends TextInputProps, LayoutProps {
|
|
|
25
26
|
gridModel?: GridModel;
|
|
26
27
|
|
|
27
28
|
/** Mode to use when searching (default 'startWord'). */
|
|
28
|
-
matchMode?:
|
|
29
|
+
matchMode?: FilterMatchMode;
|
|
29
30
|
|
|
30
31
|
/**
|
|
31
32
|
* Delay (in ms) to buffer searching the grid after the value changes from user input.
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
*/
|
|
7
7
|
import {GridModel} from '@xh/hoist/cmp/grid';
|
|
8
8
|
import {HoistModel, XH} from '@xh/hoist/core';
|
|
9
|
+
import type {FilterMatchMode, StoreRecord} from '@xh/hoist/data';
|
|
9
10
|
import {TextInputModel} from '@xh/hoist/desktop/cmp/input';
|
|
10
11
|
import {action, bindable, comparer, computed, makeObservable, observable} from '@xh/hoist/mobx';
|
|
11
12
|
import {stripTags, withDefault} from '@xh/hoist/utils/js';
|
|
@@ -32,22 +33,25 @@ export class GridFindFieldImplModel extends HoistModel {
|
|
|
32
33
|
@bindable
|
|
33
34
|
query: string = null;
|
|
34
35
|
|
|
35
|
-
get matchMode():
|
|
36
|
+
get matchMode(): FilterMatchMode {
|
|
36
37
|
return this.componentProps.matchMode ?? 'startWord';
|
|
37
38
|
}
|
|
39
|
+
|
|
38
40
|
get queryBuffer(): number {
|
|
39
41
|
return this.componentProps.queryBuffer ?? 200;
|
|
40
42
|
}
|
|
43
|
+
|
|
41
44
|
get includeFields(): string[] {
|
|
42
45
|
return this.componentProps.includeFields;
|
|
43
46
|
}
|
|
47
|
+
|
|
44
48
|
get excludeFields(): string[] {
|
|
45
49
|
return this.componentProps.excludeFields;
|
|
46
50
|
}
|
|
47
51
|
|
|
48
52
|
@observable.ref results;
|
|
49
53
|
inputRef = createObservableRef<TextInputModel>();
|
|
50
|
-
_records = null;
|
|
54
|
+
_records: StoreRecord[] = null;
|
|
51
55
|
|
|
52
56
|
get count(): number {
|
|
53
57
|
return this.results?.length;
|
|
@@ -81,7 +85,7 @@ export class GridFindFieldImplModel extends HoistModel {
|
|
|
81
85
|
}
|
|
82
86
|
|
|
83
87
|
@computed
|
|
84
|
-
get gridModel() {
|
|
88
|
+
get gridModel(): GridModel {
|
|
85
89
|
const ret = withDefault(this.componentProps.gridModel, this.lookupModel(GridModel));
|
|
86
90
|
if (!ret) {
|
|
87
91
|
this.logError("No GridModel available. Provide via a 'gridModel' prop, or context.");
|
|
@@ -100,30 +104,30 @@ export class GridFindFieldImplModel extends HoistModel {
|
|
|
100
104
|
}
|
|
101
105
|
|
|
102
106
|
override onLinked() {
|
|
103
|
-
this.addReaction(
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
107
|
+
this.addReaction(
|
|
108
|
+
{
|
|
109
|
+
track: () => this.query,
|
|
110
|
+
run: () => this.updateResults(true),
|
|
111
|
+
debounce: this.queryBuffer
|
|
112
|
+
},
|
|
113
|
+
{
|
|
114
|
+
track: () => [
|
|
115
|
+
this.gridModel?.store.records,
|
|
116
|
+
this.gridModel?.columns,
|
|
117
|
+
this.gridModel?.sortBy,
|
|
118
|
+
this.gridModel?.groupBy
|
|
119
|
+
],
|
|
120
|
+
run: () => {
|
|
121
|
+
this._records = null;
|
|
122
|
+
if (this.hasQuery) this.updateResults();
|
|
123
|
+
}
|
|
124
|
+
},
|
|
125
|
+
{
|
|
126
|
+
track: () => [this.includeFields, this.excludeFields, this.matchMode],
|
|
127
|
+
run: () => this.updateResults(),
|
|
128
|
+
equals: comparer.structural
|
|
119
129
|
}
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
this.addReaction({
|
|
123
|
-
track: () => [this.includeFields, this.excludeFields],
|
|
124
|
-
run: () => this.updateResults(),
|
|
125
|
-
equals: comparer.structural
|
|
126
|
-
});
|
|
130
|
+
);
|
|
127
131
|
}
|
|
128
132
|
|
|
129
133
|
selectPrev() {
|
|
@@ -180,7 +184,7 @@ export class GridFindFieldImplModel extends HoistModel {
|
|
|
180
184
|
}
|
|
181
185
|
}
|
|
182
186
|
|
|
183
|
-
private getRecords() {
|
|
187
|
+
private getRecords(): StoreRecord[] {
|
|
184
188
|
if (!this._records) {
|
|
185
189
|
const records = this.sortRecordsRecursive([...this.gridModel.store.rootRecords]);
|
|
186
190
|
this._records = this.sortRecordsByGroupBy(records);
|
|
@@ -189,10 +193,10 @@ export class GridFindFieldImplModel extends HoistModel {
|
|
|
189
193
|
}
|
|
190
194
|
|
|
191
195
|
// Sort records with GridModel's sortBy(s) using the Column's comparator
|
|
192
|
-
private sortRecordsRecursive(records) {
|
|
196
|
+
private sortRecordsRecursive(records: StoreRecord[]): StoreRecord[] {
|
|
193
197
|
const {gridModel} = this,
|
|
194
198
|
{sortBy, treeMode, agApi, store} = gridModel,
|
|
195
|
-
ret = [];
|
|
199
|
+
ret: StoreRecord[] = [];
|
|
196
200
|
|
|
197
201
|
[...sortBy].reverse().forEach(it => {
|
|
198
202
|
const column = gridModel.getColumn(it.colId);
|
|
@@ -225,7 +229,7 @@ export class GridFindFieldImplModel extends HoistModel {
|
|
|
225
229
|
}
|
|
226
230
|
|
|
227
231
|
// Sort records with GridModel's groupBy(s) using the GridModel's groupSortFn
|
|
228
|
-
private sortRecordsByGroupBy(records) {
|
|
232
|
+
private sortRecordsByGroupBy(records: StoreRecord[]) {
|
|
229
233
|
const {gridModel} = this,
|
|
230
234
|
{agApi, groupBy, groupSortFn, store} = gridModel;
|
|
231
235
|
|
|
@@ -249,7 +253,7 @@ export class GridFindFieldImplModel extends HoistModel {
|
|
|
249
253
|
return records;
|
|
250
254
|
}
|
|
251
255
|
|
|
252
|
-
private getRegex(searchTerm) {
|
|
256
|
+
private getRegex(searchTerm: string): RegExp {
|
|
253
257
|
searchTerm = escapeRegExp(searchTerm);
|
|
254
258
|
switch (this.matchMode) {
|
|
255
259
|
case 'any':
|
|
@@ -262,12 +266,12 @@ export class GridFindFieldImplModel extends HoistModel {
|
|
|
262
266
|
throw XH.exception('Unknown matchMode in GridFindField');
|
|
263
267
|
}
|
|
264
268
|
|
|
265
|
-
private getActiveFields() {
|
|
269
|
+
private getActiveFields(): string[] {
|
|
266
270
|
const {gridModel, includeFields, excludeFields} = this,
|
|
267
271
|
groupBy = gridModel.groupBy,
|
|
268
272
|
visibleCols = gridModel.getVisibleLeafColumns();
|
|
269
273
|
|
|
270
|
-
let ret = ['id', ...gridModel.store.
|
|
274
|
+
let ret = ['id', ...gridModel.store.fieldNames];
|
|
271
275
|
if (includeFields) ret = intersection(ret, includeFields);
|
|
272
276
|
if (excludeFields) ret = without(ret, ...excludeFields);
|
|
273
277
|
|
|
@@ -301,7 +305,7 @@ export class GridFindFieldImplModel extends HoistModel {
|
|
|
301
305
|
return ret;
|
|
302
306
|
}
|
|
303
307
|
|
|
304
|
-
private getValGetters(fieldName) {
|
|
308
|
+
private getValGetters(fieldName: string) {
|
|
305
309
|
const {gridModel} = this,
|
|
306
310
|
{store} = gridModel,
|
|
307
311
|
field = store.getField(fieldName);
|
|
@@ -313,7 +317,7 @@ export class GridFindFieldImplModel extends HoistModel {
|
|
|
313
317
|
|
|
314
318
|
return cols.map(column => {
|
|
315
319
|
const {renderer, getValueFn} = column;
|
|
316
|
-
return record => {
|
|
320
|
+
return (record: StoreRecord) => {
|
|
317
321
|
const ctx = {
|
|
318
322
|
record,
|
|
319
323
|
field: fieldName,
|
|
@@ -332,7 +336,7 @@ export class GridFindFieldImplModel extends HoistModel {
|
|
|
332
336
|
// Otherwise just match raw.
|
|
333
337
|
// Use expensive get() only when needed to support dot-separated paths.
|
|
334
338
|
return fieldName.includes('.')
|
|
335
|
-
? rec => get(rec.data, fieldName)
|
|
336
|
-
: rec => rec.data[fieldName];
|
|
339
|
+
? (rec: StoreRecord) => get(rec.data, fieldName)
|
|
340
|
+
: (rec: StoreRecord) => rec.data[fieldName];
|
|
337
341
|
}
|
|
338
342
|
}
|