@azrico/reactgrid 1.4.1 → 1.5.1
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 +52 -0
- package/dist/src/func/func_a11y.js +139 -0
- package/dist/src/hooks/useGridEvents.js +44 -0
- package/dist/src/types/types.d.ts +14 -1
- package/dist/src/views/DataGrid.js +36 -6
- package/package.json +67 -67
- package/src/example.tsx +184 -178
- package/src/func/func_a11y.ts +159 -0
- package/src/hooks/useGridEvents.ts +141 -96
- package/src/types/types.ts +12 -1
- package/src/views/DataGrid.tsx +37 -5
package/README.md
CHANGED
|
@@ -16,6 +16,33 @@ const rows = [{ __table_id: '1', name: 'Alice' }];
|
|
|
16
16
|
|
|
17
17
|
The token stylesheet is optional. Without it, the grid uses Inovua's default-light appearance.
|
|
18
18
|
|
|
19
|
+
## Accessibility
|
|
20
|
+
|
|
21
|
+
The grid exposes ARIA grid semantics, so screen readers and automation tools can read it from the accessibility tree.
|
|
22
|
+
|
|
23
|
+
| Element | Attributes |
|
|
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 |
|
|
26
|
+
| Header row | `role="row"`, `aria-rowindex="1"` |
|
|
27
|
+
| Header cells | `role="columnheader"`, `aria-colindex`, `aria-sort` (`ascending` / `descending` / `none`, sortable columns only) |
|
|
28
|
+
| Rows | `role="row"`, `aria-rowindex` (1-based, header is 1, includes the page offset), `aria-selected` when selection is enabled, `data-row-id` |
|
|
29
|
+
| Body cells | `role="gridcell"`, `aria-colindex`, `data-column="<column name>"`, `aria-readonly="true"` when the cell can't be edited |
|
|
30
|
+
| Row-number cells | `role="rowheader"` |
|
|
31
|
+
|
|
32
|
+
New props:
|
|
33
|
+
|
|
34
|
+
- `ariaLabel?: string` on the grid sets the accessible name of the grid.
|
|
35
|
+
- `ariaLabel?: (value, data) => string` on a column sets `aria-label` on its cells. Use it for icons, badges or values whose visible text isn't descriptive.
|
|
36
|
+
|
|
37
|
+
```tsx
|
|
38
|
+
const columns = [{ name: 'status', header: 'Status', ariaLabel: (value) => `Status: ${value}` }];
|
|
39
|
+
|
|
40
|
+
<AzGrid ariaLabel="Orders" columns={columns} dataSource={rows} />;
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
Override any cell attribute with the column's `cellDOMProps` / `headerDOMProps`. Values you set there are kept.
|
|
44
|
+
A `gridOptions.onRenderRow` runs after the grid's own row attributes are set, so it can change them too.
|
|
45
|
+
|
|
19
46
|
## Themes
|
|
20
47
|
|
|
21
48
|
Use `theme="light"` (default), `theme="dark"`, or a custom name. Custom names apply
|
|
@@ -134,4 +161,4 @@ Use dark defaults with an obsidian background, gold accents, square corners and
|
|
|
134
161
|
See [src/example.tsx](src/example.tsx) for light, dark and a complete custom `deco` palette.
|
|
135
162
|
|
|
136
163
|
Custom renderers must style their own content. External menu portal containers need
|
|
137
|
-
`azgrid-scope`, the matching `azgrid-theme-<name>` class and the host variables.
|
|
164
|
+
`azgrid-scope`, the matching `azgrid-theme-<name>` class and the host variables.
|
package/dist/src/example.js
CHANGED
|
@@ -39,7 +39,8 @@ const columns = [{
|
|
|
39
39
|
name: 'age',
|
|
40
40
|
title: 'Age',
|
|
41
41
|
type: 'number',
|
|
42
|
-
defaultFlex: 1
|
|
42
|
+
defaultFlex: 1,
|
|
43
|
+
ariaLabel: value => `${value} years`
|
|
43
44
|
}];
|
|
44
45
|
const editableColumns = ['job', 'age'];
|
|
45
46
|
function getData(delta, max = 200) {
|
|
@@ -157,6 +158,7 @@ function ExampleGrid() {
|
|
|
157
158
|
}
|
|
158
159
|
}, _react.default.createElement("h3", null, theme === 'deco' ? 'Art Deco (host variables)' : `Default ${theme}`), _react.default.createElement(_DataGrid.default, {
|
|
159
160
|
theme: theme,
|
|
161
|
+
ariaLabel: `Users (${theme})`,
|
|
160
162
|
delayedSize: true,
|
|
161
163
|
// details_dataSource={details_dataSource}
|
|
162
164
|
dataSource: dataSource,
|
|
@@ -185,7 +187,11 @@ function ExampleGrid() {
|
|
|
185
187
|
defaultLimit: 10,
|
|
186
188
|
pageSizes: [10, 25, 50],
|
|
187
189
|
checkboxColumn: true,
|
|
188
|
-
multiSelect: true
|
|
190
|
+
multiSelect: true,
|
|
191
|
+
// consumer onRenderRow runs after the internal one
|
|
192
|
+
onRenderRow: rowProps => {
|
|
193
|
+
rowProps['data-example-row'] = 'true';
|
|
194
|
+
}
|
|
189
195
|
// defaultFilterValue: filterValue,
|
|
190
196
|
// remoteFilter: false,
|
|
191
197
|
// multiSelect: true,
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { GridType } from '../types/types';
|
|
2
|
+
export type ColumnA11yOptions = {
|
|
3
|
+
/** role of body cells, e.g. 'gridcell' or 'rowheader' */
|
|
4
|
+
role?: string;
|
|
5
|
+
/** grid level editable flag, columns are readonly when the grid isn't editable */
|
|
6
|
+
gridEditable?: boolean;
|
|
7
|
+
/** false skips aria-readonly (for interactive special columns like the checkbox column) */
|
|
8
|
+
markReadonly?: boolean;
|
|
9
|
+
};
|
|
10
|
+
/**
|
|
11
|
+
* wraps column dom props to add ARIA attributes,
|
|
12
|
+
* consumer's cellDOMProps/headerDOMProps run first and win over ours
|
|
13
|
+
* @param col
|
|
14
|
+
* @param options
|
|
15
|
+
* @returns a new column object
|
|
16
|
+
*/
|
|
17
|
+
export declare function apply_column_a11y<T extends Record<string, any>>(col: T, options?: ColumnA11yOptions): T;
|
|
18
|
+
/**
|
|
19
|
+
* adds ARIA attributes to a row, called from onRenderRow
|
|
20
|
+
* @param rowProps
|
|
21
|
+
* @param selectionEnabled
|
|
22
|
+
*/
|
|
23
|
+
export declare function apply_row_a11y(rowProps: any, selectionEnabled: boolean): void;
|
|
24
|
+
/**
|
|
25
|
+
* total number of data rows, uses the datasource count when paginated
|
|
26
|
+
* so the real size is reported, not just the visible page
|
|
27
|
+
* @param grid
|
|
28
|
+
*/
|
|
29
|
+
export declare function get_grid_row_count(grid: GridType): number;
|
|
30
|
+
export type RootA11yOptions = {
|
|
31
|
+
ariaLabel?: string;
|
|
32
|
+
editable?: boolean;
|
|
33
|
+
};
|
|
34
|
+
/**
|
|
35
|
+
* root element of the grid, null when it isn't mounted
|
|
36
|
+
* @param grid computed props of the grid
|
|
37
|
+
*/
|
|
38
|
+
export declare function get_grid_root(grid: GridType | null | undefined): Element | null;
|
|
39
|
+
/**
|
|
40
|
+
* key of the values apply_root_a11y writes, used to skip applying when nothing changed
|
|
41
|
+
* @param grid computed props of the grid
|
|
42
|
+
* @param options
|
|
43
|
+
*/
|
|
44
|
+
export declare function get_root_a11y_key(grid: GridType | null | undefined, options: RootA11yOptions): string;
|
|
45
|
+
/**
|
|
46
|
+
* sets ARIA attributes on the grid root element
|
|
47
|
+
* inovua doesn't forward props to its root, so this is applied on the DOM node
|
|
48
|
+
* @param grid computed props of the grid
|
|
49
|
+
* @param options
|
|
50
|
+
* @returns the element the attributes were applied to, null when the grid isn't mounted
|
|
51
|
+
*/
|
|
52
|
+
export declare function apply_root_a11y(grid: GridType | null | undefined, options: RootA11yOptions): Element | null;
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.apply_column_a11y = apply_column_a11y;
|
|
7
|
+
exports.apply_root_a11y = apply_root_a11y;
|
|
8
|
+
exports.apply_row_a11y = apply_row_a11y;
|
|
9
|
+
exports.get_grid_root = get_grid_root;
|
|
10
|
+
exports.get_grid_row_count = get_grid_row_count;
|
|
11
|
+
exports.get_root_a11y_key = get_root_a11y_key;
|
|
12
|
+
function set_default(target, key, value) {
|
|
13
|
+
if (target[key] === undefined && value !== undefined && value !== null) target[key] = value;
|
|
14
|
+
}
|
|
15
|
+
function resolve_dom_props(src, cellProps) {
|
|
16
|
+
const res = typeof src === 'function' ? src(cellProps) : src;
|
|
17
|
+
return res ? {
|
|
18
|
+
...res
|
|
19
|
+
} : {};
|
|
20
|
+
}
|
|
21
|
+
function get_sort_dir(sortInfo) {
|
|
22
|
+
if (!sortInfo || !sortInfo.dir) return 'none';
|
|
23
|
+
return sortInfo.dir > 0 ? 'ascending' : 'descending';
|
|
24
|
+
}
|
|
25
|
+
function is_cell_readonly(cellProps, gridEditable) {
|
|
26
|
+
if (!gridEditable) return true;
|
|
27
|
+
const editable = cellProps.computedEditable;
|
|
28
|
+
//function editables are decided per edit, we can't know it here
|
|
29
|
+
if (typeof editable === 'function') return false;
|
|
30
|
+
return !editable;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* wraps column dom props to add ARIA attributes,
|
|
34
|
+
* consumer's cellDOMProps/headerDOMProps run first and win over ours
|
|
35
|
+
* @param col
|
|
36
|
+
* @param options
|
|
37
|
+
* @returns a new column object
|
|
38
|
+
*/
|
|
39
|
+
function apply_column_a11y(col, options = {}) {
|
|
40
|
+
const {
|
|
41
|
+
role = 'gridcell',
|
|
42
|
+
gridEditable = false,
|
|
43
|
+
markReadonly = true
|
|
44
|
+
} = options;
|
|
45
|
+
const consumerCellDOMProps = col.cellDOMProps;
|
|
46
|
+
const consumerHeaderDOMProps = col.headerDOMProps;
|
|
47
|
+
const ariaLabel = col.ariaLabel;
|
|
48
|
+
return {
|
|
49
|
+
...col,
|
|
50
|
+
cellDOMProps: cellProps => {
|
|
51
|
+
const res = resolve_dom_props(consumerCellDOMProps, cellProps);
|
|
52
|
+
set_default(res, 'role', role);
|
|
53
|
+
set_default(res, 'aria-colindex', cellProps.computedVisibleIndex + 1);
|
|
54
|
+
set_default(res, 'data-column', cellProps.name || cellProps.id);
|
|
55
|
+
if (markReadonly && is_cell_readonly(cellProps, gridEditable)) set_default(res, 'aria-readonly', 'true');
|
|
56
|
+
const data = cellProps.data;
|
|
57
|
+
if (typeof ariaLabel === 'function' && data && !data.__group && !data.__summary) set_default(res, 'aria-label', ariaLabel(cellProps.value, data));
|
|
58
|
+
return res;
|
|
59
|
+
},
|
|
60
|
+
headerDOMProps: cellProps => {
|
|
61
|
+
const res = resolve_dom_props(consumerHeaderDOMProps, cellProps);
|
|
62
|
+
set_default(res, 'role', 'columnheader');
|
|
63
|
+
set_default(res, 'aria-colindex', cellProps.computedVisibleIndex + 1);
|
|
64
|
+
if (cellProps.computedSortable) set_default(res, 'aria-sort', get_sort_dir(cellProps.computedSortInfo));
|
|
65
|
+
return res;
|
|
66
|
+
}
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* adds ARIA attributes to a row, called from onRenderRow
|
|
71
|
+
* @param rowProps
|
|
72
|
+
* @param selectionEnabled
|
|
73
|
+
*/
|
|
74
|
+
function apply_row_a11y(rowProps, selectionEnabled) {
|
|
75
|
+
var _ref, _rowProps$remoteRowIn;
|
|
76
|
+
//filler rows (showEmptyRows) are not data rows
|
|
77
|
+
if (rowProps.empty) return;
|
|
78
|
+
const index = (_ref = (_rowProps$remoteRowIn = rowProps.remoteRowIndex) !== null && _rowProps$remoteRowIn !== void 0 ? _rowProps$remoteRowIn : rowProps.realIndex) !== null && _ref !== void 0 ? _ref : 0;
|
|
79
|
+
rowProps['role'] = 'row';
|
|
80
|
+
rowProps['aria-rowindex'] = index + 2; // 1-based, header row is 1
|
|
81
|
+
if (selectionEnabled) rowProps['aria-selected'] = rowProps.selected ? 'true' : 'false';
|
|
82
|
+
}
|
|
83
|
+
function set_attr(el, key, value) {
|
|
84
|
+
if (value === undefined || value === null || value === '') {
|
|
85
|
+
if (el.hasAttribute(key)) el.removeAttribute(key);
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
const str = String(value);
|
|
89
|
+
if (el.getAttribute(key) !== str) el.setAttribute(key, str);
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* total number of data rows, uses the datasource count when paginated
|
|
93
|
+
* so the real size is reported, not just the visible page
|
|
94
|
+
* @param grid
|
|
95
|
+
*/
|
|
96
|
+
function get_grid_row_count(grid) {
|
|
97
|
+
const g = grid;
|
|
98
|
+
const rendered = Array.isArray(g.data) ? g.data.length : 0;
|
|
99
|
+
if (!g.computedPagination) return rendered;
|
|
100
|
+
const offset = g.computedLivePagination ? 0 : g.computedSkip || 0;
|
|
101
|
+
return Math.max(g.paginationCount || 0, offset + rendered);
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* root element of the grid, null when it isn't mounted
|
|
105
|
+
* @param grid computed props of the grid
|
|
106
|
+
*/
|
|
107
|
+
function get_grid_root(grid) {
|
|
108
|
+
var _grid$getDOMNode;
|
|
109
|
+
return (grid === null || grid === void 0 || (_grid$getDOMNode = grid.getDOMNode) === null || _grid$getDOMNode === void 0 ? void 0 : _grid$getDOMNode.call(grid)) || null;
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* key of the values apply_root_a11y writes, used to skip applying when nothing changed
|
|
113
|
+
* @param grid computed props of the grid
|
|
114
|
+
* @param options
|
|
115
|
+
*/
|
|
116
|
+
function get_root_a11y_key(grid, options) {
|
|
117
|
+
var _options$ariaLabel;
|
|
118
|
+
const g = grid;
|
|
119
|
+
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('|');
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* sets ARIA attributes on the grid root element
|
|
124
|
+
* inovua doesn't forward props to its root, so this is applied on the DOM node
|
|
125
|
+
* @param grid computed props of the grid
|
|
126
|
+
* @param options
|
|
127
|
+
* @returns the element the attributes were applied to, null when the grid isn't mounted
|
|
128
|
+
*/
|
|
129
|
+
function apply_root_a11y(grid, options) {
|
|
130
|
+
const g = grid;
|
|
131
|
+
const el = get_grid_root(g);
|
|
132
|
+
if (!el) return null;
|
|
133
|
+
set_attr(el, 'role', 'grid');
|
|
134
|
+
set_attr(el, 'aria-rowcount', get_grid_row_count(g) + 1);
|
|
135
|
+
set_attr(el, 'aria-colcount', (g.visibleColumns || []).length);
|
|
136
|
+
set_attr(el, 'aria-label', options.ariaLabel);
|
|
137
|
+
set_attr(el, 'aria-readonly', options.editable ? undefined : 'true');
|
|
138
|
+
return el;
|
|
139
|
+
}
|
|
@@ -7,6 +7,7 @@ exports.useGridEvents = useGridEvents;
|
|
|
7
7
|
var _react = _interopRequireDefault(require("react"));
|
|
8
8
|
var _func_grid = require("../func/func_grid");
|
|
9
9
|
var _func_child = require("../func/func_child");
|
|
10
|
+
var _func_a11y = require("../func/func_a11y");
|
|
10
11
|
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
|
|
11
12
|
function useGridMenuRow() {
|
|
12
13
|
const [menuAnchorEl, set_menuAnchorEl] = _react.default.useState(null);
|
|
@@ -79,9 +80,52 @@ function useGridEvents(grid, props) {
|
|
|
79
80
|
return false;
|
|
80
81
|
}, []);
|
|
81
82
|
/* -------------------------------------------------------------------------- */
|
|
83
|
+
/* ------------------------------ Accessibility ----------------------------- */
|
|
84
|
+
const latestProps = _react.default.useRef(props);
|
|
85
|
+
latestProps.current = props;
|
|
86
|
+
const rootSyncFrame = _react.default.useRef(0);
|
|
87
|
+
// last applied root values, so row renders (e.g. while scrolling) don't re-apply them
|
|
88
|
+
const lastRoot = _react.default.useRef({
|
|
89
|
+
el: null,
|
|
90
|
+
key: ''
|
|
91
|
+
});
|
|
92
|
+
const getRootOptions = () => ({
|
|
93
|
+
ariaLabel: latestProps.current.ariaLabel,
|
|
94
|
+
editable: !!latestProps.current.editable
|
|
95
|
+
});
|
|
96
|
+
// root attributes are set on the DOM node, batched to once per frame
|
|
97
|
+
const scheduleRootSync = _react.default.useCallback(() => {
|
|
98
|
+
var _latestProps$current$;
|
|
99
|
+
if (rootSyncFrame.current || typeof requestAnimationFrame === 'undefined') return;
|
|
100
|
+
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
|
+
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;
|
|
103
|
+
rootSyncFrame.current = requestAnimationFrame(() => {
|
|
104
|
+
var _latestProps$current$2;
|
|
105
|
+
rootSyncFrame.current = 0;
|
|
106
|
+
const liveGrid = (_latestProps$current$2 = latestProps.current.liveGridRef) === null || _latestProps$current$2 === void 0 || (_latestProps$current$2 = _latestProps$current$2.current) === null || _latestProps$current$2 === void 0 ? void 0 : _latestProps$current$2.current;
|
|
107
|
+
const options = getRootOptions();
|
|
108
|
+
const el = (0, _func_a11y.apply_root_a11y)(liveGrid, options);
|
|
109
|
+
lastRoot.current = {
|
|
110
|
+
el,
|
|
111
|
+
key: el ? (0, _func_a11y.get_root_a11y_key)(liveGrid, options) : ''
|
|
112
|
+
};
|
|
113
|
+
});
|
|
114
|
+
}, []);
|
|
115
|
+
_react.default.useEffect(() => {
|
|
116
|
+
scheduleRootSync();
|
|
117
|
+
}, [scheduleRootSync, props.ariaLabel, props.editable, props.columns, props.dataSource]);
|
|
118
|
+
_react.default.useEffect(() => () => {
|
|
119
|
+
if (rootSyncFrame.current) cancelAnimationFrame(rootSyncFrame.current);
|
|
120
|
+
rootSyncFrame.current = 0;
|
|
121
|
+
}, []);
|
|
82
122
|
const onRenderRow = _react.default.useCallback(rowProps => {
|
|
123
|
+
var _latestProps$current$3;
|
|
83
124
|
// save the original handlers to be called later
|
|
84
125
|
rowProps['data-row-id'] = (0, _func_grid.get_row_id)(rowProps.data);
|
|
126
|
+
const liveGrid = (_latestProps$current$3 = latestProps.current.liveGridRef) === null || _latestProps$current$3 === void 0 || (_latestProps$current$3 = _latestProps$current$3.current) === null || _latestProps$current$3 === void 0 ? void 0 : _latestProps$current$3.current;
|
|
127
|
+
(0, _func_a11y.apply_row_a11y)(rowProps, !!(liveGrid !== null && liveGrid !== void 0 && liveGrid.computedRowSelectionEnabled));
|
|
128
|
+
scheduleRootSync();
|
|
85
129
|
}, []);
|
|
86
130
|
return {
|
|
87
131
|
onRowClick,
|
|
@@ -28,19 +28,30 @@ export type loose_column_fields = loose_column_names & {
|
|
|
28
28
|
title: string;
|
|
29
29
|
type: string;
|
|
30
30
|
items: any[];
|
|
31
|
+
/** accessible name for cells whose visible text isn't descriptive (icons, badges, ...), set as aria-label */
|
|
32
|
+
ariaLabel: (value: any, data: any) => string;
|
|
31
33
|
};
|
|
32
34
|
export type LooseColumnDefinition = Partial<loose_column_fields & ColumnDefinition>;
|
|
33
35
|
export type CustomColumnDefinition = {
|
|
34
36
|
name: string;
|
|
35
37
|
render: (cell: any, formatterParams: any, onRendered: any) => React.ReactNode | undefined | null;
|
|
36
38
|
};
|
|
37
|
-
export type AzGridOptions = Partial<TypeDataGridProps
|
|
39
|
+
export type AzGridOptions = Partial<TypeDataGridProps> & {
|
|
40
|
+
/** called for every rendered row, after the grid's own row attributes are set */
|
|
41
|
+
onRenderRow?: (rowProps: any) => void;
|
|
42
|
+
/** extra DOM props for the header row */
|
|
43
|
+
headerProps?: {
|
|
44
|
+
[key: string]: any;
|
|
45
|
+
};
|
|
46
|
+
};
|
|
38
47
|
export type AzGridProps = {
|
|
39
48
|
dataSource?: any[] | ((args: any) => any[] | Promise<any[] | any>);
|
|
40
49
|
details_dataSource?: any[] | ((args: any) => any[] | Promise<any[] | any>);
|
|
41
50
|
details_columns?: LooseColumnDefinition[];
|
|
42
51
|
details_filter?: FilterValues;
|
|
43
52
|
licenseKey?: string;
|
|
53
|
+
/** accessible name of the grid, set as aria-label on the grid root */
|
|
54
|
+
ariaLabel?: string;
|
|
44
55
|
gridRef?: React.MutableRefObject<TypeComputedProps>;
|
|
45
56
|
gridOptions?: AzGridOptions;
|
|
46
57
|
columns?: LooseColumnDefinition[];
|
|
@@ -81,6 +92,8 @@ export type DataGridProps = ModifyFields<AzGridProps, {
|
|
|
81
92
|
export type GridType = TypeComputedProps;
|
|
82
93
|
export type DataGridWrapperProps = ModifyFields<DataGridProps, {
|
|
83
94
|
gridRef: React.MutableRefObject<TypeComputedProps>;
|
|
95
|
+
/** internal: inovua's live computed props ref (gridRef holds a snapshot) */
|
|
96
|
+
liveGridRef: React.MutableRefObject<React.MutableRefObject<TypeComputedProps> | null>;
|
|
84
97
|
static_gridOptions: AzGridOptions;
|
|
85
98
|
dynamic_gridOptions: AzGridOptions;
|
|
86
99
|
children?: React.ReactNode;
|
|
@@ -19,6 +19,7 @@ var _useGridEvents = require("../hooks/useGridEvents");
|
|
|
19
19
|
var _useColumnData = require("../hooks/useColumnData");
|
|
20
20
|
var _useDatasource = require("../hooks/useDatasource");
|
|
21
21
|
var _useGridSearch = require("../hooks/useGridSearch");
|
|
22
|
+
var _func_a11y = require("../func/func_a11y");
|
|
22
23
|
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
|
|
23
24
|
/* -------------------------------------------------------------------------- */
|
|
24
25
|
/* -------------------------------------------------------------------------- */
|
|
@@ -43,11 +44,15 @@ const dynamic_option_names = ['selected', 'cellSelection', 'loading', 'limit', '
|
|
|
43
44
|
function AzGrid(props) {
|
|
44
45
|
const gridRefBackup = _react.default.useRef(null);
|
|
45
46
|
const gridRef = props.gridRef || gridRefBackup;
|
|
47
|
+
const liveGridRef = _react.default.useRef(null);
|
|
46
48
|
/* -------------------------------------------------------------------------- */
|
|
47
49
|
/* Columns */
|
|
48
50
|
/* -------------------------------------------------------------------------- */
|
|
49
51
|
const cached_columns = (0, _useColumnData.useColumnData)(props);
|
|
50
52
|
const dataSource = (0, _useDatasource.useDataSource)(props.dataSource, props);
|
|
53
|
+
const a11y_columns = _react.default.useMemo(() => cached_columns.map(col => (0, _func_a11y.apply_column_a11y)(col, {
|
|
54
|
+
gridEditable: !!props.editable
|
|
55
|
+
})), [cached_columns, props.editable]);
|
|
51
56
|
/* -------------------------------------------------------------------------- */
|
|
52
57
|
/* Row changes */
|
|
53
58
|
/* -------------------------------------------------------------------------- */
|
|
@@ -93,9 +98,10 @@ function AzGrid(props) {
|
|
|
93
98
|
return _react.default.createElement(DataGridEventsWrapper, {
|
|
94
99
|
...props,
|
|
95
100
|
gridRef: gridRef,
|
|
101
|
+
liveGridRef: liveGridRef,
|
|
96
102
|
static_gridOptions: static_gridOptions,
|
|
97
103
|
dynamic_gridOptions: dynamic_gridOptions,
|
|
98
|
-
columns:
|
|
104
|
+
columns: a11y_columns,
|
|
99
105
|
dataSource: dataSource,
|
|
100
106
|
highlightedRow: props.highlightedRow
|
|
101
107
|
});
|
|
@@ -135,19 +141,42 @@ function DataGridEventsWrapper(props) {
|
|
|
135
141
|
* Merge click events into grid options
|
|
136
142
|
*/
|
|
137
143
|
const static_gridOptions = _react.default.useMemo(() => {
|
|
144
|
+
const opts = props.static_gridOptions || {};
|
|
138
145
|
const events = {
|
|
139
146
|
onRowClick: onRowClick,
|
|
140
147
|
onRowContextMenu: onRowContextMenu
|
|
141
148
|
};
|
|
149
|
+
// internal row attributes first, consumer's onRenderRow last so it can override them
|
|
150
|
+
const consumerOnRenderRow = opts.onRenderRow;
|
|
142
151
|
const renderProps = {
|
|
143
|
-
onRenderRow:
|
|
152
|
+
onRenderRow: consumerOnRenderRow ? rowProps => {
|
|
153
|
+
onRenderRow(rowProps);
|
|
154
|
+
consumerOnRenderRow(rowProps);
|
|
155
|
+
} : onRenderRow
|
|
144
156
|
};
|
|
157
|
+
const gridEditable = !!props.editable;
|
|
158
|
+
const a11yProps = {
|
|
159
|
+
headerProps: {
|
|
160
|
+
role: 'row',
|
|
161
|
+
'aria-rowindex': 1,
|
|
162
|
+
...opts.headerProps
|
|
163
|
+
},
|
|
164
|
+
rowIndexColumn: (0, _func_a11y.apply_column_a11y)(typeof opts.rowIndexColumn === 'object' ? opts.rowIndexColumn : {}, {
|
|
165
|
+
role: 'rowheader',
|
|
166
|
+
gridEditable
|
|
167
|
+
})
|
|
168
|
+
};
|
|
169
|
+
if (opts.checkboxColumn) a11yProps.checkboxColumn = (0, _func_a11y.apply_column_a11y)(typeof opts.checkboxColumn === 'object' ? opts.checkboxColumn : {}, {
|
|
170
|
+
gridEditable,
|
|
171
|
+
markReadonly: false
|
|
172
|
+
});
|
|
145
173
|
return {
|
|
146
|
-
...
|
|
174
|
+
...opts,
|
|
147
175
|
...events,
|
|
148
|
-
...renderProps
|
|
176
|
+
...renderProps,
|
|
177
|
+
...a11yProps
|
|
149
178
|
};
|
|
150
|
-
}, [props.static_gridOptions]);
|
|
179
|
+
}, [props.static_gridOptions, props.editable, onRowClick, onRowContextMenu, onRenderRow]);
|
|
151
180
|
/* -------------------------------------------------------------------------- */
|
|
152
181
|
//TODO ContextMenu not from mui...
|
|
153
182
|
return _react.default.createElement("div", {
|
|
@@ -257,9 +286,10 @@ function DataGridCore(props) {
|
|
|
257
286
|
}, _react.default.createElement(_reactdatagridEnterprise.default, {
|
|
258
287
|
...props.static_gridOptions,
|
|
259
288
|
...props.dynamic_gridOptions,
|
|
260
|
-
rowIndexColumn: true,
|
|
289
|
+
rowIndexColumn: props.static_gridOptions.rowIndexColumn || true,
|
|
261
290
|
handle: ref => {
|
|
262
291
|
if (ref) props.gridRef.current = ref.current;
|
|
292
|
+
if (props.liveGridRef) props.liveGridRef.current = ref;
|
|
263
293
|
},
|
|
264
294
|
theme: "default-light",
|
|
265
295
|
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'}`),
|
package/package.json
CHANGED
|
@@ -1,68 +1,68 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "@azrico/reactgrid",
|
|
3
|
-
"version": "1.
|
|
4
|
-
"main": "dist/index.js",
|
|
5
|
-
"description": "React Datagrid",
|
|
6
|
-
"author": "Azrideus <MehrdadTavangar@gmail.com>",
|
|
7
|
-
"homepage": ".",
|
|
8
|
-
"scripts": {
|
|
9
|
-
"start": "react-scripts start",
|
|
10
|
-
"build": "tsc && yarn cp_themes && babel dist --out-dir dist",
|
|
11
|
-
"test": "rem",
|
|
12
|
-
"check:theme": "node config/check-theme.cjs",
|
|
13
|
-
"theme:generate": "node config/generate-theme.cjs",
|
|
14
|
-
"cp_themes": "copyfiles ./src/themes/* ./dist/"
|
|
15
|
-
},
|
|
16
|
-
"config-overrides-path": "../../config/config-overrides.js",
|
|
17
|
-
"devDependencies": {
|
|
18
|
-
"@babel/cli": "^7.13.14",
|
|
19
|
-
"@babel/core": "^7.13.15",
|
|
20
|
-
"@babel/eslint-parser": "^7.12.17",
|
|
21
|
-
"@babel/node": "^7.13.13",
|
|
22
|
-
"@babel/plugin-proposal-class-properties": "^7.12.13",
|
|
23
|
-
"@babel/plugin-proposal-nullish-coalescing-operator": "^7.14.5",
|
|
24
|
-
"@babel/preset-env": "^7.13.15",
|
|
25
|
-
"@babel/preset-typescript": "^7.14.5",
|
|
26
|
-
"@mui/material": "*",
|
|
27
|
-
"@types/react": "^18.0.25",
|
|
28
|
-
"@types/react-dom": "^18.0.8",
|
|
29
|
-
"react-dom": "^18.2.0",
|
|
30
|
-
"react-scripts": "^5.0.1",
|
|
31
|
-
"typescript": "^4.8.4"
|
|
32
|
-
},
|
|
33
|
-
"dependencies": {
|
|
34
|
-
"@azrico/controls": "1.6.1",
|
|
35
|
-
"@azrico/object": "^1.4.97",
|
|
36
|
-
"@emotion/react": "^11.9.3",
|
|
37
|
-
"@emotion/styled": "^11.9.3",
|
|
38
|
-
"@inovua/reactdatagrid-enterprise": "^5.10.2",
|
|
39
|
-
"clsx": "^2.0.0",
|
|
40
|
-
"lodash.debounce": "^4.0.8",
|
|
41
|
-
"react": "^18.2.0",
|
|
42
|
-
"react-use-size": "^3.0.1"
|
|
43
|
-
},
|
|
44
|
-
"peerDependencies": {
|
|
45
|
-
"@azrico/controls": ">=1.6.1",
|
|
46
|
-
"@azrico/object": ">=1.4.97",
|
|
47
|
-
"@emotion/react": ">=11.9.3",
|
|
48
|
-
"@emotion/styled": ">=11.9.3",
|
|
49
|
-
"@inovua/reactdatagrid-enterprise": ">=5.10.2",
|
|
50
|
-
"@mui/material": ">=5.0.0 <8.0.0",
|
|
51
|
-
"clsx": ">=1.2.1",
|
|
52
|
-
"react": ">=18.2.0",
|
|
53
|
-
"react-use-size": ">=3.0.1"
|
|
54
|
-
},
|
|
55
|
-
"browserslist": {
|
|
56
|
-
"production": [
|
|
57
|
-
">0.2%",
|
|
58
|
-
"not dead",
|
|
59
|
-
"not op_mini all"
|
|
60
|
-
],
|
|
61
|
-
"development": [
|
|
62
|
-
"last 1 chrome version",
|
|
63
|
-
"last 1 firefox version",
|
|
64
|
-
"last 1 safari version"
|
|
65
|
-
]
|
|
66
|
-
},
|
|
67
|
-
"packageManager": "yarn@4.10.2"
|
|
1
|
+
{
|
|
2
|
+
"name": "@azrico/reactgrid",
|
|
3
|
+
"version": "1.5.1",
|
|
4
|
+
"main": "dist/index.js",
|
|
5
|
+
"description": "React Datagrid",
|
|
6
|
+
"author": "Azrideus <MehrdadTavangar@gmail.com>",
|
|
7
|
+
"homepage": ".",
|
|
8
|
+
"scripts": {
|
|
9
|
+
"start": "react-scripts start",
|
|
10
|
+
"build": "tsc && yarn cp_themes && babel dist --out-dir dist",
|
|
11
|
+
"test": "rem",
|
|
12
|
+
"check:theme": "node config/check-theme.cjs",
|
|
13
|
+
"theme:generate": "node config/generate-theme.cjs",
|
|
14
|
+
"cp_themes": "copyfiles ./src/themes/* ./dist/"
|
|
15
|
+
},
|
|
16
|
+
"config-overrides-path": "../../config/config-overrides.js",
|
|
17
|
+
"devDependencies": {
|
|
18
|
+
"@babel/cli": "^7.13.14",
|
|
19
|
+
"@babel/core": "^7.13.15",
|
|
20
|
+
"@babel/eslint-parser": "^7.12.17",
|
|
21
|
+
"@babel/node": "^7.13.13",
|
|
22
|
+
"@babel/plugin-proposal-class-properties": "^7.12.13",
|
|
23
|
+
"@babel/plugin-proposal-nullish-coalescing-operator": "^7.14.5",
|
|
24
|
+
"@babel/preset-env": "^7.13.15",
|
|
25
|
+
"@babel/preset-typescript": "^7.14.5",
|
|
26
|
+
"@mui/material": "*",
|
|
27
|
+
"@types/react": "^18.0.25",
|
|
28
|
+
"@types/react-dom": "^18.0.8",
|
|
29
|
+
"react-dom": "^18.2.0",
|
|
30
|
+
"react-scripts": "^5.0.1",
|
|
31
|
+
"typescript": "^4.8.4"
|
|
32
|
+
},
|
|
33
|
+
"dependencies": {
|
|
34
|
+
"@azrico/controls": "1.6.1",
|
|
35
|
+
"@azrico/object": "^1.4.97",
|
|
36
|
+
"@emotion/react": "^11.9.3",
|
|
37
|
+
"@emotion/styled": "^11.9.3",
|
|
38
|
+
"@inovua/reactdatagrid-enterprise": "^5.10.2",
|
|
39
|
+
"clsx": "^2.0.0",
|
|
40
|
+
"lodash.debounce": "^4.0.8",
|
|
41
|
+
"react": "^18.2.0",
|
|
42
|
+
"react-use-size": "^3.0.1"
|
|
43
|
+
},
|
|
44
|
+
"peerDependencies": {
|
|
45
|
+
"@azrico/controls": ">=1.6.1",
|
|
46
|
+
"@azrico/object": ">=1.4.97",
|
|
47
|
+
"@emotion/react": ">=11.9.3",
|
|
48
|
+
"@emotion/styled": ">=11.9.3",
|
|
49
|
+
"@inovua/reactdatagrid-enterprise": ">=5.10.2",
|
|
50
|
+
"@mui/material": ">=5.0.0 <8.0.0",
|
|
51
|
+
"clsx": ">=1.2.1",
|
|
52
|
+
"react": ">=18.2.0",
|
|
53
|
+
"react-use-size": ">=3.0.1"
|
|
54
|
+
},
|
|
55
|
+
"browserslist": {
|
|
56
|
+
"production": [
|
|
57
|
+
">0.2%",
|
|
58
|
+
"not dead",
|
|
59
|
+
"not op_mini all"
|
|
60
|
+
],
|
|
61
|
+
"development": [
|
|
62
|
+
"last 1 chrome version",
|
|
63
|
+
"last 1 firefox version",
|
|
64
|
+
"last 1 safari version"
|
|
65
|
+
]
|
|
66
|
+
},
|
|
67
|
+
"packageManager": "yarn@4.10.2"
|
|
68
68
|
}
|