@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 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
- const any_child = converted_rows.find(r => Boolean(r.parent));
31
- if (any_child) {
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, converted_rows);
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, all_rows) {
41
- const my_children = all_rows.filter(s => row.self == s.parent);
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, all_rows);
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): import("@inovua/reactdatagrid-community/types").TypeColumn[];
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
- if (inputOrProps == null) return [];
12
- if (Array.isArray(inputOrProps)) return useThisColumnData(inputOrProps);
13
- return usePropsColumnData(inputOrProps);
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
- return (0, _func_grid.applyColumnData)(cols);
18
- }, [cols]);
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 props.onRowDoubleClick && props.onRowDoubleClick(rowProps.rowIndex, rowProps.data);
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 props.onRowClick && props.onRowClick(rowProps.rowIndex, rowProps.data);
66
- }, [props.onRowClick, props.onRowDoubleClick]);
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, GridType } from '../types/types';
3
- export declare function useGridSearch(grid: GridType, props: DataGridWrapperProps): {
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
- function useGridSearch(grid, props) {
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
- const [filteredData, set_filteredData] = _react.default.useState([]);
14
- const currentSearch = _react.default.useRef('');
15
- const currentDS = _react.default.useRef([]);
16
- const try_search = _react.default.useCallback((0, _lodash.default)(() => {
17
- if (!Array.isArray(currentDS.current)) {
18
- return;
19
- }
20
- let newData = [];
21
- if (currentSearch.current) {
22
- const visibleColumns = grid.visibleColumns;
23
- const columns_to_search = (0, _object.array_intersection)(props.advancedFilterFields || [], visibleColumns.map(r => r.id));
24
- const lowerSearchText = currentSearch.current.toLowerCase().trim();
25
- if (!lowerSearchText || columns_to_search.length === 0) newData = currentDS.current;else {
26
- newData = currentDS.current.filter(p => {
27
- for (let index = 0; index < columns_to_search.length; index++) {
28
- const col = columns_to_search[index];
29
- const v = (p[col] + '').toLowerCase(); //value of column
30
- if (v && v.indexOf(lowerSearchText) != -1) {
31
- //if one column is matching then the row is a match
32
- return true;
33
- }
34
- }
35
- return false;
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
- } else newData = currentDS.current;
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
- }, [props.dataSource]);
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
- currentSearch.current = search;
49
- searchIfNeeded();
50
- }, [search]);
51
- function searchIfNeeded() {
52
- if (!search) {
53
- set_filteredData(props.dataSource);
54
- } else try_search();
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
- const events = {
63
- onSkipChange: props.onSkipChange,
64
- onLimitChange: props.onLimitChange,
65
- onEditComplete: props.onEditComplete
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.onEditComplete]);
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.gridRef.current, 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
- function DataGridCore(props) {
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: ref => {
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@azrico/reactgrid",
3
- "version": "1.5.1",
3
+ "version": "1.5.2",
4
4
  "main": "dist/index.js",
5
5
  "description": "React Datagrid",
6
6
  "author": "Azrideus <MehrdadTavangar@gmail.com>",
@@ -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
  }