@azrico/reactgrid 1.5.1 → 1.6.0
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 +10 -0
- package/README.md +1 -1
- package/dist/index.d.ts +4 -0
- package/dist/index.js +35 -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/func/func_grid.d.ts +4 -1
- package/dist/src/func/func_grid.js +10 -3
- package/dist/src/hooks/useColumnData.d.ts +1 -1
- package/dist/src/hooks/useColumnData.js +8 -12
- package/dist/src/hooks/useDatasource.js +13 -2
- 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/types/types.d.ts +15 -3
- package/dist/src/views/DataGrid.js +35 -16
- package/index.ts +5 -0
- package/package.json +1 -1
- package/src/func/func_a11y.ts +10 -0
- package/src/func/func_child.ts +92 -83
- package/src/func/func_grid.ts +16 -3
- package/src/hooks/useColumnData.ts +28 -34
- package/src/hooks/useDatasource.ts +32 -25
- package/src/hooks/useGridEvents.ts +40 -24
- package/src/hooks/useGridSearch.tsx +106 -79
- package/src/types/types.ts +16 -2
- package/src/views/DataGrid.tsx +362 -318
|
@@ -40,11 +40,17 @@ const forcedOptions = {
|
|
|
40
40
|
theme: 'default-light'
|
|
41
41
|
};
|
|
42
42
|
const dynamic_option_names = ['selected', 'cellSelection', 'loading', 'limit', 'skip'];
|
|
43
|
+
// consumer callbacks forwarded to the grid; wrapped so a new function doesn't re-render the grid
|
|
44
|
+
const callback_option_names = ['onSkipChange', 'onLimitChange', 'onEditComplete', 'onSelectionChange', 'onCellSelectionChange'];
|
|
45
|
+
// props DataGridCore renders from, other props don't need to re-render the grid
|
|
46
|
+
const core_prop_names = ['className', 'static_gridOptions', 'dynamic_gridOptions', 'columns', 'dataSource', 'gridRef', 'liveGridRef', 'allowChildRows', 'theme', 'dark', 'children'];
|
|
43
47
|
/* -------------------------------------------------------------------------- */
|
|
44
48
|
function AzGrid(props) {
|
|
45
49
|
const gridRefBackup = _react.default.useRef(null);
|
|
46
50
|
const gridRef = props.gridRef || gridRefBackup;
|
|
47
51
|
const liveGridRef = _react.default.useRef(null);
|
|
52
|
+
const latestProps = _react.default.useRef(props);
|
|
53
|
+
latestProps.current = props;
|
|
48
54
|
/* -------------------------------------------------------------------------- */
|
|
49
55
|
/* Columns */
|
|
50
56
|
/* -------------------------------------------------------------------------- */
|
|
@@ -59,14 +65,15 @@ function AzGrid(props) {
|
|
|
59
65
|
const base_gridOptions = _react.default.useMemo(() => {
|
|
60
66
|
const firstcolumn = cached_columns[0];
|
|
61
67
|
const licenseKey = props.licenseKey || (0, _func_grid.get_default_key)();
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
68
|
+
// call the latest consumer callback, only passed when the consumer gave one
|
|
69
|
+
const events = {};
|
|
70
|
+
callback_option_names.forEach(name => {
|
|
71
|
+
if (props[name]) events[name] = (...args) => {
|
|
72
|
+
var _latestProps$current$, _latestProps$current;
|
|
73
|
+
return (_latestProps$current$ = (_latestProps$current = latestProps.current)[name]) === null || _latestProps$current$ === void 0 ? void 0 : _latestProps$current$.call(_latestProps$current, ...args);
|
|
74
|
+
};
|
|
75
|
+
});
|
|
67
76
|
const extra_ops = {
|
|
68
|
-
onSelectionChange: props.onSelectionChange,
|
|
69
|
-
onCellSelectionChange: props.onCellSelectionChange,
|
|
70
77
|
licenseKey: licenseKey,
|
|
71
78
|
editable: props.editable,
|
|
72
79
|
treeColumn: props.allowChildRows ? firstcolumn.name : undefined,
|
|
@@ -77,7 +84,7 @@ function AzGrid(props) {
|
|
|
77
84
|
...events,
|
|
78
85
|
...extra_ops
|
|
79
86
|
};
|
|
80
|
-
}, [cached_columns, props.allowChildRows, props.editable, props.RowDetail, props.
|
|
87
|
+
}, [cached_columns, props.allowChildRows, props.editable, props.RowDetail, props.licenseKey, ...callback_option_names.map(name => !!props[name])]);
|
|
81
88
|
const static_gridOptions = _react.default.useMemo(() => {
|
|
82
89
|
const finalres = {
|
|
83
90
|
...base_gridOptions,
|
|
@@ -132,6 +139,7 @@ function DataGridEventsWrapper(props) {
|
|
|
132
139
|
onRowClick,
|
|
133
140
|
onRowContextMenu,
|
|
134
141
|
onRenderRow,
|
|
142
|
+
onRenderLoadMask,
|
|
135
143
|
update_menuRow,
|
|
136
144
|
menuAnchorEl,
|
|
137
145
|
menuRow
|
|
@@ -152,7 +160,12 @@ function DataGridEventsWrapper(props) {
|
|
|
152
160
|
onRenderRow: consumerOnRenderRow ? rowProps => {
|
|
153
161
|
onRenderRow(rowProps);
|
|
154
162
|
consumerOnRenderRow(rowProps);
|
|
155
|
-
} : onRenderRow
|
|
163
|
+
} : onRenderRow,
|
|
164
|
+
// track loading for aria-busy, the consumer's renderLoadMask (if any) still renders the mask
|
|
165
|
+
renderLoadMask: maskProps => {
|
|
166
|
+
onRenderLoadMask(maskProps);
|
|
167
|
+
return opts.renderLoadMask ? opts.renderLoadMask(maskProps) : undefined;
|
|
168
|
+
}
|
|
156
169
|
};
|
|
157
170
|
const gridEditable = !!props.editable;
|
|
158
171
|
const a11yProps = {
|
|
@@ -176,7 +189,7 @@ function DataGridEventsWrapper(props) {
|
|
|
176
189
|
...renderProps,
|
|
177
190
|
...a11yProps
|
|
178
191
|
};
|
|
179
|
-
}, [props.static_gridOptions, props.editable, onRowClick, onRowContextMenu, onRenderRow]);
|
|
192
|
+
}, [props.static_gridOptions, props.editable, onRowClick, onRowContextMenu, onRenderRow, onRenderLoadMask]);
|
|
180
193
|
/* -------------------------------------------------------------------------- */
|
|
181
194
|
//TODO ContextMenu not from mui...
|
|
182
195
|
return _react.default.createElement("div", {
|
|
@@ -271,7 +284,7 @@ function DataGridSearchWrapper(props) {
|
|
|
271
284
|
const {
|
|
272
285
|
element,
|
|
273
286
|
filteredData
|
|
274
|
-
} = (0, _useGridSearch.useGridSearch)(props
|
|
287
|
+
} = (0, _useGridSearch.useGridSearch)(props);
|
|
275
288
|
return _react.default.createElement("div", {
|
|
276
289
|
className: 'azgrid-flexed azgrid-flexlayout azgrid-nooverflow'
|
|
277
290
|
}, element, _react.default.createElement(DataGridCore, {
|
|
@@ -279,18 +292,24 @@ function DataGridSearchWrapper(props) {
|
|
|
279
292
|
dataSource: filteredData
|
|
280
293
|
}));
|
|
281
294
|
}
|
|
282
|
-
|
|
295
|
+
const DataGridCore = _react.default.memo(DataGridCoreView, (prev, next) => core_prop_names.every(name => prev[name] === next[name]));
|
|
296
|
+
function DataGridCoreView(props) {
|
|
283
297
|
var _props$theme3;
|
|
298
|
+
const {
|
|
299
|
+
gridRef,
|
|
300
|
+
liveGridRef
|
|
301
|
+
} = props;
|
|
302
|
+
const handle = _react.default.useCallback(ref => {
|
|
303
|
+
if (ref) gridRef.current = ref.current;
|
|
304
|
+
if (liveGridRef) liveGridRef.current = ref;
|
|
305
|
+
}, [gridRef, liveGridRef]);
|
|
284
306
|
return _react.default.createElement("div", {
|
|
285
307
|
className: (0, _clsx.default)(props.className, 'azgrid-flexed', 'azgrid-flexlayout')
|
|
286
308
|
}, _react.default.createElement(_reactdatagridEnterprise.default, {
|
|
287
309
|
...props.static_gridOptions,
|
|
288
310
|
...props.dynamic_gridOptions,
|
|
289
311
|
rowIndexColumn: props.static_gridOptions.rowIndexColumn || true,
|
|
290
|
-
handle:
|
|
291
|
-
if (ref) props.gridRef.current = ref.current;
|
|
292
|
-
if (props.liveGridRef) props.liveGridRef.current = ref;
|
|
293
|
-
},
|
|
312
|
+
handle: handle,
|
|
294
313
|
theme: "default-light",
|
|
295
314
|
className: (0, _clsx.default)('azgrid azgrid-flexed', props.allowChildRows && 'azgrid-tree', `azgrid-theme-${(_props$theme3 = props.theme) !== null && _props$theme3 !== void 0 ? _props$theme3 : props.dark ? 'dark' : 'light'}`),
|
|
296
315
|
columns: props.columns,
|
package/index.ts
CHANGED
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
export * from './src/views/DataGrid';
|
|
2
2
|
export * from './src/types/types';
|
|
3
3
|
export { get_row_id, set_row_id, get_col_name, get_col_header } from './src/func/func_grid';
|
|
4
|
+
// filter editors for column.filterEditor, so consumers don't import @inovua directly
|
|
5
|
+
export { default as SelectFilter } from '@inovua/reactdatagrid-enterprise/SelectFilter';
|
|
6
|
+
export { default as NumberFilter } from '@inovua/reactdatagrid-enterprise/NumberFilter';
|
|
7
|
+
export { default as DateFilter } from '@inovua/reactdatagrid-enterprise/DateFilter';
|
|
8
|
+
export { default as BoolFilter } from '@inovua/reactdatagrid-enterprise/BoolFilter';
|
|
4
9
|
|
|
5
10
|
import { AzGrid } from './src/views/DataGrid';
|
|
6
11
|
export default AzGrid;
|
package/package.json
CHANGED
package/src/func/func_a11y.ts
CHANGED
|
@@ -115,6 +115,14 @@ export function get_grid_row_count(grid: GridType): number {
|
|
|
115
115
|
|
|
116
116
|
export type RootA11yOptions = { ariaLabel?: string; editable?: boolean };
|
|
117
117
|
|
|
118
|
+
/**
|
|
119
|
+
* true while the grid is loading (loading prop or async datasource)
|
|
120
|
+
* @param grid computed props of the grid
|
|
121
|
+
*/
|
|
122
|
+
export function is_grid_busy(grid: GridType | null | undefined) {
|
|
123
|
+
return !!(grid as any)?.computedLoading;
|
|
124
|
+
}
|
|
125
|
+
|
|
118
126
|
/**
|
|
119
127
|
* root element of the grid, null when it isn't mounted
|
|
120
128
|
* @param grid computed props of the grid
|
|
@@ -136,6 +144,7 @@ export function get_root_a11y_key(grid: GridType | null | undefined, options: Ro
|
|
|
136
144
|
(g.visibleColumns || []).length,
|
|
137
145
|
options.ariaLabel ?? '',
|
|
138
146
|
options.editable ? 1 : 0,
|
|
147
|
+
is_grid_busy(g) ? 1 : 0,
|
|
139
148
|
].join('|');
|
|
140
149
|
}
|
|
141
150
|
|
|
@@ -155,5 +164,6 @@ export function apply_root_a11y(grid: GridType | null | undefined, options: Root
|
|
|
155
164
|
set_attr(el, 'aria-colcount', (g.visibleColumns || []).length);
|
|
156
165
|
set_attr(el, 'aria-label', options.ariaLabel);
|
|
157
166
|
set_attr(el, 'aria-readonly', options.editable ? undefined : 'true');
|
|
167
|
+
set_attr(el, 'aria-busy', is_grid_busy(g) ? 'true' : undefined);
|
|
158
168
|
return el;
|
|
159
169
|
}
|
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
|
+
}
|
package/src/func/func_grid.ts
CHANGED
|
@@ -17,12 +17,24 @@ const defaultColumnOptions: ColumnDefinition = {
|
|
|
17
17
|
editable: false,
|
|
18
18
|
};
|
|
19
19
|
|
|
20
|
-
export async function ApplyDataSource(
|
|
20
|
+
export async function ApplyDataSource(
|
|
21
|
+
datasource,
|
|
22
|
+
props,
|
|
23
|
+
remote: { remoteFilter?: boolean; remoteSort?: boolean } = {}
|
|
24
|
+
) {
|
|
21
25
|
let result_data: any = undefined;
|
|
22
26
|
|
|
23
27
|
let isUsingCache = false;
|
|
24
28
|
|
|
25
|
-
|
|
29
|
+
//filter/sort are only part of the key when they are sent to a remote dataSource,
|
|
30
|
+
//local filtering/sorting keeps reusing the cached rows
|
|
31
|
+
const cacheInfo = {
|
|
32
|
+
ds: datasource,
|
|
33
|
+
skip: props.skip,
|
|
34
|
+
limit: props.limit,
|
|
35
|
+
filterValue: remote.remoteFilter ? props.filterValue : undefined,
|
|
36
|
+
sortInfo: remote.remoteSort ? props.sortInfo : undefined,
|
|
37
|
+
};
|
|
26
38
|
//check if we can used cached data
|
|
27
39
|
//if the datasource is changed since last render, we should invalidate cache
|
|
28
40
|
if (!validateCache(props.last_cache_info.current, cacheInfo, props.looseCacheCheck)) {
|
|
@@ -289,7 +301,8 @@ export function get_default_key() {
|
|
|
289
301
|
|
|
290
302
|
//check if two cache objects are the same
|
|
291
303
|
export function validateCache(current: any, old: any, looseCheck = false) {
|
|
292
|
-
|
|
304
|
+
//deep compare skip/limit/filterValue/sortInfo, array order matters (sort priority)
|
|
305
|
+
if (!old || !object_equals(current, old, { strictArrayCheck: true })) return false;
|
|
293
306
|
|
|
294
307
|
if (looseCheck) return true;
|
|
295
308
|
|
|
@@ -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
|
+
}
|
|
@@ -1,25 +1,32 @@
|
|
|
1
|
-
import React from 'react';
|
|
2
|
-
import { AzGridProps, DataGridWrapperProps, GridType } from '../types/types';
|
|
3
|
-
import {
|
|
4
|
-
applyColumnData,
|
|
5
|
-
ApplyDataSource,
|
|
6
|
-
find_realData,
|
|
7
|
-
get_row_id,
|
|
8
|
-
} from '../func/func_grid';
|
|
9
|
-
import { func_selection } from '../func/func_child';
|
|
10
|
-
|
|
11
|
-
export function useDataSource(ds: any, props: AzGridProps) {
|
|
12
|
-
const cached_data = React.useRef<any>();
|
|
13
|
-
const last_cache_info = React.useRef<any>();
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { AzDataSourceParams, AzGridProps, DataGridWrapperProps, GridType } from '../types/types';
|
|
3
|
+
import {
|
|
4
|
+
applyColumnData,
|
|
5
|
+
ApplyDataSource,
|
|
6
|
+
find_realData,
|
|
7
|
+
get_row_id,
|
|
8
|
+
} from '../func/func_grid';
|
|
9
|
+
import { func_selection } from '../func/func_child';
|
|
10
|
+
|
|
11
|
+
export function useDataSource(ds: any, props: AzGridProps) {
|
|
12
|
+
const cached_data = React.useRef<any>();
|
|
13
|
+
const last_cache_info = React.useRef<any>();
|
|
14
|
+
// filter/sort only change the result when the grid sends them to the dataSource
|
|
15
|
+
const remoteFilter = !!props?.gridOptions?.remoteFilter;
|
|
16
|
+
const remoteSort = !!props?.gridOptions?.remoteSort;
|
|
17
|
+
return React.useCallback(
|
|
18
|
+
async (dt) => {
|
|
19
|
+
const params: AzDataSourceParams = {
|
|
20
|
+
skip: dt?.skip,
|
|
21
|
+
limit: dt?.limit,
|
|
22
|
+
filterValue: dt?.filterValue,
|
|
23
|
+
sortInfo: dt?.sortInfo,
|
|
24
|
+
groupBy: dt?.groupBy,
|
|
25
|
+
cached_data: cached_data,
|
|
26
|
+
last_cache_info: last_cache_info,
|
|
27
|
+
};
|
|
28
|
+
return await ApplyDataSource(ds, params, { remoteFilter, remoteSort });
|
|
29
|
+
},
|
|
30
|
+
[ds, remoteFilter, remoteSort]
|
|
31
|
+
);
|
|
32
|
+
}
|
|
@@ -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,
|