@azrico/reactgrid 1.6.1 → 1.6.4

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.
@@ -4,11 +4,13 @@ Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
6
  exports.apply_column_a11y = apply_column_a11y;
7
+ exports.apply_header_a11y = apply_header_a11y;
7
8
  exports.apply_root_a11y = apply_root_a11y;
8
9
  exports.apply_row_a11y = apply_row_a11y;
9
10
  exports.get_grid_root = get_grid_root;
10
11
  exports.get_grid_row_count = get_grid_row_count;
11
12
  exports.get_root_a11y_key = get_root_a11y_key;
13
+ exports.has_unsynced_header = has_unsynced_header;
12
14
  exports.is_grid_busy = is_grid_busy;
13
15
  function set_default(target, key, value) {
14
16
  if (target[key] === undefined && value !== undefined && value !== null) target[key] = value;
@@ -38,11 +40,13 @@ function is_cell_readonly(cellProps, gridEditable) {
38
40
  * @returns a new column object
39
41
  */
40
42
  function apply_column_a11y(col, options = {}) {
43
+ var _ref, _col$id;
41
44
  const {
42
45
  role = 'gridcell',
43
46
  gridEditable = false,
44
47
  markReadonly = true
45
48
  } = options;
49
+ const id = (_ref = (_col$id = col.id) !== null && _col$id !== void 0 ? _col$id : col.name) !== null && _ref !== void 0 ? _ref : options.id;
46
50
  const consumerCellDOMProps = col.cellDOMProps;
47
51
  const consumerHeaderDOMProps = col.headerDOMProps;
48
52
  const ariaLabel = col.ariaLabel;
@@ -58,25 +62,40 @@ function apply_column_a11y(col, options = {}) {
58
62
  if (typeof ariaLabel === 'function' && data && !data.__group && !data.__summary) set_default(res, 'aria-label', ariaLabel(cellProps.value, data));
59
63
  return res;
60
64
  },
61
- headerDOMProps: cellProps => {
62
- const res = resolve_dom_props(consumerHeaderDOMProps, cellProps);
63
- set_default(res, 'role', 'columnheader');
64
- set_default(res, 'aria-colindex', cellProps.computedVisibleIndex + 1);
65
- if (cellProps.computedSortable) set_default(res, 'aria-sort', get_sort_dir(cellProps.computedSortInfo));
66
- return res;
67
- }
65
+ headerDOMProps: get_header_dom_props(consumerHeaderDOMProps, id)
68
66
  };
69
67
  }
68
+ /**
69
+ * header DOM props as an object (inovua's prop types reject a function),
70
+ * aria-colindex/aria-sort change with the grid state and are set by apply_header_a11y
71
+ * @param consumer consumer's headerDOMProps
72
+ * @param id column id, looked up in the grid's columnsMap
73
+ */
74
+ function get_header_dom_props(consumer, id) {
75
+ //a consumer function is kept (inovua runs it), it gets the attributes from the cell props
76
+ if (typeof consumer === 'function') return cellProps => {
77
+ const res = resolve_dom_props(consumer, cellProps);
78
+ set_default(res, 'role', 'columnheader');
79
+ set_default(res, 'data-column', id);
80
+ set_default(res, 'aria-colindex', cellProps.computedVisibleIndex + 1);
81
+ if (cellProps.computedSortable) set_default(res, 'aria-sort', get_sort_dir(cellProps.computedSortInfo));
82
+ return res;
83
+ };
84
+ const res = resolve_dom_props(consumer, undefined);
85
+ set_default(res, 'role', 'columnheader');
86
+ set_default(res, 'data-column', id);
87
+ return res;
88
+ }
70
89
  /**
71
90
  * adds ARIA attributes to a row, called from onRenderRow
72
91
  * @param rowProps
73
92
  * @param selectionEnabled
74
93
  */
75
94
  function apply_row_a11y(rowProps, selectionEnabled) {
76
- var _ref, _rowProps$remoteRowIn;
95
+ var _ref2, _rowProps$remoteRowIn;
77
96
  //filler rows (showEmptyRows) are not data rows
78
97
  if (rowProps.empty) return;
79
- const index = (_ref = (_rowProps$remoteRowIn = rowProps.remoteRowIndex) !== null && _rowProps$remoteRowIn !== void 0 ? _rowProps$remoteRowIn : rowProps.realIndex) !== null && _ref !== void 0 ? _ref : 0;
98
+ const index = (_ref2 = (_rowProps$remoteRowIn = rowProps.remoteRowIndex) !== null && _rowProps$remoteRowIn !== void 0 ? _rowProps$remoteRowIn : rowProps.realIndex) !== null && _ref2 !== void 0 ? _ref2 : 0;
80
99
  rowProps['role'] = 'row';
81
100
  rowProps['aria-rowindex'] = index + 2; // 1-based, header row is 1
82
101
  if (selectionEnabled) rowProps['aria-selected'] = rowProps.selected ? 'true' : 'false';
@@ -101,6 +120,34 @@ function get_grid_row_count(grid) {
101
120
  const offset = g.computedLivePagination ? 0 : g.computedSkip || 0;
102
121
  return Math.max(g.paginationCount || 0, offset + rendered);
103
122
  }
123
+ const HEADER_CELL_SELECTOR = '[role="columnheader"][data-column]';
124
+ /**
125
+ * sets aria-colindex/aria-sort on the header cells of the grid,
126
+ * attributes the consumer set in an object headerDOMProps are left alone
127
+ * @param grid computed props of the grid
128
+ * @param root root element of the grid
129
+ */
130
+ function apply_header_a11y(grid, root) {
131
+ const columnsMap = grid === null || grid === void 0 ? void 0 : grid.columnsMap;
132
+ if (!columnsMap) return;
133
+ root.querySelectorAll(HEADER_CELL_SELECTOR).forEach(el => {
134
+ //skip headers of grids nested in row details
135
+ if (el.closest('[role="grid"]') !== root) return;
136
+ const col = columnsMap[el.getAttribute('data-column')];
137
+ const own = col === null || col === void 0 ? void 0 : col.headerDOMProps;
138
+ //a function headerDOMProps sets the attributes itself
139
+ if (!own || typeof own !== 'object') return;
140
+ if (own['aria-colindex'] === undefined) set_attr(el, 'aria-colindex', col.computedVisibleIndex + 1);
141
+ if (own['aria-sort'] === undefined) set_attr(el, 'aria-sort', col.computedSortable ? get_sort_dir(col.computedSortInfo) : undefined);
142
+ });
143
+ }
144
+ /**
145
+ * true when a header cell was (re)mounted without its aria-colindex
146
+ * @param root root element of the grid
147
+ */
148
+ function has_unsynced_header(root) {
149
+ return !!(root !== null && root !== void 0 && root.querySelector(`${HEADER_CELL_SELECTOR}:not([aria-colindex])`));
150
+ }
104
151
  /**
105
152
  * true while the grid is loading (loading prop or async datasource)
106
153
  * @param grid computed props of the grid
@@ -125,7 +172,12 @@ function get_root_a11y_key(grid, options) {
125
172
  var _options$ariaLabel;
126
173
  const g = grid;
127
174
  if (!g) return '';
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('|');
175
+ 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,
176
+ //header cells: order and sort direction
177
+ (g.visibleColumns || []).map(c => {
178
+ var _c$computedSortInfo;
179
+ return `${c.id}:${((_c$computedSortInfo = c.computedSortInfo) === null || _c$computedSortInfo === void 0 ? void 0 : _c$computedSortInfo.dir) || 0}`;
180
+ }).join(',')].join('|');
129
181
  }
130
182
  /**
131
183
  * sets ARIA attributes on the grid root element
@@ -144,5 +196,6 @@ function apply_root_a11y(grid, options) {
144
196
  set_attr(el, 'aria-label', options.ariaLabel);
145
197
  set_attr(el, 'aria-readonly', options.editable ? undefined : 'true');
146
198
  set_attr(el, 'aria-busy', is_grid_busy(g) ? 'true' : undefined);
199
+ apply_header_a11y(g, el);
147
200
  return el;
148
201
  }
@@ -14,6 +14,12 @@ export declare function ApplyDataSource(datasource: any, props: any, remote?: {
14
14
  * @returns
15
15
  */
16
16
  export declare function applyColumnData(column_data: any): ColumnDefinition[];
17
+ /**
18
+ * converts select items to an editor dataSource, plain values keep their case as the id
19
+ * @param arr
20
+ * @param idProperty
21
+ * @param valueAsID false uses generated ids instead of the values
22
+ */
17
23
  export declare function to_datasource(arr: any[], idProperty: string, valueAsID?: boolean): any[];
18
24
  /**
19
25
  * Gets name of a column from its column object
@@ -20,10 +20,10 @@ var _object = require("@azrico/object");
20
20
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
21
21
  // import NumericEditor from '@inovua/reactdatagrid-community/NumericEditor';
22
22
 
23
+ //editable is defaulted after types/extensions are applied, so they can tell if the consumer set it
23
24
  const defaultColumnOptions = {
24
25
  draggable: false,
25
- lockable: false,
26
- editable: false
26
+ lockable: false
27
27
  };
28
28
  async function ApplyDataSource(datasource, props, remote = {}) {
29
29
  let result_data = undefined;
@@ -90,6 +90,8 @@ function applyColumnData(column_data) {
90
90
  let editableColumns = column_data.editableColumns || [];
91
91
  let customColumns = column_data.customColumns || [];
92
92
  if (!Array.isArray(mcols)) return [];
93
+ //columns given as objects are readonly unless something makes them editable
94
+ const object_columns = new Set();
93
95
  /* ------------------------------- fix columns ------------------------------ */
94
96
  mcols.map(r => {
95
97
  if (r == null) return null;
@@ -108,6 +110,7 @@ function applyColumnData(column_data) {
108
110
  ...r,
109
111
  ...final_cl
110
112
  };
113
+ object_columns.add(final_cl);
111
114
  return final_cl;
112
115
  }
113
116
  return null;
@@ -182,8 +185,10 @@ function applyColumnData(column_data) {
182
185
  col.editor = _BoolEditor.default;
183
186
  break;
184
187
  }
185
- if (col.editable != false) col.editable = true;
188
+ //typed columns are editable unless the consumer set editable (a function decides per cell)
189
+ if (col.editable === undefined) col.editable = true;
186
190
  }
191
+ if (col.editable === undefined && object_columns.has(col)) col.editable = false;
187
192
  //@ts-ignore
188
193
  if (!col.editorProps) col.editorProps = {};
189
194
  if (col.items) {
@@ -223,13 +228,19 @@ function applyColumnData(column_data) {
223
228
  //console.log(final_columns);
224
229
  return final_columns;
225
230
  }
231
+ /**
232
+ * converts select items to an editor dataSource, plain values keep their case as the id
233
+ * @param arr
234
+ * @param idProperty
235
+ * @param valueAsID false uses generated ids instead of the values
236
+ */
226
237
  function to_datasource(arr, idProperty, valueAsID = true) {
227
238
  const res = [];
228
239
  let index = 0;
229
240
  if (Array.isArray(arr)) {
230
241
  arr.map(s => {
231
242
  let elem = {};
232
- if (valueAsID) elem[idProperty] = String(s).toLowerCase();else elem[idProperty] = '_cid_' + index;
243
+ if (valueAsID) elem[idProperty] = String(s);else elem[idProperty] = '_cid_' + index;
233
244
  if (typeof s === 'object') elem = {
234
245
  ...elem,
235
246
  ...s
@@ -60,15 +60,16 @@ function useGridEvents(grid, props) {
60
60
  onRowDoubleClick
61
61
  } = latestProps.current;
62
62
  const nowDate = Date.now();
63
- const lastclick = click_data.current[(0, _func_grid.get_row_id)(rowProps)];
63
+ const rowId = (0, _func_grid.get_row_id)(rowProps.data);
64
+ const lastclick = click_data.current[rowId];
64
65
  if (lastclick) {
65
66
  const diffTime = Math.abs(nowDate - lastclick);
66
67
  if (diffTime < 400) {
67
- click_data.current[(0, _func_grid.get_row_id)(rowProps)] = null;
68
+ click_data.current[rowId] = null;
68
69
  return onRowDoubleClick && onRowDoubleClick(rowProps.rowIndex, rowProps.data);
69
70
  }
70
71
  }
71
- click_data.current[(0, _func_grid.get_row_id)(rowProps)] = nowDate;
72
+ click_data.current[rowId] = nowDate;
72
73
  return onRowClick && onRowClick(rowProps.rowIndex, rowProps.data);
73
74
  }, []);
74
75
  _react.default.useEffect(() => {
@@ -99,13 +100,13 @@ function useGridEvents(grid, props) {
99
100
  ariaLabel: latestProps.current.ariaLabel,
100
101
  editable: !!latestProps.current.editable
101
102
  });
102
- // root attributes are set on the DOM node, batched to once per frame
103
+ // root and header cell attributes are set on the DOM nodes, batched to once per frame
103
104
  const scheduleRootSync = _react.default.useCallback((force = false) => {
104
105
  var _latestProps$current$;
105
106
  if (rootSyncFrame.current || typeof requestAnimationFrame === 'undefined') return;
106
107
  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;
107
108
  const key = (0, _func_a11y.get_root_a11y_key)(grid, getRootOptions());
108
- if (!force && key && key === lastRoot.current.key && (0, _func_a11y.get_grid_root)(grid) === lastRoot.current.el) return;
109
+ if (!force && key && key === lastRoot.current.key && (0, _func_a11y.get_grid_root)(grid) === lastRoot.current.el && !(0, _func_a11y.has_unsynced_header)(lastRoot.current.el)) return;
109
110
  rootSyncFrame.current = requestAnimationFrame(() => {
110
111
  var _latestProps$current$2;
111
112
  rootSyncFrame.current = 0;
@@ -1,7 +1,7 @@
1
1
  /* Token overrides for Inovua 5.10.2. Generated by config/generate-theme.cjs.
2
2
  * index.css retains layout and icons. Public tokens are inherited; preset tokens
3
3
  * are fallback implementation details. Do not set public defaults on the root. */
4
- .azgrid-scope, .azgrid, .azgrid-theme-light {
4
+ .azgrid-scope, .azgrid, .azgrid-theme-light, .azgrid-context-menu {
5
5
  --azgrid-preset-bg: #fff;
6
6
  --azgrid-preset-fg: #555e68;
7
7
  --azgrid-preset-border: #e4e3e2;
@@ -2010,23 +2010,18 @@
2010
2010
  .azgrid-scope .inovua-react-toolkit-menu {
2011
2011
  border-radius: var(--azgrid-radius, var(--azgrid-preset-radius));
2012
2012
  }
2013
- /* Three classes exceed MUI's generated Paper/MenuItem selector specificity. */
2014
- .azgrid-scope .azgrid-context-menu.MuiPaper-root {
2015
- background: var(--azgrid-menu-bg, var(--azgrid-preset-menu-bg));
2016
- color: var(--azgrid-menu-fg, var(--azgrid-preset-menu-fg));
2013
+ /* The row context menu is portaled to <body>, outside .azgrid-scope: these rules stay
2014
+ * unscoped and read the tokens from the theme class DataGrid puts on the menu.
2015
+ * Two classes exceed @azrico/controls' single-class menu rules. */
2016
+ .azgrid-context-menu.azrico-controls-contextmenu {
2017
+ --azrico-contextmenu-color: var(--azgrid-menu-fg, var(--azgrid-preset-menu-fg));
2018
+ --azrico-contextmenu-background: var(--azgrid-menu-bg, var(--azgrid-preset-menu-bg));
2019
+ --azrico-contextmenu-hover: var(--azgrid-menu-bg-hover, var(--azgrid-preset-menu-bg-hover));
2020
+ --azrico-contextmenu-focus: var(--azgrid-accent, var(--azgrid-preset-accent));
2021
+ --azrico-contextmenu-radius: var(--azgrid-radius, var(--azgrid-preset-radius));
2022
+ --azrico-contextmenu-shadow: 0 3px 8px var(--azgrid-shadow-color, var(--azgrid-preset-shadow-color));
2023
+ --azrico-contextmenu-font: var(--azgrid-font-family, var(--azgrid-preset-font-family));
2017
2024
  border: 1px solid var(--azgrid-menu-border, var(--azgrid-preset-menu-border));
2018
- border-radius: var(--azgrid-radius, var(--azgrid-preset-radius));
2019
- box-shadow: 0 3px 8px var(--azgrid-shadow-color, var(--azgrid-preset-shadow-color));
2020
- font-family: var(--azgrid-font-family, var(--azgrid-preset-font-family));
2021
- }
2022
- .azgrid-scope .azgrid-context-menu .MuiTypography-root,
2023
- .azgrid-scope .azgrid-context-menu .MuiMenuItem-root {
2024
- color: inherit;
2025
- font-family: inherit;
2026
- }
2027
- .azgrid-scope .azgrid-context-menu .MuiMenuItem-root:hover,
2028
- .azgrid-scope .azgrid-context-menu .MuiMenuItem-root.Mui-focusVisible {
2029
- background: var(--azgrid-menu-bg-hover, var(--azgrid-preset-menu-bg-hover));
2030
2025
  }
2031
2026
  .azgrid-scope, .azgrid-scope * {
2032
2027
  accent-color: var(--azgrid-accent, var(--azgrid-preset-accent));
@@ -73,6 +73,8 @@ export type AzGridProps = {
73
73
  rightColumns?: string[];
74
74
  customColumns?: CustomColumnDefinition[];
75
75
  contextMenuItems?: ConvertableToMenuItem;
76
+ /** Extra classes on the row context menu, which is portaled to <body> */
77
+ contextMenuClassName?: string;
76
78
  highlightedRow?: any;
77
79
  RowDetail?: RowDetailsFunc;
78
80
  selected?: RowSelectionValue;
@@ -100,6 +102,8 @@ export type AzGridProps = {
100
102
  allowChildRows?: boolean;
101
103
  looseCacheCheck?: boolean;
102
104
  className?: string;
105
+ /** rendered after the grid, inside its container */
106
+ children?: React.ReactNode;
103
107
  };
104
108
  export type DataGridProps = ModifyFields<AzGridProps, {
105
109
  columns: ColumnDefinition[];
@@ -11,7 +11,6 @@ require("../themes/grid.css");
11
11
  var _func_grid = require("../func/func_grid");
12
12
  var _contextMenu = require("@azrico/controls/context-menu");
13
13
  var _react = _interopRequireDefault(require("react"));
14
- var _styles = require("@mui/material/styles");
15
14
  var _reactdatagridEnterprise = _interopRequireDefault(require("@inovua/reactdatagrid-enterprise"));
16
15
  var _clsx = _interopRequireDefault(require("clsx"));
17
16
  var _reactUseSize = require("react-use-size");
@@ -20,6 +19,8 @@ var _useColumnData = require("../hooks/useColumnData");
20
19
  var _useDatasource = require("../hooks/useDatasource");
21
20
  var _useGridSearch = require("../hooks/useGridSearch");
22
21
  var _func_a11y = require("../func/func_a11y");
22
+ var _defaultRowIndexColumnId = require("@inovua/reactdatagrid-community/normalizeColumns/defaultRowIndexColumnId");
23
+ var _defaultCheckColumnId = require("@inovua/reactdatagrid-community/normalizeColumns/defaultCheckColumnId");
23
24
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
24
25
  /* -------------------------------------------------------------------------- */
25
26
  /* -------------------------------------------------------------------------- */
@@ -121,18 +122,6 @@ function AzGrid(props) {
121
122
  */
122
123
  function DataGridEventsWrapper(props) {
123
124
  var _props$theme;
124
- const scopeRef = _react.default.useRef(null);
125
- const hostTheme = (0, _styles.useTheme)();
126
- // Keep the existing context menu and its portal in this grid's CSS scope.
127
- const menuTheme = _react.default.useMemo(() => (0, _styles.createTheme)(hostTheme, {
128
- components: {
129
- MuiPopper: {
130
- defaultProps: {
131
- container: () => scopeRef.current
132
- }
133
- }
134
- }
135
- }), [hostTheme]);
136
125
  const themeClass = `azgrid-theme-${(_props$theme = props.theme) !== null && _props$theme !== void 0 ? _props$theme : props.dark ? 'dark' : 'light'}`;
137
126
  /* -------------------------------------------------------------------------- */
138
127
  const {
@@ -176,12 +165,14 @@ function DataGridEventsWrapper(props) {
176
165
  },
177
166
  rowIndexColumn: (0, _func_a11y.apply_column_a11y)(typeof opts.rowIndexColumn === 'object' ? opts.rowIndexColumn : {}, {
178
167
  role: 'rowheader',
179
- gridEditable
168
+ gridEditable,
169
+ id: _defaultRowIndexColumnId.id
180
170
  })
181
171
  };
182
172
  if (opts.checkboxColumn) a11yProps.checkboxColumn = (0, _func_a11y.apply_column_a11y)(typeof opts.checkboxColumn === 'object' ? opts.checkboxColumn : {}, {
183
173
  gridEditable,
184
- markReadonly: false
174
+ markReadonly: false,
175
+ id: _defaultCheckColumnId.id
185
176
  });
186
177
  return {
187
178
  ...opts,
@@ -191,20 +182,17 @@ function DataGridEventsWrapper(props) {
191
182
  };
192
183
  }, [props.static_gridOptions, props.editable, onRowClick, onRowContextMenu, onRenderRow, onRenderLoadMask]);
193
184
  /* -------------------------------------------------------------------------- */
194
- //TODO ContextMenu not from mui...
185
+ // The menu is portaled to <body>, so it carries the theme class itself to get the theme tokens.
195
186
  return _react.default.createElement("div", {
196
- ref: scopeRef,
197
187
  className: (0, _clsx.default)('azgrid-scope azgrid-flexed azgrid-flexlayout', themeClass)
198
- }, _react.default.createElement(_styles.ThemeProvider, {
199
- theme: menuTheme
200
188
  }, _react.default.createElement(_contextMenu.ContextMenu, {
201
- classname: "azgrid-context-menu",
189
+ classname: (0, _clsx.default)('azgrid-context-menu', themeClass, props.contextMenuClassName),
202
190
  menuAnchorEl: menuAnchorEl,
203
191
  menuRow: menuRow,
204
192
  close: update_menuRow,
205
193
  dontAutoClose: false,
206
194
  menu_items: props.contextMenuItems
207
- })), props.details_dataSource ? _react.default.createElement(DataGridDetailsWrapper, {
195
+ }), props.details_dataSource ? _react.default.createElement(DataGridDetailsWrapper, {
208
196
  ...props,
209
197
  static_gridOptions: static_gridOptions
210
198
  }) : _react.default.createElement(DataGridSizeWrapper, {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@azrico/reactgrid",
3
- "version": "1.6.1",
3
+ "version": "1.6.4",
4
4
  "main": "dist/index.js",
5
5
  "description": "React Datagrid",
6
6
  "author": "Azrideus <MehrdadTavangar@gmail.com>",