@azrico/reactgrid 1.4.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/.claude/settings.local.json +8 -0
- package/README.md +28 -1
- package/dist/src/example.js +8 -2
- package/dist/src/func/func_a11y.d.ts +57 -0
- package/dist/src/func/func_a11y.js +148 -0
- 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 +59 -3
- package/dist/src/hooks/useGridSearch.d.ts +3 -3
- package/dist/src/hooks/useGridSearch.js +68 -42
- package/dist/src/types/types.d.ts +14 -1
- package/dist/src/views/DataGrid.js +68 -19
- package/package.json +67 -67
- package/src/example.tsx +184 -178
- package/src/func/func_a11y.ts +169 -0
- package/src/func/func_child.ts +92 -83
- package/src/hooks/useColumnData.ts +28 -34
- package/src/hooks/useGridEvents.ts +157 -96
- package/src/hooks/useGridSearch.tsx +106 -79
- package/src/types/types.ts +12 -1
- package/src/views/DataGrid.tsx +362 -286
|
@@ -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
|
+
}
|
package/src/types/types.ts
CHANGED
|
@@ -42,6 +42,8 @@ export type loose_column_fields = loose_column_names & {
|
|
|
42
42
|
title: string;
|
|
43
43
|
type: string;
|
|
44
44
|
items: any[];
|
|
45
|
+
/** accessible name for cells whose visible text isn't descriptive (icons, badges, ...), set as aria-label */
|
|
46
|
+
ariaLabel: (value: any, data: any) => string;
|
|
45
47
|
};
|
|
46
48
|
export type LooseColumnDefinition = Partial<loose_column_fields & ColumnDefinition>;
|
|
47
49
|
//
|
|
@@ -50,7 +52,12 @@ export type CustomColumnDefinition = {
|
|
|
50
52
|
name: string;
|
|
51
53
|
render: (cell: any, formatterParams, onRendered) => React.ReactNode | undefined | null;
|
|
52
54
|
};
|
|
53
|
-
export type AzGridOptions = Partial<TypeDataGridProps
|
|
55
|
+
export type AzGridOptions = Partial<TypeDataGridProps> & {
|
|
56
|
+
/** called for every rendered row, after the grid's own row attributes are set */
|
|
57
|
+
onRenderRow?: (rowProps: any) => void;
|
|
58
|
+
/** extra DOM props for the header row */
|
|
59
|
+
headerProps?: { [key: string]: any };
|
|
60
|
+
};
|
|
54
61
|
/* -------------------------------------------------------------------------- */
|
|
55
62
|
export type AzGridProps = {
|
|
56
63
|
dataSource?: any[] | ((args: any) => any[] | Promise<any[] | any>);
|
|
@@ -59,6 +66,8 @@ export type AzGridProps = {
|
|
|
59
66
|
details_filter?: FilterValues;
|
|
60
67
|
|
|
61
68
|
licenseKey?: string;
|
|
69
|
+
/** accessible name of the grid, set as aria-label on the grid root */
|
|
70
|
+
ariaLabel?: string;
|
|
62
71
|
|
|
63
72
|
gridRef?: React.MutableRefObject<TypeComputedProps>;
|
|
64
73
|
gridOptions?: AzGridOptions;
|
|
@@ -117,6 +126,8 @@ export type DataGridWrapperProps = ModifyFields<
|
|
|
117
126
|
DataGridProps,
|
|
118
127
|
{
|
|
119
128
|
gridRef: React.MutableRefObject<TypeComputedProps>;
|
|
129
|
+
/** internal: inovua's live computed props ref (gridRef holds a snapshot) */
|
|
130
|
+
liveGridRef: React.MutableRefObject<React.MutableRefObject<TypeComputedProps> | null>;
|
|
120
131
|
static_gridOptions: AzGridOptions;
|
|
121
132
|
dynamic_gridOptions: AzGridOptions;
|
|
122
133
|
children?: React.ReactNode;
|