@azrico/reactgrid 1.6.12 → 1.6.13

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,37 +1,39 @@
1
- # Changelog
2
-
3
- All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
4
-
5
- ### Unreleased
6
-
7
- * Fix rows stopping 3px short of the grid's right and bottom edges. The grid's native scroller is now hidden (`scrollbar-width: none`) in the base theme stylesheet; `scrollbar-color` made Chrome use a 15px standard scrollbar that overflowed the 12px offset. The custom scrollbars are unaffected.
8
- * Make closing the row context menu with Escape opt-in through `escapeClosesContextMenu` (default `false`).
9
- * Keep the right-clicked row highlighted with the theme's hover colours while its context menu is open, preserving caller row classes and clearing the highlight on close. Correct row hover rules to use hover tokens instead of empty-state tokens.
10
- * Add `dontAutoCloseContextMenu` to keep the row context menu open on outside clicks, and `hideContextMenu` to hide it while leaving the browser's context menu available.
11
- * Fix columns with a `type` (`number`, `select`, `bool`, ...) staying read-only unless they were listed in `editableColumns`. A typed column is editable unless you set `editable: false`.
12
- * Fix a typed column's `editable` function being replaced by `true`. Functions and booleans are kept, so per-cell editability works again.
13
- * Fix select `items` given as plain strings storing a lowercased value (`"Shelf A"` was saved as `"shelf a"`). The item id is now the original string.
14
- * Fix the "Failed prop type: Invalid prop `headerDOMProps` of type `function`" warning on every grid. `headerDOMProps` is now an object; `aria-colindex` and `aria-sort` are set on the header cells from the grid state. Header cells also get `data-column`.
15
- * Add `children` to `AzGridProps`, rendered after the grid.
16
- * Fix the row context menu ignoring the grid theme with `@azrico/controls` 1.6.2, whose menu is no longer MUI-based and is portaled to `<body>`. The menu now gets the grid's theme class (`azgrid-theme-<name>`) and is styled through the controls' `--azrico-contextmenu-*` variables from the `--azgrid-menu-*` tokens. The MUI `ThemeProvider` around the menu is removed.
17
- * Add `contextMenuClassName` to add your own classes to the context menu. Use it to bring host variables set on a wrapper element, which the portaled menu no longer inherits.
18
-
19
- ### 1.6.5 (2026-10-15)
20
-
21
- * Leave the browser's context menu available when a row has no resolved `contextMenuItems`, and close any previously open grid menu. Respect right-clicks already handled by nested row details.
22
-
23
- ### 1.7.1 (2026-10-14)
24
-
25
- * Fix two quick clicks on different rows firing `onRowDoubleClick`: the double-click check now keys clicks by the row's id instead of `undefined`.
26
-
27
- ### 1.7.0 (2026-10-14)
28
-
29
- * Add `advancedFilterLabel` to rename the quick-search label (default "Search").
30
- * Add `advancedFilterPlaceholder` for the quick-search input. A string is the native placeholder; any other node (custom HTML) is shown over the empty input.
31
-
32
- ### 1.6.0 (2026-10-14)
33
-
34
- * Pass `filterValue`, `sortInfo` and `groupBy` to a `dataSource` function next to `skip`, `limit`, `cached_data` and `last_cache_info`, so header filters can drive a server-side search.
35
- * With `gridOptions.remoteFilter` / `remoteSort`, a filter or sort change invalidates the data cache and calls `dataSource` again. Local filtering and sorting still reuse the cached rows.
36
- * Export the typed `AzDataSourceParams` and `AzDataSourceFunc` for the `dataSource` and `details_dataSource` props.
37
- * Re-export `SelectFilter`, `NumberFilter`, `DateFilter` and `BoolFilter` for `column.filterEditor`.
1
+ # Changelog
2
+
3
+ All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
4
+
5
+ ### Unreleased
6
+
7
+ * Add `gridId` to save the column widths a user resizes in `localStorage` and restore them on the next load. A saved width replaces the column's `width`/`flex`, saved names that are no longer columns are ignored, and storage errors are ignored. With a `gridId`, a column's `width` is used as `defaultWidth` so the column can be resized. Without it nothing changes.
8
+ * Export `setGridStorageScope(scope)` to scope saved sizes per user (call it before the grids render) and `clearColumnSizes(gridId)` to reset a grid's saved sizes.
9
+ * Fix rows stopping 3px short of the grid's right and bottom edges. The grid's native scroller is now hidden (`scrollbar-width: none`) in the base theme stylesheet; `scrollbar-color` made Chrome use a 15px standard scrollbar that overflowed the 12px offset. The custom scrollbars are unaffected.
10
+ * Make closing the row context menu with Escape opt-in through `escapeClosesContextMenu` (default `false`).
11
+ * Keep the right-clicked row highlighted with the theme's hover colours while its context menu is open, preserving caller row classes and clearing the highlight on close. Correct row hover rules to use hover tokens instead of empty-state tokens.
12
+ * Add `dontAutoCloseContextMenu` to keep the row context menu open on outside clicks, and `hideContextMenu` to hide it while leaving the browser's context menu available.
13
+ * Fix columns with a `type` (`number`, `select`, `bool`, ...) staying read-only unless they were listed in `editableColumns`. A typed column is editable unless you set `editable: false`.
14
+ * Fix a typed column's `editable` function being replaced by `true`. Functions and booleans are kept, so per-cell editability works again.
15
+ * Fix select `items` given as plain strings storing a lowercased value (`"Shelf A"` was saved as `"shelf a"`). The item id is now the original string.
16
+ * Fix the "Failed prop type: Invalid prop `headerDOMProps` of type `function`" warning on every grid. `headerDOMProps` is now an object; `aria-colindex` and `aria-sort` are set on the header cells from the grid state. Header cells also get `data-column`.
17
+ * Add `children` to `AzGridProps`, rendered after the grid.
18
+ * Fix the row context menu ignoring the grid theme with `@azrico/controls` 1.6.2, whose menu is no longer MUI-based and is portaled to `<body>`. The menu now gets the grid's theme class (`azgrid-theme-<name>`) and is styled through the controls' `--azrico-contextmenu-*` variables from the `--azgrid-menu-*` tokens. The MUI `ThemeProvider` around the menu is removed.
19
+ * Add `contextMenuClassName` to add your own classes to the context menu. Use it to bring host variables set on a wrapper element, which the portaled menu no longer inherits.
20
+
21
+ ### 1.6.5 (2026-10-15)
22
+
23
+ * Leave the browser's context menu available when a row has no resolved `contextMenuItems`, and close any previously open grid menu. Respect right-clicks already handled by nested row details.
24
+
25
+ ### 1.7.1 (2026-10-14)
26
+
27
+ * Fix two quick clicks on different rows firing `onRowDoubleClick`: the double-click check now keys clicks by the row's id instead of `undefined`.
28
+
29
+ ### 1.7.0 (2026-10-14)
30
+
31
+ * Add `advancedFilterLabel` to rename the quick-search label (default "Search").
32
+ * Add `advancedFilterPlaceholder` for the quick-search input. A string is the native placeholder; any other node (custom HTML) is shown over the empty input.
33
+
34
+ ### 1.6.0 (2026-10-14)
35
+
36
+ * Pass `filterValue`, `sortInfo` and `groupBy` to a `dataSource` function next to `skip`, `limit`, `cached_data` and `last_cache_info`, so header filters can drive a server-side search.
37
+ * With `gridOptions.remoteFilter` / `remoteSort`, a filter or sort change invalidates the data cache and calls `dataSource` again. Local filtering and sorting still reuse the cached rows.
38
+ * Export the typed `AzDataSourceParams` and `AzDataSourceFunc` for the `dataSource` and `details_dataSource` props.
39
+ * Re-export `SelectFilter`, `NumberFilter`, `DateFilter` and `BoolFilter` for `column.filterEditor`.
package/README.md CHANGED
@@ -16,6 +16,22 @@ 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
+ ## Saved column widths
20
+
21
+ Set `gridId` to keep the column widths a user resizes across reloads. Widths are saved in `localStorage` and matched to columns by name.
22
+
23
+ ```tsx
24
+ import { AzGrid, setGridStorageScope, clearColumnSizes } from '@azrico/reactgrid';
25
+
26
+ setGridStorageScope(user.id); // once, before the grids render, so users of one browser don't share widths
27
+
28
+ <AzGrid gridId="current-stock" columns={columns} dataSource={rows} />;
29
+
30
+ clearColumnSizes('current-stock'); // reset, takes effect when the grid remounts
31
+ ```
32
+
33
+ A column's `width` is used as its `defaultWidth` when `gridId` is set, so it can be resized.
34
+
19
35
  ## Accessibility
20
36
 
21
37
  The grid exposes ARIA grid semantics, so screen readers and automation tools can read it from the accessibility tree.
package/dist/index.d.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  export * from './src/views/DataGrid';
2
2
  export * from './src/types/types';
3
3
  export { get_row_id, set_row_id, get_col_name, get_col_header } from './src/func/func_grid';
4
+ export { setGridStorageScope, clearColumnSizes } from './src/func/func_colsizes';
4
5
  export { default as SelectFilter } from '@inovua/reactdatagrid-enterprise/SelectFilter';
5
6
  export { default as NumberFilter } from '@inovua/reactdatagrid-enterprise/NumberFilter';
6
7
  export { default as DateFilter } from '@inovua/reactdatagrid-enterprise/DateFilter';
package/dist/index.js CHANGED
@@ -8,6 +8,8 @@ var _exportNames = {
8
8
  set_row_id: true,
9
9
  get_col_name: true,
10
10
  get_col_header: true,
11
+ setGridStorageScope: true,
12
+ clearColumnSizes: true,
11
13
  SelectFilter: true,
12
14
  NumberFilter: true,
13
15
  DateFilter: true,
@@ -37,6 +39,12 @@ Object.defineProperty(exports, "SelectFilter", {
37
39
  return _SelectFilter.default;
38
40
  }
39
41
  });
42
+ Object.defineProperty(exports, "clearColumnSizes", {
43
+ enumerable: true,
44
+ get: function () {
45
+ return _func_colsizes.clearColumnSizes;
46
+ }
47
+ });
40
48
  exports.default = void 0;
41
49
  Object.defineProperty(exports, "get_col_header", {
42
50
  enumerable: true,
@@ -56,6 +64,12 @@ Object.defineProperty(exports, "get_row_id", {
56
64
  return _func_grid.get_row_id;
57
65
  }
58
66
  });
67
+ Object.defineProperty(exports, "setGridStorageScope", {
68
+ enumerable: true,
69
+ get: function () {
70
+ return _func_colsizes.setGridStorageScope;
71
+ }
72
+ });
59
73
  Object.defineProperty(exports, "set_row_id", {
60
74
  enumerable: true,
61
75
  get: function () {
@@ -87,6 +101,7 @@ Object.keys(_types).forEach(function (key) {
87
101
  });
88
102
  });
89
103
  var _func_grid = require("./src/func/func_grid");
104
+ var _func_colsizes = require("./src/func/func_colsizes");
90
105
  var _SelectFilter = _interopRequireDefault(require("@inovua/reactdatagrid-enterprise/SelectFilter"));
91
106
  var _NumberFilter = _interopRequireDefault(require("@inovua/reactdatagrid-enterprise/NumberFilter"));
92
107
  var _DateFilter = _interopRequireDefault(require("@inovua/reactdatagrid-enterprise/DateFilter"));
@@ -0,0 +1,22 @@
1
+ import { ColumnDefinition } from '../types/types';
2
+ /** column name -> width in px */
3
+ export type ColumnSizes = {
4
+ [columnName: string]: number;
5
+ };
6
+ /**
7
+ * Scope the saved column sizes, usually by user id, so different users of one browser don't share them.
8
+ * Grids read their sizes when they mount, so call this before the grids render.
9
+ */
10
+ export declare function setGridStorageScope(scope: string | undefined): void;
11
+ /** Saved column widths of a grid, an empty object when there are none or storage is unavailable. */
12
+ export declare function loadColumnSizes(gridId: string): ColumnSizes;
13
+ export declare function saveColumnSizes(gridId: string, sizes: ColumnSizes): void;
14
+ /** Forget the saved column widths of a grid. Grids already on screen keep their current sizes until they remount. */
15
+ export declare function clearColumnSizes(gridId: string): void;
16
+ export declare function isValidWidth(width: any): width is number;
17
+ /**
18
+ * Make the columns resizable and restore the saved widths.
19
+ * A column with `width` is controlled by the grid, so it could not be resized: `width` becomes `defaultWidth`.
20
+ * A saved width replaces the column's width and flex. Saved names that are not columns any more are ignored.
21
+ */
22
+ export declare function apply_column_sizes(columns: ColumnDefinition[], sizes: ColumnSizes): any[];
@@ -0,0 +1,86 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.apply_column_sizes = apply_column_sizes;
7
+ exports.clearColumnSizes = clearColumnSizes;
8
+ exports.isValidWidth = isValidWidth;
9
+ exports.loadColumnSizes = loadColumnSizes;
10
+ exports.saveColumnSizes = saveColumnSizes;
11
+ exports.setGridStorageScope = setGridStorageScope;
12
+ const KEY_PREFIX = 'azgrid:colsizes';
13
+ let storageScope = '';
14
+ /**
15
+ * Scope the saved column sizes, usually by user id, so different users of one browser don't share them.
16
+ * Grids read their sizes when they mount, so call this before the grids render.
17
+ */
18
+ function setGridStorageScope(scope) {
19
+ storageScope = scope == null ? '' : String(scope);
20
+ }
21
+ function storageKey(gridId) {
22
+ return `${KEY_PREFIX}:${storageScope}:${gridId}`;
23
+ }
24
+ /** Saved column widths of a grid, an empty object when there are none or storage is unavailable. */
25
+ function loadColumnSizes(gridId) {
26
+ const sizes = {};
27
+ try {
28
+ const parsed = JSON.parse(localStorage.getItem(storageKey(gridId)) || 'null');
29
+ if (parsed && typeof parsed === 'object') Object.keys(parsed).forEach(name => {
30
+ if (isValidWidth(parsed[name])) sizes[name] = parsed[name];
31
+ });
32
+ } catch (e) {
33
+ //private mode, blocked storage or corrupt JSON: behave as if nothing was saved
34
+ }
35
+ return sizes;
36
+ }
37
+ function saveColumnSizes(gridId, sizes) {
38
+ try {
39
+ localStorage.setItem(storageKey(gridId), JSON.stringify(sizes));
40
+ } catch (e) {
41
+ //quota or blocked storage: the sizes just won't persist
42
+ }
43
+ }
44
+ /** Forget the saved column widths of a grid. Grids already on screen keep their current sizes until they remount. */
45
+ function clearColumnSizes(gridId) {
46
+ try {
47
+ localStorage.removeItem(storageKey(gridId));
48
+ } catch (e) {
49
+ //nothing to clear
50
+ }
51
+ }
52
+ function isValidWidth(width) {
53
+ return typeof width === 'number' && isFinite(width) && width > 0;
54
+ }
55
+ /**
56
+ * Make the columns resizable and restore the saved widths.
57
+ * A column with `width` is controlled by the grid, so it could not be resized: `width` becomes `defaultWidth`.
58
+ * A saved width replaces the column's width and flex. Saved names that are not columns any more are ignored.
59
+ */
60
+ function apply_column_sizes(columns, sizes) {
61
+ return columns.map(col => {
62
+ var _col$name;
63
+ const saved = sizes[(_col$name = col.name) !== null && _col$name !== void 0 ? _col$name : col.id];
64
+ if (!isValidWidth(saved) && col.width == null) return col;
65
+ if (isValidWidth(saved)) {
66
+ const {
67
+ width,
68
+ flex,
69
+ defaultFlex,
70
+ ...rest
71
+ } = col;
72
+ return {
73
+ ...rest,
74
+ defaultWidth: saved
75
+ };
76
+ }
77
+ const {
78
+ width,
79
+ ...rest
80
+ } = col;
81
+ return {
82
+ ...rest,
83
+ defaultWidth: width
84
+ };
85
+ });
86
+ }
@@ -1,5 +1,6 @@
1
1
  export * from './views/DataGrid';
2
2
  export * from './types/types';
3
3
  export { get_row_id, set_row_id, get_col_name, get_col_header } from './func/func_grid';
4
+ export { setGridStorageScope, clearColumnSizes } from './func/func_colsizes';
4
5
  import AzGrid from './views/DataGrid';
5
6
  export default AzGrid;
package/dist/src/index.js CHANGED
@@ -7,8 +7,16 @@ var _exportNames = {
7
7
  get_row_id: true,
8
8
  set_row_id: true,
9
9
  get_col_name: true,
10
- get_col_header: true
10
+ get_col_header: true,
11
+ setGridStorageScope: true,
12
+ clearColumnSizes: true
11
13
  };
14
+ Object.defineProperty(exports, "clearColumnSizes", {
15
+ enumerable: true,
16
+ get: function () {
17
+ return _func_colsizes.clearColumnSizes;
18
+ }
19
+ });
12
20
  exports.default = void 0;
13
21
  Object.defineProperty(exports, "get_col_header", {
14
22
  enumerable: true,
@@ -28,6 +36,12 @@ Object.defineProperty(exports, "get_row_id", {
28
36
  return _func_grid.get_row_id;
29
37
  }
30
38
  });
39
+ Object.defineProperty(exports, "setGridStorageScope", {
40
+ enumerable: true,
41
+ get: function () {
42
+ return _func_colsizes.setGridStorageScope;
43
+ }
44
+ });
31
45
  Object.defineProperty(exports, "set_row_id", {
32
46
  enumerable: true,
33
47
  get: function () {
@@ -59,6 +73,7 @@ Object.keys(_types).forEach(function (key) {
59
73
  });
60
74
  });
61
75
  var _func_grid = require("./func/func_grid");
76
+ var _func_colsizes = require("./func/func_colsizes");
62
77
  var _react = _interopRequireDefault(require("react"));
63
78
  var _client = _interopRequireDefault(require("react-dom/client"));
64
79
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
@@ -62,6 +62,13 @@ export type AzGridProps = {
62
62
  details_columns?: LooseColumnDefinition[];
63
63
  details_filter?: FilterValues;
64
64
  licenseKey?: string;
65
+ /**
66
+ * Stable id of this grid. When set, the column widths the user resizes are saved in localStorage
67
+ * (see `setGridStorageScope`) and restored on the next load. Leave it out to keep the columns' own widths.
68
+ * Saved widths are matched to columns by name, so grids showing different column sets can share an id.
69
+ * Columns with `width` become resizable (`width` is used as `defaultWidth`).
70
+ */
71
+ gridId?: string;
65
72
  /** accessible name of the grid, set as aria-label on the grid root */
66
73
  ariaLabel?: string;
67
74
  gridRef?: React.MutableRefObject<TypeComputedProps>;
@@ -19,6 +19,8 @@ var _useColumnData = require("../hooks/useColumnData");
19
19
  var _useDatasource = require("../hooks/useDatasource");
20
20
  var _useGridSearch = require("../hooks/useGridSearch");
21
21
  var _func_a11y = require("../func/func_a11y");
22
+ var _func_colsizes = require("../func/func_colsizes");
23
+ var _lodash = _interopRequireDefault(require("lodash.debounce"));
22
24
  var _defaultRowIndexColumnId = require("@inovua/reactdatagrid-community/normalizeColumns/defaultRowIndexColumnId");
23
25
  var _defaultCheckColumnId = require("@inovua/reactdatagrid-community/normalizeColumns/defaultCheckColumnId");
24
26
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
@@ -55,8 +57,22 @@ function AzGrid(props) {
55
57
  /* -------------------------------------------------------------------------- */
56
58
  /* Columns */
57
59
  /* -------------------------------------------------------------------------- */
58
- const cached_columns = (0, _useColumnData.useColumnData)(props);
60
+ const data_columns = (0, _useColumnData.useColumnData)(props);
59
61
  const dataSource = (0, _useDatasource.useDataSource)(props.dataSource, props);
62
+ // saved widths are read once per grid id, resizing only updates this map so the columns aren't re-created mid-drag
63
+ const savedSizes = _react.default.useRef({
64
+ sizes: {}
65
+ });
66
+ const saveSizesDebounced = _react.default.useMemo(() => props.gridId ? (0, _lodash.default)(() => (0, _func_colsizes.saveColumnSizes)(props.gridId, savedSizes.current.sizes), 300) : null, [props.gridId]);
67
+ _react.default.useEffect(() => () => saveSizesDebounced === null || saveSizesDebounced === void 0 ? void 0 : saveSizesDebounced.flush(), [saveSizesDebounced]);
68
+ const cached_columns = _react.default.useMemo(() => {
69
+ if (!props.gridId) return data_columns;
70
+ if (savedSizes.current.gridId !== props.gridId) savedSizes.current = {
71
+ gridId: props.gridId,
72
+ sizes: (0, _func_colsizes.loadColumnSizes)(props.gridId)
73
+ };
74
+ return (0, _func_colsizes.apply_column_sizes)(data_columns, savedSizes.current.sizes);
75
+ }, [data_columns, props.gridId]);
60
76
  const a11y_columns = _react.default.useMemo(() => cached_columns.map(col => (0, _func_a11y.apply_column_a11y)(col, {
61
77
  gridEditable: !!props.editable
62
78
  })), [cached_columns, props.editable]);
@@ -94,8 +110,17 @@ function AzGrid(props) {
94
110
  };
95
111
  //append filter types instead of replacing it
96
112
  if (finalres.filterTypes) finalres.filterTypes = Object.assign({}, _reactdatagridEnterprise.default.defaultProps.filterTypes, finalres.filterTypes);
113
+ if (saveSizesDebounced) finalres.onColumnResize = (info, ...rest) => {
114
+ var _info$column$name, _latestProps$current$2, _latestProps$current$3;
115
+ const name = (_info$column$name = info.column.name) !== null && _info$column$name !== void 0 ? _info$column$name : info.column.id;
116
+ if (name != null && (0, _func_colsizes.isValidWidth)(info.width)) {
117
+ savedSizes.current.sizes[name] = info.width;
118
+ saveSizesDebounced();
119
+ }
120
+ (_latestProps$current$2 = latestProps.current.gridOptions) === null || _latestProps$current$2 === void 0 || (_latestProps$current$3 = _latestProps$current$2.onColumnResize) === null || _latestProps$current$3 === void 0 || _latestProps$current$3.call(_latestProps$current$2, info, ...rest);
121
+ };
97
122
  return finalres;
98
- }, [props.gridOptions, base_gridOptions, forcedOptions]);
123
+ }, [props.gridOptions, base_gridOptions, forcedOptions, saveSizesDebounced]);
99
124
  const dynamic_gridOptions = _react.default.useMemo(() => {
100
125
  const res = {};
101
126
  dynamic_option_names.map(r => {
package/index.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  export * from './src/views/DataGrid';
2
2
  export * from './src/types/types';
3
3
  export { get_row_id, set_row_id, get_col_name, get_col_header } from './src/func/func_grid';
4
+ export { setGridStorageScope, clearColumnSizes } from './src/func/func_colsizes';
4
5
  // filter editors for column.filterEditor, so consumers don't import @inovua directly
5
6
  export { default as SelectFilter } from '@inovua/reactdatagrid-enterprise/SelectFilter';
6
7
  export { default as NumberFilter } from '@inovua/reactdatagrid-enterprise/NumberFilter';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@azrico/reactgrid",
3
- "version": "1.6.12",
3
+ "version": "1.6.13",
4
4
  "main": "dist/index.js",
5
5
  "description": "React Datagrid",
6
6
  "author": "Azrideus <MehrdadTavangar@gmail.com>",
@@ -0,0 +1,74 @@
1
+ import { ColumnDefinition } from '../types/types';
2
+
3
+ /** column name -> width in px */
4
+ export type ColumnSizes = { [columnName: string]: number };
5
+
6
+ const KEY_PREFIX = 'azgrid:colsizes';
7
+ let storageScope = '';
8
+
9
+ /**
10
+ * Scope the saved column sizes, usually by user id, so different users of one browser don't share them.
11
+ * Grids read their sizes when they mount, so call this before the grids render.
12
+ */
13
+ export function setGridStorageScope(scope: string | undefined) {
14
+ storageScope = scope == null ? '' : String(scope);
15
+ }
16
+
17
+ function storageKey(gridId: string) {
18
+ return `${KEY_PREFIX}:${storageScope}:${gridId}`;
19
+ }
20
+
21
+ /** Saved column widths of a grid, an empty object when there are none or storage is unavailable. */
22
+ export function loadColumnSizes(gridId: string): ColumnSizes {
23
+ const sizes: ColumnSizes = {};
24
+ try {
25
+ const parsed = JSON.parse(localStorage.getItem(storageKey(gridId)) || 'null');
26
+ if (parsed && typeof parsed === 'object')
27
+ Object.keys(parsed).forEach((name) => {
28
+ if (isValidWidth(parsed[name])) sizes[name] = parsed[name];
29
+ });
30
+ } catch (e) {
31
+ //private mode, blocked storage or corrupt JSON: behave as if nothing was saved
32
+ }
33
+ return sizes;
34
+ }
35
+
36
+ export function saveColumnSizes(gridId: string, sizes: ColumnSizes) {
37
+ try {
38
+ localStorage.setItem(storageKey(gridId), JSON.stringify(sizes));
39
+ } catch (e) {
40
+ //quota or blocked storage: the sizes just won't persist
41
+ }
42
+ }
43
+
44
+ /** Forget the saved column widths of a grid. Grids already on screen keep their current sizes until they remount. */
45
+ export function clearColumnSizes(gridId: string) {
46
+ try {
47
+ localStorage.removeItem(storageKey(gridId));
48
+ } catch (e) {
49
+ //nothing to clear
50
+ }
51
+ }
52
+
53
+ export function isValidWidth(width: any): width is number {
54
+ return typeof width === 'number' && isFinite(width) && width > 0;
55
+ }
56
+
57
+ /**
58
+ * Make the columns resizable and restore the saved widths.
59
+ * A column with `width` is controlled by the grid, so it could not be resized: `width` becomes `defaultWidth`.
60
+ * A saved width replaces the column's width and flex. Saved names that are not columns any more are ignored.
61
+ */
62
+ export function apply_column_sizes(columns: ColumnDefinition[], sizes: ColumnSizes) {
63
+ return columns.map((col: any) => {
64
+ const saved = sizes[col.name ?? col.id];
65
+ if (!isValidWidth(saved) && col.width == null) return col;
66
+
67
+ if (isValidWidth(saved)) {
68
+ const { width, flex, defaultFlex, ...rest } = col;
69
+ return { ...rest, defaultWidth: saved } as ColumnDefinition;
70
+ }
71
+ const { width, ...rest } = col;
72
+ return { ...rest, defaultWidth: width } as ColumnDefinition;
73
+ });
74
+ }
package/src/index.tsx CHANGED
@@ -2,6 +2,7 @@ export * from './views/DataGrid';
2
2
  export * from './types/types';
3
3
 
4
4
  export { get_row_id, set_row_id, get_col_name, get_col_header } from './func/func_grid';
5
+ export { setGridStorageScope, clearColumnSizes } from './func/func_colsizes';
5
6
 
6
7
  import AzGrid from './views/DataGrid';
7
8
  import React from 'react';