zubin-grid 0.41.13 → 0.42.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/README.md +75 -21
- package/dist/adaptors/react/index.d.ts +71 -0
- package/dist/adaptors/react/index.d.ts.map +1 -0
- package/dist/adaptors/react/index.js +249 -0
- package/dist/adaptors/react/index.js.map +1 -0
- package/dist/core/cell.d.ts +17 -8
- package/dist/core/cell.d.ts.map +1 -1
- package/dist/core/cell.js +28 -97
- package/dist/core/cell.js.map +1 -1
- package/dist/core/grid.d.ts +3 -4
- package/dist/core/grid.d.ts.map +1 -1
- package/dist/core/grid.js +242 -77
- package/dist/core/grid.js.map +1 -1
- package/dist/core/head.d.ts +1 -5
- package/dist/core/head.d.ts.map +1 -1
- package/dist/core/head.js +0 -54
- package/dist/core/head.js.map +1 -1
- package/dist/core/persist.d.ts.map +1 -1
- package/dist/core/persist.js +11 -1
- package/dist/core/persist.js.map +1 -1
- package/dist/core/tail.d.ts +1 -4
- package/dist/core/tail.d.ts.map +1 -1
- package/dist/core/tail.js +0 -23
- package/dist/core/tail.js.map +1 -1
- package/dist/core/types/grid.types.d.ts +14 -9
- package/dist/core/types/grid.types.d.ts.map +1 -1
- package/dist/core/types/persist.types.d.ts +2 -0
- package/dist/core/types/persist.types.d.ts.map +1 -1
- package/dist/index.d.ts +5 -5
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -4
- package/dist/index.js.map +1 -1
- package/package.json +5 -1
package/dist/core/grid.js
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { useCallback, useEffect, useRef, useSyncExternalStore } from "react";
|
|
2
1
|
import { cell } from "./cell.js";
|
|
3
2
|
import { assertGridId, createGridIdKey, formatGridId } from "./grid-id.js";
|
|
4
3
|
import { createGridPersistController, defaultGridPersistAdapter } from "./persist.js";
|
|
@@ -28,10 +27,36 @@ export function createDimensionGrid(parentGrid, initialCells, dimension, options
|
|
|
28
27
|
createGridIdKey(boundId),
|
|
29
28
|
cell(initialCells[index]),
|
|
30
29
|
]));
|
|
30
|
+
const dirtyBoundIdKeys = new Set();
|
|
31
|
+
const persistedCellValuesById = new Map(boundIds.map((boundId, index) => [createGridIdKey(boundId), initialCells[index]]));
|
|
31
32
|
let dimensionGridApi;
|
|
33
|
+
const resetPersistedDimensionCells = () => {
|
|
34
|
+
persistedCellValuesById.clear();
|
|
35
|
+
boundIds.forEach((boundId) => {
|
|
36
|
+
const idKey = createGridIdKey(boundId);
|
|
37
|
+
persistedCellValuesById.set(idKey, cellsById.get(idKey)?.get());
|
|
38
|
+
});
|
|
39
|
+
dirtyBoundIdKeys.clear();
|
|
40
|
+
};
|
|
41
|
+
const syncDimensionCellDirty = (boundId) => {
|
|
42
|
+
const idKey = createGridIdKey(boundId);
|
|
43
|
+
const currentValue = cellsById.get(idKey)?.get();
|
|
44
|
+
if (!persistedCellValuesById.has(idKey)) {
|
|
45
|
+
if (currentValue === undefined)
|
|
46
|
+
dirtyBoundIdKeys.delete(idKey);
|
|
47
|
+
else
|
|
48
|
+
dirtyBoundIdKeys.add(idKey);
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
if (haveSameRuntimeValue(currentValue, persistedCellValuesById.get(idKey))) {
|
|
52
|
+
dirtyBoundIdKeys.delete(idKey);
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
dirtyBoundIdKeys.add(idKey);
|
|
56
|
+
};
|
|
32
57
|
const getCellAtIndex = (index) => {
|
|
33
58
|
if (!Number.isInteger(index) || index < 0 || index >= boundIds.length) {
|
|
34
|
-
throw new Error(`Dimension grid ${dimension} index ${index} is out of
|
|
59
|
+
throw new Error(`Dimension grid ${dimension} index ${index} is out of range. ${describeIndexRange(boundIds.length, index)}`);
|
|
35
60
|
}
|
|
36
61
|
const boundId = boundIds[index];
|
|
37
62
|
const currentCell = cellsById.get(createGridIdKey(boundId));
|
|
@@ -40,6 +65,17 @@ export function createDimensionGrid(parentGrid, initialCells, dimension, options
|
|
|
40
65
|
}
|
|
41
66
|
return currentCell;
|
|
42
67
|
};
|
|
68
|
+
const readCellAtIndex = (index) => {
|
|
69
|
+
const currentCell = getCellAtIndex(index);
|
|
70
|
+
const boundId = boundIds[index];
|
|
71
|
+
return {
|
|
72
|
+
value: currentCell.get(),
|
|
73
|
+
meta: {
|
|
74
|
+
existsInDb: currentCell.get() !== undefined,
|
|
75
|
+
isDirty: dirtyBoundIdKeys.has(createGridIdKey(boundId)),
|
|
76
|
+
},
|
|
77
|
+
};
|
|
78
|
+
};
|
|
43
79
|
const getState = () => ({
|
|
44
80
|
dimension,
|
|
45
81
|
cells: boundIds.map((boundId) => {
|
|
@@ -78,6 +114,12 @@ export function createDimensionGrid(parentGrid, initialCells, dimension, options
|
|
|
78
114
|
finally {
|
|
79
115
|
isApplyingState = false;
|
|
80
116
|
}
|
|
117
|
+
changedIndices.forEach((index) => {
|
|
118
|
+
const boundId = boundIds[index];
|
|
119
|
+
if (boundId !== undefined) {
|
|
120
|
+
syncDimensionCellDirty(boundId);
|
|
121
|
+
}
|
|
122
|
+
});
|
|
81
123
|
return changedIndices;
|
|
82
124
|
};
|
|
83
125
|
const { hydrate, markStateChanged } = createGridPersistController(resolvedPersistOption, {
|
|
@@ -86,6 +128,8 @@ export function createDimensionGrid(parentGrid, initialCells, dimension, options
|
|
|
86
128
|
applyDimensionCells(normalizeDimensionGridStateInput(nextState, dimension).cells, "replace");
|
|
87
129
|
},
|
|
88
130
|
isApplyingState: () => isApplyingState,
|
|
131
|
+
onHydratedStateApplied: resetPersistedDimensionCells,
|
|
132
|
+
onPersistedStateApplied: resetPersistedDimensionCells,
|
|
89
133
|
});
|
|
90
134
|
const emitDimensionGridUpdate = (diff, persistStateChanged = false) => {
|
|
91
135
|
if (persistStateChanged) {
|
|
@@ -110,6 +154,20 @@ export function createDimensionGrid(parentGrid, initialCells, dimension, options
|
|
|
110
154
|
nextCellsById.forEach((currentCell, boundIdKey) => {
|
|
111
155
|
cellsById.set(boundIdKey, currentCell);
|
|
112
156
|
});
|
|
157
|
+
const nextIdKeys = new Set(nextBoundIds.map((boundId) => createGridIdKey(boundId)));
|
|
158
|
+
[...persistedCellValuesById.keys()].forEach((idKey) => {
|
|
159
|
+
if (nextIdKeys.has(idKey))
|
|
160
|
+
return;
|
|
161
|
+
persistedCellValuesById.delete(idKey);
|
|
162
|
+
dirtyBoundIdKeys.delete(idKey);
|
|
163
|
+
});
|
|
164
|
+
nextBoundIds.forEach((boundId) => {
|
|
165
|
+
const idKey = createGridIdKey(boundId);
|
|
166
|
+
if (!persistedCellValuesById.has(idKey)) {
|
|
167
|
+
persistedCellValuesById.set(idKey, undefined);
|
|
168
|
+
}
|
|
169
|
+
syncDimensionCellDirty(boundId);
|
|
170
|
+
});
|
|
113
171
|
emitDimensionGridUpdate({
|
|
114
172
|
type: "dimension",
|
|
115
173
|
action: previousSize === nextBoundIds.length ? "update" : "resize",
|
|
@@ -160,37 +218,43 @@ export function createDimensionGrid(parentGrid, initialCells, dimension, options
|
|
|
160
218
|
},
|
|
161
219
|
getState,
|
|
162
220
|
setGrid,
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
clearGrid,
|
|
167
|
-
subscribeGrid: (callback) => {
|
|
168
|
-
dimensionSubscribers.add(callback);
|
|
169
|
-
return () => {
|
|
170
|
-
dimensionSubscribers.delete(callback);
|
|
171
|
-
};
|
|
172
|
-
},
|
|
173
|
-
__setCellValue: (index, newValue) => {
|
|
221
|
+
readCell: readCellAtIndex,
|
|
222
|
+
setCell: (index, nextValue) => {
|
|
223
|
+
const boundId = boundIds[index];
|
|
174
224
|
const currentCell = getCellAtIndex(index);
|
|
175
|
-
|
|
225
|
+
const resolvedNextValue = resolveUpdater(nextValue, currentCell.get());
|
|
226
|
+
if (Object.is(currentCell.get(), resolvedNextValue)) {
|
|
176
227
|
return;
|
|
177
228
|
}
|
|
178
229
|
isApplyingState = true;
|
|
179
230
|
try {
|
|
180
|
-
currentCell.set(
|
|
231
|
+
currentCell.set(resolvedNextValue);
|
|
181
232
|
}
|
|
182
233
|
finally {
|
|
183
234
|
isApplyingState = false;
|
|
184
235
|
}
|
|
236
|
+
if (boundId !== undefined) {
|
|
237
|
+
syncDimensionCellDirty(boundId);
|
|
238
|
+
}
|
|
185
239
|
emitDimensionGridUpdate({
|
|
186
240
|
type: "cells",
|
|
187
|
-
action: "update",
|
|
188
|
-
source: "
|
|
241
|
+
action: resolvedNextValue === undefined ? "clear" : "update",
|
|
242
|
+
source: "setCell",
|
|
189
243
|
dimension,
|
|
190
244
|
size: boundIds.length,
|
|
191
245
|
indices: [index],
|
|
192
246
|
}, true);
|
|
193
247
|
},
|
|
248
|
+
getCell: getCellAtIndex,
|
|
249
|
+
getValue: (index) => getCellAtIndex(index).get(),
|
|
250
|
+
hasCell: (index) => Number.isInteger(index) && index >= 0 && index < boundIds.length,
|
|
251
|
+
clearGrid,
|
|
252
|
+
subscribeGrid: (callback) => {
|
|
253
|
+
dimensionSubscribers.add(callback);
|
|
254
|
+
return () => {
|
|
255
|
+
dimensionSubscribers.delete(callback);
|
|
256
|
+
};
|
|
257
|
+
},
|
|
194
258
|
};
|
|
195
259
|
Object.defineProperty(dimensionGridApi, "__persistOption", {
|
|
196
260
|
value: resolvedPersistOption,
|
|
@@ -347,7 +411,12 @@ function createGridStore(initialRows, initialColumns, initialCells, stateAdapter
|
|
|
347
411
|
const columnTailUpdaters = new Map();
|
|
348
412
|
const cellsMap = new Map();
|
|
349
413
|
const cellSubscriptions = new Map();
|
|
414
|
+
const dirtyCellKeys = new Set();
|
|
350
415
|
const gridSubscribers = new Set();
|
|
416
|
+
let persistedCellValues = new Map(initialCells.map(({ rowId, columnId, cell: currentCell }) => [
|
|
417
|
+
createGridKey(rowId, columnId),
|
|
418
|
+
currentCell.get(),
|
|
419
|
+
]));
|
|
351
420
|
let nextRowFallbackOrder = initialRows.length;
|
|
352
421
|
let nextColumnFallbackOrder = initialColumns.length;
|
|
353
422
|
let isApplyingState = false;
|
|
@@ -374,6 +443,44 @@ function createGridStore(initialRows, initialColumns, initialCells, stateAdapter
|
|
|
374
443
|
const normalizeCellValue = (rowId, columnId, value) => {
|
|
375
444
|
return stateAdapter.deserializeCell(stateAdapter.serializeCell(rowId, columnId, value)).value;
|
|
376
445
|
};
|
|
446
|
+
const assertCellAxesExist = (rowId, columnId) => {
|
|
447
|
+
if (!rowHeadCells.has(createGridIdKey(rowId))) {
|
|
448
|
+
throw new Error(`Missing row header "${formatGridId(rowId)}".`);
|
|
449
|
+
}
|
|
450
|
+
if (!columnHeadCells.has(createGridIdKey(columnId))) {
|
|
451
|
+
throw new Error(`Missing column header "${formatGridId(columnId)}".`);
|
|
452
|
+
}
|
|
453
|
+
};
|
|
454
|
+
const syncDirtyCellKey = (gridKey) => {
|
|
455
|
+
const currentCell = cellsMap.get(gridKey);
|
|
456
|
+
if (!persistedCellValues.has(gridKey)) {
|
|
457
|
+
if (!currentCell)
|
|
458
|
+
dirtyCellKeys.delete(gridKey);
|
|
459
|
+
else
|
|
460
|
+
dirtyCellKeys.add(gridKey);
|
|
461
|
+
return;
|
|
462
|
+
}
|
|
463
|
+
if (currentCell &&
|
|
464
|
+
haveSameRuntimeValue(currentCell.get(), persistedCellValues.get(gridKey))) {
|
|
465
|
+
dirtyCellKeys.delete(gridKey);
|
|
466
|
+
return;
|
|
467
|
+
}
|
|
468
|
+
dirtyCellKeys.add(gridKey);
|
|
469
|
+
};
|
|
470
|
+
const refreshDirtyCellKeys = () => {
|
|
471
|
+
const nextGridKeys = new Set([...persistedCellValues.keys(), ...cellsMap.keys()]);
|
|
472
|
+
dirtyCellKeys.clear();
|
|
473
|
+
nextGridKeys.forEach((gridKey) => {
|
|
474
|
+
syncDirtyCellKey(gridKey);
|
|
475
|
+
});
|
|
476
|
+
};
|
|
477
|
+
const resetPersistedCellState = () => {
|
|
478
|
+
persistedCellValues = new Map([...cellsMap.entries()].map(([gridKey, currentCell]) => [
|
|
479
|
+
gridKey,
|
|
480
|
+
currentCell.get(),
|
|
481
|
+
]));
|
|
482
|
+
dirtyCellKeys.clear();
|
|
483
|
+
};
|
|
377
484
|
const recomputeRowTailInternal = (rowId) => {
|
|
378
485
|
const onRowUpdate = rowTailUpdaters.get(createGridIdKey(rowId));
|
|
379
486
|
if (!onRowUpdate)
|
|
@@ -410,12 +517,7 @@ function createGridStore(initialRows, initialColumns, initialCells, stateAdapter
|
|
|
410
517
|
cellsMap.delete(gridKey);
|
|
411
518
|
};
|
|
412
519
|
const attachCell = (rowId, columnId, currentCell) => {
|
|
413
|
-
|
|
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
|
-
}
|
|
520
|
+
assertCellAxesExist(rowId, columnId);
|
|
419
521
|
const gridKey = createGridKey(rowId, columnId);
|
|
420
522
|
if (cellsMap.has(gridKey)) {
|
|
421
523
|
throw new Error(`Duplicate cell for row "${formatGridId(rowId)}" and column "${formatGridId(columnId)}".`);
|
|
@@ -428,6 +530,7 @@ function createGridStore(initialRows, initialColumns, initialCells, stateAdapter
|
|
|
428
530
|
previousValue = nextValue;
|
|
429
531
|
return;
|
|
430
532
|
}
|
|
533
|
+
syncDirtyCellKey(gridKey);
|
|
431
534
|
const rowTailIds = recomputeRowTailInternal(rowId) ? [rowId] : [];
|
|
432
535
|
const columnTailIds = recomputeColumnTailInternal(columnId) ? [columnId] : [];
|
|
433
536
|
emitGridUpdate({
|
|
@@ -447,12 +550,25 @@ function createGridStore(initialRows, initialColumns, initialCells, stateAdapter
|
|
|
447
550
|
cellSubscriptions.set(gridKey, unsubscribe);
|
|
448
551
|
};
|
|
449
552
|
const getCell = (rowId, columnId) => {
|
|
553
|
+
assertCellAxesExist(rowId, columnId);
|
|
450
554
|
const currentCell = cellsMap.get(createGridKey(rowId, columnId));
|
|
451
555
|
if (!currentCell) {
|
|
452
556
|
throw new Error(`Missing cell for row "${formatGridId(rowId)}" and column "${formatGridId(columnId)}".`);
|
|
453
557
|
}
|
|
454
558
|
return currentCell;
|
|
455
559
|
};
|
|
560
|
+
const readCell = (rowId, columnId) => {
|
|
561
|
+
assertCellAxesExist(rowId, columnId);
|
|
562
|
+
const gridKey = createGridKey(rowId, columnId);
|
|
563
|
+
const currentCell = cellsMap.get(gridKey);
|
|
564
|
+
return {
|
|
565
|
+
value: currentCell?.get(),
|
|
566
|
+
meta: {
|
|
567
|
+
existsInDb: currentCell !== undefined,
|
|
568
|
+
isDirty: dirtyCellKeys.has(gridKey),
|
|
569
|
+
},
|
|
570
|
+
};
|
|
571
|
+
};
|
|
456
572
|
const createAxisCellSnapshot = (rowId, columnId) => {
|
|
457
573
|
const currentCell = getCell(rowId, columnId);
|
|
458
574
|
return {
|
|
@@ -548,6 +664,7 @@ function createGridStore(initialRows, initialColumns, initialCells, stateAdapter
|
|
|
548
664
|
const rowTailIds = recomputeAllRowTails();
|
|
549
665
|
const columnTailIds = recomputeAllColumnTails();
|
|
550
666
|
refreshAxisIdsSnapshot();
|
|
667
|
+
refreshDirtyCellKeys();
|
|
551
668
|
return {
|
|
552
669
|
rowTailIds,
|
|
553
670
|
columnTailIds,
|
|
@@ -557,6 +674,8 @@ function createGridStore(initialRows, initialColumns, initialCells, stateAdapter
|
|
|
557
674
|
getState,
|
|
558
675
|
replaceState,
|
|
559
676
|
isApplyingState: () => isApplyingState,
|
|
677
|
+
onHydratedStateApplied: resetPersistedCellState,
|
|
678
|
+
onPersistedStateApplied: resetPersistedCellState,
|
|
560
679
|
});
|
|
561
680
|
const emitGridUpdate = (diff, persistStateChanged = false) => {
|
|
562
681
|
refreshAxisIdsSnapshot();
|
|
@@ -605,6 +724,9 @@ function createGridStore(initialRows, initialColumns, initialCells, stateAdapter
|
|
|
605
724
|
finally {
|
|
606
725
|
isApplyingState = false;
|
|
607
726
|
}
|
|
727
|
+
cellKeys.forEach((gridKey) => {
|
|
728
|
+
syncDirtyCellKey(gridKey);
|
|
729
|
+
});
|
|
608
730
|
const rowIds = [...touchedRowIds.values()];
|
|
609
731
|
const columnIds = [...touchedColumnIds.values()];
|
|
610
732
|
return {
|
|
@@ -803,6 +925,66 @@ function createGridStore(initialRows, initialColumns, initialCells, stateAdapter
|
|
|
803
925
|
const upsertCell = (nextCell) => {
|
|
804
926
|
emitCellUpsert("upsertCell", [nextCell]);
|
|
805
927
|
};
|
|
928
|
+
const setCell = (rowId, columnId, nextValue) => {
|
|
929
|
+
assertCellAxesExist(rowId, columnId);
|
|
930
|
+
const gridKey = createGridKey(rowId, columnId);
|
|
931
|
+
const currentCell = cellsMap.get(gridKey);
|
|
932
|
+
const currentValue = currentCell?.get();
|
|
933
|
+
const resolvedNextValue = resolveUpdater(nextValue, currentValue);
|
|
934
|
+
if (resolvedNextValue === undefined) {
|
|
935
|
+
if (!currentCell) {
|
|
936
|
+
return;
|
|
937
|
+
}
|
|
938
|
+
detachCell(gridKey);
|
|
939
|
+
syncDirtyCellKey(gridKey);
|
|
940
|
+
const rowTailIds = recomputeRowTailInternal(rowId) ? [rowId] : [];
|
|
941
|
+
const columnTailIds = recomputeColumnTailInternal(columnId) ? [columnId] : [];
|
|
942
|
+
emitGridUpdate({
|
|
943
|
+
type: "cells",
|
|
944
|
+
action: "clear",
|
|
945
|
+
source: "setCell",
|
|
946
|
+
rowIds: [rowId],
|
|
947
|
+
columnIds: [columnId],
|
|
948
|
+
rowTailIds: toOptionalArray(rowTailIds),
|
|
949
|
+
columnTailIds: toOptionalArray(columnTailIds),
|
|
950
|
+
cellKeys: [gridKey],
|
|
951
|
+
previousCells: [
|
|
952
|
+
stateAdapter.serializeCell(rowId, columnId, currentValue),
|
|
953
|
+
],
|
|
954
|
+
}, true);
|
|
955
|
+
return;
|
|
956
|
+
}
|
|
957
|
+
const normalizedValue = normalizeCellValue(rowId, columnId, resolvedNextValue);
|
|
958
|
+
isApplyingState = true;
|
|
959
|
+
try {
|
|
960
|
+
if (currentCell) {
|
|
961
|
+
currentCell.set(normalizedValue);
|
|
962
|
+
}
|
|
963
|
+
else {
|
|
964
|
+
attachCell(rowId, columnId, cell(normalizedValue));
|
|
965
|
+
}
|
|
966
|
+
}
|
|
967
|
+
finally {
|
|
968
|
+
isApplyingState = false;
|
|
969
|
+
}
|
|
970
|
+
syncDirtyCellKey(gridKey);
|
|
971
|
+
const rowTailIds = recomputeRowTailInternal(rowId) ? [rowId] : [];
|
|
972
|
+
const columnTailIds = recomputeColumnTailInternal(columnId) ? [columnId] : [];
|
|
973
|
+
emitGridUpdate({
|
|
974
|
+
type: "cells",
|
|
975
|
+
action: currentCell ? "update" : "upsert",
|
|
976
|
+
source: "setCell",
|
|
977
|
+
rowIds: [rowId],
|
|
978
|
+
columnIds: [columnId],
|
|
979
|
+
rowTailIds: toOptionalArray(rowTailIds),
|
|
980
|
+
columnTailIds: toOptionalArray(columnTailIds),
|
|
981
|
+
cellKeys: [gridKey],
|
|
982
|
+
cells: [stateAdapter.serializeCell(rowId, columnId, normalizedValue)],
|
|
983
|
+
previousCells: currentCell
|
|
984
|
+
? [stateAdapter.serializeCell(rowId, columnId, currentValue)]
|
|
985
|
+
: undefined,
|
|
986
|
+
}, true);
|
|
987
|
+
};
|
|
806
988
|
function getState() {
|
|
807
989
|
const rows = getOrderedRowHeads();
|
|
808
990
|
const columns = getOrderedColumnHeads();
|
|
@@ -1012,31 +1194,10 @@ function createGridStore(initialRows, initialColumns, initialCells, stateAdapter
|
|
|
1012
1194
|
},
|
|
1013
1195
|
getState,
|
|
1014
1196
|
setGrid,
|
|
1197
|
+
readCell,
|
|
1198
|
+
setCell,
|
|
1015
1199
|
getCell,
|
|
1016
1200
|
getValue: (rowId, columnId) => getCell(rowId, columnId).get(),
|
|
1017
|
-
__setCellValue: (rowId, columnId, newValue) => {
|
|
1018
|
-
const currentCell = getCell(rowId, columnId);
|
|
1019
|
-
isApplyingState = true;
|
|
1020
|
-
try {
|
|
1021
|
-
currentCell.set(normalizeCellValue(rowId, columnId, newValue));
|
|
1022
|
-
}
|
|
1023
|
-
finally {
|
|
1024
|
-
isApplyingState = false;
|
|
1025
|
-
}
|
|
1026
|
-
const rowTailIds = recomputeRowTailInternal(rowId) ? [rowId] : [];
|
|
1027
|
-
const columnTailIds = recomputeColumnTailInternal(columnId) ? [columnId] : [];
|
|
1028
|
-
emitGridUpdate({
|
|
1029
|
-
type: "cells",
|
|
1030
|
-
action: "update",
|
|
1031
|
-
source: "cell.set",
|
|
1032
|
-
rowIds: [rowId],
|
|
1033
|
-
columnIds: [columnId],
|
|
1034
|
-
rowTailIds: toOptionalArray(rowTailIds),
|
|
1035
|
-
columnTailIds: toOptionalArray(columnTailIds),
|
|
1036
|
-
cellKeys: [createGridKey(rowId, columnId)],
|
|
1037
|
-
cells: [stateAdapter.serializeCell(rowId, columnId, currentCell.get())],
|
|
1038
|
-
}, true);
|
|
1039
|
-
},
|
|
1040
1201
|
hasCell: (rowId, columnId) => cellsMap.has(createGridKey(rowId, columnId)),
|
|
1041
1202
|
getRowHead: (rowId) => getHeadCell(rowHeadCells, rowId, "row").get(),
|
|
1042
1203
|
getColumnHead: (columnId) => getHeadCell(columnHeadCells, columnId, "column").get(),
|
|
@@ -1094,40 +1255,44 @@ function resolveUpdater(nextValue, currentValue) {
|
|
|
1094
1255
|
export function createGridKey(rowId, columnId) {
|
|
1095
1256
|
return `row=${createGridIdKey(rowId)}|col=${createGridIdKey(columnId)}`;
|
|
1096
1257
|
}
|
|
1097
|
-
export function useGrid(currentGrid, options) {
|
|
1098
|
-
const snapshotRef = useRef({
|
|
1099
|
-
rows: currentGrid.rowHeaders,
|
|
1100
|
-
cols: currentGrid.colHeaders,
|
|
1101
|
-
});
|
|
1102
|
-
const onGridUpdateRef = useRef(options?.onGridUpdate);
|
|
1103
|
-
useEffect(() => {
|
|
1104
|
-
onGridUpdateRef.current = options?.onGridUpdate;
|
|
1105
|
-
}, [options?.onGridUpdate]);
|
|
1106
|
-
useEffect(() => {
|
|
1107
|
-
return currentGrid.subscribeGrid((updatedGrid, diff) => {
|
|
1108
|
-
onGridUpdateRef.current?.(updatedGrid, diff);
|
|
1109
|
-
});
|
|
1110
|
-
}, [currentGrid]);
|
|
1111
|
-
const subscribe = useCallback((callback) => currentGrid.subscribeGrid(() => callback()), [currentGrid]);
|
|
1112
|
-
const getSnapshot = useCallback(() => {
|
|
1113
|
-
const nextRows = currentGrid.rowHeaders;
|
|
1114
|
-
const nextCols = currentGrid.colHeaders;
|
|
1115
|
-
const currentSnapshot = snapshotRef.current;
|
|
1116
|
-
if (currentSnapshot.rows === nextRows && currentSnapshot.cols === nextCols) {
|
|
1117
|
-
return currentSnapshot;
|
|
1118
|
-
}
|
|
1119
|
-
const nextSnapshot = {
|
|
1120
|
-
rows: nextRows,
|
|
1121
|
-
cols: nextCols,
|
|
1122
|
-
};
|
|
1123
|
-
snapshotRef.current = nextSnapshot;
|
|
1124
|
-
return nextSnapshot;
|
|
1125
|
-
}, [currentGrid]);
|
|
1126
|
-
return useSyncExternalStore(subscribe, getSnapshot, getSnapshot);
|
|
1127
|
-
}
|
|
1128
1258
|
function toOptionalArray(values) {
|
|
1129
1259
|
return values.length === 0 ? undefined : values;
|
|
1130
1260
|
}
|
|
1261
|
+
function describeIndexRange(size, index) {
|
|
1262
|
+
if (size === 0) {
|
|
1263
|
+
return `The grid currently has no items, so index ${index} cannot be selected.`;
|
|
1264
|
+
}
|
|
1265
|
+
return `Expected an index between 0 and ${size - 1} (${size} total items).`;
|
|
1266
|
+
}
|
|
1267
|
+
function haveSameRuntimeValue(leftValue, rightValue) {
|
|
1268
|
+
if (Object.is(leftValue, rightValue)) {
|
|
1269
|
+
return true;
|
|
1270
|
+
}
|
|
1271
|
+
if (leftValue instanceof Date && rightValue instanceof Date) {
|
|
1272
|
+
return leftValue.getTime() === rightValue.getTime();
|
|
1273
|
+
}
|
|
1274
|
+
if (Array.isArray(leftValue) && Array.isArray(rightValue)) {
|
|
1275
|
+
return (leftValue.length === rightValue.length &&
|
|
1276
|
+
leftValue.every((currentValue, index) => haveSameRuntimeValue(currentValue, rightValue[index])));
|
|
1277
|
+
}
|
|
1278
|
+
if (!isPlainObject(leftValue) || !isPlainObject(rightValue)) {
|
|
1279
|
+
return false;
|
|
1280
|
+
}
|
|
1281
|
+
const leftKeys = Object.keys(leftValue);
|
|
1282
|
+
const rightKeys = Object.keys(rightValue);
|
|
1283
|
+
if (leftKeys.length !== rightKeys.length) {
|
|
1284
|
+
return false;
|
|
1285
|
+
}
|
|
1286
|
+
return leftKeys.every((key) => {
|
|
1287
|
+
if (!(key in rightValue)) {
|
|
1288
|
+
return false;
|
|
1289
|
+
}
|
|
1290
|
+
return haveSameRuntimeValue(leftValue[key], rightValue[key]);
|
|
1291
|
+
});
|
|
1292
|
+
}
|
|
1293
|
+
function isPlainObject(value) {
|
|
1294
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
1295
|
+
}
|
|
1131
1296
|
function haveSameItems(left, right) {
|
|
1132
1297
|
return (left.length === right.length &&
|
|
1133
1298
|
left.every((currentValue, index) => createGridIdKey(currentValue) === createGridIdKey(right[index])));
|