zubin-grid 0.4.13 → 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 (47) hide show
  1. package/README.md +100 -19
  2. package/dist/core/cell.d.ts +6 -6
  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 +10 -9
  11. package/dist/core/grid.d.ts.map +1 -1
  12. package/dist/core/grid.js +388 -144
  13. package/dist/core/grid.js.map +1 -1
  14. package/dist/core/head.d.ts +9 -11
  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 +3 -3
  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/persist.d.ts.map +1 -1
  23. package/dist/core/persist.js +42 -41
  24. package/dist/core/persist.js.map +1 -1
  25. package/dist/core/tail.d.ts +7 -9
  26. package/dist/core/tail.d.ts.map +1 -1
  27. package/dist/core/tail.js +17 -19
  28. package/dist/core/tail.js.map +1 -1
  29. package/dist/core/types/grid.types.d.ts +69 -27
  30. package/dist/core/types/grid.types.d.ts.map +1 -1
  31. package/dist/core/types/head.types.d.ts +7 -6
  32. package/dist/core/types/head.types.d.ts.map +1 -1
  33. package/dist/core/types/tail.types.d.ts +3 -2
  34. package/dist/core/types/tail.types.d.ts.map +1 -1
  35. package/dist/index.d.ts +4 -4
  36. package/dist/index.d.ts.map +1 -1
  37. package/dist/index.js +2 -2
  38. package/dist/index.js.map +1 -1
  39. package/package.json +1 -1
  40. package/dist/core/gridPersist.d.ts +0 -3
  41. package/dist/core/gridPersist.d.ts.map +0 -1
  42. package/dist/core/gridPersist.js +0 -2
  43. package/dist/core/gridPersist.js.map +0 -1
  44. package/dist/core/types/gridPersist.types.d.ts +0 -2
  45. package/dist/core/types/gridPersist.types.d.ts.map +0 -1
  46. package/dist/core/types/gridPersist.types.js +0 -2
  47. package/dist/core/types/gridPersist.types.js.map +0 -1
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, defaultGridPersistAdapter, } 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,12 +14,212 @@ 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
  }
16
- export function createSubGrid(parentGrid, cellsOrOptionsOrPersist, persist) {
17
- const { initialCells, persistOption } = resolveCreateSubGridArgs(cellsOrOptionsOrPersist, persist);
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) {
18
219
  const parentState = parentGrid.getState();
19
220
  const stateAdapter = createGridStateCellAdapter();
20
221
  const resolvedInitialCells = assertGridStateCellsWithinAxes(initialCells, parentState.rows, parentState.columns, "Sub grid cells");
21
- const subGridStore = createGridStore(parentState.rows, parentState.columns, createGridInitialCellsFromState(resolvedInitialCells, stateAdapter), stateAdapter, createSubGridPersistOption(parentGrid, persistOption));
222
+ const subGridStore = createGridStore(parentState.rows, parentState.columns, createGridInitialCellsFromState(resolvedInitialCells, stateAdapter), stateAdapter, createSubGridPersistOption(parentGrid, options?.persist));
22
223
  const getSubGridState = () => {
23
224
  const nextParentState = parentGrid.getState();
24
225
  return createSubGridState(nextParentState.rows, nextParentState.columns, subGridStore.getState().cells);
@@ -174,13 +375,13 @@ function createGridStore(initialRows, initialColumns, initialCells, stateAdapter
174
375
  return stateAdapter.deserializeCell(stateAdapter.serializeCell(rowId, columnId, value)).value;
175
376
  };
176
377
  const recomputeRowTailInternal = (rowId) => {
177
- const onRowUpdate = rowTailUpdaters.get(rowId);
378
+ const onRowUpdate = rowTailUpdaters.get(createGridIdKey(rowId));
178
379
  if (!onRowUpdate)
179
380
  return false;
180
381
  return setTailCellResult(getTailCell(rowTailCells, rowId, "row"), onRowUpdate(getRowCells(rowId)));
181
382
  };
182
383
  const recomputeColumnTailInternal = (columnId) => {
183
- const onColumnUpdate = columnTailUpdaters.get(columnId);
384
+ const onColumnUpdate = columnTailUpdaters.get(createGridIdKey(columnId));
184
385
  if (!onColumnUpdate)
185
386
  return false;
186
387
  return setTailCellResult(getTailCell(columnTailCells, columnId, "column"), onColumnUpdate(getColumnCells(columnId)));
@@ -209,13 +410,16 @@ function createGridStore(initialRows, initialColumns, initialCells, stateAdapter
209
410
  cellsMap.delete(gridKey);
210
411
  };
211
412
  const attachCell = (rowId, columnId, currentCell) => {
212
- if (!rowHeadCells.has(rowId))
213
- throw new Error(`Missing row header "${rowId}".`);
214
- if (!columnHeadCells.has(columnId))
215
- 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
+ }
216
419
  const gridKey = createGridKey(rowId, columnId);
217
- if (cellsMap.has(gridKey))
218
- 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
+ }
219
423
  cellsMap.set(gridKey, currentCell);
220
424
  let previousValue = currentCell.get();
221
425
  const unsubscribe = currentCell.subscribe(() => {
@@ -244,8 +448,9 @@ function createGridStore(initialRows, initialColumns, initialCells, stateAdapter
244
448
  };
245
449
  const getCell = (rowId, columnId) => {
246
450
  const currentCell = cellsMap.get(createGridKey(rowId, columnId));
247
- if (!currentCell)
248
- 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
+ }
249
454
  return currentCell;
250
455
  };
251
456
  const createAxisCellSnapshot = (rowId, columnId) => {
@@ -272,15 +477,16 @@ function createGridStore(initialRows, initialColumns, initialCells, stateAdapter
272
477
  assertUniqueHeadIds(nextHeads, axis);
273
478
  const nextIds = new Set();
274
479
  nextHeads.forEach((nextHead, index) => {
275
- nextIds.add(nextHead.id);
276
- const currentHeadCell = headCells.get(nextHead.id);
480
+ const idKey = createGridIdKey(nextHead.id);
481
+ nextIds.add(idKey);
482
+ const currentHeadCell = headCells.get(idKey);
277
483
  if (currentHeadCell)
278
484
  currentHeadCell.set(nextHead);
279
485
  else
280
- headCells.set(nextHead.id, cell(nextHead));
281
- if (!tailCells.has(nextHead.id))
282
- tailCells.set(nextHead.id, cell(createEmptyTailState()));
283
- 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);
284
490
  });
285
491
  [...headCells.keys()].forEach((id) => {
286
492
  if (nextIds.has(id))
@@ -294,24 +500,24 @@ function createGridStore(initialRows, initialColumns, initialCells, stateAdapter
294
500
  const replaceState = (nextState) => {
295
501
  isApplyingState = true;
296
502
  try {
297
- syncAxisHeads(nextState.rows, "row", rowHeadCells, rowTailCells, rowHeadOrder, (id) => {
298
- rowTailUpdaters.delete(id);
503
+ syncAxisHeads(nextState.rows, "row", rowHeadCells, rowTailCells, rowHeadOrder, (idKey) => {
504
+ rowTailUpdaters.delete(idKey);
299
505
  });
300
- syncAxisHeads(nextState.columns, "column", columnHeadCells, columnTailCells, columnHeadOrder, (id) => {
301
- columnTailUpdaters.delete(id);
506
+ syncAxisHeads(nextState.columns, "column", columnHeadCells, columnTailCells, columnHeadOrder, (idKey) => {
507
+ columnTailUpdaters.delete(idKey);
302
508
  });
303
509
  const nextCells = new Map();
304
510
  nextState.cells.forEach((nextStateCell) => {
305
511
  const { rowId, columnId, value } = stateAdapter.deserializeCell(nextStateCell);
306
- if (!rowHeadCells.has(rowId)) {
307
- throw new Error(`Missing row header "${rowId}".`);
512
+ if (!rowHeadCells.has(createGridIdKey(rowId))) {
513
+ throw new Error(`Missing row header "${formatGridId(rowId)}".`);
308
514
  }
309
- if (!columnHeadCells.has(columnId)) {
310
- throw new Error(`Missing column header "${columnId}".`);
515
+ if (!columnHeadCells.has(createGridIdKey(columnId))) {
516
+ throw new Error(`Missing column header "${formatGridId(columnId)}".`);
311
517
  }
312
518
  const gridKey = createGridKey(rowId, columnId);
313
519
  if (nextCells.has(gridKey)) {
314
- 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)}".`);
315
521
  }
316
522
  nextCells.set(gridKey, {
317
523
  rowId,
@@ -365,8 +571,8 @@ function createGridStore(initialRows, initialColumns, initialCells, stateAdapter
365
571
  if (nextCells.length === 0) {
366
572
  return null;
367
573
  }
368
- const touchedRowIds = new Set();
369
- const touchedColumnIds = new Set();
574
+ const touchedRowIds = new Map();
575
+ const touchedColumnIds = new Map();
370
576
  const normalizedCells = [];
371
577
  const previousCells = [];
372
578
  const cellKeys = [];
@@ -374,10 +580,12 @@ function createGridStore(initialRows, initialColumns, initialCells, stateAdapter
374
580
  try {
375
581
  nextCells.forEach((nextStateCell) => {
376
582
  const { rowId, columnId, value } = stateAdapter.deserializeCell(nextStateCell);
377
- if (!rowHeadCells.has(rowId))
378
- throw new Error(`Missing row header "${rowId}".`);
379
- if (!columnHeadCells.has(columnId))
380
- 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
+ }
381
589
  const normalizedValue = normalizeCellValue(rowId, columnId, value);
382
590
  const gridKey = createGridKey(rowId, columnId);
383
591
  const currentCell = cellsMap.get(gridKey);
@@ -389,16 +597,16 @@ function createGridStore(initialRows, initialColumns, initialCells, stateAdapter
389
597
  attachCell(rowId, columnId, cell(normalizedValue));
390
598
  }
391
599
  normalizedCells.push(stateAdapter.serializeCell(rowId, columnId, normalizedValue));
392
- touchedRowIds.add(rowId);
393
- touchedColumnIds.add(columnId);
600
+ touchedRowIds.set(createGridIdKey(rowId), rowId);
601
+ touchedColumnIds.set(createGridIdKey(columnId), columnId);
394
602
  cellKeys.push(gridKey);
395
603
  });
396
604
  }
397
605
  finally {
398
606
  isApplyingState = false;
399
607
  }
400
- const rowIds = [...touchedRowIds];
401
- const columnIds = [...touchedColumnIds];
608
+ const rowIds = [...touchedRowIds.values()];
609
+ const columnIds = [...touchedColumnIds.values()];
402
610
  return {
403
611
  rowIds,
404
612
  columnIds,
@@ -431,35 +639,34 @@ function createGridStore(initialRows, initialColumns, initialCells, stateAdapter
431
639
  if (nextRowHeads.length === 0) {
432
640
  return null;
433
641
  }
434
- const rowIds = new Set();
642
+ const rowIds = new Map();
435
643
  const rows = [];
436
644
  const previousRows = [];
437
645
  let shouldRecomputeColumns = false;
438
646
  nextRowHeads.forEach((nextRowHead) => {
439
647
  const rowHeadLike = nextRowHead;
440
- if (typeof rowHeadLike.id !== "string") {
441
- throw new Error("Row header upserts must include a string id.");
442
- }
443
- 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);
444
651
  const currentHead = currentHeadCell?.get();
445
652
  const resolvedHead = normalizeUpsertHead(rowHeadLike, currentHead, nextRowFallbackOrder);
446
653
  if (currentHeadCell) {
447
654
  currentHeadCell.set(resolvedHead);
448
655
  }
449
656
  else {
450
- rowHeadCells.set(resolvedHead.id, cell(resolvedHead));
451
- rowTailCells.set(resolvedHead.id, cell(createEmptyTailState()));
452
- rowHeadOrder.set(resolvedHead.id, nextRowFallbackOrder);
657
+ rowHeadCells.set(rowIdKey, cell(resolvedHead));
658
+ rowTailCells.set(rowIdKey, cell(createEmptyTailState()));
659
+ rowHeadOrder.set(rowIdKey, nextRowFallbackOrder);
453
660
  nextRowFallbackOrder += 1;
454
661
  }
455
662
  rows.push(resolvedHead);
456
663
  if (currentHead) {
457
664
  previousRows.push(currentHead);
458
665
  }
459
- rowIds.add(resolvedHead.id);
666
+ rowIds.set(rowIdKey, resolvedHead.id);
460
667
  shouldRecomputeColumns ||= !currentHead || currentHead.order !== resolvedHead.order;
461
668
  });
462
- const resolvedRowIds = [...rowIds];
669
+ const resolvedRowIds = [...rowIds.values()];
463
670
  return {
464
671
  rows,
465
672
  previousRows,
@@ -488,35 +695,34 @@ function createGridStore(initialRows, initialColumns, initialCells, stateAdapter
488
695
  if (nextColumnHeads.length === 0) {
489
696
  return null;
490
697
  }
491
- const columnIds = new Set();
698
+ const columnIds = new Map();
492
699
  const columns = [];
493
700
  const previousColumns = [];
494
701
  let shouldRecomputeRows = false;
495
702
  nextColumnHeads.forEach((nextColumnHead) => {
496
703
  const columnHeadLike = nextColumnHead;
497
- if (typeof columnHeadLike.id !== "string") {
498
- throw new Error("Column header upserts must include a string id.");
499
- }
500
- 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);
501
707
  const currentHead = currentHeadCell?.get();
502
708
  const resolvedHead = normalizeUpsertHead(columnHeadLike, currentHead, nextColumnFallbackOrder);
503
709
  if (currentHeadCell) {
504
710
  currentHeadCell.set(resolvedHead);
505
711
  }
506
712
  else {
507
- columnHeadCells.set(resolvedHead.id, cell(resolvedHead));
508
- columnTailCells.set(resolvedHead.id, cell(createEmptyTailState()));
509
- columnHeadOrder.set(resolvedHead.id, nextColumnFallbackOrder);
713
+ columnHeadCells.set(columnIdKey, cell(resolvedHead));
714
+ columnTailCells.set(columnIdKey, cell(createEmptyTailState()));
715
+ columnHeadOrder.set(columnIdKey, nextColumnFallbackOrder);
510
716
  nextColumnFallbackOrder += 1;
511
717
  }
512
718
  columns.push(resolvedHead);
513
719
  if (currentHead) {
514
720
  previousColumns.push(currentHead);
515
721
  }
516
- columnIds.add(resolvedHead.id);
722
+ columnIds.set(columnIdKey, resolvedHead.id);
517
723
  shouldRecomputeRows ||= !currentHead || currentHead.order !== resolvedHead.order;
518
724
  });
519
- const resolvedColumnIds = [...columnIds];
725
+ const resolvedColumnIds = [...columnIds.values()];
520
726
  return {
521
727
  columns,
522
728
  previousColumns,
@@ -644,23 +850,25 @@ function createGridStore(initialRows, initialColumns, initialCells, stateAdapter
644
850
  });
645
851
  };
646
852
  function registerRowTail(rowId, onRowUpdate) {
647
- rowTailUpdaters.set(rowId, onRowUpdate);
853
+ const rowIdKey = createGridIdKey(rowId);
854
+ rowTailUpdaters.set(rowIdKey, onRowUpdate);
648
855
  recomputeRowTailInternal(rowId);
649
856
  return () => {
650
- if (rowTailUpdaters.get(rowId) !== onRowUpdate) {
857
+ if (rowTailUpdaters.get(rowIdKey) !== onRowUpdate) {
651
858
  return;
652
859
  }
653
- rowTailUpdaters.delete(rowId);
860
+ rowTailUpdaters.delete(rowIdKey);
654
861
  };
655
862
  }
656
863
  function registerColumnTail(columnId, onColumnUpdate) {
657
- columnTailUpdaters.set(columnId, onColumnUpdate);
864
+ const columnIdKey = createGridIdKey(columnId);
865
+ columnTailUpdaters.set(columnIdKey, onColumnUpdate);
658
866
  recomputeColumnTailInternal(columnId);
659
867
  return () => {
660
- if (columnTailUpdaters.get(columnId) !== onColumnUpdate) {
868
+ if (columnTailUpdaters.get(columnIdKey) !== onColumnUpdate) {
661
869
  return;
662
870
  }
663
- columnTailUpdaters.delete(columnId);
871
+ columnTailUpdaters.delete(columnIdKey);
664
872
  };
665
873
  }
666
874
  const setGrid = (nextState, mode = "replace") => {
@@ -871,6 +1079,11 @@ function createGridStore(initialRows, initialColumns, initialCells, stateAdapter
871
1079
  recomputeRowTail,
872
1080
  recomputeColumnTail,
873
1081
  };
1082
+ Object.defineProperty(gridApi, "__persistOption", {
1083
+ value: persist,
1084
+ enumerable: false,
1085
+ configurable: true,
1086
+ });
874
1087
  return gridApi;
875
1088
  }
876
1089
  function resolveUpdater(nextValue, currentValue) {
@@ -879,7 +1092,7 @@ function resolveUpdater(nextValue, currentValue) {
879
1092
  : nextValue;
880
1093
  }
881
1094
  export function createGridKey(rowId, columnId) {
882
- return `${rowId}:${columnId}`;
1095
+ return `row=${createGridIdKey(rowId)}|col=${createGridIdKey(columnId)}`;
883
1096
  }
884
1097
  export function useGrid(currentGrid, options) {
885
1098
  const snapshotRef = useRef({
@@ -917,7 +1130,7 @@ function toOptionalArray(values) {
917
1130
  }
918
1131
  function haveSameItems(left, right) {
919
1132
  return (left.length === right.length &&
920
- left.every((currentValue, index) => Object.is(currentValue, right[index])));
1133
+ left.every((currentValue, index) => createGridIdKey(currentValue) === createGridIdKey(right[index])));
921
1134
  }
922
1135
  function normalizeGridStateInput(value) {
923
1136
  if (!value || typeof value !== "object") {
@@ -969,16 +1182,16 @@ function mergeHeadStates(currentHeads, nextHeads) {
969
1182
  const currentIds = new Set();
970
1183
  const nextHeadsById = new Map();
971
1184
  currentHeads.forEach((currentHead) => {
972
- currentIds.add(currentHead.id);
1185
+ currentIds.add(createGridIdKey(currentHead.id));
973
1186
  });
974
1187
  nextHeads.forEach((nextHead) => {
975
- nextHeadsById.set(nextHead.id, nextHead);
1188
+ nextHeadsById.set(createGridIdKey(nextHead.id), nextHead);
976
1189
  });
977
1190
  const resolvedHeads = currentHeads.map((currentHead) => {
978
- return nextHeadsById.get(currentHead.id) ?? currentHead;
1191
+ return nextHeadsById.get(createGridIdKey(currentHead.id)) ?? currentHead;
979
1192
  });
980
1193
  nextHeads.forEach((nextHead) => {
981
- if (currentIds.has(nextHead.id)) {
1194
+ if (currentIds.has(createGridIdKey(nextHead.id))) {
982
1195
  return;
983
1196
  }
984
1197
  resolvedHeads.push(nextHead);
@@ -1013,7 +1226,7 @@ function normalizeGridState(value) {
1013
1226
  throw new Error("Grid state initializer must return an object.");
1014
1227
  }
1015
1228
  if (Array.isArray(value)) {
1016
- 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.");
1017
1230
  }
1018
1231
  const partialState = value;
1019
1232
  const { cells = [], rows = [], columns = [] } = partialState;
@@ -1029,9 +1242,7 @@ function normalizeGridState(value) {
1029
1242
  function normalizeRecordHeads(heads, idKey) {
1030
1243
  return heads.map((head, index) => {
1031
1244
  const id = head[idKey];
1032
- if (typeof id !== "string") {
1033
- throw new Error(`Grid header key "${String(idKey)}" must resolve to a string id.`);
1034
- }
1245
+ assertGridId(id, `Grid header key "${String(idKey)}"`);
1035
1246
  return {
1036
1247
  ...head,
1037
1248
  id,
@@ -1047,7 +1258,7 @@ function createGridInitialCellsFromState(stateCells, stateAdapter) {
1047
1258
  const { rowId, columnId, value } = stateAdapter.deserializeCell(stateCell);
1048
1259
  const gridKey = createGridKey(rowId, columnId);
1049
1260
  if (seenKeys.has(gridKey)) {
1050
- 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)}".`);
1051
1262
  }
1052
1263
  seenKeys.add(gridKey);
1053
1264
  initialCells.push({
@@ -1063,9 +1274,8 @@ function createSchemaStateAdapter(rowCellKey, columnCellKey) {
1063
1274
  deserializeCell: (stateCell) => {
1064
1275
  const rowId = stateCell[rowCellKey];
1065
1276
  const columnId = stateCell[columnCellKey];
1066
- if (typeof rowId !== "string" || typeof columnId !== "string") {
1067
- throw new Error(`Grid cell keys "${String(rowCellKey)}" and "${String(columnCellKey)}" must resolve to string ids.`);
1068
- }
1277
+ assertGridId(rowId, `Grid cell key "${String(rowCellKey)}"`);
1278
+ assertGridId(columnId, `Grid cell key "${String(columnCellKey)}"`);
1069
1279
  return {
1070
1280
  rowId: rowId,
1071
1281
  columnId: columnId,
@@ -1104,10 +1314,11 @@ function normalizeUpsertHead(nextHead, currentHead, fallbackOrder) {
1104
1314
  function assertUniqueHeadIds(heads, axis) {
1105
1315
  const seenIds = new Set();
1106
1316
  heads.forEach((head) => {
1107
- if (seenIds.has(head.id)) {
1108
- 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)}".`);
1109
1320
  }
1110
- seenIds.add(head.id);
1321
+ seenIds.add(idKey);
1111
1322
  });
1112
1323
  }
1113
1324
  function createEmptyTailState() {
@@ -1125,54 +1336,7 @@ function readGridHeadLabel(head, id) {
1125
1336
  if (typeof name === "string") {
1126
1337
  return name;
1127
1338
  }
1128
- return id;
1129
- }
1130
- function resolveCreateSubGridArgs(cellsOrOptionsOrPersist, persist) {
1131
- if (persist) {
1132
- return {
1133
- initialCells: readCreateSubGridCells(cellsOrOptionsOrPersist),
1134
- persistOption: persist,
1135
- };
1136
- }
1137
- if (!cellsOrOptionsOrPersist) {
1138
- return {
1139
- initialCells: [],
1140
- persistOption: undefined,
1141
- };
1142
- }
1143
- if (isCreateSubGridOptions(cellsOrOptionsOrPersist)) {
1144
- const subGridOptions = cellsOrOptionsOrPersist;
1145
- return {
1146
- initialCells: subGridOptions.cells ?? [],
1147
- persistOption: subGridOptions.persist,
1148
- };
1149
- }
1150
- if (isGridPersistOption(cellsOrOptionsOrPersist)) {
1151
- return {
1152
- initialCells: [],
1153
- persistOption: cellsOrOptionsOrPersist,
1154
- };
1155
- }
1156
- return {
1157
- initialCells: cellsOrOptionsOrPersist,
1158
- persistOption: undefined,
1159
- };
1160
- }
1161
- function readCreateSubGridCells(value) {
1162
- if (!value || isGridPersistOption(value)) {
1163
- return [];
1164
- }
1165
- if (isCreateSubGridOptions(value)) {
1166
- return (value
1167
- .cells ?? []);
1168
- }
1169
- return value;
1170
- }
1171
- function isCreateSubGridOptions(value) {
1172
- return typeof value === "object" && value !== null && !Array.isArray(value);
1173
- }
1174
- function isGridPersistOption(value) {
1175
- return Array.isArray(value) && value.length > 0 && typeof value[0] === "string";
1339
+ return formatGridId(id);
1176
1340
  }
1177
1341
  function createGridStateCellAdapter() {
1178
1342
  return {
@@ -1189,12 +1353,13 @@ function createGridStateCellAdapter() {
1189
1353
  };
1190
1354
  }
1191
1355
  function createSubGridPersistOption(parentGrid, persist) {
1192
- if (!persist) {
1356
+ const resolvedPersist = resolveDependentGridPersistOption(parentGrid, persist, "sub-grid");
1357
+ if (!resolvedPersist) {
1193
1358
  return undefined;
1194
1359
  }
1195
- const [storageKey, adapter] = persist;
1196
- const resolvedAdapter = adapter ??
1197
- defaultGridPersistAdapter;
1360
+ const [storageKey, adapter] = resolvedPersist;
1361
+ const resolvedAdapter = (adapter ??
1362
+ defaultGridPersistAdapter);
1198
1363
  return [
1199
1364
  storageKey,
1200
1365
  {
@@ -1220,6 +1385,73 @@ function createSubGridPersistOption(parentGrid, persist) {
1220
1385
  },
1221
1386
  ];
1222
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
+ }
1223
1455
  function createSubGridState(rows, columns, cells) {
1224
1456
  return {
1225
1457
  rows,
@@ -1228,22 +1460,23 @@ function createSubGridState(rows, columns, cells) {
1228
1460
  };
1229
1461
  }
1230
1462
  function assertGridStateCellsWithinAxes(cells, rows, columns, contextLabel) {
1231
- const rowIds = new Set(rows.map((rowHead) => rowHead.id));
1232
- const columnIds = new Set(columns.map((columnHead) => columnHead.id));
1463
+ const rowIds = new Set(rows.map((rowHead) => createGridIdKey(rowHead.id)));
1464
+ const columnIds = new Set(columns.map((columnHead) => createGridIdKey(columnHead.id)));
1233
1465
  cells.forEach((currentCell) => {
1234
- if (!rowIds.has(currentCell.rowId)) {
1235
- throw new Error(`${contextLabel} must reference an existing parent row header. Missing row "${currentCell.rowId}".`);
1466
+ if (!rowIds.has(createGridIdKey(currentCell.rowId))) {
1467
+ throw new Error(`${contextLabel} must reference an existing parent row header. Missing row "${formatGridId(currentCell.rowId)}".`);
1236
1468
  }
1237
- if (!columnIds.has(currentCell.columnId)) {
1238
- throw new Error(`${contextLabel} must reference an existing parent column header. Missing column "${currentCell.columnId}".`);
1469
+ if (!columnIds.has(createGridIdKey(currentCell.columnId))) {
1470
+ throw new Error(`${contextLabel} must reference an existing parent column header. Missing column "${formatGridId(currentCell.columnId)}".`);
1239
1471
  }
1240
1472
  });
1241
1473
  return cells;
1242
1474
  }
1243
1475
  function filterGridStateCellsToAxes(cells, rows, columns) {
1244
- const rowIds = new Set(rows.map((rowHead) => rowHead.id));
1245
- const columnIds = new Set(columns.map((columnHead) => columnHead.id));
1246
- return cells.filter((currentCell) => rowIds.has(currentCell.rowId) && columnIds.has(currentCell.columnId));
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)));
1247
1480
  }
1248
1481
  function didGridDiffAffectAxes(diff) {
1249
1482
  return (!haveSameHeadSnapshots(diff.rows, diff.previousRows) ||
@@ -1277,7 +1510,18 @@ function haveSameShallowRecord(leftRecord, rightRecord) {
1277
1510
  if (!(key in rightRecord)) {
1278
1511
  return false;
1279
1512
  }
1280
- return Object.is(leftRecord[key], rightRecord[key]);
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);
1281
1519
  });
1282
1520
  }
1521
+ function isGridIdValue(value) {
1522
+ return (typeof value === "string" ||
1523
+ typeof value === "number" ||
1524
+ typeof value === "boolean" ||
1525
+ value instanceof Date);
1526
+ }
1283
1527
  //# sourceMappingURL=grid.js.map