@rowsncolumns/y-spreadsheet 14.0.32
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/LICENSE.md +100 -0
- package/Readme.md +77 -0
- package/dist/cjs/helpers.d.ts +179 -0
- package/dist/cjs/helpers.d.ts.map +1 -0
- package/dist/cjs/helpers.js +1635 -0
- package/dist/cjs/index.d.ts +6 -0
- package/dist/cjs/index.d.ts.map +1 -0
- package/dist/cjs/index.js +8 -0
- package/dist/cjs/observables.d.ts +10 -0
- package/dist/cjs/observables.d.ts.map +1 -0
- package/dist/cjs/observables.js +90 -0
- package/dist/cjs/profiling/batch-profile.spec.d.ts +2 -0
- package/dist/cjs/profiling/batch-profile.spec.d.ts.map +1 -0
- package/dist/cjs/profiling/batch-profile.spec.js +175 -0
- package/dist/cjs/tsconfig.cjs.tsbuildinfo +1 -0
- package/dist/cjs/types.d.ts +62 -0
- package/dist/cjs/types.d.ts.map +1 -0
- package/dist/cjs/types.js +2 -0
- package/dist/cjs/use-yspreadsheet-v2.d.ts +152 -0
- package/dist/cjs/use-yspreadsheet-v2.d.ts.map +1 -0
- package/dist/cjs/use-yspreadsheet-v2.js +980 -0
- package/dist/cjs/use-yspreadsheet.d.ts +39 -0
- package/dist/cjs/use-yspreadsheet.d.ts.map +1 -0
- package/dist/cjs/use-yspreadsheet.js +430 -0
- package/dist/cjs/vitest.config.d.ts +3 -0
- package/dist/cjs/vitest.config.d.ts.map +1 -0
- package/dist/cjs/vitest.config.js +23 -0
- package/dist/esm/helpers.d.ts +179 -0
- package/dist/esm/helpers.d.ts.map +1 -0
- package/dist/esm/helpers.js +1346 -0
- package/dist/esm/index.d.ts +6 -0
- package/dist/esm/index.d.ts.map +1 -0
- package/dist/esm/index.js +5 -0
- package/dist/esm/observables.d.ts +10 -0
- package/dist/esm/observables.d.ts.map +1 -0
- package/dist/esm/observables.js +51 -0
- package/dist/esm/profiling/batch-profile.spec.d.ts +2 -0
- package/dist/esm/profiling/batch-profile.spec.d.ts.map +1 -0
- package/dist/esm/profiling/batch-profile.spec.js +131 -0
- package/dist/esm/tsconfig.esm.tsbuildinfo +1 -0
- package/dist/esm/types.d.ts +62 -0
- package/dist/esm/types.d.ts.map +1 -0
- package/dist/esm/types.js +1 -0
- package/dist/esm/use-yspreadsheet-v2.d.ts +152 -0
- package/dist/esm/use-yspreadsheet-v2.d.ts.map +1 -0
- package/dist/esm/use-yspreadsheet-v2.js +814 -0
- package/dist/esm/use-yspreadsheet.d.ts +39 -0
- package/dist/esm/use-yspreadsheet.d.ts.map +1 -0
- package/dist/esm/use-yspreadsheet.js +304 -0
- package/dist/esm/vitest.config.d.ts +3 -0
- package/dist/esm/vitest.config.d.ts.map +1 -0
- package/dist/esm/vitest.config.js +21 -0
- package/package.json +54 -0
|
@@ -0,0 +1,980 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.useYSpreadsheetV2 = void 0;
|
|
4
|
+
var tslib_1 = require("tslib");
|
|
5
|
+
var spreadsheet_1 = require("@rowsncolumns/spreadsheet");
|
|
6
|
+
var utils_1 = require("@rowsncolumns/utils");
|
|
7
|
+
var ui_1 = require("@rowsncolumns/ui");
|
|
8
|
+
var react_1 = require("react");
|
|
9
|
+
var react_dom_1 = require("react-dom");
|
|
10
|
+
var Y = tslib_1.__importStar(require("yjs"));
|
|
11
|
+
var helpers_1 = require("./helpers");
|
|
12
|
+
/**
|
|
13
|
+
* Convert Y.js events to SheetDataEvent format for incremental processing
|
|
14
|
+
*/
|
|
15
|
+
var convertToSheetDataEvents = function (events) {
|
|
16
|
+
return events.map(function (event) {
|
|
17
|
+
var isYArrayEvent = event instanceof Y.YArrayEvent;
|
|
18
|
+
return {
|
|
19
|
+
path: event.path,
|
|
20
|
+
delta: isYArrayEvent
|
|
21
|
+
? event.changes.delta
|
|
22
|
+
: undefined,
|
|
23
|
+
isYArrayEvent: isYArrayEvent,
|
|
24
|
+
};
|
|
25
|
+
});
|
|
26
|
+
};
|
|
27
|
+
/**
|
|
28
|
+
* Read user entered value directly from Y.js data to avoid race conditions
|
|
29
|
+
* with React state updates. This is used when recalcCells observer fires
|
|
30
|
+
* before sheetData observer has updated React state.
|
|
31
|
+
*/
|
|
32
|
+
var getUserEnteredValueFromYDoc = function (ySheetData, sheetId, rowIndex, columnIndex) {
|
|
33
|
+
var sheet = ySheetData.get(String(sheetId));
|
|
34
|
+
if (!sheet || rowIndex >= sheet.length)
|
|
35
|
+
return undefined;
|
|
36
|
+
var row = sheet.get(rowIndex);
|
|
37
|
+
if (!row)
|
|
38
|
+
return undefined;
|
|
39
|
+
var values = row.get("values");
|
|
40
|
+
if (!values || columnIndex >= values.length)
|
|
41
|
+
return undefined;
|
|
42
|
+
var cell = values.get(columnIndex);
|
|
43
|
+
// Extract value from ExtendedValue, matching SheetCell.getUserEnteredValue() logic
|
|
44
|
+
// ue is the abbreviated form of userEnteredValue
|
|
45
|
+
var userEnteredValue = (0, utils_1.getCellUserEnteredValue)(cell);
|
|
46
|
+
return (0, utils_1.getExtendedValueFormula)(userEnteredValue);
|
|
47
|
+
};
|
|
48
|
+
/**
|
|
49
|
+
* Read user entered value from V3 Y.js data structure
|
|
50
|
+
*/
|
|
51
|
+
var getUserEnteredValueFromYDocV3 = function (ySheetDataV3, sheetId, rowIndex, columnIndex) {
|
|
52
|
+
var cellKey = (0, helpers_1.createCellKeyV3)(sheetId, rowIndex, columnIndex);
|
|
53
|
+
var cellDataV3 = ySheetDataV3.get(cellKey);
|
|
54
|
+
return (0, utils_1.getExtendedValueFormula)((0, utils_1.getCellUserEnteredValue)(cellDataV3 === null || cellDataV3 === void 0 ? void 0 : cellDataV3.value));
|
|
55
|
+
};
|
|
56
|
+
var observables_1 = require("./observables");
|
|
57
|
+
// Add a single null entry to the YSheetData
|
|
58
|
+
var origin = "local";
|
|
59
|
+
var useYSpreadsheetV2 = function (_a) {
|
|
60
|
+
var onChangeSheetData = _a.onChangeSheetData, onChangeSheets = _a.onChangeSheets, onChangeTables = _a.onChangeTables, onChangeCharts = _a.onChangeCharts, onChangeEmbeds = _a.onChangeEmbeds, onChangeConditionalFormats = _a.onChangeConditionalFormats, onChangeDataValidations = _a.onChangeDataValidations, onChangeCitations = _a.onChangeCitations, onAfterBroadcastPatch = _a.onAfterBroadcastPatch, recalcCellsMaxEntries = _a.recalcCellsMaxEntries, onChangeNamedRanges = _a.onChangeNamedRanges, onChangeProtectedRanges = _a.onChangeProtectedRanges, onChangePivotTables = _a.onChangePivotTables, enqueueCalculation = _a.enqueueCalculation, enqueueGraphOperation = _a.enqueueGraphOperation, reevaluateDerivationsForCells = _a.reevaluateDerivationsForCells, onChangeCellXfs = _a.onChangeCellXfs, onChangeSharedStrings = _a.onChangeSharedStrings, calculateNow = _a.calculateNow, onChangeActiveSheet = _a.onChangeActiveSheet, clientOnlyFunctionPattern = _a.clientOnlyFunctionPattern, initialSheets = _a.initialSheets, awarenessScopeId = _a.awarenessScopeId, userId = _a.userId, title = _a.title, sheetId = _a.sheetId, activeCell = _a.activeCell, kind = _a.kind, agentInitiator = _a.agentInitiator, selections = _a.selections, doc = _a.doc, provider = _a.provider, _b = _a.useV3, useV3 = _b === void 0 ? false : _b, _c = _a.sheetDataVersion, sheetDataVersion = _c === void 0 ? "v2" : _c, onMigrationComplete = _a.onMigrationComplete, _d = _a.deleteV2AfterMigration, deleteV2AfterMigration = _d === void 0 ? false : _d, _e = _a.skipInitialSync, skipInitialSync = _e === void 0 ? false : _e, _f = _a.supportLegacySharedStringsArray, supportLegacySharedStringsArray = _f === void 0 ? false : _f;
|
|
61
|
+
var _g = tslib_1.__read((0, react_1.useState)(false), 2), synced = _g[0], setSynced = _g[1];
|
|
62
|
+
var _h = tslib_1.__read((0, react_1.useState)([]), 2), users = _h[0], setUsers = _h[1];
|
|
63
|
+
var _j = tslib_1.__read((0, react_1.useState)(false), 2), isLeader = _j[0], setIsLeader = _j[1];
|
|
64
|
+
var isLeaderRef = (0, react_1.useRef)(false);
|
|
65
|
+
var resolvedAwarenessScopeId = awarenessScopeId;
|
|
66
|
+
// Listen to awareness
|
|
67
|
+
var awareness = provider === null || provider === void 0 ? void 0 : provider.awareness;
|
|
68
|
+
(0, ui_1.useIsomorphicLayoutEffect)(function () {
|
|
69
|
+
if (!awareness)
|
|
70
|
+
return;
|
|
71
|
+
var updateLeader = function () {
|
|
72
|
+
var entries = Array.from(awareness.getStates().entries());
|
|
73
|
+
var docStates = resolvedAwarenessScopeId
|
|
74
|
+
? entries.filter(function (_a) {
|
|
75
|
+
var _b = tslib_1.__read(_a, 2), _ = _b[0], state = _b[1];
|
|
76
|
+
return (state === null || state === void 0 ? void 0 : state.docGuid) === resolvedAwarenessScopeId;
|
|
77
|
+
})
|
|
78
|
+
: entries;
|
|
79
|
+
if (!docStates.length) {
|
|
80
|
+
setIsLeader(false);
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
var states = docStates.map(function (_a) {
|
|
84
|
+
var _b = tslib_1.__read(_a, 1), clientId = _b[0];
|
|
85
|
+
return clientId;
|
|
86
|
+
});
|
|
87
|
+
var minId = Math.min.apply(Math, tslib_1.__spreadArray([], tslib_1.__read(states), false));
|
|
88
|
+
// Always include self in the elec
|
|
89
|
+
setIsLeader(doc.clientID === minId);
|
|
90
|
+
};
|
|
91
|
+
awareness.on("change", updateLeader);
|
|
92
|
+
updateLeader(); // Initial check
|
|
93
|
+
return function () { return awareness.off("change", updateLeader); };
|
|
94
|
+
}, [awareness, doc, resolvedAwarenessScopeId]);
|
|
95
|
+
(0, ui_1.useIsomorphicLayoutEffect)(function () {
|
|
96
|
+
isLeaderRef.current = isLeader;
|
|
97
|
+
}, [isLeader]);
|
|
98
|
+
var ySheetDataV2 = (0, react_1.useMemo)(function () { return doc.getMap("sheetDataV2"); }, [doc]);
|
|
99
|
+
// V3: Flat map structure - Map<cellKey, CellDataV3>
|
|
100
|
+
// cellKey format: `${sheetId}!${A1Address}` (e.g., "1!A1", "2!B5")
|
|
101
|
+
var ySheetDataV3 = (0, react_1.useMemo)(function () { return doc.getMap("sheetDataV3"); }, [doc]);
|
|
102
|
+
// Doc metadata - used as fallback for version when prop is undefined
|
|
103
|
+
var yDocMeta = (0, react_1.useMemo)(function () { return doc.getMap("docMeta"); }, [doc]);
|
|
104
|
+
var yDisableRecalc = (0, react_1.useMemo)(function () { return doc.getMap("disableRecalc"); }, [doc]);
|
|
105
|
+
// Recalc cells
|
|
106
|
+
var yRecalcCells = (0, react_1.useMemo)(function () { return doc.getArray("recalcCells"); }, [doc]);
|
|
107
|
+
// Make sure initialSheets is always filled
|
|
108
|
+
var getInitialSheets = (0, react_1.useCallback)(function () {
|
|
109
|
+
if (initialSheets.length) {
|
|
110
|
+
return initialSheets;
|
|
111
|
+
}
|
|
112
|
+
return [(0, spreadsheet_1.createNewSheet)(1, "Sheet1")];
|
|
113
|
+
}, [initialSheets]);
|
|
114
|
+
// cellxfs
|
|
115
|
+
var yCellXfs = (0, react_1.useMemo)(function () { return doc.getMap("cellXfs"); }, [doc]);
|
|
116
|
+
// Shared strings - always use Map for new storage, Array kept for migration
|
|
117
|
+
var ySharedStringsArray = (0, react_1.useMemo)(function () { return doc.getArray("sharedStrings"); }, [doc]);
|
|
118
|
+
var ySharedStringsMap = (0, react_1.useMemo)(function () { return doc.getMap("sharedStringsMap"); }, [doc]);
|
|
119
|
+
// Track if initial sync has changed active sheet
|
|
120
|
+
var isActiveSheetChanged = (0, react_1.useRef)(false);
|
|
121
|
+
/**
|
|
122
|
+
* Handle shared strings updates from the Y.Map (new format).
|
|
123
|
+
* Merges incoming Map entries into the current shared strings state.
|
|
124
|
+
* Used for documents that store shared strings in the Map format.
|
|
125
|
+
*/
|
|
126
|
+
var handleSharedStringsMapChange = (0, react_1.useCallback)(function (value) {
|
|
127
|
+
onChangeSharedStrings === null || onChangeSharedStrings === void 0 ? void 0 : onChangeSharedStrings(function (currentMap) {
|
|
128
|
+
var e_1, _a;
|
|
129
|
+
var newMap = new Map(currentMap);
|
|
130
|
+
try {
|
|
131
|
+
for (var value_1 = tslib_1.__values(value), value_1_1 = value_1.next(); !value_1_1.done; value_1_1 = value_1.next()) {
|
|
132
|
+
var _b = tslib_1.__read(value_1_1.value, 2), key = _b[0], val = _b[1];
|
|
133
|
+
newMap.set(key, val);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
catch (e_1_1) { e_1 = { error: e_1_1 }; }
|
|
137
|
+
finally {
|
|
138
|
+
try {
|
|
139
|
+
if (value_1_1 && !value_1_1.done && (_a = value_1.return)) _a.call(value_1);
|
|
140
|
+
}
|
|
141
|
+
finally { if (e_1) throw e_1.error; }
|
|
142
|
+
}
|
|
143
|
+
return newMap;
|
|
144
|
+
});
|
|
145
|
+
}, [onChangeSharedStrings]);
|
|
146
|
+
/**
|
|
147
|
+
* Handle shared strings updates from the Y.Array (legacy format).
|
|
148
|
+
* Converts array indices to string keys and merges into current state.
|
|
149
|
+
* Provides backwards compatibility for older documents using array storage.
|
|
150
|
+
*/
|
|
151
|
+
var handleSharedStringsArrayChange = (0, react_1.useCallback)(function (value) {
|
|
152
|
+
onChangeSharedStrings === null || onChangeSharedStrings === void 0 ? void 0 : onChangeSharedStrings(function (currentMap) {
|
|
153
|
+
var newMap = new Map(currentMap);
|
|
154
|
+
value.forEach(function (v, index) {
|
|
155
|
+
newMap.set(String(index), v);
|
|
156
|
+
});
|
|
157
|
+
return newMap;
|
|
158
|
+
});
|
|
159
|
+
}, [onChangeSharedStrings]);
|
|
160
|
+
var documentMap = (0, react_1.useMemo)(function () {
|
|
161
|
+
var entries = tslib_1.__spreadArray([
|
|
162
|
+
["sheets", [doc.getArray("sheets"), onChangeSheets]],
|
|
163
|
+
["tables", [doc.getArray("tables"), onChangeTables]],
|
|
164
|
+
[
|
|
165
|
+
"embeds",
|
|
166
|
+
[doc.getArray("embeds"), onChangeEmbeds],
|
|
167
|
+
],
|
|
168
|
+
[
|
|
169
|
+
"charts",
|
|
170
|
+
[doc.getArray("charts"), onChangeCharts],
|
|
171
|
+
],
|
|
172
|
+
[
|
|
173
|
+
"conditionalFormats",
|
|
174
|
+
[
|
|
175
|
+
doc.getArray("conditionalFormats"),
|
|
176
|
+
onChangeConditionalFormats,
|
|
177
|
+
],
|
|
178
|
+
],
|
|
179
|
+
[
|
|
180
|
+
"dataValidations",
|
|
181
|
+
[
|
|
182
|
+
doc.getArray("dataValidations"),
|
|
183
|
+
onChangeDataValidations,
|
|
184
|
+
],
|
|
185
|
+
],
|
|
186
|
+
[
|
|
187
|
+
"pivotTables",
|
|
188
|
+
[doc.getArray("pivotTables"), onChangePivotTables],
|
|
189
|
+
],
|
|
190
|
+
[
|
|
191
|
+
"namedRanges",
|
|
192
|
+
[doc.getArray("namedRanges"), onChangeNamedRanges],
|
|
193
|
+
],
|
|
194
|
+
[
|
|
195
|
+
"protectedRanges",
|
|
196
|
+
[
|
|
197
|
+
doc.getArray("protectedRanges"),
|
|
198
|
+
onChangeProtectedRanges,
|
|
199
|
+
],
|
|
200
|
+
],
|
|
201
|
+
[
|
|
202
|
+
"citations",
|
|
203
|
+
[doc.getArray("citations"), onChangeCitations],
|
|
204
|
+
],
|
|
205
|
+
[
|
|
206
|
+
"sharedStrings",
|
|
207
|
+
[ySharedStringsMap, handleSharedStringsMapChange],
|
|
208
|
+
]
|
|
209
|
+
], tslib_1.__read((supportLegacySharedStringsArray
|
|
210
|
+
? [
|
|
211
|
+
[
|
|
212
|
+
"sharedStringsArray",
|
|
213
|
+
[ySharedStringsArray, handleSharedStringsArrayChange],
|
|
214
|
+
],
|
|
215
|
+
]
|
|
216
|
+
: [])), false);
|
|
217
|
+
return new Map(entries);
|
|
218
|
+
}, [
|
|
219
|
+
doc,
|
|
220
|
+
onChangeSheets,
|
|
221
|
+
onChangeTables,
|
|
222
|
+
onChangeEmbeds,
|
|
223
|
+
onChangeCharts,
|
|
224
|
+
onChangeConditionalFormats,
|
|
225
|
+
onChangeDataValidations,
|
|
226
|
+
onChangePivotTables,
|
|
227
|
+
onChangeNamedRanges,
|
|
228
|
+
onChangeProtectedRanges,
|
|
229
|
+
onChangeCitations,
|
|
230
|
+
ySharedStringsMap,
|
|
231
|
+
handleSharedStringsMapChange,
|
|
232
|
+
supportLegacySharedStringsArray,
|
|
233
|
+
ySharedStringsArray,
|
|
234
|
+
handleSharedStringsArrayChange,
|
|
235
|
+
]);
|
|
236
|
+
// Get backlog of recalc cells from backend
|
|
237
|
+
var getBacklog = (0, react_1.useCallback)(function () {
|
|
238
|
+
return yRecalcCells
|
|
239
|
+
.toArray()
|
|
240
|
+
.flatMap(function (entry) { return entry.patches; })
|
|
241
|
+
.filter(function (_a) {
|
|
242
|
+
var _b = tslib_1.__read(_a, 3), source = _b[2];
|
|
243
|
+
return source === "backend" || source === "agent";
|
|
244
|
+
});
|
|
245
|
+
}, [yRecalcCells]);
|
|
246
|
+
// Trigger cell recalcs
|
|
247
|
+
var syncRecalcCells = (0, react_1.useCallback)(function (recalcCells, origin) {
|
|
248
|
+
var e_2, _a;
|
|
249
|
+
try {
|
|
250
|
+
for (var recalcCells_1 = tslib_1.__values(recalcCells), recalcCells_1_1 = recalcCells_1.next(); !recalcCells_1_1.done; recalcCells_1_1 = recalcCells_1.next()) {
|
|
251
|
+
var _b = tslib_1.__read(recalcCells_1_1.value, 3), _1 = _b[0], position = _b[1], source = _b[2];
|
|
252
|
+
// backend will calculate results
|
|
253
|
+
var isAgent = source === "agent"; // Agent already does the calculation
|
|
254
|
+
// Read directly from Y.js to avoid race condition where recalcCells
|
|
255
|
+
// observer fires before sheetData observer has updated React state
|
|
256
|
+
var formulaValue = useV3
|
|
257
|
+
? getUserEnteredValueFromYDocV3(ySheetDataV3, position.sheetId, position.rowIndex, position.columnIndex)
|
|
258
|
+
: getUserEnteredValueFromYDoc(ySheetDataV2, position.sheetId, position.rowIndex, position.columnIndex);
|
|
259
|
+
// Was this from a changeBatch event in the back-end?
|
|
260
|
+
// To support client functions to run only
|
|
261
|
+
var isChangeBatch = position.force && isAgent;
|
|
262
|
+
var isClientOnly = !(0, utils_1.isNil)(formulaValue) &&
|
|
263
|
+
!(0, utils_1.isNil)(clientOnlyFunctionPattern) &&
|
|
264
|
+
(0, utils_1.isAFormula)(formulaValue)
|
|
265
|
+
? clientOnlyFunctionPattern.test(String(formulaValue))
|
|
266
|
+
: false;
|
|
267
|
+
/**
|
|
268
|
+
* Determines whether to force immediate execution of the operation.
|
|
269
|
+
*
|
|
270
|
+
* Force execution is allowed only for leaders and under these conditions:
|
|
271
|
+
* - No source specified (backwards compatibility - treats as force exec)
|
|
272
|
+
* - Source is "backend" (backend-initiated changes)
|
|
273
|
+
* - Client-only mode with change batch (batch operations need immediate execution)
|
|
274
|
+
*
|
|
275
|
+
* Note: Agent sources never force execution since they've pre-calculated results.
|
|
276
|
+
*/
|
|
277
|
+
var forceExecution = isLeaderRef.current &&
|
|
278
|
+
(!source || // backwards compatible: no source means force exec for leader
|
|
279
|
+
source === "backend" ||
|
|
280
|
+
(isClientOnly && isChangeBatch));
|
|
281
|
+
var enqueue = forceExecution
|
|
282
|
+
? enqueueCalculation
|
|
283
|
+
: enqueueGraphOperation;
|
|
284
|
+
/**
|
|
285
|
+
* Selects the appropriate queue function for the operation.
|
|
286
|
+
*
|
|
287
|
+
* - enqueueCalculation: Used when forcing execution (immediate formula evaluation)
|
|
288
|
+
* - enqueueGraphOperation: Used for normal graph operations (no formula evaluation)
|
|
289
|
+
*/
|
|
290
|
+
enqueue === null || enqueue === void 0 ? void 0 : enqueue({
|
|
291
|
+
type: "add",
|
|
292
|
+
position: position,
|
|
293
|
+
source: source,
|
|
294
|
+
force: forceExecution,
|
|
295
|
+
});
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
catch (e_2_1) { e_2 = { error: e_2_1 }; }
|
|
299
|
+
finally {
|
|
300
|
+
try {
|
|
301
|
+
if (recalcCells_1_1 && !recalcCells_1_1.done && (_a = recalcCells_1.return)) _a.call(recalcCells_1);
|
|
302
|
+
}
|
|
303
|
+
finally { if (e_2) throw e_2.error; }
|
|
304
|
+
}
|
|
305
|
+
// The agent/backend pre-computes cell VALUES (hence the graph-only
|
|
306
|
+
// enqueues above), but conditional-format / data-validation RESULTS
|
|
307
|
+
// are derived on the client — re-evaluate them for the changed cells
|
|
308
|
+
// so their paint tracks the new values.
|
|
309
|
+
if (recalcCells.length) {
|
|
310
|
+
reevaluateDerivationsForCells === null || reevaluateDerivationsForCells === void 0 ? void 0 : reevaluateDerivationsForCells(recalcCells.map(function (_a) {
|
|
311
|
+
var _b = tslib_1.__read(_a, 2), position = _b[1];
|
|
312
|
+
return ({
|
|
313
|
+
sheetId: position.sheetId,
|
|
314
|
+
rowIndex: position.rowIndex,
|
|
315
|
+
columnIndex: position.columnIndex,
|
|
316
|
+
});
|
|
317
|
+
}));
|
|
318
|
+
}
|
|
319
|
+
}, [
|
|
320
|
+
synced,
|
|
321
|
+
clientOnlyFunctionPattern,
|
|
322
|
+
ySheetDataV2,
|
|
323
|
+
ySheetDataV3,
|
|
324
|
+
useV3,
|
|
325
|
+
reevaluateDerivationsForCells,
|
|
326
|
+
]);
|
|
327
|
+
// Setup initial sync
|
|
328
|
+
var setupInitialSync = (0, react_1.useCallback)(function () {
|
|
329
|
+
var e_3, _a;
|
|
330
|
+
var _b;
|
|
331
|
+
// Migration from sharedStrings (Array) to sharedStringsMap (Map) is disabled.
|
|
332
|
+
// Both formats are now supported simultaneously via handleSharedStringsMapChange
|
|
333
|
+
// and handleSharedStringsArrayChange callbacks.
|
|
334
|
+
var arrayData = ySharedStringsArray.toArray();
|
|
335
|
+
var mapData = ySharedStringsMap.toJSON();
|
|
336
|
+
var hasArrayData = arrayData.length > 0;
|
|
337
|
+
var isMapEmpty = Object.keys(mapData).length === 0;
|
|
338
|
+
// Migrate if array has data and map is empty
|
|
339
|
+
if (!supportLegacySharedStringsArray && hasArrayData && isMapEmpty) {
|
|
340
|
+
doc.transact(function () {
|
|
341
|
+
// Convert array to map with string keys
|
|
342
|
+
arrayData.forEach(function (value, index) {
|
|
343
|
+
ySharedStringsMap.set(String(index), value);
|
|
344
|
+
});
|
|
345
|
+
// Optional: Clear the old array to save space
|
|
346
|
+
// Uncomment if you want to remove old data after migration
|
|
347
|
+
// ySharedStringsArray.delete(0, ySharedStringsArray.length);
|
|
348
|
+
}, "migration");
|
|
349
|
+
}
|
|
350
|
+
try {
|
|
351
|
+
for (var _c = tslib_1.__values(documentMap.entries()), _d = _c.next(); !_d.done; _d = _c.next()) {
|
|
352
|
+
var _e = tslib_1.__read(_d.value, 2), mappingKey = _e[0], _f = tslib_1.__read(_e[1], 2), ydoc = _f[0], callback = _f[1];
|
|
353
|
+
var value = ydoc.toJSON();
|
|
354
|
+
if (mappingKey === "sheets") {
|
|
355
|
+
var sheets = (Array.isArray(value) && value.length
|
|
356
|
+
? (0, helpers_1.validateSheets)(value)
|
|
357
|
+
: getInitialSheets());
|
|
358
|
+
callback === null || callback === void 0 ? void 0 : callback(sheets);
|
|
359
|
+
// Set active sheet id
|
|
360
|
+
if (sheets.length && !isActiveSheetChanged.current) {
|
|
361
|
+
onChangeActiveSheet === null || onChangeActiveSheet === void 0 ? void 0 : onChangeActiveSheet((0, utils_1.sortSheetsByIndex)(sheets)[0].sheetId);
|
|
362
|
+
isActiveSheetChanged.current = true;
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
else if (mappingKey === "sharedStrings") {
|
|
366
|
+
// Convert Map object to Map instance for shared strings
|
|
367
|
+
var sharedStringsMap = new Map(Object.entries(value !== null && value !== void 0 ? value : {}));
|
|
368
|
+
callback === null || callback === void 0 ? void 0 : callback(sharedStringsMap);
|
|
369
|
+
}
|
|
370
|
+
else {
|
|
371
|
+
callback === null || callback === void 0 ? void 0 : callback(value);
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
catch (e_3_1) { e_3 = { error: e_3_1 }; }
|
|
376
|
+
finally {
|
|
377
|
+
try {
|
|
378
|
+
if (_d && !_d.done && (_a = _c.return)) _a.call(_c);
|
|
379
|
+
}
|
|
380
|
+
finally { if (e_3) throw e_3.error; }
|
|
381
|
+
}
|
|
382
|
+
// Load sheet data based on V2 or V3 mode
|
|
383
|
+
if (useV3) {
|
|
384
|
+
// Check if we need to migrate from V2 to V3
|
|
385
|
+
// Use prop if provided, otherwise fall back to yDocMeta for backwards compatibility
|
|
386
|
+
var resolvedVersion = sheetDataVersion !== null && sheetDataVersion !== void 0 ? sheetDataVersion : yDocMeta.get("sheetDataVersion");
|
|
387
|
+
if (resolvedVersion !== "v3") {
|
|
388
|
+
var v2Data_1 = ySheetDataV2.toJSON();
|
|
389
|
+
var hasV2Data = Object.keys(v2Data_1).length > 0;
|
|
390
|
+
if (hasV2Data) {
|
|
391
|
+
// Migrate V2 data to V3
|
|
392
|
+
doc.transact(function () {
|
|
393
|
+
var e_4, _a, e_5, _b;
|
|
394
|
+
var v3Data = (0, helpers_1.sheetDataV2ToV3)(v2Data_1);
|
|
395
|
+
try {
|
|
396
|
+
// Add all V3 entries to the Y.Map
|
|
397
|
+
for (var _c = tslib_1.__values(v3Data.entries()), _d = _c.next(); !_d.done; _d = _c.next()) {
|
|
398
|
+
var _e = tslib_1.__read(_d.value, 2), key = _e[0], cellData = _e[1];
|
|
399
|
+
ySheetDataV3.set(key, cellData);
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
catch (e_4_1) { e_4 = { error: e_4_1 }; }
|
|
403
|
+
finally {
|
|
404
|
+
try {
|
|
405
|
+
if (_d && !_d.done && (_a = _c.return)) _a.call(_c);
|
|
406
|
+
}
|
|
407
|
+
finally { if (e_4) throw e_4.error; }
|
|
408
|
+
}
|
|
409
|
+
// Optionally clear V2 data after migration
|
|
410
|
+
if (deleteV2AfterMigration) {
|
|
411
|
+
try {
|
|
412
|
+
for (var _f = tslib_1.__values(ySheetDataV2.keys()), _g = _f.next(); !_g.done; _g = _f.next()) {
|
|
413
|
+
var sheetId_1 = _g.value;
|
|
414
|
+
ySheetDataV2.delete(sheetId_1);
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
catch (e_5_1) { e_5 = { error: e_5_1 }; }
|
|
418
|
+
finally {
|
|
419
|
+
try {
|
|
420
|
+
if (_g && !_g.done && (_b = _f.return)) _b.call(_f);
|
|
421
|
+
}
|
|
422
|
+
finally { if (e_5) throw e_5.error; }
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
}, "migrate-v2-to-v3");
|
|
426
|
+
// Notify backend that migration completed
|
|
427
|
+
onMigrationComplete === null || onMigrationComplete === void 0 ? void 0 : onMigrationComplete("v3");
|
|
428
|
+
}
|
|
429
|
+
else {
|
|
430
|
+
// No V2 data, notify backend of version
|
|
431
|
+
onMigrationComplete === null || onMigrationComplete === void 0 ? void 0 : onMigrationComplete("v3");
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
// V3: Convert flat map to V2 format for React state
|
|
435
|
+
var v3Data = ySheetDataV3.toJSON();
|
|
436
|
+
var v3Map = new Map(Object.entries(v3Data));
|
|
437
|
+
onChangeSheetData === null || onChangeSheetData === void 0 ? void 0 : onChangeSheetData((0, helpers_1.sheetDataV3ToV2)(v3Map));
|
|
438
|
+
}
|
|
439
|
+
else {
|
|
440
|
+
onChangeSheetData === null || onChangeSheetData === void 0 ? void 0 : onChangeSheetData(ySheetDataV2.toJSON());
|
|
441
|
+
}
|
|
442
|
+
// Cellxfs
|
|
443
|
+
var cellXfs = new Map(Object.entries((_b = yCellXfs.toJSON()) !== null && _b !== void 0 ? _b : {}));
|
|
444
|
+
onChangeCellXfs === null || onChangeCellXfs === void 0 ? void 0 : onChangeCellXfs(cellXfs);
|
|
445
|
+
}, [
|
|
446
|
+
documentMap,
|
|
447
|
+
onChangeCellXfs,
|
|
448
|
+
onChangeSheetData,
|
|
449
|
+
ySheetDataV2,
|
|
450
|
+
ySheetDataV3,
|
|
451
|
+
yDocMeta,
|
|
452
|
+
yCellXfs,
|
|
453
|
+
getInitialSheets,
|
|
454
|
+
onChangeActiveSheet,
|
|
455
|
+
ySharedStringsArray,
|
|
456
|
+
ySharedStringsMap,
|
|
457
|
+
doc,
|
|
458
|
+
useV3,
|
|
459
|
+
sheetDataVersion,
|
|
460
|
+
onMigrationComplete,
|
|
461
|
+
deleteV2AfterMigration,
|
|
462
|
+
]);
|
|
463
|
+
// Synced
|
|
464
|
+
var onSynced = (0, react_1.useCallback)(function () {
|
|
465
|
+
if (!synced) {
|
|
466
|
+
if (!skipInitialSync) {
|
|
467
|
+
// Use flushSync to ensure React state is updated before continuing
|
|
468
|
+
// This guarantees sheetData is available when syncRecalcCells runs
|
|
469
|
+
(0, react_dom_1.flushSync)(function () {
|
|
470
|
+
setupInitialSync();
|
|
471
|
+
});
|
|
472
|
+
// Build dependency graph, then sync backlog cells
|
|
473
|
+
requestAnimationFrame(function () { return tslib_1.__awaiter(void 0, void 0, void 0, function () {
|
|
474
|
+
var backlog;
|
|
475
|
+
return tslib_1.__generator(this, function (_a) {
|
|
476
|
+
switch (_a.label) {
|
|
477
|
+
case 0: return [4 /*yield*/, calculateNow({
|
|
478
|
+
disableEvaluation: true,
|
|
479
|
+
shouldResetCellDependencyGraph: true,
|
|
480
|
+
})];
|
|
481
|
+
case 1:
|
|
482
|
+
_a.sent();
|
|
483
|
+
backlog = getBacklog();
|
|
484
|
+
if (backlog.length) {
|
|
485
|
+
syncRecalcCells(backlog, "backend");
|
|
486
|
+
// Clear processed backlog entries from yRecalcCells
|
|
487
|
+
doc.transact(function () {
|
|
488
|
+
yRecalcCells.delete(0, yRecalcCells.length);
|
|
489
|
+
}, origin);
|
|
490
|
+
}
|
|
491
|
+
return [2 /*return*/];
|
|
492
|
+
}
|
|
493
|
+
});
|
|
494
|
+
}); });
|
|
495
|
+
}
|
|
496
|
+
setSynced(true);
|
|
497
|
+
}
|
|
498
|
+
}, [
|
|
499
|
+
calculateNow,
|
|
500
|
+
setupInitialSync,
|
|
501
|
+
synced,
|
|
502
|
+
syncRecalcCells,
|
|
503
|
+
getBacklog,
|
|
504
|
+
doc,
|
|
505
|
+
yRecalcCells,
|
|
506
|
+
skipInitialSync,
|
|
507
|
+
]);
|
|
508
|
+
// Sync state
|
|
509
|
+
if ((provider === null || provider === void 0 ? void 0 : provider.synced) && !synced) {
|
|
510
|
+
onSynced();
|
|
511
|
+
}
|
|
512
|
+
(0, ui_1.useIsomorphicLayoutEffect)(function () {
|
|
513
|
+
provider === null || provider === void 0 ? void 0 : provider.on("synced", onSynced);
|
|
514
|
+
return function () {
|
|
515
|
+
provider === null || provider === void 0 ? void 0 : provider.off("synced", onSynced);
|
|
516
|
+
};
|
|
517
|
+
}, [provider]);
|
|
518
|
+
// Only for sheet Data (V2 mode)
|
|
519
|
+
(0, ui_1.useIsomorphicLayoutEffect)(function () {
|
|
520
|
+
if (useV3)
|
|
521
|
+
return; // Skip V2 observer in V3 mode
|
|
522
|
+
// Subscribe to sheet data
|
|
523
|
+
var subscription = (0, observables_1.createSheetDataObservable)(ySheetDataV2).subscribe(function (events) {
|
|
524
|
+
var _a, _b;
|
|
525
|
+
/**
|
|
526
|
+
* Disable recalc is a flag that is set ONLY locally
|
|
527
|
+
* when a calculation completes
|
|
528
|
+
*
|
|
529
|
+
* This is to prevent yjs from observing sheetdata
|
|
530
|
+
* and updating it, which is not necessary
|
|
531
|
+
*/
|
|
532
|
+
if (events.length) {
|
|
533
|
+
var origin_1 = (_b = (_a = events === null || events === void 0 ? void 0 : events[0]) === null || _a === void 0 ? void 0 : _a.transaction) === null || _b === void 0 ? void 0 : _b.origin;
|
|
534
|
+
if (origin_1 !== "local") {
|
|
535
|
+
// Convert Y.js events to SheetDataEvent format and apply incremental updates
|
|
536
|
+
var sheetDataEvents_1 = convertToSheetDataEvents(events);
|
|
537
|
+
onChangeSheetData(function (prevSheetData) {
|
|
538
|
+
var result = (0, helpers_1.processSheetDataEvents)(sheetDataEvents_1, prevSheetData, ySheetDataV2);
|
|
539
|
+
// Fall back to full serialization only if incremental update fails
|
|
540
|
+
return result !== null && result !== void 0 ? result : ySheetDataV2.toJSON();
|
|
541
|
+
});
|
|
542
|
+
}
|
|
543
|
+
}
|
|
544
|
+
});
|
|
545
|
+
return function () {
|
|
546
|
+
subscription.unsubscribe();
|
|
547
|
+
};
|
|
548
|
+
}, [onChangeSheetData, ySheetDataV2, useV3]);
|
|
549
|
+
// Only for sheet Data (V3 mode)
|
|
550
|
+
(0, ui_1.useIsomorphicLayoutEffect)(function () {
|
|
551
|
+
if (!useV3)
|
|
552
|
+
return; // Skip V3 observer in V2 mode
|
|
553
|
+
// Subscribe to V3 sheet data (flat Map)
|
|
554
|
+
var callback = function (event) {
|
|
555
|
+
if (event.transaction.origin !== "local") {
|
|
556
|
+
// Extract changes synchronously - Yjs requires accessing event.changes
|
|
557
|
+
// within the event handler, not in a deferred callback like setState
|
|
558
|
+
var changedKeys_1 = new Map();
|
|
559
|
+
event.changes.keys.forEach(function (change, key) {
|
|
560
|
+
changedKeys_1.set(key, { action: change.action });
|
|
561
|
+
});
|
|
562
|
+
// Now we can safely use the extracted data in the async setState callback
|
|
563
|
+
onChangeSheetData(function (prevSheetData) {
|
|
564
|
+
var result = (0, helpers_1.processSheetDataEventsV3)(changedKeys_1, prevSheetData, ySheetDataV3);
|
|
565
|
+
if (result) {
|
|
566
|
+
return result;
|
|
567
|
+
}
|
|
568
|
+
// Fall back to full conversion
|
|
569
|
+
var v3Data = ySheetDataV3.toJSON();
|
|
570
|
+
var v3Map = new Map(Object.entries(v3Data));
|
|
571
|
+
return (0, helpers_1.sheetDataV3ToV2)(v3Map);
|
|
572
|
+
});
|
|
573
|
+
}
|
|
574
|
+
};
|
|
575
|
+
ySheetDataV3.observe(callback);
|
|
576
|
+
return function () {
|
|
577
|
+
ySheetDataV3.unobserve(callback);
|
|
578
|
+
};
|
|
579
|
+
}, [onChangeSheetData, ySheetDataV3, useV3]);
|
|
580
|
+
// CellXfs
|
|
581
|
+
(0, ui_1.useIsomorphicLayoutEffect)(function () {
|
|
582
|
+
// Subscribe to sheet data
|
|
583
|
+
var callback = function (event) {
|
|
584
|
+
var _a;
|
|
585
|
+
if (event.transaction.origin !== "local") {
|
|
586
|
+
var cellXfs = new Map(Object.entries((_a = yCellXfs.toJSON()) !== null && _a !== void 0 ? _a : {}));
|
|
587
|
+
onChangeCellXfs === null || onChangeCellXfs === void 0 ? void 0 : onChangeCellXfs(cellXfs);
|
|
588
|
+
}
|
|
589
|
+
};
|
|
590
|
+
yCellXfs.observe(callback);
|
|
591
|
+
return function () {
|
|
592
|
+
yCellXfs.unobserve(callback);
|
|
593
|
+
};
|
|
594
|
+
}, [onChangeCellXfs, yCellXfs]);
|
|
595
|
+
// All others
|
|
596
|
+
(0, ui_1.useIsomorphicLayoutEffect)(function () {
|
|
597
|
+
var e_6, _a;
|
|
598
|
+
var subs = [];
|
|
599
|
+
var _loop_1 = function (mappingKey, ydoc, callback) {
|
|
600
|
+
// Only observe Arrays (shared strings Map is handled separately if needed)
|
|
601
|
+
if (ydoc instanceof Y.Array) {
|
|
602
|
+
var subscription = (0, observables_1.createDeepObservable)(ydoc).subscribe(function (events) {
|
|
603
|
+
var e_7, _a;
|
|
604
|
+
var _b;
|
|
605
|
+
try {
|
|
606
|
+
for (var events_1 = (e_7 = void 0, tslib_1.__values(events)), events_1_1 = events_1.next(); !events_1_1.done; events_1_1 = events_1.next()) {
|
|
607
|
+
var event_1 = events_1_1.value;
|
|
608
|
+
if (event_1.transaction.origin !== "local") {
|
|
609
|
+
var json = ydoc.toJSON();
|
|
610
|
+
if (Array.isArray(json) &&
|
|
611
|
+
json.length &&
|
|
612
|
+
((_b = json[0]) === null || _b === void 0 ? void 0 : _b.userId)) {
|
|
613
|
+
continue;
|
|
614
|
+
}
|
|
615
|
+
callback === null || callback === void 0 ? void 0 : callback(json);
|
|
616
|
+
}
|
|
617
|
+
}
|
|
618
|
+
}
|
|
619
|
+
catch (e_7_1) { e_7 = { error: e_7_1 }; }
|
|
620
|
+
finally {
|
|
621
|
+
try {
|
|
622
|
+
if (events_1_1 && !events_1_1.done && (_a = events_1.return)) _a.call(events_1);
|
|
623
|
+
}
|
|
624
|
+
finally { if (e_7) throw e_7.error; }
|
|
625
|
+
}
|
|
626
|
+
});
|
|
627
|
+
subs.push(subscription);
|
|
628
|
+
}
|
|
629
|
+
else if (ydoc instanceof Y.Map && mappingKey === "sharedStrings") {
|
|
630
|
+
// Handle shared strings Map observation
|
|
631
|
+
var callback_map_1 = function (event) {
|
|
632
|
+
if (event.transaction.origin !== "local") {
|
|
633
|
+
var json = ydoc.toJSON();
|
|
634
|
+
var sharedStringsMap = new Map(Object.entries(json !== null && json !== void 0 ? json : {}));
|
|
635
|
+
callback === null || callback === void 0 ? void 0 : callback(sharedStringsMap);
|
|
636
|
+
}
|
|
637
|
+
};
|
|
638
|
+
ydoc.observe(callback_map_1);
|
|
639
|
+
subs.push({
|
|
640
|
+
unsubscribe: function () { return ydoc.unobserve(callback_map_1); },
|
|
641
|
+
});
|
|
642
|
+
}
|
|
643
|
+
};
|
|
644
|
+
try {
|
|
645
|
+
for (var _b = tslib_1.__values(documentMap.entries()), _c = _b.next(); !_c.done; _c = _b.next()) {
|
|
646
|
+
var _d = tslib_1.__read(_c.value, 2), mappingKey = _d[0], _e = tslib_1.__read(_d[1], 2), ydoc = _e[0], callback = _e[1];
|
|
647
|
+
_loop_1(mappingKey, ydoc, callback);
|
|
648
|
+
}
|
|
649
|
+
}
|
|
650
|
+
catch (e_6_1) { e_6 = { error: e_6_1 }; }
|
|
651
|
+
finally {
|
|
652
|
+
try {
|
|
653
|
+
if (_c && !_c.done && (_a = _b.return)) _a.call(_b);
|
|
654
|
+
}
|
|
655
|
+
finally { if (e_6) throw e_6.error; }
|
|
656
|
+
}
|
|
657
|
+
return function () {
|
|
658
|
+
subs.forEach(function (sub) { return sub.unsubscribe(); });
|
|
659
|
+
};
|
|
660
|
+
}, [documentMap]);
|
|
661
|
+
// Only for recalc cells
|
|
662
|
+
(0, ui_1.useIsomorphicLayoutEffect)(function () {
|
|
663
|
+
if (!synced) {
|
|
664
|
+
return;
|
|
665
|
+
}
|
|
666
|
+
// Sync all cells that are updated in the backend
|
|
667
|
+
// // Observe recalc cells separat
|
|
668
|
+
var onRecalcCellsChange = function (event) {
|
|
669
|
+
var patches = (0, observables_1.getPatchDeltasFromEvent)(event);
|
|
670
|
+
var origin = event.transaction.origin;
|
|
671
|
+
if (origin !== "local") {
|
|
672
|
+
requestAnimationFrame(function () {
|
|
673
|
+
syncRecalcCells(patches, origin);
|
|
674
|
+
});
|
|
675
|
+
}
|
|
676
|
+
};
|
|
677
|
+
// Skip the first recalc cells change
|
|
678
|
+
var sub = (0, observables_1.createObservable)(yRecalcCells).subscribe(onRecalcCellsChange);
|
|
679
|
+
return function () { return sub.unsubscribe(); };
|
|
680
|
+
}, [syncRecalcCells, recalcCellsMaxEntries, synced, yRecalcCells]);
|
|
681
|
+
// Broadcast patch
|
|
682
|
+
var onBroadcastPatch = (0, react_1.useCallback)(function (patches) {
|
|
683
|
+
var e_8, _a, e_9, _b;
|
|
684
|
+
var _c;
|
|
685
|
+
// Ignore push flag
|
|
686
|
+
var disableRecalc = Boolean(patches.find(function (_a) {
|
|
687
|
+
var _b = tslib_1.__read(_a, 1), patch = _b[0];
|
|
688
|
+
return patch === null || patch === void 0 ? void 0 : patch.disableRecalc;
|
|
689
|
+
}));
|
|
690
|
+
// Check if this is a full recalc
|
|
691
|
+
var isFullRecalc = Boolean(patches.find(function (_a) {
|
|
692
|
+
var _b = tslib_1.__read(_a, 1), patch = _b[0];
|
|
693
|
+
return patch === null || patch === void 0 ? void 0 : patch.isFullRecalc;
|
|
694
|
+
}));
|
|
695
|
+
var disableSync = isFullRecalc ? false : disableRecalc && !isLeader;
|
|
696
|
+
// Only leader can broadcast recalc patches. Error effective values are
|
|
697
|
+
// the exception: async custom-function failures (#VALUE!, #NAME?, …)
|
|
698
|
+
// only exist as disableRecalc calc-result patches, and the leader's
|
|
699
|
+
// write is deferred/lossy — if non-leaders drop them too, the error
|
|
700
|
+
// paints during the live session but the cell is blank after reload.
|
|
701
|
+
// Sync just the error-bearing cell patches; ordinary recalc results
|
|
702
|
+
// stay leader-gated so N clients don't all write identical results.
|
|
703
|
+
if (disableSync) {
|
|
704
|
+
var errorCellPatches = [];
|
|
705
|
+
try {
|
|
706
|
+
for (var patches_1 = tslib_1.__values(patches), patches_1_1 = patches_1.next(); !patches_1_1.done; patches_1_1 = patches_1.next()) {
|
|
707
|
+
var _d = tslib_1.__read(patches_1_1.value, 2), patch = _d[0], _e = _d[1], type = _e === void 0 ? "redo" : _e;
|
|
708
|
+
if (!(patch === null || patch === void 0 ? void 0 : patch.sheetData)) {
|
|
709
|
+
continue;
|
|
710
|
+
}
|
|
711
|
+
type = type !== null && type !== void 0 ? type : "redo";
|
|
712
|
+
var patchKey = type === "redo" ? "patches" : "inversePatches";
|
|
713
|
+
var sheetDataPatches = patch.sheetData[patchKey];
|
|
714
|
+
if (sheetDataPatches === null || sheetDataPatches === void 0 ? void 0 : sheetDataPatches.length) {
|
|
715
|
+
errorCellPatches.push.apply(errorCellPatches, tslib_1.__spreadArray([], tslib_1.__read((0, helpers_1.filterErrorCellPatches)(sheetDataPatches)), false));
|
|
716
|
+
}
|
|
717
|
+
}
|
|
718
|
+
}
|
|
719
|
+
catch (e_8_1) { e_8 = { error: e_8_1 }; }
|
|
720
|
+
finally {
|
|
721
|
+
try {
|
|
722
|
+
if (patches_1_1 && !patches_1_1.done && (_a = patches_1.return)) _a.call(patches_1);
|
|
723
|
+
}
|
|
724
|
+
finally { if (e_8) throw e_8.error; }
|
|
725
|
+
}
|
|
726
|
+
// Containment for this non-leader write path: an error may only
|
|
727
|
+
// fill a cell with no effective value (the blank-after-reload case)
|
|
728
|
+
// or replace an existing error — never a non-error result; a cell
|
|
729
|
+
// whose formula (ue) changed since the calculation started is
|
|
730
|
+
// skipped; identical persisted errors are skipped so concurrent
|
|
731
|
+
// non-leaders don't append redundant Y.js updates; and a proceeding
|
|
732
|
+
// write merges ONLY the error effective value into the existing doc
|
|
733
|
+
// cell, preserving every other persisted field.
|
|
734
|
+
var persistableErrorCellPatches_1 = [];
|
|
735
|
+
try {
|
|
736
|
+
for (var errorCellPatches_1 = tslib_1.__values(errorCellPatches), errorCellPatches_1_1 = errorCellPatches_1.next(); !errorCellPatches_1_1.done; errorCellPatches_1_1 = errorCellPatches_1.next()) {
|
|
737
|
+
var errorPatch = errorCellPatches_1_1.value;
|
|
738
|
+
var _f = tslib_1.__read(errorPatch.path, 4), patchSheetId = _f[0], rowIndex = _f[1], columnIndex = _f[3];
|
|
739
|
+
var currentCellData = useV3
|
|
740
|
+
? (_c = ySheetDataV3.get((0, helpers_1.createCellKeyV3)(Number(patchSheetId), Number(rowIndex), Number(columnIndex)))) === null || _c === void 0 ? void 0 : _c.value
|
|
741
|
+
: (0, helpers_1.getCellDataFromYSheetData)(ySheetDataV2, patchSheetId, Number(rowIndex), Number(columnIndex));
|
|
742
|
+
var incomingCellData = errorPatch.value;
|
|
743
|
+
if (!(0, helpers_1.shouldPersistErrorCellPatch)(currentCellData, incomingCellData)) {
|
|
744
|
+
continue;
|
|
745
|
+
}
|
|
746
|
+
persistableErrorCellPatches_1.push(tslib_1.__assign(tslib_1.__assign({}, errorPatch), { value: (0, helpers_1.mergeErrorEffectiveValueIntoCellData)(currentCellData, incomingCellData) }));
|
|
747
|
+
}
|
|
748
|
+
}
|
|
749
|
+
catch (e_9_1) { e_9 = { error: e_9_1 }; }
|
|
750
|
+
finally {
|
|
751
|
+
try {
|
|
752
|
+
if (errorCellPatches_1_1 && !errorCellPatches_1_1.done && (_b = errorCellPatches_1.return)) _b.call(errorCellPatches_1);
|
|
753
|
+
}
|
|
754
|
+
finally { if (e_9) throw e_9.error; }
|
|
755
|
+
}
|
|
756
|
+
if (persistableErrorCellPatches_1.length) {
|
|
757
|
+
doc.transact(function () {
|
|
758
|
+
if (useV3) {
|
|
759
|
+
(0, helpers_1.addSheetDataPatchesV3)(ySheetDataV3, persistableErrorCellPatches_1);
|
|
760
|
+
}
|
|
761
|
+
else {
|
|
762
|
+
(0, helpers_1.addSheetDataPatches)(ySheetDataV2, persistableErrorCellPatches_1);
|
|
763
|
+
}
|
|
764
|
+
}, origin);
|
|
765
|
+
}
|
|
766
|
+
return;
|
|
767
|
+
}
|
|
768
|
+
doc.transact(function () {
|
|
769
|
+
var e_10, _a, e_11, _b;
|
|
770
|
+
var _c, _d, _e, _f;
|
|
771
|
+
try {
|
|
772
|
+
for (var patches_2 = tslib_1.__values(patches), patches_2_1 = patches_2.next(); !patches_2_1.done; patches_2_1 = patches_2.next()) {
|
|
773
|
+
var _g = tslib_1.__read(patches_2_1.value, 2), patch = _g[0], _h = _g[1], type = _h === void 0 ? "redo" : _h;
|
|
774
|
+
// Check for existence of patch
|
|
775
|
+
// Investigate when when importing csv, it create a [undefined, undefined] patch object
|
|
776
|
+
// todo @Vinay
|
|
777
|
+
if (!patch) {
|
|
778
|
+
continue;
|
|
779
|
+
}
|
|
780
|
+
type = type !== null && type !== void 0 ? type : "redo";
|
|
781
|
+
var patchKey = type === "redo" ? "patches" : "inversePatches";
|
|
782
|
+
if (patch.sheetData) {
|
|
783
|
+
var patches_3 = (_c = patch.sheetData) === null || _c === void 0 ? void 0 : _c[patchKey];
|
|
784
|
+
if (useV3) {
|
|
785
|
+
(0, helpers_1.addSheetDataPatchesV3)(ySheetDataV3, patches_3);
|
|
786
|
+
}
|
|
787
|
+
else {
|
|
788
|
+
(0, helpers_1.addSheetDataPatches)(ySheetDataV2, patches_3);
|
|
789
|
+
}
|
|
790
|
+
}
|
|
791
|
+
if (patch.cellXfs) {
|
|
792
|
+
var patches_4 = (_d = patch.cellXfs) === null || _d === void 0 ? void 0 : _d[patchKey];
|
|
793
|
+
(0, helpers_1.applyMapPatches)(yCellXfs, patches_4);
|
|
794
|
+
}
|
|
795
|
+
try {
|
|
796
|
+
for (var _j = (e_11 = void 0, tslib_1.__values(documentMap.entries())), _k = _j.next(); !_k.done; _k = _j.next()) {
|
|
797
|
+
var _l = tslib_1.__read(_k.value, 2), mappingKey = _l[0], _m = tslib_1.__read(_l[1], 1), ydoc = _m[0];
|
|
798
|
+
if (patch === null || patch === void 0 ? void 0 : patch[mappingKey]) {
|
|
799
|
+
var patches_5 = (_e = patch[mappingKey]) === null || _e === void 0 ? void 0 : _e[patchKey];
|
|
800
|
+
// Handle shared strings Map (always use Map now)
|
|
801
|
+
if (mappingKey === "sharedStrings" && ydoc instanceof Y.Map) {
|
|
802
|
+
(0, helpers_1.applyMapPatches)(ydoc, patches_5);
|
|
803
|
+
}
|
|
804
|
+
else if (ydoc instanceof Y.Array) {
|
|
805
|
+
// Check if we are updating sheets
|
|
806
|
+
if (mappingKey === "sheets" && ydoc.length === 0) {
|
|
807
|
+
// Add initial sheets
|
|
808
|
+
ydoc.insert(0, getInitialSheets());
|
|
809
|
+
}
|
|
810
|
+
(0, helpers_1.updateArrayType)(ydoc, patches_5);
|
|
811
|
+
}
|
|
812
|
+
}
|
|
813
|
+
}
|
|
814
|
+
}
|
|
815
|
+
catch (e_11_1) { e_11 = { error: e_11_1 }; }
|
|
816
|
+
finally {
|
|
817
|
+
try {
|
|
818
|
+
if (_k && !_k.done && (_b = _j.return)) _b.call(_j);
|
|
819
|
+
}
|
|
820
|
+
finally { if (e_11) throw e_11.error; }
|
|
821
|
+
}
|
|
822
|
+
// Recalc cells
|
|
823
|
+
if ((_f = patch.recalcCells) === null || _f === void 0 ? void 0 : _f[type]) {
|
|
824
|
+
if (isLeader) {
|
|
825
|
+
// Remove old cells
|
|
826
|
+
yRecalcCells.delete(0, yRecalcCells.length);
|
|
827
|
+
}
|
|
828
|
+
yRecalcCells.push([
|
|
829
|
+
{
|
|
830
|
+
userId: userId,
|
|
831
|
+
patches: Array.from(patch.recalcCells[type]),
|
|
832
|
+
},
|
|
833
|
+
]);
|
|
834
|
+
}
|
|
835
|
+
}
|
|
836
|
+
}
|
|
837
|
+
catch (e_10_1) { e_10 = { error: e_10_1 }; }
|
|
838
|
+
finally {
|
|
839
|
+
try {
|
|
840
|
+
if (patches_2_1 && !patches_2_1.done && (_a = patches_2.return)) _a.call(patches_2);
|
|
841
|
+
}
|
|
842
|
+
finally { if (e_10) throw e_10.error; }
|
|
843
|
+
}
|
|
844
|
+
}, origin);
|
|
845
|
+
onAfterBroadcastPatch === null || onAfterBroadcastPatch === void 0 ? void 0 : onAfterBroadcastPatch(patches, doc);
|
|
846
|
+
}, [
|
|
847
|
+
onAfterBroadcastPatch,
|
|
848
|
+
documentMap,
|
|
849
|
+
yRecalcCells,
|
|
850
|
+
doc,
|
|
851
|
+
userId,
|
|
852
|
+
yDisableRecalc,
|
|
853
|
+
ySheetDataV2,
|
|
854
|
+
ySheetDataV3,
|
|
855
|
+
isLeader,
|
|
856
|
+
recalcCellsMaxEntries,
|
|
857
|
+
useV3,
|
|
858
|
+
]);
|
|
859
|
+
// Listen to active cell change
|
|
860
|
+
(0, ui_1.useIsomorphicLayoutEffect)(function () {
|
|
861
|
+
if (activeCell) {
|
|
862
|
+
awareness === null || awareness === void 0 ? void 0 : awareness.setLocalStateField("activeCell", activeCell);
|
|
863
|
+
}
|
|
864
|
+
}, [awareness, activeCell]);
|
|
865
|
+
// Listen to sheet change
|
|
866
|
+
(0, ui_1.useIsomorphicLayoutEffect)(function () {
|
|
867
|
+
awareness === null || awareness === void 0 ? void 0 : awareness.setLocalStateField("sheetId", sheetId);
|
|
868
|
+
}, [awareness, sheetId]);
|
|
869
|
+
// Listen to selection range change
|
|
870
|
+
(0, ui_1.useIsomorphicLayoutEffect)(function () {
|
|
871
|
+
if (selections && selections.length > 0) {
|
|
872
|
+
awareness === null || awareness === void 0 ? void 0 : awareness.setLocalStateField("selections", selections);
|
|
873
|
+
}
|
|
874
|
+
}, [awareness, selections]);
|
|
875
|
+
// Awareness
|
|
876
|
+
(0, ui_1.useIsomorphicLayoutEffect)(function () {
|
|
877
|
+
var onChangeAwareness = function () {
|
|
878
|
+
var e_12, _a;
|
|
879
|
+
var _b, _c, _d, _e, _f;
|
|
880
|
+
if (awareness) {
|
|
881
|
+
var collaborators = [];
|
|
882
|
+
try {
|
|
883
|
+
for (var _g = tslib_1.__values(awareness.getStates().entries()), _h = _g.next(); !_h.done; _h = _g.next()) {
|
|
884
|
+
var _j = tslib_1.__read(_h.value, 2), _2 = _j[0], value = _j[1];
|
|
885
|
+
if (!value)
|
|
886
|
+
continue;
|
|
887
|
+
// States with no docGuid are accepted as a wildcard so REST
|
|
888
|
+
// writers or pre-scoped clients still appear as collaborators.
|
|
889
|
+
var docMatches = !resolvedAwarenessScopeId ||
|
|
890
|
+
!value.docGuid ||
|
|
891
|
+
value.docGuid === resolvedAwarenessScopeId;
|
|
892
|
+
if (!docMatches)
|
|
893
|
+
continue;
|
|
894
|
+
// Canonical shape is the nested `state.user = { kind, name, userId,
|
|
895
|
+
// agentInitiator? }` used by Olympus, pptx-studio, and the keryx
|
|
896
|
+
// agentTask helper. Fall back to legacy flat fields for one
|
|
897
|
+
// release so callers can migrate at their own pace.
|
|
898
|
+
var userBlock = ((_b = value.user) !== null && _b !== void 0 ? _b : null);
|
|
899
|
+
var resolvedUserId = (_c = userBlock === null || userBlock === void 0 ? void 0 : userBlock.userId) !== null && _c !== void 0 ? _c : value.userId;
|
|
900
|
+
var resolvedTitle = (_d = userBlock === null || userBlock === void 0 ? void 0 : userBlock.name) !== null && _d !== void 0 ? _d : value.title;
|
|
901
|
+
var resolvedKind = (_e = userBlock === null || userBlock === void 0 ? void 0 : userBlock.kind) !== null && _e !== void 0 ? _e : value.kind;
|
|
902
|
+
var resolvedAgentInitiator = (_f = userBlock === null || userBlock === void 0 ? void 0 : userBlock.agentInitiator) !== null && _f !== void 0 ? _f : value.agentInitiator;
|
|
903
|
+
if (!resolvedUserId)
|
|
904
|
+
continue;
|
|
905
|
+
if (!value.activeCell && !value.selections)
|
|
906
|
+
continue;
|
|
907
|
+
collaborators.push({
|
|
908
|
+
activeCell: value.activeCell,
|
|
909
|
+
title: (resolvedTitle !== null && resolvedTitle !== void 0 ? resolvedTitle : ""),
|
|
910
|
+
userId: resolvedUserId,
|
|
911
|
+
sheetId: value.sheetId,
|
|
912
|
+
kind: resolvedKind,
|
|
913
|
+
agentInitiator: resolvedAgentInitiator,
|
|
914
|
+
selections: value.selections,
|
|
915
|
+
});
|
|
916
|
+
}
|
|
917
|
+
}
|
|
918
|
+
catch (e_12_1) { e_12 = { error: e_12_1 }; }
|
|
919
|
+
finally {
|
|
920
|
+
try {
|
|
921
|
+
if (_h && !_h.done && (_a = _g.return)) _a.call(_g);
|
|
922
|
+
}
|
|
923
|
+
finally { if (e_12) throw e_12.error; }
|
|
924
|
+
}
|
|
925
|
+
setUsers(collaborators);
|
|
926
|
+
}
|
|
927
|
+
};
|
|
928
|
+
// Listen to awareness
|
|
929
|
+
awareness === null || awareness === void 0 ? void 0 : awareness.on("change", onChangeAwareness);
|
|
930
|
+
return function () {
|
|
931
|
+
awareness === null || awareness === void 0 ? void 0 : awareness.off("change", onChangeAwareness);
|
|
932
|
+
};
|
|
933
|
+
}, [awareness, resolvedAwarenessScopeId]);
|
|
934
|
+
// Set local awareness state.
|
|
935
|
+
//
|
|
936
|
+
// Use setLocalStateField for each field individually so sibling consumers
|
|
937
|
+
// (Olympus's unified presence layer, agent-task helpers, etc.) can compose
|
|
938
|
+
// their own fields on the same awareness state without us clobbering them.
|
|
939
|
+
(0, ui_1.useIsomorphicLayoutEffect)(function () {
|
|
940
|
+
if (!awareness)
|
|
941
|
+
return;
|
|
942
|
+
awareness.setLocalStateField("userId", userId);
|
|
943
|
+
awareness.setLocalStateField("title", title);
|
|
944
|
+
awareness.setLocalStateField("sheetId", sheetId);
|
|
945
|
+
if (resolvedAwarenessScopeId) {
|
|
946
|
+
awareness.setLocalStateField("docGuid", resolvedAwarenessScopeId);
|
|
947
|
+
}
|
|
948
|
+
// Mirror the canonical nested shape so other Athena editors can read
|
|
949
|
+
// state.user.* without falling back to the legacy flat fields.
|
|
950
|
+
var userBlock = {
|
|
951
|
+
userId: userId,
|
|
952
|
+
name: title,
|
|
953
|
+
};
|
|
954
|
+
if (kind)
|
|
955
|
+
userBlock.kind = kind;
|
|
956
|
+
if (agentInitiator)
|
|
957
|
+
userBlock.agentInitiator = agentInitiator;
|
|
958
|
+
awareness.setLocalStateField("user", userBlock);
|
|
959
|
+
if (kind)
|
|
960
|
+
awareness.setLocalStateField("kind", kind);
|
|
961
|
+
if (agentInitiator)
|
|
962
|
+
awareness.setLocalStateField("agentInitiator", agentInitiator);
|
|
963
|
+
}, [
|
|
964
|
+
awareness,
|
|
965
|
+
resolvedAwarenessScopeId,
|
|
966
|
+
userId,
|
|
967
|
+
title,
|
|
968
|
+
sheetId,
|
|
969
|
+
kind,
|
|
970
|
+
agentInitiator,
|
|
971
|
+
]);
|
|
972
|
+
return {
|
|
973
|
+
synced: synced,
|
|
974
|
+
users: users,
|
|
975
|
+
onBroadcastPatch: onBroadcastPatch,
|
|
976
|
+
isLeader: isLeader,
|
|
977
|
+
setupInitialSync: setupInitialSync,
|
|
978
|
+
};
|
|
979
|
+
};
|
|
980
|
+
exports.useYSpreadsheetV2 = useYSpreadsheetV2;
|