@azrico/reactgrid 1.5.1 → 1.5.2
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/README.md +1 -1
- package/dist/src/func/func_a11y.d.ts +5 -0
- package/dist/src/func/func_a11y.js +10 -1
- package/dist/src/func/func_child.js +15 -7
- package/dist/src/hooks/useColumnData.d.ts +1 -1
- package/dist/src/hooks/useColumnData.js +8 -12
- package/dist/src/hooks/useGridEvents.d.ts +3 -0
- package/dist/src/hooks/useGridEvents.js +21 -9
- package/dist/src/hooks/useGridSearch.d.ts +3 -3
- package/dist/src/hooks/useGridSearch.js +68 -42
- package/dist/src/views/DataGrid.js +35 -16
- package/package.json +1 -1
- package/src/func/func_a11y.ts +10 -0
- package/src/func/func_child.ts +92 -83
- package/src/hooks/useColumnData.ts +28 -34
- package/src/hooks/useGridEvents.ts +40 -24
- package/src/hooks/useGridSearch.tsx +106 -79
- package/src/views/DataGrid.tsx +362 -318
package/src/func/func_child.ts
CHANGED
|
@@ -1,83 +1,92 @@
|
|
|
1
|
-
type mrow = { __parent__table_id: any; __table_id: any; __children?: any[] };
|
|
2
|
-
type converted_row = { parent: any; self: any; data: mrow };
|
|
3
|
-
|
|
4
|
-
function getParentId(obj) {
|
|
5
|
-
return obj.__parent__table_id ?? obj.parentid ?? obj.parent;
|
|
6
|
-
}
|
|
7
|
-
function getSelfId(obj) {
|
|
8
|
-
return obj.__table_id ?? obj._id;
|
|
9
|
-
}
|
|
10
|
-
//given an array of rows, fixes parent,child relations
|
|
11
|
-
export function fix_parent_child_relations<T extends mrow>(rows: T[]): T[] {
|
|
12
|
-
if (!Array.isArray(rows)) rows = [];
|
|
13
|
-
const anyparentid = rows.find((r) => getParentId(r));
|
|
14
|
-
const anyselfid = rows.find((r) => getSelfId(r));
|
|
15
|
-
if (!anyparentid || !anyselfid) return rows;
|
|
16
|
-
|
|
17
|
-
const converted_rows: converted_row[] = rows
|
|
18
|
-
.filter((r) => r != null)
|
|
19
|
-
.map((r) => {
|
|
20
|
-
return { parent: getParentId(r), self: getSelfId(r), data: r };
|
|
21
|
-
});
|
|
22
|
-
|
|
23
|
-
const top_level_rows: converted_row[] = converted_rows.filter((r) => !r.parent);
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
)
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
1
|
+
type mrow = { __parent__table_id: any; __table_id: any; __children?: any[] };
|
|
2
|
+
type converted_row = { parent: any; self: any; data: mrow };
|
|
3
|
+
|
|
4
|
+
function getParentId(obj) {
|
|
5
|
+
return obj.__parent__table_id ?? obj.parentid ?? obj.parent;
|
|
6
|
+
}
|
|
7
|
+
function getSelfId(obj) {
|
|
8
|
+
return obj.__table_id ?? obj._id;
|
|
9
|
+
}
|
|
10
|
+
//given an array of rows, fixes parent,child relations
|
|
11
|
+
export function fix_parent_child_relations<T extends mrow>(rows: T[]): T[] {
|
|
12
|
+
if (!Array.isArray(rows)) rows = [];
|
|
13
|
+
const anyparentid = rows.find((r) => getParentId(r));
|
|
14
|
+
const anyselfid = rows.find((r) => getSelfId(r));
|
|
15
|
+
if (!anyparentid || !anyselfid) return rows;
|
|
16
|
+
|
|
17
|
+
const converted_rows: converted_row[] = rows
|
|
18
|
+
.filter((r) => r != null)
|
|
19
|
+
.map((r) => {
|
|
20
|
+
return { parent: getParentId(r), self: getSelfId(r), data: r };
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
const top_level_rows: converted_row[] = converted_rows.filter((r) => !r.parent);
|
|
24
|
+
//group rows by parent id once, instead of scanning all rows for every parent
|
|
25
|
+
const children_by_parent = new Map<string, converted_row[]>();
|
|
26
|
+
for (let index = 0; index < converted_rows.length; index++) {
|
|
27
|
+
const r = converted_rows[index];
|
|
28
|
+
if (!r.parent) continue;
|
|
29
|
+
const key = String(r.parent);
|
|
30
|
+
const siblings = children_by_parent.get(key);
|
|
31
|
+
if (siblings) siblings.push(r);
|
|
32
|
+
else children_by_parent.set(key, [r]);
|
|
33
|
+
}
|
|
34
|
+
if (children_by_parent.size > 0) {
|
|
35
|
+
//find children of top-level parents:
|
|
36
|
+
for (let index = 0; index < top_level_rows.length; index++) {
|
|
37
|
+
const mrow = top_level_rows[index];
|
|
38
|
+
assign_children(mrow, children_by_parent);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
return top_level_rows.map((r) => r.data) as T[];
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function assign_children(row: converted_row, children_by_parent: Map<string, converted_row[]>) {
|
|
45
|
+
const my_children = row.self == null ? undefined : children_by_parent.get(String(row.self));
|
|
46
|
+
if (my_children && my_children.length > 0) {
|
|
47
|
+
//find children of my children:
|
|
48
|
+
for (let index = 0; index < my_children.length; index++) {
|
|
49
|
+
const child = my_children[index];
|
|
50
|
+
assign_children(child, children_by_parent);
|
|
51
|
+
}
|
|
52
|
+
row.data.__children = my_children.map((r) => r.data);
|
|
53
|
+
} else if (!Array.isArray(row.data.__children) || row.data.__children.length == 0)
|
|
54
|
+
row.data.__children = undefined;
|
|
55
|
+
}
|
|
56
|
+
export class func_selection {
|
|
57
|
+
static getVirtualElement(x = 0, y = 0) {
|
|
58
|
+
const boundsRect = func_selection.generateGetBoundingClientRect(x, y);
|
|
59
|
+
return {
|
|
60
|
+
getBoundingClientRect: boundsRect,
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
static generateGetBoundingClientRect(x = 0, y = 0) {
|
|
64
|
+
return () => ({
|
|
65
|
+
width: 0,
|
|
66
|
+
height: 0,
|
|
67
|
+
top: y,
|
|
68
|
+
right: x,
|
|
69
|
+
bottom: y,
|
|
70
|
+
left: x,
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
static get_selectedText(): string {
|
|
74
|
+
var text: any = '';
|
|
75
|
+
if (window.getSelection) {
|
|
76
|
+
text = window.getSelection()?.toString();
|
|
77
|
+
} else if (
|
|
78
|
+
(document as any).selection &&
|
|
79
|
+
(document as any).selection.type != 'Control'
|
|
80
|
+
) {
|
|
81
|
+
text = (document as any).selection.createRange()?.text;
|
|
82
|
+
}
|
|
83
|
+
return text || '';
|
|
84
|
+
}
|
|
85
|
+
static has_selection(): boolean {
|
|
86
|
+
const slct = window.getSelection();
|
|
87
|
+
if (slct == null) return false;
|
|
88
|
+
if (slct.type == null) return func_selection.get_selectedText().length !== 0;
|
|
89
|
+
if (slct.type === 'Range') return true;
|
|
90
|
+
return false;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
@@ -1,34 +1,28 @@
|
|
|
1
|
-
import React from 'react';
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
return React.useMemo(() => {
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
props.rightColumns,
|
|
30
|
-
props.editableColumns,
|
|
31
|
-
props.columnExtensions,
|
|
32
|
-
props.customColumns,
|
|
33
|
-
]);
|
|
34
|
-
}
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { AzGridProps, LooseColumnDefinition } from '../types/types';
|
|
3
|
+
import { applyColumnData } from '../func/func_grid';
|
|
4
|
+
|
|
5
|
+
const EMPTY_COLUMNS: any[] = [];
|
|
6
|
+
|
|
7
|
+
export function useColumnData(
|
|
8
|
+
inputOrProps: undefined | any[] | LooseColumnDefinition[] | AzGridProps
|
|
9
|
+
) {
|
|
10
|
+
// one useMemo for every input kind, so switching between them keeps the hook order
|
|
11
|
+
const isProps = inputOrProps != null && !Array.isArray(inputOrProps);
|
|
12
|
+
const props = (isProps ? inputOrProps : {}) as AzGridProps;
|
|
13
|
+
const deps = isProps
|
|
14
|
+
? [
|
|
15
|
+
props.columns,
|
|
16
|
+
props.leftColumns,
|
|
17
|
+
props.rightColumns,
|
|
18
|
+
props.editableColumns,
|
|
19
|
+
props.columnExtensions,
|
|
20
|
+
props.customColumns,
|
|
21
|
+
]
|
|
22
|
+
: [inputOrProps, null, null, null, null, null];
|
|
23
|
+
|
|
24
|
+
return React.useMemo(() => {
|
|
25
|
+
if (inputOrProps == null) return EMPTY_COLUMNS;
|
|
26
|
+
return applyColumnData(inputOrProps);
|
|
27
|
+
}, [isProps, ...deps]);
|
|
28
|
+
}
|
|
@@ -7,6 +7,7 @@ import {
|
|
|
7
7
|
apply_row_a11y,
|
|
8
8
|
get_grid_root,
|
|
9
9
|
get_root_a11y_key,
|
|
10
|
+
is_grid_busy,
|
|
10
11
|
} from '../func/func_a11y';
|
|
11
12
|
|
|
12
13
|
function useGridMenuRow() {
|
|
@@ -20,6 +21,9 @@ function useGridMenuRow() {
|
|
|
20
21
|
}
|
|
21
22
|
export function useGridEvents(grid: GridType, props: DataGridWrapperProps) {
|
|
22
23
|
const click_data = React.useRef<any>(null);
|
|
24
|
+
// latest props, so the callbacks below stay stable and don't re-render the grid
|
|
25
|
+
const latestProps = React.useRef(props);
|
|
26
|
+
latestProps.current = props;
|
|
23
27
|
const { update_menuRow, menuAnchorEl, menuRow } = useGridMenuRow();
|
|
24
28
|
|
|
25
29
|
const refreshColumn = React.useCallback(
|
|
@@ -46,26 +50,21 @@ export function useGridEvents(grid: GridType, props: DataGridWrapperProps) {
|
|
|
46
50
|
/* -------------------------------------------------------------------------- */
|
|
47
51
|
/* events */
|
|
48
52
|
/* -------------------------------------------------------------------------- */
|
|
49
|
-
const onRowClick = React.useCallback(
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
props.onRowDoubleClick &&
|
|
59
|
-
props.onRowDoubleClick(rowProps.rowIndex, rowProps.data)
|
|
60
|
-
);
|
|
61
|
-
}
|
|
53
|
+
const onRowClick = React.useCallback((rowProps, event) => {
|
|
54
|
+
const { onRowClick, onRowDoubleClick } = latestProps.current;
|
|
55
|
+
const nowDate = Date.now();
|
|
56
|
+
const lastclick: number = click_data.current[get_row_id(rowProps)];
|
|
57
|
+
if (lastclick) {
|
|
58
|
+
const diffTime = Math.abs(nowDate - lastclick);
|
|
59
|
+
if (diffTime < 400) {
|
|
60
|
+
click_data.current[get_row_id(rowProps)] = null;
|
|
61
|
+
return onRowDoubleClick && onRowDoubleClick(rowProps.rowIndex, rowProps.data);
|
|
62
62
|
}
|
|
63
|
+
}
|
|
63
64
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
[props.onRowClick, props.onRowDoubleClick]
|
|
68
|
-
);
|
|
65
|
+
click_data.current[get_row_id(rowProps)] = nowDate;
|
|
66
|
+
return onRowClick && onRowClick(rowProps.rowIndex, rowProps.data);
|
|
67
|
+
}, []);
|
|
69
68
|
|
|
70
69
|
React.useEffect(() => {
|
|
71
70
|
click_data.current = {};
|
|
@@ -87,28 +86,39 @@ export function useGridEvents(grid: GridType, props: DataGridWrapperProps) {
|
|
|
87
86
|
|
|
88
87
|
/* -------------------------------------------------------------------------- */
|
|
89
88
|
/* ------------------------------ Accessibility ----------------------------- */
|
|
90
|
-
const latestProps = React.useRef(props);
|
|
91
|
-
latestProps.current = props;
|
|
92
89
|
const rootSyncFrame = React.useRef(0);
|
|
93
90
|
// last applied root values, so row renders (e.g. while scrolling) don't re-apply them
|
|
94
|
-
const lastRoot = React.useRef<{ el: Element | null; key: string }>({
|
|
91
|
+
const lastRoot = React.useRef<{ el: Element | null; key: string; busy: boolean }>({
|
|
92
|
+
el: null,
|
|
93
|
+
key: '',
|
|
94
|
+
busy: false,
|
|
95
|
+
});
|
|
95
96
|
const getRootOptions = () => ({
|
|
96
97
|
ariaLabel: latestProps.current.ariaLabel,
|
|
97
98
|
editable: !!latestProps.current.editable,
|
|
98
99
|
});
|
|
99
100
|
// root attributes are set on the DOM node, batched to once per frame
|
|
100
|
-
const scheduleRootSync = React.useCallback(() => {
|
|
101
|
+
const scheduleRootSync = React.useCallback((force = false) => {
|
|
101
102
|
if (rootSyncFrame.current || typeof requestAnimationFrame === 'undefined') return;
|
|
102
103
|
const grid = latestProps.current.liveGridRef?.current?.current;
|
|
103
104
|
const key = get_root_a11y_key(grid, getRootOptions());
|
|
104
|
-
if (
|
|
105
|
+
if (
|
|
106
|
+
!force &&
|
|
107
|
+
key &&
|
|
108
|
+
key === lastRoot.current.key &&
|
|
109
|
+
get_grid_root(grid) === lastRoot.current.el
|
|
110
|
+
)
|
|
105
111
|
return;
|
|
106
112
|
rootSyncFrame.current = requestAnimationFrame(() => {
|
|
107
113
|
rootSyncFrame.current = 0;
|
|
108
114
|
const liveGrid = latestProps.current.liveGridRef?.current?.current;
|
|
109
115
|
const options = getRootOptions();
|
|
110
116
|
const el = apply_root_a11y(liveGrid, options);
|
|
111
|
-
lastRoot.current = {
|
|
117
|
+
lastRoot.current = {
|
|
118
|
+
el,
|
|
119
|
+
key: el ? get_root_a11y_key(liveGrid, options) : '',
|
|
120
|
+
busy: el ? is_grid_busy(liveGrid) : false,
|
|
121
|
+
};
|
|
112
122
|
});
|
|
113
123
|
}, []);
|
|
114
124
|
React.useEffect(() => {
|
|
@@ -130,8 +140,14 @@ export function useGridEvents(grid: GridType, props: DataGridWrapperProps) {
|
|
|
130
140
|
scheduleRootSync();
|
|
131
141
|
}, []);
|
|
132
142
|
|
|
143
|
+
// called by the grid on every render with the load mask state, keeps aria-busy in sync
|
|
144
|
+
const onRenderLoadMask = React.useCallback((maskProps: { visible?: boolean }) => {
|
|
145
|
+
if (!!maskProps?.visible !== lastRoot.current.busy) scheduleRootSync(true);
|
|
146
|
+
}, []);
|
|
147
|
+
|
|
133
148
|
return {
|
|
134
149
|
onRowClick,
|
|
150
|
+
onRenderLoadMask,
|
|
135
151
|
onRowContextMenu,
|
|
136
152
|
onRenderRow,
|
|
137
153
|
update_menuRow,
|
|
@@ -1,79 +1,106 @@
|
|
|
1
|
-
import React from 'react';
|
|
2
|
-
import {
|
|
3
|
-
import debounce from 'lodash.debounce';
|
|
4
|
-
import { array_intersection, fn_eval } from '@azrico/object';
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
)
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
);
|
|
78
|
-
|
|
79
|
-
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { DataGridWrapperProps } from '../types/types';
|
|
3
|
+
import debounce from 'lodash.debounce';
|
|
4
|
+
import { array_intersection, fn_eval } from '@azrico/object';
|
|
5
|
+
|
|
6
|
+
const SEARCH_DELAY = 300;
|
|
7
|
+
|
|
8
|
+
function to_rows(result: any): any[] {
|
|
9
|
+
return result && typeof result === 'object' ? result.data || result : result;
|
|
10
|
+
}
|
|
11
|
+
//false when the result is only a page of the data (remote pagination)
|
|
12
|
+
function is_complete(result: any) {
|
|
13
|
+
if (!result || typeof result !== 'object' || Array.isArray(result)) return true;
|
|
14
|
+
const loaded = Array.isArray(result.data) ? result.data.length : 0;
|
|
15
|
+
return !(result.count > loaded);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export function useGridSearch(props: DataGridWrapperProps) {
|
|
19
|
+
const [search, set_search] = React.useState('');
|
|
20
|
+
// null shows the datasource itself, an array shows search results
|
|
21
|
+
const [searchResult, set_searchResult] = React.useState<any[] | null>(null);
|
|
22
|
+
const latestProps = React.useRef(props);
|
|
23
|
+
latestProps.current = props;
|
|
24
|
+
// last result the grid loaded, reused by search instead of loading the data again
|
|
25
|
+
const loaded = React.useRef<{ ds: any; result: any } | null>(null);
|
|
26
|
+
const searchId = React.useRef(0);
|
|
27
|
+
|
|
28
|
+
const trackedDataSource = React.useCallback(
|
|
29
|
+
async (args: any) => {
|
|
30
|
+
const result = await fn_eval(props.dataSource, args);
|
|
31
|
+
loaded.current = { ds: props.dataSource, result };
|
|
32
|
+
return result;
|
|
33
|
+
},
|
|
34
|
+
[props.dataSource]
|
|
35
|
+
);
|
|
36
|
+
|
|
37
|
+
const get_search_rows = React.useCallback(async () => {
|
|
38
|
+
const ds = latestProps.current.dataSource;
|
|
39
|
+
const last = loaded.current;
|
|
40
|
+
if (last && last.ds === ds && is_complete(last.result)) return to_rows(last.result);
|
|
41
|
+
return to_rows(await fn_eval(ds));
|
|
42
|
+
}, []);
|
|
43
|
+
|
|
44
|
+
const filter_rows = React.useCallback((rows: any[], text: string) => {
|
|
45
|
+
const { gridRef, advancedFilterFields } = latestProps.current;
|
|
46
|
+
const visibleColumns = gridRef.current?.visibleColumns;
|
|
47
|
+
const columns_to_search: string[] = visibleColumns
|
|
48
|
+
? array_intersection(
|
|
49
|
+
advancedFilterFields || [],
|
|
50
|
+
visibleColumns.map((r) => r.id)
|
|
51
|
+
)
|
|
52
|
+
: advancedFilterFields || [];
|
|
53
|
+
const lowerSearchText = text.toLowerCase().trim();
|
|
54
|
+
if (!lowerSearchText || columns_to_search.length === 0) return rows;
|
|
55
|
+
return rows.filter((p) => {
|
|
56
|
+
for (let index = 0; index < columns_to_search.length; index++) {
|
|
57
|
+
const col = columns_to_search[index];
|
|
58
|
+
const v = (p[col] + '').toLowerCase(); //value of column
|
|
59
|
+
if (v && v.indexOf(lowerSearchText) != -1) {
|
|
60
|
+
//if one column is matching then the row is a match
|
|
61
|
+
return true;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
return false;
|
|
65
|
+
});
|
|
66
|
+
}, []);
|
|
67
|
+
|
|
68
|
+
const run_search = React.useMemo(
|
|
69
|
+
() =>
|
|
70
|
+
debounce(async (text: string) => {
|
|
71
|
+
const id = ++searchId.current;
|
|
72
|
+
const rows = await get_search_rows();
|
|
73
|
+
if (id !== searchId.current || !Array.isArray(rows)) return; //a newer search started
|
|
74
|
+
set_searchResult(filter_rows(rows, text));
|
|
75
|
+
}, SEARCH_DELAY),
|
|
76
|
+
[]
|
|
77
|
+
);
|
|
78
|
+
React.useEffect(() => () => run_search.cancel(), [run_search]);
|
|
79
|
+
|
|
80
|
+
React.useEffect(() => {
|
|
81
|
+
if (search) {
|
|
82
|
+
run_search(search);
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
run_search.cancel();
|
|
86
|
+
searchId.current++;
|
|
87
|
+
set_searchResult(null);
|
|
88
|
+
}, [search, props.dataSource]);
|
|
89
|
+
|
|
90
|
+
const element = (
|
|
91
|
+
<>
|
|
92
|
+
<form className={'azgrid-searcharea'}>
|
|
93
|
+
<label>
|
|
94
|
+
Search
|
|
95
|
+
<input
|
|
96
|
+
type="text"
|
|
97
|
+
name="name"
|
|
98
|
+
value={search}
|
|
99
|
+
onChange={(e) => set_search(e.target.value)}
|
|
100
|
+
/>
|
|
101
|
+
</label>
|
|
102
|
+
</form>
|
|
103
|
+
</>
|
|
104
|
+
);
|
|
105
|
+
return { filteredData: searchResult ?? trackedDataSource, element };
|
|
106
|
+
}
|