@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/README.md
CHANGED
|
@@ -22,7 +22,7 @@ The grid exposes ARIA grid semantics, so screen readers and automation tools can
|
|
|
22
22
|
|
|
23
23
|
| Element | Attributes |
|
|
24
24
|
| --- | --- |
|
|
25
|
-
| Grid root | `role="grid"`, `aria-rowcount` (total rows incl. header; the full data count when paginated), `aria-colcount` (visible columns), `aria-label`, `aria-readonly="true"` when the grid isn't editable |
|
|
25
|
+
| Grid root | `role="grid"`, `aria-rowcount` (total rows incl. header; the full data count when paginated), `aria-colcount` (visible columns), `aria-label`, `aria-readonly="true"` when the grid isn't editable, `aria-busy="true"` while loading |
|
|
26
26
|
| Header row | `role="row"`, `aria-rowindex="1"` |
|
|
27
27
|
| Header cells | `role="columnheader"`, `aria-colindex`, `aria-sort` (`ascending` / `descending` / `none`, sortable columns only) |
|
|
28
28
|
| Rows | `role="row"`, `aria-rowindex` (1-based, header is 1, includes the page offset), `aria-selected` when selection is enabled, `data-row-id` |
|
|
@@ -31,6 +31,11 @@ export type RootA11yOptions = {
|
|
|
31
31
|
ariaLabel?: string;
|
|
32
32
|
editable?: boolean;
|
|
33
33
|
};
|
|
34
|
+
/**
|
|
35
|
+
* true while the grid is loading (loading prop or async datasource)
|
|
36
|
+
* @param grid computed props of the grid
|
|
37
|
+
*/
|
|
38
|
+
export declare function is_grid_busy(grid: GridType | null | undefined): boolean;
|
|
34
39
|
/**
|
|
35
40
|
* root element of the grid, null when it isn't mounted
|
|
36
41
|
* @param grid computed props of the grid
|
|
@@ -9,6 +9,7 @@ exports.apply_row_a11y = apply_row_a11y;
|
|
|
9
9
|
exports.get_grid_root = get_grid_root;
|
|
10
10
|
exports.get_grid_row_count = get_grid_row_count;
|
|
11
11
|
exports.get_root_a11y_key = get_root_a11y_key;
|
|
12
|
+
exports.is_grid_busy = is_grid_busy;
|
|
12
13
|
function set_default(target, key, value) {
|
|
13
14
|
if (target[key] === undefined && value !== undefined && value !== null) target[key] = value;
|
|
14
15
|
}
|
|
@@ -100,6 +101,13 @@ function get_grid_row_count(grid) {
|
|
|
100
101
|
const offset = g.computedLivePagination ? 0 : g.computedSkip || 0;
|
|
101
102
|
return Math.max(g.paginationCount || 0, offset + rendered);
|
|
102
103
|
}
|
|
104
|
+
/**
|
|
105
|
+
* true while the grid is loading (loading prop or async datasource)
|
|
106
|
+
* @param grid computed props of the grid
|
|
107
|
+
*/
|
|
108
|
+
function is_grid_busy(grid) {
|
|
109
|
+
return !!(grid !== null && grid !== void 0 && grid.computedLoading);
|
|
110
|
+
}
|
|
103
111
|
/**
|
|
104
112
|
* root element of the grid, null when it isn't mounted
|
|
105
113
|
* @param grid computed props of the grid
|
|
@@ -117,7 +125,7 @@ function get_root_a11y_key(grid, options) {
|
|
|
117
125
|
var _options$ariaLabel;
|
|
118
126
|
const g = grid;
|
|
119
127
|
if (!g) return '';
|
|
120
|
-
return [get_grid_row_count(g), (g.visibleColumns || []).length, (_options$ariaLabel = options.ariaLabel) !== null && _options$ariaLabel !== void 0 ? _options$ariaLabel : '', options.editable ? 1 : 0].join('|');
|
|
128
|
+
return [get_grid_row_count(g), (g.visibleColumns || []).length, (_options$ariaLabel = options.ariaLabel) !== null && _options$ariaLabel !== void 0 ? _options$ariaLabel : '', options.editable ? 1 : 0, is_grid_busy(g) ? 1 : 0].join('|');
|
|
121
129
|
}
|
|
122
130
|
/**
|
|
123
131
|
* sets ARIA attributes on the grid root element
|
|
@@ -135,5 +143,6 @@ function apply_root_a11y(grid, options) {
|
|
|
135
143
|
set_attr(el, 'aria-colcount', (g.visibleColumns || []).length);
|
|
136
144
|
set_attr(el, 'aria-label', options.ariaLabel);
|
|
137
145
|
set_attr(el, 'aria-readonly', options.editable ? undefined : 'true');
|
|
146
|
+
set_attr(el, 'aria-busy', is_grid_busy(g) ? 'true' : undefined);
|
|
138
147
|
return el;
|
|
139
148
|
}
|
|
@@ -27,23 +27,31 @@ function fix_parent_child_relations(rows) {
|
|
|
27
27
|
};
|
|
28
28
|
});
|
|
29
29
|
const top_level_rows = converted_rows.filter(r => !r.parent);
|
|
30
|
-
|
|
31
|
-
|
|
30
|
+
//group rows by parent id once, instead of scanning all rows for every parent
|
|
31
|
+
const children_by_parent = new Map();
|
|
32
|
+
for (let index = 0; index < converted_rows.length; index++) {
|
|
33
|
+
const r = converted_rows[index];
|
|
34
|
+
if (!r.parent) continue;
|
|
35
|
+
const key = String(r.parent);
|
|
36
|
+
const siblings = children_by_parent.get(key);
|
|
37
|
+
if (siblings) siblings.push(r);else children_by_parent.set(key, [r]);
|
|
38
|
+
}
|
|
39
|
+
if (children_by_parent.size > 0) {
|
|
32
40
|
//find children of top-level parents:
|
|
33
41
|
for (let index = 0; index < top_level_rows.length; index++) {
|
|
34
42
|
const mrow = top_level_rows[index];
|
|
35
|
-
assign_children(mrow,
|
|
43
|
+
assign_children(mrow, children_by_parent);
|
|
36
44
|
}
|
|
37
45
|
}
|
|
38
46
|
return top_level_rows.map(r => r.data);
|
|
39
47
|
}
|
|
40
|
-
function assign_children(row,
|
|
41
|
-
const my_children =
|
|
42
|
-
if (my_children.length > 0) {
|
|
48
|
+
function assign_children(row, children_by_parent) {
|
|
49
|
+
const my_children = row.self == null ? undefined : children_by_parent.get(String(row.self));
|
|
50
|
+
if (my_children && my_children.length > 0) {
|
|
43
51
|
//find children of my children:
|
|
44
52
|
for (let index = 0; index < my_children.length; index++) {
|
|
45
53
|
const child = my_children[index];
|
|
46
|
-
assign_children(child,
|
|
54
|
+
assign_children(child, children_by_parent);
|
|
47
55
|
}
|
|
48
56
|
row.data.__children = my_children.map(r => r.data);
|
|
49
57
|
} else if (!Array.isArray(row.data.__children) || row.data.__children.length == 0) row.data.__children = undefined;
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
import { AzGridProps, LooseColumnDefinition } from '../types/types';
|
|
2
|
-
export declare function useColumnData(inputOrProps: undefined | any[] | LooseColumnDefinition[] | AzGridProps):
|
|
2
|
+
export declare function useColumnData(inputOrProps: undefined | any[] | LooseColumnDefinition[] | AzGridProps): any[];
|
|
@@ -7,18 +7,14 @@ exports.useColumnData = useColumnData;
|
|
|
7
7
|
var _react = _interopRequireDefault(require("react"));
|
|
8
8
|
var _func_grid = require("../func/func_grid");
|
|
9
9
|
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
|
|
10
|
+
const EMPTY_COLUMNS = [];
|
|
10
11
|
function useColumnData(inputOrProps) {
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
function useThisColumnData(cols) {
|
|
12
|
+
// one useMemo for every input kind, so switching between them keeps the hook order
|
|
13
|
+
const isProps = inputOrProps != null && !Array.isArray(inputOrProps);
|
|
14
|
+
const props = isProps ? inputOrProps : {};
|
|
15
|
+
const deps = isProps ? [props.columns, props.leftColumns, props.rightColumns, props.editableColumns, props.columnExtensions, props.customColumns] : [inputOrProps, null, null, null, null, null];
|
|
16
16
|
return _react.default.useMemo(() => {
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
}
|
|
20
|
-
function usePropsColumnData(props) {
|
|
21
|
-
return _react.default.useMemo(() => {
|
|
22
|
-
return (0, _func_grid.applyColumnData)(props);
|
|
23
|
-
}, [props.columns, props.leftColumns, props.rightColumns, props.editableColumns, props.columnExtensions, props.customColumns]);
|
|
17
|
+
if (inputOrProps == null) return EMPTY_COLUMNS;
|
|
18
|
+
return (0, _func_grid.applyColumnData)(inputOrProps);
|
|
19
|
+
}, [isProps, ...deps]);
|
|
24
20
|
}
|
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
import { DataGridWrapperProps, GridType } from '../types/types';
|
|
2
2
|
export declare function useGridEvents(grid: GridType, props: DataGridWrapperProps): {
|
|
3
3
|
onRowClick: (rowProps: any, event: any) => void | undefined;
|
|
4
|
+
onRenderLoadMask: (maskProps: {
|
|
5
|
+
visible?: boolean;
|
|
6
|
+
}) => void;
|
|
4
7
|
onRowContextMenu: (rowProps: any, event: any) => boolean;
|
|
5
8
|
onRenderRow: (rowProps: any) => void;
|
|
6
9
|
update_menuRow: (r?: any, el?: any) => void;
|
|
@@ -24,6 +24,9 @@ function useGridMenuRow() {
|
|
|
24
24
|
}
|
|
25
25
|
function useGridEvents(grid, props) {
|
|
26
26
|
const click_data = _react.default.useRef(null);
|
|
27
|
+
// latest props, so the callbacks below stay stable and don't re-render the grid
|
|
28
|
+
const latestProps = _react.default.useRef(props);
|
|
29
|
+
latestProps.current = props;
|
|
27
30
|
const {
|
|
28
31
|
update_menuRow,
|
|
29
32
|
menuAnchorEl,
|
|
@@ -52,18 +55,22 @@ function useGridEvents(grid, props) {
|
|
|
52
55
|
/* events */
|
|
53
56
|
/* -------------------------------------------------------------------------- */
|
|
54
57
|
const onRowClick = _react.default.useCallback((rowProps, event) => {
|
|
58
|
+
const {
|
|
59
|
+
onRowClick,
|
|
60
|
+
onRowDoubleClick
|
|
61
|
+
} = latestProps.current;
|
|
55
62
|
const nowDate = Date.now();
|
|
56
63
|
const lastclick = click_data.current[(0, _func_grid.get_row_id)(rowProps)];
|
|
57
64
|
if (lastclick) {
|
|
58
65
|
const diffTime = Math.abs(nowDate - lastclick);
|
|
59
66
|
if (diffTime < 400) {
|
|
60
67
|
click_data.current[(0, _func_grid.get_row_id)(rowProps)] = null;
|
|
61
|
-
return
|
|
68
|
+
return onRowDoubleClick && onRowDoubleClick(rowProps.rowIndex, rowProps.data);
|
|
62
69
|
}
|
|
63
70
|
}
|
|
64
71
|
click_data.current[(0, _func_grid.get_row_id)(rowProps)] = nowDate;
|
|
65
|
-
return
|
|
66
|
-
}, [
|
|
72
|
+
return onRowClick && onRowClick(rowProps.rowIndex, rowProps.data);
|
|
73
|
+
}, []);
|
|
67
74
|
_react.default.useEffect(() => {
|
|
68
75
|
click_data.current = {};
|
|
69
76
|
}, [props.dataSource]);
|
|
@@ -81,25 +88,24 @@ function useGridEvents(grid, props) {
|
|
|
81
88
|
}, []);
|
|
82
89
|
/* -------------------------------------------------------------------------- */
|
|
83
90
|
/* ------------------------------ Accessibility ----------------------------- */
|
|
84
|
-
const latestProps = _react.default.useRef(props);
|
|
85
|
-
latestProps.current = props;
|
|
86
91
|
const rootSyncFrame = _react.default.useRef(0);
|
|
87
92
|
// last applied root values, so row renders (e.g. while scrolling) don't re-apply them
|
|
88
93
|
const lastRoot = _react.default.useRef({
|
|
89
94
|
el: null,
|
|
90
|
-
key: ''
|
|
95
|
+
key: '',
|
|
96
|
+
busy: false
|
|
91
97
|
});
|
|
92
98
|
const getRootOptions = () => ({
|
|
93
99
|
ariaLabel: latestProps.current.ariaLabel,
|
|
94
100
|
editable: !!latestProps.current.editable
|
|
95
101
|
});
|
|
96
102
|
// root attributes are set on the DOM node, batched to once per frame
|
|
97
|
-
const scheduleRootSync = _react.default.useCallback(() => {
|
|
103
|
+
const scheduleRootSync = _react.default.useCallback((force = false) => {
|
|
98
104
|
var _latestProps$current$;
|
|
99
105
|
if (rootSyncFrame.current || typeof requestAnimationFrame === 'undefined') return;
|
|
100
106
|
const grid = (_latestProps$current$ = latestProps.current.liveGridRef) === null || _latestProps$current$ === void 0 || (_latestProps$current$ = _latestProps$current$.current) === null || _latestProps$current$ === void 0 ? void 0 : _latestProps$current$.current;
|
|
101
107
|
const key = (0, _func_a11y.get_root_a11y_key)(grid, getRootOptions());
|
|
102
|
-
if (key && key === lastRoot.current.key && (0, _func_a11y.get_grid_root)(grid) === lastRoot.current.el) return;
|
|
108
|
+
if (!force && key && key === lastRoot.current.key && (0, _func_a11y.get_grid_root)(grid) === lastRoot.current.el) return;
|
|
103
109
|
rootSyncFrame.current = requestAnimationFrame(() => {
|
|
104
110
|
var _latestProps$current$2;
|
|
105
111
|
rootSyncFrame.current = 0;
|
|
@@ -108,7 +114,8 @@ function useGridEvents(grid, props) {
|
|
|
108
114
|
const el = (0, _func_a11y.apply_root_a11y)(liveGrid, options);
|
|
109
115
|
lastRoot.current = {
|
|
110
116
|
el,
|
|
111
|
-
key: el ? (0, _func_a11y.get_root_a11y_key)(liveGrid, options) : ''
|
|
117
|
+
key: el ? (0, _func_a11y.get_root_a11y_key)(liveGrid, options) : '',
|
|
118
|
+
busy: el ? (0, _func_a11y.is_grid_busy)(liveGrid) : false
|
|
112
119
|
};
|
|
113
120
|
});
|
|
114
121
|
}, []);
|
|
@@ -127,8 +134,13 @@ function useGridEvents(grid, props) {
|
|
|
127
134
|
(0, _func_a11y.apply_row_a11y)(rowProps, !!(liveGrid !== null && liveGrid !== void 0 && liveGrid.computedRowSelectionEnabled));
|
|
128
135
|
scheduleRootSync();
|
|
129
136
|
}, []);
|
|
137
|
+
// called by the grid on every render with the load mask state, keeps aria-busy in sync
|
|
138
|
+
const onRenderLoadMask = _react.default.useCallback(maskProps => {
|
|
139
|
+
if (!!(maskProps !== null && maskProps !== void 0 && maskProps.visible) !== lastRoot.current.busy) scheduleRootSync(true);
|
|
140
|
+
}, []);
|
|
130
141
|
return {
|
|
131
142
|
onRowClick,
|
|
143
|
+
onRenderLoadMask,
|
|
132
144
|
onRowContextMenu,
|
|
133
145
|
onRenderRow,
|
|
134
146
|
update_menuRow,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
-
import { DataGridWrapperProps
|
|
3
|
-
export declare function useGridSearch(
|
|
4
|
-
filteredData: any;
|
|
2
|
+
import { DataGridWrapperProps } from '../types/types';
|
|
3
|
+
export declare function useGridSearch(props: DataGridWrapperProps): {
|
|
4
|
+
filteredData: any[] | ((args: any) => Promise<any>);
|
|
5
5
|
element: React.JSX.Element;
|
|
6
6
|
};
|
|
@@ -8,51 +8,77 @@ var _react = _interopRequireDefault(require("react"));
|
|
|
8
8
|
var _lodash = _interopRequireDefault(require("lodash.debounce"));
|
|
9
9
|
var _object = require("@azrico/object");
|
|
10
10
|
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
|
|
11
|
-
|
|
11
|
+
const SEARCH_DELAY = 300;
|
|
12
|
+
function to_rows(result) {
|
|
13
|
+
return result && typeof result === 'object' ? result.data || result : result;
|
|
14
|
+
}
|
|
15
|
+
//false when the result is only a page of the data (remote pagination)
|
|
16
|
+
function is_complete(result) {
|
|
17
|
+
if (!result || typeof result !== 'object' || Array.isArray(result)) return true;
|
|
18
|
+
const loaded = Array.isArray(result.data) ? result.data.length : 0;
|
|
19
|
+
return !(result.count > loaded);
|
|
20
|
+
}
|
|
21
|
+
function useGridSearch(props) {
|
|
12
22
|
const [search, set_search] = _react.default.useState('');
|
|
13
|
-
|
|
14
|
-
const
|
|
15
|
-
const
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
23
|
+
// null shows the datasource itself, an array shows search results
|
|
24
|
+
const [searchResult, set_searchResult] = _react.default.useState(null);
|
|
25
|
+
const latestProps = _react.default.useRef(props);
|
|
26
|
+
latestProps.current = props;
|
|
27
|
+
// last result the grid loaded, reused by search instead of loading the data again
|
|
28
|
+
const loaded = _react.default.useRef(null);
|
|
29
|
+
const searchId = _react.default.useRef(0);
|
|
30
|
+
const trackedDataSource = _react.default.useCallback(async args => {
|
|
31
|
+
const result = await (0, _object.fn_eval)(props.dataSource, args);
|
|
32
|
+
loaded.current = {
|
|
33
|
+
ds: props.dataSource,
|
|
34
|
+
result
|
|
35
|
+
};
|
|
36
|
+
return result;
|
|
37
|
+
}, [props.dataSource]);
|
|
38
|
+
const get_search_rows = _react.default.useCallback(async () => {
|
|
39
|
+
const ds = latestProps.current.dataSource;
|
|
40
|
+
const last = loaded.current;
|
|
41
|
+
if (last && last.ds === ds && is_complete(last.result)) return to_rows(last.result);
|
|
42
|
+
return to_rows(await (0, _object.fn_eval)(ds));
|
|
43
|
+
}, []);
|
|
44
|
+
const filter_rows = _react.default.useCallback((rows, text) => {
|
|
45
|
+
var _gridRef$current;
|
|
46
|
+
const {
|
|
47
|
+
gridRef,
|
|
48
|
+
advancedFilterFields
|
|
49
|
+
} = latestProps.current;
|
|
50
|
+
const visibleColumns = (_gridRef$current = gridRef.current) === null || _gridRef$current === void 0 ? void 0 : _gridRef$current.visibleColumns;
|
|
51
|
+
const columns_to_search = visibleColumns ? (0, _object.array_intersection)(advancedFilterFields || [], visibleColumns.map(r => r.id)) : advancedFilterFields || [];
|
|
52
|
+
const lowerSearchText = text.toLowerCase().trim();
|
|
53
|
+
if (!lowerSearchText || columns_to_search.length === 0) return rows;
|
|
54
|
+
return rows.filter(p => {
|
|
55
|
+
for (let index = 0; index < columns_to_search.length; index++) {
|
|
56
|
+
const col = columns_to_search[index];
|
|
57
|
+
const v = (p[col] + '').toLowerCase(); //value of column
|
|
58
|
+
if (v && v.indexOf(lowerSearchText) != -1) {
|
|
59
|
+
//if one column is matching then the row is a match
|
|
60
|
+
return true;
|
|
61
|
+
}
|
|
37
62
|
}
|
|
38
|
-
|
|
39
|
-
set_filteredData(newData);
|
|
40
|
-
}, 1000), [grid]);
|
|
41
|
-
_react.default.useEffect(() => {
|
|
42
|
-
(0, _object.fn_eval)(props.dataSource).then(r => {
|
|
43
|
-
currentDS.current = typeof r === 'object' ? r.data || r : r;
|
|
44
|
-
searchIfNeeded();
|
|
63
|
+
return false;
|
|
45
64
|
});
|
|
46
|
-
}, [
|
|
65
|
+
}, []);
|
|
66
|
+
const run_search = _react.default.useMemo(() => (0, _lodash.default)(async text => {
|
|
67
|
+
const id = ++searchId.current;
|
|
68
|
+
const rows = await get_search_rows();
|
|
69
|
+
if (id !== searchId.current || !Array.isArray(rows)) return; //a newer search started
|
|
70
|
+
set_searchResult(filter_rows(rows, text));
|
|
71
|
+
}, SEARCH_DELAY), []);
|
|
72
|
+
_react.default.useEffect(() => () => run_search.cancel(), [run_search]);
|
|
47
73
|
_react.default.useEffect(() => {
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
}
|
|
74
|
+
if (search) {
|
|
75
|
+
run_search(search);
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
run_search.cancel();
|
|
79
|
+
searchId.current++;
|
|
80
|
+
set_searchResult(null);
|
|
81
|
+
}, [search, props.dataSource]);
|
|
56
82
|
const element = _react.default.createElement(_react.default.Fragment, null, _react.default.createElement("form", {
|
|
57
83
|
className: 'azgrid-searcharea'
|
|
58
84
|
}, _react.default.createElement("label", null, "Search", _react.default.createElement("input", {
|
|
@@ -62,7 +88,7 @@ function useGridSearch(grid, props) {
|
|
|
62
88
|
onChange: e => set_search(e.target.value)
|
|
63
89
|
}))));
|
|
64
90
|
return {
|
|
65
|
-
filteredData,
|
|
91
|
+
filteredData: searchResult !== null && searchResult !== void 0 ? searchResult : trackedDataSource,
|
|
66
92
|
element
|
|
67
93
|
};
|
|
68
94
|
}
|
|
@@ -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/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
|
}
|