zubin-grid 0.4.12 → 0.41.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.
Files changed (63) hide show
  1. package/README.md +147 -5
  2. package/dist/core/cell.d.ts +8 -8
  3. package/dist/core/cell.d.ts.map +1 -1
  4. package/dist/core/cell.js +125 -38
  5. package/dist/core/cell.js.map +1 -1
  6. package/dist/core/grid-id.d.ts +7 -0
  7. package/dist/core/grid-id.d.ts.map +1 -0
  8. package/dist/core/grid-id.js +51 -0
  9. package/dist/core/grid-id.js.map +1 -0
  10. package/dist/core/grid.d.ts +11 -9
  11. package/dist/core/grid.d.ts.map +1 -1
  12. package/dist/core/grid.js +597 -80
  13. package/dist/core/grid.js.map +1 -1
  14. package/dist/core/head.d.ts +11 -13
  15. package/dist/core/head.d.ts.map +1 -1
  16. package/dist/core/head.js +38 -33
  17. package/dist/core/head.js.map +1 -1
  18. package/dist/core/helpers.d.ts +4 -4
  19. package/dist/core/helpers.d.ts.map +1 -1
  20. package/dist/core/helpers.js +5 -2
  21. package/dist/core/helpers.js.map +1 -1
  22. package/dist/core/{gridPersist.d.ts → persist.d.ts} +3 -3
  23. package/dist/core/persist.d.ts.map +1 -0
  24. package/dist/core/{gridPersist.js → persist.js} +43 -42
  25. package/dist/core/persist.js.map +1 -0
  26. package/dist/core/tail.d.ts +9 -11
  27. package/dist/core/tail.d.ts.map +1 -1
  28. package/dist/core/tail.js +17 -19
  29. package/dist/core/tail.js.map +1 -1
  30. package/dist/core/types/cell.types.d.ts.map +1 -0
  31. package/dist/core/{cell.types.js.map → types/cell.types.js.map} +1 -1
  32. package/dist/core/{grid.types.d.ts → types/grid.types.d.ts} +79 -16
  33. package/dist/core/types/grid.types.d.ts.map +1 -0
  34. package/dist/core/{grid.types.js.map → types/grid.types.js.map} +1 -1
  35. package/dist/core/{head.types.d.ts → types/head.types.d.ts} +7 -6
  36. package/dist/core/types/head.types.d.ts.map +1 -0
  37. package/dist/core/{head.types.js.map → types/head.types.js.map} +1 -1
  38. package/dist/core/{gridPersist.types.d.ts → types/persist.types.d.ts} +1 -1
  39. package/dist/core/types/persist.types.d.ts.map +1 -0
  40. package/dist/core/types/persist.types.js +2 -0
  41. package/dist/core/types/persist.types.js.map +1 -0
  42. package/dist/core/{tail.types.d.ts → types/tail.types.d.ts} +3 -2
  43. package/dist/core/types/tail.types.d.ts.map +1 -0
  44. package/dist/core/{tail.types.js.map → types/tail.types.js.map} +1 -1
  45. package/dist/index.d.ts +6 -6
  46. package/dist/index.d.ts.map +1 -1
  47. package/dist/index.js +2 -2
  48. package/dist/index.js.map +1 -1
  49. package/package.json +1 -1
  50. package/dist/core/cell.types.d.ts.map +0 -1
  51. package/dist/core/grid.types.d.ts.map +0 -1
  52. package/dist/core/gridPersist.d.ts.map +0 -1
  53. package/dist/core/gridPersist.js.map +0 -1
  54. package/dist/core/gridPersist.types.d.ts.map +0 -1
  55. package/dist/core/gridPersist.types.js +0 -2
  56. package/dist/core/gridPersist.types.js.map +0 -1
  57. package/dist/core/head.types.d.ts.map +0 -1
  58. package/dist/core/tail.types.d.ts.map +0 -1
  59. /package/dist/core/{cell.types.d.ts → types/cell.types.d.ts} +0 -0
  60. /package/dist/core/{cell.types.js → types/cell.types.js} +0 -0
  61. /package/dist/core/{grid.types.js → types/grid.types.js} +0 -0
  62. /package/dist/core/{head.types.js → types/head.types.js} +0 -0
  63. /package/dist/core/{tail.types.js → types/tail.types.js} +0 -0
package/dist/core/grid.js CHANGED
@@ -1,6 +1,7 @@
1
1
  import { useCallback, useEffect, useRef, useSyncExternalStore } from "react";
2
2
  import { cell } from "./cell.js";
3
- import { createGridPersistController } from "./gridPersist.js";
3
+ import { assertGridId, createGridIdKey, formatGridId } from "./grid-id.js";
4
+ import { createGridPersistController, defaultGridPersistAdapter } from "./persist.js";
4
5
  import { assertHeadId, createHeadCellMap, createHeadOrderIndex, getHeadCell, getOrderedHeads, } from "./head.js";
5
6
  import { createTailCellMap, getTailCell, setTailCellResult } from "./tail.js";
6
7
  export function grid(input, options) {
@@ -13,6 +14,326 @@ export function grid(input, options) {
13
14
  const stateAdapter = createSchemaStateAdapter(rowCellKey, columnCellKey);
14
15
  return createGridStore(normalizedRowHeads, normalizedColumnHeads, createGridInitialCellsFromState(initialState.cells, stateAdapter), stateAdapter, schemaOptions.persist);
15
16
  }
17
+ export function createDimensionGrid(parentGrid, initialCells, dimension, options) {
18
+ const readBoundIds = () => (dimension === "rows"
19
+ ? parentGrid.rowHeaders
20
+ : parentGrid.colHeaders);
21
+ const emptyRowHeaders = [];
22
+ const emptyColumnHeaders = [];
23
+ const dimensionSubscribers = new Set();
24
+ const resolvedPersistOption = createDimensionGridPersistOption(parentGrid, dimension, options?.persist);
25
+ let isApplyingState = false;
26
+ let boundIds = [...readBoundIds()];
27
+ const cellsById = new Map(boundIds.map((boundId, index) => [
28
+ createGridIdKey(boundId),
29
+ cell(initialCells[index]),
30
+ ]));
31
+ let dimensionGridApi;
32
+ const getCellAtIndex = (index) => {
33
+ if (!Number.isInteger(index) || index < 0 || index >= boundIds.length) {
34
+ throw new Error(`Dimension grid ${dimension} index ${index} is out of bounds.`);
35
+ }
36
+ const boundId = boundIds[index];
37
+ const currentCell = cellsById.get(createGridIdKey(boundId));
38
+ if (!currentCell) {
39
+ throw new Error(`Missing dimension cell for ${dimension} index ${index}.`);
40
+ }
41
+ return currentCell;
42
+ };
43
+ const getState = () => ({
44
+ dimension,
45
+ cells: boundIds.map((boundId) => {
46
+ const currentCell = cellsById.get(createGridIdKey(boundId));
47
+ if (!currentCell) {
48
+ throw new Error(`Missing dimension cell for ${dimension} id "${formatGridId(boundId)}".`);
49
+ }
50
+ return currentCell.get();
51
+ }),
52
+ });
53
+ const applyDimensionCells = (nextCells, mode) => {
54
+ const changedIndices = [];
55
+ const limit = mode === "update" ? Math.min(nextCells.length, boundIds.length) : boundIds.length;
56
+ isApplyingState = true;
57
+ try {
58
+ for (let index = 0; index < limit; index += 1) {
59
+ const currentCell = getCellAtIndex(index);
60
+ const nextValue = nextCells[index];
61
+ if (Object.is(currentCell.get(), nextValue)) {
62
+ continue;
63
+ }
64
+ currentCell.set(nextValue);
65
+ changedIndices.push(index);
66
+ }
67
+ if (mode === "replace") {
68
+ for (let index = nextCells.length; index < boundIds.length; index += 1) {
69
+ const currentCell = getCellAtIndex(index);
70
+ if (Object.is(currentCell.get(), undefined)) {
71
+ continue;
72
+ }
73
+ currentCell.set(undefined);
74
+ changedIndices.push(index);
75
+ }
76
+ }
77
+ }
78
+ finally {
79
+ isApplyingState = false;
80
+ }
81
+ return changedIndices;
82
+ };
83
+ const { hydrate, markStateChanged } = createGridPersistController(resolvedPersistOption, {
84
+ getState,
85
+ replaceState: (nextState) => {
86
+ applyDimensionCells(normalizeDimensionGridStateInput(nextState, dimension).cells, "replace");
87
+ },
88
+ isApplyingState: () => isApplyingState,
89
+ });
90
+ const emitDimensionGridUpdate = (diff, persistStateChanged = false) => {
91
+ if (persistStateChanged) {
92
+ markStateChanged();
93
+ }
94
+ [...dimensionSubscribers].forEach((callback) => {
95
+ callback(dimensionGridApi, diff);
96
+ });
97
+ };
98
+ const syncDimensionFromParent = () => {
99
+ const nextBoundIds = [...readBoundIds()];
100
+ if (haveSameItems(boundIds, nextBoundIds)) {
101
+ return;
102
+ }
103
+ const nextCellsById = new Map(nextBoundIds.map((boundId) => [
104
+ createGridIdKey(boundId),
105
+ cellsById.get(createGridIdKey(boundId)) ?? cell(undefined),
106
+ ]));
107
+ const previousSize = boundIds.length;
108
+ boundIds = nextBoundIds;
109
+ cellsById.clear();
110
+ nextCellsById.forEach((currentCell, boundIdKey) => {
111
+ cellsById.set(boundIdKey, currentCell);
112
+ });
113
+ emitDimensionGridUpdate({
114
+ type: "dimension",
115
+ action: previousSize === nextBoundIds.length ? "update" : "resize",
116
+ source: "parent",
117
+ dimension,
118
+ size: nextBoundIds.length,
119
+ previousSize,
120
+ }, true);
121
+ };
122
+ const setGrid = (nextState, mode = "replace") => {
123
+ const changedIndices = applyDimensionCells(normalizeDimensionGridStateInput(nextState, dimension).cells, mode);
124
+ if (changedIndices.length === 0) {
125
+ return;
126
+ }
127
+ emitDimensionGridUpdate({
128
+ type: "cells",
129
+ action: mode === "update" ? "update" : "replace",
130
+ source: "setGrid",
131
+ dimension,
132
+ size: boundIds.length,
133
+ indices: changedIndices,
134
+ }, true);
135
+ };
136
+ const clearGrid = () => {
137
+ const changedIndices = applyDimensionCells([], "replace");
138
+ if (changedIndices.length === 0) {
139
+ return;
140
+ }
141
+ emitDimensionGridUpdate({
142
+ type: "cells",
143
+ action: "clear",
144
+ source: "clearGrid",
145
+ dimension,
146
+ size: boundIds.length,
147
+ indices: changedIndices,
148
+ }, true);
149
+ };
150
+ dimensionGridApi = {
151
+ dimension,
152
+ get size() {
153
+ return boundIds.length;
154
+ },
155
+ get rowHeaders() {
156
+ return dimension === "rows" ? parentGrid.rowHeaders : emptyRowHeaders;
157
+ },
158
+ get colHeaders() {
159
+ return dimension === "columns" ? parentGrid.colHeaders : emptyColumnHeaders;
160
+ },
161
+ getState,
162
+ setGrid,
163
+ getCell: getCellAtIndex,
164
+ getValue: (index) => getCellAtIndex(index).get(),
165
+ hasCell: (index) => Number.isInteger(index) && index >= 0 && index < boundIds.length,
166
+ clearGrid,
167
+ subscribeGrid: (callback) => {
168
+ dimensionSubscribers.add(callback);
169
+ return () => {
170
+ dimensionSubscribers.delete(callback);
171
+ };
172
+ },
173
+ __setCellValue: (index, newValue) => {
174
+ const currentCell = getCellAtIndex(index);
175
+ if (Object.is(currentCell.get(), newValue)) {
176
+ return;
177
+ }
178
+ isApplyingState = true;
179
+ try {
180
+ currentCell.set(newValue);
181
+ }
182
+ finally {
183
+ isApplyingState = false;
184
+ }
185
+ emitDimensionGridUpdate({
186
+ type: "cells",
187
+ action: "update",
188
+ source: "cell.set",
189
+ dimension,
190
+ size: boundIds.length,
191
+ indices: [index],
192
+ }, true);
193
+ },
194
+ };
195
+ Object.defineProperty(dimensionGridApi, "__persistOption", {
196
+ value: resolvedPersistOption,
197
+ enumerable: false,
198
+ configurable: true,
199
+ });
200
+ hydrate();
201
+ void parentGrid.subscribeGrid((_currentParentGrid, diff) => {
202
+ if (dimension === "rows") {
203
+ if (diff.type === "rows" ||
204
+ diff.type === "row-head" ||
205
+ (diff.type === "grid" && didGridDiffAffectAxes(diff))) {
206
+ syncDimensionFromParent();
207
+ }
208
+ return;
209
+ }
210
+ if (diff.type === "columns" ||
211
+ diff.type === "column-head" ||
212
+ (diff.type === "grid" && didGridDiffAffectAxes(diff))) {
213
+ syncDimensionFromParent();
214
+ }
215
+ });
216
+ return dimensionGridApi;
217
+ }
218
+ export function createSubGrid(parentGrid, initialCells = [], options) {
219
+ const parentState = parentGrid.getState();
220
+ const stateAdapter = createGridStateCellAdapter();
221
+ const resolvedInitialCells = assertGridStateCellsWithinAxes(initialCells, parentState.rows, parentState.columns, "Sub grid cells");
222
+ const subGridStore = createGridStore(parentState.rows, parentState.columns, createGridInitialCellsFromState(resolvedInitialCells, stateAdapter), stateAdapter, createSubGridPersistOption(parentGrid, options?.persist));
223
+ const getSubGridState = () => {
224
+ const nextParentState = parentGrid.getState();
225
+ return createSubGridState(nextParentState.rows, nextParentState.columns, subGridStore.getState().cells);
226
+ };
227
+ const syncAxesFromParent = () => {
228
+ const nextParentState = parentGrid.getState();
229
+ const currentSubGridState = subGridStore.getState();
230
+ const nextCells = filterGridStateCellsToAxes(currentSubGridState.cells, nextParentState.rows, nextParentState.columns);
231
+ if (haveSameHeadSnapshots(currentSubGridState.rows, nextParentState.rows) &&
232
+ haveSameHeadSnapshots(currentSubGridState.columns, nextParentState.columns) &&
233
+ nextCells.length === currentSubGridState.cells.length) {
234
+ return;
235
+ }
236
+ subGridStore.setGrid({
237
+ rows: nextParentState.rows,
238
+ columns: nextParentState.columns,
239
+ cells: nextCells,
240
+ }, "replace");
241
+ };
242
+ void parentGrid.subscribeGrid((_currentParentGrid, diff) => {
243
+ if (diff.type === "row-head" && diff.rows?.length) {
244
+ diff.rows.forEach((rowHead) => {
245
+ subGridStore.updateRowHead(rowHead.id, rowHead);
246
+ });
247
+ return;
248
+ }
249
+ if (diff.type === "column-head" && diff.columns?.length) {
250
+ diff.columns.forEach((columnHead) => {
251
+ subGridStore.updateColumnHead(columnHead.id, columnHead);
252
+ });
253
+ return;
254
+ }
255
+ if (diff.type === "rows" && diff.rows?.length) {
256
+ subGridStore.upsertRows(diff.rows);
257
+ return;
258
+ }
259
+ if (diff.type === "columns" && diff.columns?.length) {
260
+ subGridStore.upsertColumns(diff.columns);
261
+ return;
262
+ }
263
+ if (diff.type === "grid" && didGridDiffAffectAxes(diff)) {
264
+ syncAxesFromParent();
265
+ }
266
+ });
267
+ const subGridApi = Object.create(subGridStore);
268
+ Object.defineProperties(subGridApi, {
269
+ rowHeaders: {
270
+ enumerable: true,
271
+ configurable: true,
272
+ get: () => parentGrid.rowHeaders,
273
+ },
274
+ colHeaders: {
275
+ enumerable: true,
276
+ configurable: true,
277
+ get: () => parentGrid.colHeaders,
278
+ },
279
+ });
280
+ subGridApi.getState = getSubGridState;
281
+ subGridApi.getRowHead = (rowId) => parentGrid.getRowHead(rowId);
282
+ subGridApi.getColumnHead = (columnId) => parentGrid.getColumnHead(columnId);
283
+ subGridApi.updateRowHead = (rowId, nextRowHead) => {
284
+ parentGrid.updateRowHead(rowId, nextRowHead);
285
+ };
286
+ subGridApi.updateColumnHead = (columnId, nextColumnHead) => {
287
+ parentGrid.updateColumnHead(columnId, nextColumnHead);
288
+ };
289
+ subGridApi.upsertRow = (nextRowHead) => {
290
+ parentGrid.upsertRow(nextRowHead);
291
+ };
292
+ subGridApi.upsertRows = (nextRowHeads) => {
293
+ parentGrid.upsertRows(nextRowHeads);
294
+ };
295
+ subGridApi.upsertColumn = (nextColumnHead) => {
296
+ parentGrid.upsertColumn(nextColumnHead);
297
+ };
298
+ subGridApi.upsertColumns = (nextColumnHeads) => {
299
+ parentGrid.upsertColumns(nextColumnHeads);
300
+ };
301
+ subGridApi.subscribeRowHead = (rowId, callback) => parentGrid.subscribeRowHead(rowId, callback);
302
+ subGridApi.subscribeColumnHead = (columnId, callback) => parentGrid.subscribeColumnHead(columnId, callback);
303
+ subGridApi.setGrid = (nextState, mode = "replace") => {
304
+ const patch = normalizeGridStatePatchInput(nextState);
305
+ const currentParentState = parentGrid.getState();
306
+ if (mode === "update") {
307
+ if (patch.rows?.length) {
308
+ parentGrid.upsertRows(patch.rows);
309
+ }
310
+ if (patch.columns?.length) {
311
+ parentGrid.upsertColumns(patch.columns);
312
+ }
313
+ if (patch.cells?.length) {
314
+ subGridStore.setGrid({
315
+ rows: currentParentState.rows,
316
+ columns: currentParentState.columns,
317
+ cells: assertGridStateCellsWithinAxes(patch.cells, currentParentState.rows, currentParentState.columns, "Sub grid cells"),
318
+ }, "update");
319
+ }
320
+ return;
321
+ }
322
+ if ((patch.rows && !haveSameHeadSnapshots(patch.rows, currentParentState.rows)) ||
323
+ (patch.columns && !haveSameHeadSnapshots(patch.columns, currentParentState.columns))) {
324
+ throw new Error("Sub grids inherit rows and columns from their parent. Replace parent dimensions on the parent grid instead.");
325
+ }
326
+ subGridStore.setGrid({
327
+ rows: currentParentState.rows,
328
+ columns: currentParentState.columns,
329
+ cells: assertGridStateCellsWithinAxes(patch.cells ?? [], currentParentState.rows, currentParentState.columns, "Sub grid cells"),
330
+ }, "replace");
331
+ };
332
+ subGridApi.clearGrid = () => {
333
+ subGridStore.clearCells();
334
+ };
335
+ return subGridApi;
336
+ }
16
337
  function createGridStore(initialRows, initialColumns, initialCells, stateAdapter, persist) {
17
338
  assertUniqueHeadIds(initialRows, "row");
18
339
  assertUniqueHeadIds(initialColumns, "column");
@@ -54,13 +375,13 @@ function createGridStore(initialRows, initialColumns, initialCells, stateAdapter
54
375
  return stateAdapter.deserializeCell(stateAdapter.serializeCell(rowId, columnId, value)).value;
55
376
  };
56
377
  const recomputeRowTailInternal = (rowId) => {
57
- const onRowUpdate = rowTailUpdaters.get(rowId);
378
+ const onRowUpdate = rowTailUpdaters.get(createGridIdKey(rowId));
58
379
  if (!onRowUpdate)
59
380
  return false;
60
381
  return setTailCellResult(getTailCell(rowTailCells, rowId, "row"), onRowUpdate(getRowCells(rowId)));
61
382
  };
62
383
  const recomputeColumnTailInternal = (columnId) => {
63
- const onColumnUpdate = columnTailUpdaters.get(columnId);
384
+ const onColumnUpdate = columnTailUpdaters.get(createGridIdKey(columnId));
64
385
  if (!onColumnUpdate)
65
386
  return false;
66
387
  return setTailCellResult(getTailCell(columnTailCells, columnId, "column"), onColumnUpdate(getColumnCells(columnId)));
@@ -89,13 +410,16 @@ function createGridStore(initialRows, initialColumns, initialCells, stateAdapter
89
410
  cellsMap.delete(gridKey);
90
411
  };
91
412
  const attachCell = (rowId, columnId, currentCell) => {
92
- if (!rowHeadCells.has(rowId))
93
- throw new Error(`Missing row header "${rowId}".`);
94
- if (!columnHeadCells.has(columnId))
95
- throw new Error(`Missing column header "${columnId}".`);
413
+ if (!rowHeadCells.has(createGridIdKey(rowId))) {
414
+ throw new Error(`Missing row header "${formatGridId(rowId)}".`);
415
+ }
416
+ if (!columnHeadCells.has(createGridIdKey(columnId))) {
417
+ throw new Error(`Missing column header "${formatGridId(columnId)}".`);
418
+ }
96
419
  const gridKey = createGridKey(rowId, columnId);
97
- if (cellsMap.has(gridKey))
98
- throw new Error(`Duplicate cell for row "${rowId}" and column "${columnId}".`);
420
+ if (cellsMap.has(gridKey)) {
421
+ throw new Error(`Duplicate cell for row "${formatGridId(rowId)}" and column "${formatGridId(columnId)}".`);
422
+ }
99
423
  cellsMap.set(gridKey, currentCell);
100
424
  let previousValue = currentCell.get();
101
425
  const unsubscribe = currentCell.subscribe(() => {
@@ -124,8 +448,9 @@ function createGridStore(initialRows, initialColumns, initialCells, stateAdapter
124
448
  };
125
449
  const getCell = (rowId, columnId) => {
126
450
  const currentCell = cellsMap.get(createGridKey(rowId, columnId));
127
- if (!currentCell)
128
- throw new Error(`Missing cell for row "${rowId}" and column "${columnId}".`);
451
+ if (!currentCell) {
452
+ throw new Error(`Missing cell for row "${formatGridId(rowId)}" and column "${formatGridId(columnId)}".`);
453
+ }
129
454
  return currentCell;
130
455
  };
131
456
  const createAxisCellSnapshot = (rowId, columnId) => {
@@ -152,15 +477,16 @@ function createGridStore(initialRows, initialColumns, initialCells, stateAdapter
152
477
  assertUniqueHeadIds(nextHeads, axis);
153
478
  const nextIds = new Set();
154
479
  nextHeads.forEach((nextHead, index) => {
155
- nextIds.add(nextHead.id);
156
- const currentHeadCell = headCells.get(nextHead.id);
480
+ const idKey = createGridIdKey(nextHead.id);
481
+ nextIds.add(idKey);
482
+ const currentHeadCell = headCells.get(idKey);
157
483
  if (currentHeadCell)
158
484
  currentHeadCell.set(nextHead);
159
485
  else
160
- headCells.set(nextHead.id, cell(nextHead));
161
- if (!tailCells.has(nextHead.id))
162
- tailCells.set(nextHead.id, cell(createEmptyTailState()));
163
- headOrder.set(nextHead.id, index);
486
+ headCells.set(idKey, cell(nextHead));
487
+ if (!tailCells.has(idKey))
488
+ tailCells.set(idKey, cell(createEmptyTailState()));
489
+ headOrder.set(idKey, index);
164
490
  });
165
491
  [...headCells.keys()].forEach((id) => {
166
492
  if (nextIds.has(id))
@@ -174,24 +500,24 @@ function createGridStore(initialRows, initialColumns, initialCells, stateAdapter
174
500
  const replaceState = (nextState) => {
175
501
  isApplyingState = true;
176
502
  try {
177
- syncAxisHeads(nextState.rows, "row", rowHeadCells, rowTailCells, rowHeadOrder, (id) => {
178
- rowTailUpdaters.delete(id);
503
+ syncAxisHeads(nextState.rows, "row", rowHeadCells, rowTailCells, rowHeadOrder, (idKey) => {
504
+ rowTailUpdaters.delete(idKey);
179
505
  });
180
- syncAxisHeads(nextState.columns, "column", columnHeadCells, columnTailCells, columnHeadOrder, (id) => {
181
- columnTailUpdaters.delete(id);
506
+ syncAxisHeads(nextState.columns, "column", columnHeadCells, columnTailCells, columnHeadOrder, (idKey) => {
507
+ columnTailUpdaters.delete(idKey);
182
508
  });
183
509
  const nextCells = new Map();
184
510
  nextState.cells.forEach((nextStateCell) => {
185
511
  const { rowId, columnId, value } = stateAdapter.deserializeCell(nextStateCell);
186
- if (!rowHeadCells.has(rowId)) {
187
- throw new Error(`Missing row header "${rowId}".`);
512
+ if (!rowHeadCells.has(createGridIdKey(rowId))) {
513
+ throw new Error(`Missing row header "${formatGridId(rowId)}".`);
188
514
  }
189
- if (!columnHeadCells.has(columnId)) {
190
- throw new Error(`Missing column header "${columnId}".`);
515
+ if (!columnHeadCells.has(createGridIdKey(columnId))) {
516
+ throw new Error(`Missing column header "${formatGridId(columnId)}".`);
191
517
  }
192
518
  const gridKey = createGridKey(rowId, columnId);
193
519
  if (nextCells.has(gridKey)) {
194
- throw new Error(`Duplicate cell for row "${rowId}" and column "${columnId}".`);
520
+ throw new Error(`Duplicate cell for row "${formatGridId(rowId)}" and column "${formatGridId(columnId)}".`);
195
521
  }
196
522
  nextCells.set(gridKey, {
197
523
  rowId,
@@ -245,8 +571,8 @@ function createGridStore(initialRows, initialColumns, initialCells, stateAdapter
245
571
  if (nextCells.length === 0) {
246
572
  return null;
247
573
  }
248
- const touchedRowIds = new Set();
249
- const touchedColumnIds = new Set();
574
+ const touchedRowIds = new Map();
575
+ const touchedColumnIds = new Map();
250
576
  const normalizedCells = [];
251
577
  const previousCells = [];
252
578
  const cellKeys = [];
@@ -254,10 +580,12 @@ function createGridStore(initialRows, initialColumns, initialCells, stateAdapter
254
580
  try {
255
581
  nextCells.forEach((nextStateCell) => {
256
582
  const { rowId, columnId, value } = stateAdapter.deserializeCell(nextStateCell);
257
- if (!rowHeadCells.has(rowId))
258
- throw new Error(`Missing row header "${rowId}".`);
259
- if (!columnHeadCells.has(columnId))
260
- throw new Error(`Missing column header "${columnId}".`);
583
+ if (!rowHeadCells.has(createGridIdKey(rowId))) {
584
+ throw new Error(`Missing row header "${formatGridId(rowId)}".`);
585
+ }
586
+ if (!columnHeadCells.has(createGridIdKey(columnId))) {
587
+ throw new Error(`Missing column header "${formatGridId(columnId)}".`);
588
+ }
261
589
  const normalizedValue = normalizeCellValue(rowId, columnId, value);
262
590
  const gridKey = createGridKey(rowId, columnId);
263
591
  const currentCell = cellsMap.get(gridKey);
@@ -269,16 +597,16 @@ function createGridStore(initialRows, initialColumns, initialCells, stateAdapter
269
597
  attachCell(rowId, columnId, cell(normalizedValue));
270
598
  }
271
599
  normalizedCells.push(stateAdapter.serializeCell(rowId, columnId, normalizedValue));
272
- touchedRowIds.add(rowId);
273
- touchedColumnIds.add(columnId);
600
+ touchedRowIds.set(createGridIdKey(rowId), rowId);
601
+ touchedColumnIds.set(createGridIdKey(columnId), columnId);
274
602
  cellKeys.push(gridKey);
275
603
  });
276
604
  }
277
605
  finally {
278
606
  isApplyingState = false;
279
607
  }
280
- const rowIds = [...touchedRowIds];
281
- const columnIds = [...touchedColumnIds];
608
+ const rowIds = [...touchedRowIds.values()];
609
+ const columnIds = [...touchedColumnIds.values()];
282
610
  return {
283
611
  rowIds,
284
612
  columnIds,
@@ -311,35 +639,34 @@ function createGridStore(initialRows, initialColumns, initialCells, stateAdapter
311
639
  if (nextRowHeads.length === 0) {
312
640
  return null;
313
641
  }
314
- const rowIds = new Set();
642
+ const rowIds = new Map();
315
643
  const rows = [];
316
644
  const previousRows = [];
317
645
  let shouldRecomputeColumns = false;
318
646
  nextRowHeads.forEach((nextRowHead) => {
319
647
  const rowHeadLike = nextRowHead;
320
- if (typeof rowHeadLike.id !== "string") {
321
- throw new Error("Row header upserts must include a string id.");
322
- }
323
- const currentHeadCell = rowHeadCells.get(rowHeadLike.id);
648
+ const rowIdKey = createGridIdKey(rowHeadLike.id);
649
+ assertGridId(rowHeadLike.id, "Row header upserts");
650
+ const currentHeadCell = rowHeadCells.get(rowIdKey);
324
651
  const currentHead = currentHeadCell?.get();
325
652
  const resolvedHead = normalizeUpsertHead(rowHeadLike, currentHead, nextRowFallbackOrder);
326
653
  if (currentHeadCell) {
327
654
  currentHeadCell.set(resolvedHead);
328
655
  }
329
656
  else {
330
- rowHeadCells.set(resolvedHead.id, cell(resolvedHead));
331
- rowTailCells.set(resolvedHead.id, cell(createEmptyTailState()));
332
- rowHeadOrder.set(resolvedHead.id, nextRowFallbackOrder);
657
+ rowHeadCells.set(rowIdKey, cell(resolvedHead));
658
+ rowTailCells.set(rowIdKey, cell(createEmptyTailState()));
659
+ rowHeadOrder.set(rowIdKey, nextRowFallbackOrder);
333
660
  nextRowFallbackOrder += 1;
334
661
  }
335
662
  rows.push(resolvedHead);
336
663
  if (currentHead) {
337
664
  previousRows.push(currentHead);
338
665
  }
339
- rowIds.add(resolvedHead.id);
666
+ rowIds.set(rowIdKey, resolvedHead.id);
340
667
  shouldRecomputeColumns ||= !currentHead || currentHead.order !== resolvedHead.order;
341
668
  });
342
- const resolvedRowIds = [...rowIds];
669
+ const resolvedRowIds = [...rowIds.values()];
343
670
  return {
344
671
  rows,
345
672
  previousRows,
@@ -368,35 +695,34 @@ function createGridStore(initialRows, initialColumns, initialCells, stateAdapter
368
695
  if (nextColumnHeads.length === 0) {
369
696
  return null;
370
697
  }
371
- const columnIds = new Set();
698
+ const columnIds = new Map();
372
699
  const columns = [];
373
700
  const previousColumns = [];
374
701
  let shouldRecomputeRows = false;
375
702
  nextColumnHeads.forEach((nextColumnHead) => {
376
703
  const columnHeadLike = nextColumnHead;
377
- if (typeof columnHeadLike.id !== "string") {
378
- throw new Error("Column header upserts must include a string id.");
379
- }
380
- const currentHeadCell = columnHeadCells.get(columnHeadLike.id);
704
+ const columnIdKey = createGridIdKey(columnHeadLike.id);
705
+ assertGridId(columnHeadLike.id, "Column header upserts");
706
+ const currentHeadCell = columnHeadCells.get(columnIdKey);
381
707
  const currentHead = currentHeadCell?.get();
382
708
  const resolvedHead = normalizeUpsertHead(columnHeadLike, currentHead, nextColumnFallbackOrder);
383
709
  if (currentHeadCell) {
384
710
  currentHeadCell.set(resolvedHead);
385
711
  }
386
712
  else {
387
- columnHeadCells.set(resolvedHead.id, cell(resolvedHead));
388
- columnTailCells.set(resolvedHead.id, cell(createEmptyTailState()));
389
- columnHeadOrder.set(resolvedHead.id, nextColumnFallbackOrder);
713
+ columnHeadCells.set(columnIdKey, cell(resolvedHead));
714
+ columnTailCells.set(columnIdKey, cell(createEmptyTailState()));
715
+ columnHeadOrder.set(columnIdKey, nextColumnFallbackOrder);
390
716
  nextColumnFallbackOrder += 1;
391
717
  }
392
718
  columns.push(resolvedHead);
393
719
  if (currentHead) {
394
720
  previousColumns.push(currentHead);
395
721
  }
396
- columnIds.add(resolvedHead.id);
722
+ columnIds.set(columnIdKey, resolvedHead.id);
397
723
  shouldRecomputeRows ||= !currentHead || currentHead.order !== resolvedHead.order;
398
724
  });
399
- const resolvedColumnIds = [...columnIds];
725
+ const resolvedColumnIds = [...columnIds.values()];
400
726
  return {
401
727
  columns,
402
728
  previousColumns,
@@ -524,23 +850,25 @@ function createGridStore(initialRows, initialColumns, initialCells, stateAdapter
524
850
  });
525
851
  };
526
852
  function registerRowTail(rowId, onRowUpdate) {
527
- rowTailUpdaters.set(rowId, onRowUpdate);
853
+ const rowIdKey = createGridIdKey(rowId);
854
+ rowTailUpdaters.set(rowIdKey, onRowUpdate);
528
855
  recomputeRowTailInternal(rowId);
529
856
  return () => {
530
- if (rowTailUpdaters.get(rowId) !== onRowUpdate) {
857
+ if (rowTailUpdaters.get(rowIdKey) !== onRowUpdate) {
531
858
  return;
532
859
  }
533
- rowTailUpdaters.delete(rowId);
860
+ rowTailUpdaters.delete(rowIdKey);
534
861
  };
535
862
  }
536
863
  function registerColumnTail(columnId, onColumnUpdate) {
537
- columnTailUpdaters.set(columnId, onColumnUpdate);
864
+ const columnIdKey = createGridIdKey(columnId);
865
+ columnTailUpdaters.set(columnIdKey, onColumnUpdate);
538
866
  recomputeColumnTailInternal(columnId);
539
867
  return () => {
540
- if (columnTailUpdaters.get(columnId) !== onColumnUpdate) {
868
+ if (columnTailUpdaters.get(columnIdKey) !== onColumnUpdate) {
541
869
  return;
542
870
  }
543
- columnTailUpdaters.delete(columnId);
871
+ columnTailUpdaters.delete(columnIdKey);
544
872
  };
545
873
  }
546
874
  const setGrid = (nextState, mode = "replace") => {
@@ -751,6 +1079,11 @@ function createGridStore(initialRows, initialColumns, initialCells, stateAdapter
751
1079
  recomputeRowTail,
752
1080
  recomputeColumnTail,
753
1081
  };
1082
+ Object.defineProperty(gridApi, "__persistOption", {
1083
+ value: persist,
1084
+ enumerable: false,
1085
+ configurable: true,
1086
+ });
754
1087
  return gridApi;
755
1088
  }
756
1089
  function resolveUpdater(nextValue, currentValue) {
@@ -759,7 +1092,7 @@ function resolveUpdater(nextValue, currentValue) {
759
1092
  : nextValue;
760
1093
  }
761
1094
  export function createGridKey(rowId, columnId) {
762
- return `${rowId}:${columnId}`;
1095
+ return `row=${createGridIdKey(rowId)}|col=${createGridIdKey(columnId)}`;
763
1096
  }
764
1097
  export function useGrid(currentGrid, options) {
765
1098
  const snapshotRef = useRef({
@@ -797,7 +1130,7 @@ function toOptionalArray(values) {
797
1130
  }
798
1131
  function haveSameItems(left, right) {
799
1132
  return (left.length === right.length &&
800
- left.every((currentValue, index) => Object.is(currentValue, right[index])));
1133
+ left.every((currentValue, index) => createGridIdKey(currentValue) === createGridIdKey(right[index])));
801
1134
  }
802
1135
  function normalizeGridStateInput(value) {
803
1136
  if (!value || typeof value !== "object") {
@@ -849,16 +1182,16 @@ function mergeHeadStates(currentHeads, nextHeads) {
849
1182
  const currentIds = new Set();
850
1183
  const nextHeadsById = new Map();
851
1184
  currentHeads.forEach((currentHead) => {
852
- currentIds.add(currentHead.id);
1185
+ currentIds.add(createGridIdKey(currentHead.id));
853
1186
  });
854
1187
  nextHeads.forEach((nextHead) => {
855
- nextHeadsById.set(nextHead.id, nextHead);
1188
+ nextHeadsById.set(createGridIdKey(nextHead.id), nextHead);
856
1189
  });
857
1190
  const resolvedHeads = currentHeads.map((currentHead) => {
858
- return nextHeadsById.get(currentHead.id) ?? currentHead;
1191
+ return nextHeadsById.get(createGridIdKey(currentHead.id)) ?? currentHead;
859
1192
  });
860
1193
  nextHeads.forEach((nextHead) => {
861
- if (currentIds.has(nextHead.id)) {
1194
+ if (currentIds.has(createGridIdKey(nextHead.id))) {
862
1195
  return;
863
1196
  }
864
1197
  resolvedHeads.push(nextHead);
@@ -893,7 +1226,7 @@ function normalizeGridState(value) {
893
1226
  throw new Error("Grid state initializer must return an object.");
894
1227
  }
895
1228
  if (Array.isArray(value)) {
896
- throw new Error("Legacy matrix grid input has been removed. Pass a schema object with rows, columns, and cells instead.");
1229
+ throw new Error("Grid state initializer must be a schema object with rows, columns, and cells.");
897
1230
  }
898
1231
  const partialState = value;
899
1232
  const { cells = [], rows = [], columns = [] } = partialState;
@@ -909,9 +1242,7 @@ function normalizeGridState(value) {
909
1242
  function normalizeRecordHeads(heads, idKey) {
910
1243
  return heads.map((head, index) => {
911
1244
  const id = head[idKey];
912
- if (typeof id !== "string") {
913
- throw new Error(`Grid header key "${String(idKey)}" must resolve to a string id.`);
914
- }
1245
+ assertGridId(id, `Grid header key "${String(idKey)}"`);
915
1246
  return {
916
1247
  ...head,
917
1248
  id,
@@ -927,7 +1258,7 @@ function createGridInitialCellsFromState(stateCells, stateAdapter) {
927
1258
  const { rowId, columnId, value } = stateAdapter.deserializeCell(stateCell);
928
1259
  const gridKey = createGridKey(rowId, columnId);
929
1260
  if (seenKeys.has(gridKey)) {
930
- throw new Error(`Duplicate cell for row "${rowId}" and column "${columnId}".`);
1261
+ throw new Error(`Duplicate cell for row "${formatGridId(rowId)}" and column "${formatGridId(columnId)}".`);
931
1262
  }
932
1263
  seenKeys.add(gridKey);
933
1264
  initialCells.push({
@@ -943,9 +1274,8 @@ function createSchemaStateAdapter(rowCellKey, columnCellKey) {
943
1274
  deserializeCell: (stateCell) => {
944
1275
  const rowId = stateCell[rowCellKey];
945
1276
  const columnId = stateCell[columnCellKey];
946
- if (typeof rowId !== "string" || typeof columnId !== "string") {
947
- throw new Error(`Grid cell keys "${String(rowCellKey)}" and "${String(columnCellKey)}" must resolve to string ids.`);
948
- }
1277
+ assertGridId(rowId, `Grid cell key "${String(rowCellKey)}"`);
1278
+ assertGridId(columnId, `Grid cell key "${String(columnCellKey)}"`);
949
1279
  return {
950
1280
  rowId: rowId,
951
1281
  columnId: columnId,
@@ -984,10 +1314,11 @@ function normalizeUpsertHead(nextHead, currentHead, fallbackOrder) {
984
1314
  function assertUniqueHeadIds(heads, axis) {
985
1315
  const seenIds = new Set();
986
1316
  heads.forEach((head) => {
987
- if (seenIds.has(head.id)) {
988
- throw new Error(`Duplicate ${axis} header "${head.id}".`);
1317
+ const idKey = createGridIdKey(head.id);
1318
+ if (seenIds.has(idKey)) {
1319
+ throw new Error(`Duplicate ${axis} header "${formatGridId(head.id)}".`);
989
1320
  }
990
- seenIds.add(head.id);
1321
+ seenIds.add(idKey);
991
1322
  });
992
1323
  }
993
1324
  function createEmptyTailState() {
@@ -1005,6 +1336,192 @@ function readGridHeadLabel(head, id) {
1005
1336
  if (typeof name === "string") {
1006
1337
  return name;
1007
1338
  }
1008
- return id;
1339
+ return formatGridId(id);
1340
+ }
1341
+ function createGridStateCellAdapter() {
1342
+ return {
1343
+ deserializeCell: (stateCell) => ({
1344
+ rowId: stateCell.rowId,
1345
+ columnId: stateCell.columnId,
1346
+ value: stateCell.value,
1347
+ }),
1348
+ serializeCell: (rowId, columnId, value) => ({
1349
+ rowId,
1350
+ columnId,
1351
+ value,
1352
+ }),
1353
+ };
1354
+ }
1355
+ function createSubGridPersistOption(parentGrid, persist) {
1356
+ const resolvedPersist = resolveDependentGridPersistOption(parentGrid, persist, "sub-grid");
1357
+ if (!resolvedPersist) {
1358
+ return undefined;
1359
+ }
1360
+ const [storageKey, adapter] = resolvedPersist;
1361
+ const resolvedAdapter = (adapter ??
1362
+ defaultGridPersistAdapter);
1363
+ return [
1364
+ storageKey,
1365
+ {
1366
+ get: async (nextStorageKey) => {
1367
+ const persistedState = await Promise.resolve(resolvedAdapter.get(nextStorageKey));
1368
+ if (persistedState === null) {
1369
+ return null;
1370
+ }
1371
+ try {
1372
+ const normalizedState = normalizeGridStateInput(persistedState);
1373
+ const parentState = parentGrid.getState();
1374
+ return createSubGridState(parentState.rows, parentState.columns, normalizedState.cells);
1375
+ }
1376
+ catch {
1377
+ return null;
1378
+ }
1379
+ },
1380
+ set: (nextStorageKey, nextState) => {
1381
+ const parentState = parentGrid.getState();
1382
+ return resolvedAdapter.set(nextStorageKey, createSubGridState(parentState.rows, parentState.columns, nextState.cells));
1383
+ },
1384
+ remove: (nextStorageKey) => resolvedAdapter.remove(nextStorageKey),
1385
+ },
1386
+ ];
1387
+ }
1388
+ function createDimensionGridPersistOption(parentGrid, dimension, persist) {
1389
+ const resolvedPersist = resolveDependentGridPersistOption(parentGrid, persist, `dimension-grid:${dimension}`);
1390
+ if (!resolvedPersist) {
1391
+ return undefined;
1392
+ }
1393
+ const [storageKey, adapter] = resolvedPersist;
1394
+ const resolvedAdapter = (adapter ?? defaultGridPersistAdapter);
1395
+ return [
1396
+ storageKey,
1397
+ {
1398
+ get: async (nextStorageKey) => {
1399
+ const persistedState = await Promise.resolve(resolvedAdapter.get(nextStorageKey));
1400
+ if (persistedState === null) {
1401
+ return null;
1402
+ }
1403
+ try {
1404
+ return normalizeDimensionGridStateInput(persistedState, dimension);
1405
+ }
1406
+ catch {
1407
+ return null;
1408
+ }
1409
+ },
1410
+ set: (nextStorageKey, nextState) => {
1411
+ return resolvedAdapter.set(nextStorageKey, normalizeDimensionGridStateInput(nextState, dimension));
1412
+ },
1413
+ remove: (nextStorageKey) => resolvedAdapter.remove(nextStorageKey),
1414
+ },
1415
+ ];
1416
+ }
1417
+ function normalizeDimensionGridStateInput(value, dimension) {
1418
+ if (Array.isArray(value)) {
1419
+ return {
1420
+ dimension,
1421
+ cells: value,
1422
+ };
1423
+ }
1424
+ if (!value || typeof value !== "object") {
1425
+ throw new Error("Dimension grid state updates must be arrays or objects.");
1426
+ }
1427
+ const nextState = value;
1428
+ if (nextState.dimension !== undefined && nextState.dimension !== dimension) {
1429
+ throw new Error(`Dimension grid state must use the "${dimension}" dimension when updating this grid.`);
1430
+ }
1431
+ if (!Array.isArray(nextState.cells)) {
1432
+ throw new Error("Dimension grid state updates must provide a cells array.");
1433
+ }
1434
+ return {
1435
+ dimension,
1436
+ cells: nextState.cells,
1437
+ };
1438
+ }
1439
+ function resolveDependentGridPersistOption(parentGrid, persist, suffix) {
1440
+ if (persist === false) {
1441
+ return undefined;
1442
+ }
1443
+ if (persist) {
1444
+ return persist;
1445
+ }
1446
+ const inheritedPersist = parentGrid.__persistOption;
1447
+ if (!inheritedPersist) {
1448
+ return undefined;
1449
+ }
1450
+ return [
1451
+ `${inheritedPersist[0]}:${suffix}`,
1452
+ inheritedPersist[1],
1453
+ ];
1454
+ }
1455
+ function createSubGridState(rows, columns, cells) {
1456
+ return {
1457
+ rows,
1458
+ columns,
1459
+ cells: filterGridStateCellsToAxes(cells, rows, columns),
1460
+ };
1461
+ }
1462
+ function assertGridStateCellsWithinAxes(cells, rows, columns, contextLabel) {
1463
+ const rowIds = new Set(rows.map((rowHead) => createGridIdKey(rowHead.id)));
1464
+ const columnIds = new Set(columns.map((columnHead) => createGridIdKey(columnHead.id)));
1465
+ cells.forEach((currentCell) => {
1466
+ if (!rowIds.has(createGridIdKey(currentCell.rowId))) {
1467
+ throw new Error(`${contextLabel} must reference an existing parent row header. Missing row "${formatGridId(currentCell.rowId)}".`);
1468
+ }
1469
+ if (!columnIds.has(createGridIdKey(currentCell.columnId))) {
1470
+ throw new Error(`${contextLabel} must reference an existing parent column header. Missing column "${formatGridId(currentCell.columnId)}".`);
1471
+ }
1472
+ });
1473
+ return cells;
1474
+ }
1475
+ function filterGridStateCellsToAxes(cells, rows, columns) {
1476
+ const rowIds = new Set(rows.map((rowHead) => createGridIdKey(rowHead.id)));
1477
+ const columnIds = new Set(columns.map((columnHead) => createGridIdKey(columnHead.id)));
1478
+ return cells.filter((currentCell) => rowIds.has(createGridIdKey(currentCell.rowId)) &&
1479
+ columnIds.has(createGridIdKey(currentCell.columnId)));
1480
+ }
1481
+ function didGridDiffAffectAxes(diff) {
1482
+ return (!haveSameHeadSnapshots(diff.rows, diff.previousRows) ||
1483
+ !haveSameHeadSnapshots(diff.columns, diff.previousColumns));
1484
+ }
1485
+ function haveSameHeadSnapshots(leftHeads, rightHeads) {
1486
+ if (leftHeads === rightHeads) {
1487
+ return true;
1488
+ }
1489
+ if (!leftHeads || !rightHeads || leftHeads.length !== rightHeads.length) {
1490
+ return false;
1491
+ }
1492
+ return leftHeads.every((leftHead, index) => {
1493
+ const rightHead = rightHeads[index];
1494
+ if (leftHead === undefined || rightHead === undefined) {
1495
+ return leftHead === rightHead;
1496
+ }
1497
+ return haveSameShallowRecord(leftHead, rightHead);
1498
+ });
1499
+ }
1500
+ function haveSameShallowRecord(leftRecord, rightRecord) {
1501
+ if (Object.is(leftRecord, rightRecord)) {
1502
+ return true;
1503
+ }
1504
+ const leftKeys = Object.keys(leftRecord);
1505
+ const rightKeys = Object.keys(rightRecord);
1506
+ if (leftKeys.length !== rightKeys.length) {
1507
+ return false;
1508
+ }
1509
+ return leftKeys.every((key) => {
1510
+ if (!(key in rightRecord)) {
1511
+ return false;
1512
+ }
1513
+ const leftValue = leftRecord[key];
1514
+ const rightValue = rightRecord[key];
1515
+ if (isGridIdValue(leftValue) && isGridIdValue(rightValue)) {
1516
+ return createGridIdKey(leftValue) === createGridIdKey(rightValue);
1517
+ }
1518
+ return Object.is(leftValue, rightValue);
1519
+ });
1520
+ }
1521
+ function isGridIdValue(value) {
1522
+ return (typeof value === "string" ||
1523
+ typeof value === "number" ||
1524
+ typeof value === "boolean" ||
1525
+ value instanceof Date);
1009
1526
  }
1010
1527
  //# sourceMappingURL=grid.js.map