@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,39 @@
|
|
|
1
|
+
import type { HocuspocusProvider } from "@hocuspocus/provider";
|
|
2
|
+
import type { CellInterface } from "@rowsncolumns/grid";
|
|
3
|
+
import type { CellData, Collaborator, ConditionalFormatRule, DataValidationRuleRecord, EmbeddedChart, EmbeddedObject, NamedRange, ProtectedRange, Sheet, TableView } from "@rowsncolumns/spreadsheet";
|
|
4
|
+
import type { CalculateNowOptions, CalculationOperation, HistoryType, SheetData, SpreadsheetPatch } from "@rowsncolumns/spreadsheet-state";
|
|
5
|
+
import type { Patch } from "immer";
|
|
6
|
+
import type { WebsocketProvider } from "y-websocket";
|
|
7
|
+
import type * as Y from "yjs";
|
|
8
|
+
export type useYSpreadsheetProps<T extends CellData, S extends Sheet, C extends EmbeddedChart, O extends EmbeddedObject> = {
|
|
9
|
+
userId: string | number;
|
|
10
|
+
title: string;
|
|
11
|
+
sheetId: number;
|
|
12
|
+
activeCell?: CellInterface;
|
|
13
|
+
provider?: HocuspocusProvider | WebsocketProvider | any;
|
|
14
|
+
doc: Y.Doc;
|
|
15
|
+
calculateNow(options?: CalculateNowOptions): void;
|
|
16
|
+
onChangeSheetData: React.Dispatch<React.SetStateAction<SheetData<T>>>;
|
|
17
|
+
onChangeTables: React.Dispatch<React.SetStateAction<TableView[]>>;
|
|
18
|
+
onChangeSheets: React.Dispatch<React.SetStateAction<S[]>>;
|
|
19
|
+
onChangeCharts?: React.Dispatch<React.SetStateAction<C[]>>;
|
|
20
|
+
onChangeEmbeds?: React.Dispatch<React.SetStateAction<O[]>>;
|
|
21
|
+
onChangeNamedRanges?: React.Dispatch<React.SetStateAction<NamedRange[]>>;
|
|
22
|
+
onChangeProtectedRanges?: React.Dispatch<React.SetStateAction<ProtectedRange[]>>;
|
|
23
|
+
onChangeConditionalFormats?: React.Dispatch<React.SetStateAction<ConditionalFormatRule[]>>;
|
|
24
|
+
onChangeDataValidations?: React.Dispatch<React.SetStateAction<DataValidationRuleRecord[]>>;
|
|
25
|
+
enqueueCalculation: (operation: CalculationOperation) => void;
|
|
26
|
+
onError?(err: unknown): void;
|
|
27
|
+
onInitialDataLoaded?(): void;
|
|
28
|
+
};
|
|
29
|
+
/**
|
|
30
|
+
* Connects to a Y server and picks up the most recent change
|
|
31
|
+
* Does not load initial data
|
|
32
|
+
* @returns
|
|
33
|
+
*/
|
|
34
|
+
export declare const useYSpreadsheet: <T extends CellData, S extends Sheet, C extends EmbeddedChart, O extends EmbeddedObject>({ onChangeSheetData, onChangeSheets, onChangeTables, onChangeCharts, onChangeEmbeds, onChangeConditionalFormats, onChangeDataValidations, onChangeNamedRanges, onChangeProtectedRanges, onError, enqueueCalculation, calculateNow, userId, title, sheetId, activeCell, doc, provider, }: useYSpreadsheetProps<T, S, C, O>) => {
|
|
35
|
+
onBroadcastPatch: (patches: [patch: SpreadsheetPatch<Patch>, type?: HistoryType | undefined][]) => void;
|
|
36
|
+
users: Collaborator[];
|
|
37
|
+
synced: boolean;
|
|
38
|
+
};
|
|
39
|
+
//# sourceMappingURL=use-yspreadsheet.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"use-yspreadsheet.d.ts","sourceRoot":"","sources":["../../use-yspreadsheet.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,304 @@
|
|
|
1
|
+
import { scheduleNormalPriorityCallback, useIsomorphicLayoutEffect, } from "@rowsncolumns/spreadsheet";
|
|
2
|
+
import { applyPatches } from "immer";
|
|
3
|
+
import uniqBy from "lodash/uniqBy";
|
|
4
|
+
import { useCallback, useMemo, useRef, useState } from "react";
|
|
5
|
+
import { applyUpdate, diffUpdate, Doc, encodeStateAsUpdate, encodeStateVector, } from "yjs";
|
|
6
|
+
const getStateVector = (doc) => encodeStateVector(doc);
|
|
7
|
+
// Function to get updates between two state vectors
|
|
8
|
+
const getUpdatesBetween = (doc, stateVector1, stateVector2) => {
|
|
9
|
+
const diff = encodeStateAsUpdate(doc, stateVector1);
|
|
10
|
+
const newDiff = encodeStateAsUpdate(doc, stateVector2);
|
|
11
|
+
return diffUpdate(diff, newDiff);
|
|
12
|
+
};
|
|
13
|
+
/**
|
|
14
|
+
* Connects to a Y server and picks up the most recent change
|
|
15
|
+
* Does not load initial data
|
|
16
|
+
* @returns
|
|
17
|
+
*/
|
|
18
|
+
export const useYSpreadsheet = ({ onChangeSheetData, onChangeSheets, onChangeTables, onChangeCharts, onChangeEmbeds, onChangeConditionalFormats, onChangeDataValidations, onChangeNamedRanges, onChangeProtectedRanges, onError, enqueueCalculation, calculateNow, userId, title, sheetId, activeCell, doc, provider, }) => {
|
|
19
|
+
const [users, setUsers] = useState([]);
|
|
20
|
+
const [synced, setSynced] = useState(false);
|
|
21
|
+
const previousStateVectorRef = useRef(undefined);
|
|
22
|
+
const currentStateVectorRef = useRef(undefined);
|
|
23
|
+
// Recalc cells
|
|
24
|
+
const yRecalcCells = useMemo(() => doc.getArray("recalcCells"), [doc]);
|
|
25
|
+
const yDisableRecalc = doc.getMap("disableRecalc");
|
|
26
|
+
// Counter
|
|
27
|
+
// Create a counter to keep track of when the last sync was done
|
|
28
|
+
const docSyncCounterRef = useRef(new Map());
|
|
29
|
+
// Create a doc map
|
|
30
|
+
const docMap = useMemo(() => {
|
|
31
|
+
const docMap = new Map([
|
|
32
|
+
[
|
|
33
|
+
"sheets",
|
|
34
|
+
[doc.getArray("sheets"), onChangeSheets, "sheetId"],
|
|
35
|
+
],
|
|
36
|
+
[
|
|
37
|
+
"sheetData",
|
|
38
|
+
[doc.getArray("sheetData"), onChangeSheetData],
|
|
39
|
+
],
|
|
40
|
+
["tables", [doc.getArray("tables"), onChangeTables, "id"]],
|
|
41
|
+
["embeds", [doc.getArray("embeds"), onChangeEmbeds, "id"]],
|
|
42
|
+
["charts", [doc.getArray("charts"), onChangeCharts, "id"]],
|
|
43
|
+
[
|
|
44
|
+
"conditionalFormats",
|
|
45
|
+
[
|
|
46
|
+
doc.getArray("conditionalFormats"),
|
|
47
|
+
onChangeConditionalFormats,
|
|
48
|
+
"id",
|
|
49
|
+
],
|
|
50
|
+
],
|
|
51
|
+
[
|
|
52
|
+
"dataValidations",
|
|
53
|
+
[
|
|
54
|
+
doc.getArray("dataValidations"),
|
|
55
|
+
onChangeDataValidations,
|
|
56
|
+
"id",
|
|
57
|
+
],
|
|
58
|
+
],
|
|
59
|
+
[
|
|
60
|
+
"namedRanges",
|
|
61
|
+
[doc.getArray("namedRanges"), onChangeNamedRanges, "id"],
|
|
62
|
+
],
|
|
63
|
+
[
|
|
64
|
+
"protectedRanges",
|
|
65
|
+
[
|
|
66
|
+
doc.getArray("protectedRanges"),
|
|
67
|
+
onChangeProtectedRanges,
|
|
68
|
+
"id",
|
|
69
|
+
],
|
|
70
|
+
],
|
|
71
|
+
]);
|
|
72
|
+
return docMap;
|
|
73
|
+
}, [
|
|
74
|
+
onChangeSheets,
|
|
75
|
+
onChangeSheetData,
|
|
76
|
+
onChangeTables,
|
|
77
|
+
onChangeCharts,
|
|
78
|
+
onChangeConditionalFormats,
|
|
79
|
+
onChangeDataValidations,
|
|
80
|
+
onChangeNamedRanges,
|
|
81
|
+
onChangeProtectedRanges,
|
|
82
|
+
onChangeEmbeds,
|
|
83
|
+
doc,
|
|
84
|
+
]);
|
|
85
|
+
// Listen to awareness
|
|
86
|
+
const awareness = provider?.awareness;
|
|
87
|
+
// Callback when the content is first synced
|
|
88
|
+
const onSynced = useCallback(() => {
|
|
89
|
+
let syncedDoc = doc;
|
|
90
|
+
if (previousStateVectorRef.current) {
|
|
91
|
+
// Capture the current state vector on subsequent syncs
|
|
92
|
+
currentStateVectorRef.current = getStateVector(doc);
|
|
93
|
+
const updates = getUpdatesBetween(doc, previousStateVectorRef.current, currentStateVectorRef.current);
|
|
94
|
+
const newDoc = new Doc({ gc: true });
|
|
95
|
+
applyUpdate(newDoc, updates);
|
|
96
|
+
syncedDoc = newDoc;
|
|
97
|
+
previousStateVectorRef.current = currentStateVectorRef.current;
|
|
98
|
+
}
|
|
99
|
+
const getInitialPatches = (ydoc, counterStart = 0) => {
|
|
100
|
+
const patches = [];
|
|
101
|
+
let endCounter = counterStart;
|
|
102
|
+
for (let i = counterStart; i < ydoc.length; i++) {
|
|
103
|
+
const { patches: userPatches } = ydoc.get(i);
|
|
104
|
+
for (const patch of userPatches) {
|
|
105
|
+
patches.push(patch);
|
|
106
|
+
}
|
|
107
|
+
endCounter++;
|
|
108
|
+
}
|
|
109
|
+
return [patches, endCounter];
|
|
110
|
+
};
|
|
111
|
+
for (const [mappingKey, [_, setStateFn, idKey]] of docMap) {
|
|
112
|
+
const startCounter = docSyncCounterRef.current.get(mappingKey) ?? 0;
|
|
113
|
+
const [patches, endCounter] = getInitialPatches(syncedDoc.getArray(mappingKey), startCounter);
|
|
114
|
+
if (patches.length) {
|
|
115
|
+
docSyncCounterRef.current.set(mappingKey, endCounter);
|
|
116
|
+
setStateFn?.((prev) => {
|
|
117
|
+
try {
|
|
118
|
+
const nextState = idKey
|
|
119
|
+
? uniqBy(applyPatches(prev, patches), idKey)
|
|
120
|
+
: applyPatches(prev, patches);
|
|
121
|
+
return nextState;
|
|
122
|
+
}
|
|
123
|
+
catch (err) {
|
|
124
|
+
console.error(err);
|
|
125
|
+
onError?.(err);
|
|
126
|
+
}
|
|
127
|
+
return prev;
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
// Update recalc cells
|
|
132
|
+
scheduleNormalPriorityCallback(() => {
|
|
133
|
+
calculateNow({
|
|
134
|
+
disableEvaluation: true,
|
|
135
|
+
shouldResetCellDependencyGraph: true,
|
|
136
|
+
});
|
|
137
|
+
});
|
|
138
|
+
// Set synced
|
|
139
|
+
setSynced(true);
|
|
140
|
+
}, [calculateNow, docMap, onError, doc]);
|
|
141
|
+
// Sync state
|
|
142
|
+
if (provider?.synced && !synced) {
|
|
143
|
+
onSynced();
|
|
144
|
+
}
|
|
145
|
+
// Listen to first sync event
|
|
146
|
+
useIsomorphicLayoutEffect(() => {
|
|
147
|
+
provider?.on("synced", onSynced);
|
|
148
|
+
const onConnectionStatusChange = ({ status }) => {
|
|
149
|
+
if (status === "disconnected") {
|
|
150
|
+
previousStateVectorRef.current = getStateVector(doc);
|
|
151
|
+
}
|
|
152
|
+
};
|
|
153
|
+
provider?.on("status", onConnectionStatusChange);
|
|
154
|
+
return () => {
|
|
155
|
+
provider?.off("synced", onSynced);
|
|
156
|
+
provider?.off("status", onConnectionStatusChange);
|
|
157
|
+
};
|
|
158
|
+
}, [provider, onSynced, doc]);
|
|
159
|
+
// Awareness
|
|
160
|
+
useIsomorphicLayoutEffect(() => {
|
|
161
|
+
const onChangeAwareness = () => {
|
|
162
|
+
if (awareness) {
|
|
163
|
+
const collaborators = [];
|
|
164
|
+
for (const [_, value] of awareness.getStates().entries()) {
|
|
165
|
+
if (value && value.activeCell && value.userId) {
|
|
166
|
+
collaborators.push({
|
|
167
|
+
activeCell: value.activeCell,
|
|
168
|
+
title: value.title,
|
|
169
|
+
userId: value.userId,
|
|
170
|
+
sheetId: value.sheetId,
|
|
171
|
+
});
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
setUsers(collaborators);
|
|
175
|
+
}
|
|
176
|
+
};
|
|
177
|
+
// Listen to awareness
|
|
178
|
+
awareness?.on("change", onChangeAwareness);
|
|
179
|
+
return () => {
|
|
180
|
+
awareness?.off("change", onChangeAwareness);
|
|
181
|
+
};
|
|
182
|
+
}, [awareness]);
|
|
183
|
+
// Set local awareness state
|
|
184
|
+
useIsomorphicLayoutEffect(() => {
|
|
185
|
+
awareness?.setLocalState({
|
|
186
|
+
userId,
|
|
187
|
+
title,
|
|
188
|
+
sheetId,
|
|
189
|
+
});
|
|
190
|
+
}, [awareness, userId, title, sheetId]);
|
|
191
|
+
// Sheet data changes
|
|
192
|
+
useIsomorphicLayoutEffect(() => {
|
|
193
|
+
const getPatchDeltasFromEvent = (event) => {
|
|
194
|
+
const patches = [];
|
|
195
|
+
if (event.transaction.origin !== "locale") {
|
|
196
|
+
for (const changes of event.changes.delta) {
|
|
197
|
+
for (const type in changes) {
|
|
198
|
+
if (type === "insert" && changes[type] !== undefined) {
|
|
199
|
+
for (const { patches: userPatches } of changes[type]) {
|
|
200
|
+
for (const patch of userPatches) {
|
|
201
|
+
patches.push(patch);
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
return patches;
|
|
209
|
+
};
|
|
210
|
+
const syncRecalcCells = (recalcCells) => {
|
|
211
|
+
for (const [_, position] of recalcCells) {
|
|
212
|
+
enqueueCalculation({
|
|
213
|
+
type: "add",
|
|
214
|
+
position,
|
|
215
|
+
});
|
|
216
|
+
}
|
|
217
|
+
};
|
|
218
|
+
// Trigger cell recalcs
|
|
219
|
+
const onRecalcCellsChange = (event) => {
|
|
220
|
+
const patches = getPatchDeltasFromEvent(event);
|
|
221
|
+
if (event.transaction.origin !== "local") {
|
|
222
|
+
syncRecalcCells(patches);
|
|
223
|
+
}
|
|
224
|
+
};
|
|
225
|
+
// Listen to changes after sync
|
|
226
|
+
if (synced) {
|
|
227
|
+
const subs = [];
|
|
228
|
+
for (const [_, [doc, setStateFn]] of docMap) {
|
|
229
|
+
const callback = (event) => {
|
|
230
|
+
const patches = getPatchDeltasFromEvent(event);
|
|
231
|
+
const disableRecalc = yDisableRecalc.get("disableRecalc");
|
|
232
|
+
if (event.transaction.origin !== "local" && disableRecalc !== true) {
|
|
233
|
+
if (patches.length) {
|
|
234
|
+
setStateFn?.((prev) => {
|
|
235
|
+
try {
|
|
236
|
+
return applyPatches(prev, patches);
|
|
237
|
+
}
|
|
238
|
+
catch (err) {
|
|
239
|
+
console.error(err);
|
|
240
|
+
onError?.(err);
|
|
241
|
+
}
|
|
242
|
+
return prev;
|
|
243
|
+
});
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
};
|
|
247
|
+
doc.observe(callback);
|
|
248
|
+
subs.push([doc, callback]);
|
|
249
|
+
}
|
|
250
|
+
// Observe recalc cells separat
|
|
251
|
+
yRecalcCells.observe(onRecalcCellsChange);
|
|
252
|
+
return () => {
|
|
253
|
+
subs.forEach(([doc, callback]) => doc.unobserve(callback));
|
|
254
|
+
yRecalcCells.unobserve(onRecalcCellsChange);
|
|
255
|
+
};
|
|
256
|
+
}
|
|
257
|
+
}, [userId, provider, synced, doc, yDisableRecalc]);
|
|
258
|
+
// Broadcast patch
|
|
259
|
+
const onBroadcastPatch = useCallback((patches) => {
|
|
260
|
+
doc.transact(() => {
|
|
261
|
+
for (const [patch, type = "redo"] of patches) {
|
|
262
|
+
const patchKey = type === "redo" ? "patches" : "inversePatches";
|
|
263
|
+
for (const [mappingKey, [doc]] of docMap) {
|
|
264
|
+
const currentPatch = patch[mappingKey];
|
|
265
|
+
const patches = currentPatch?.[patchKey];
|
|
266
|
+
if (patches) {
|
|
267
|
+
const startCounter = docSyncCounterRef.current.get(mappingKey) ?? 0;
|
|
268
|
+
doc.push([
|
|
269
|
+
{
|
|
270
|
+
userId,
|
|
271
|
+
patches,
|
|
272
|
+
},
|
|
273
|
+
]);
|
|
274
|
+
// Update last counter
|
|
275
|
+
docSyncCounterRef.current.set(mappingKey, startCounter + patches.length);
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
if (patch.recalcCells?.[type]) {
|
|
279
|
+
yRecalcCells.push([
|
|
280
|
+
{
|
|
281
|
+
userId,
|
|
282
|
+
patches: Array.from(patch.recalcCells[type]),
|
|
283
|
+
},
|
|
284
|
+
]);
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
}, "local");
|
|
288
|
+
}, [userId, docMap, doc, yRecalcCells, yDisableRecalc]);
|
|
289
|
+
// Listen to active cell change
|
|
290
|
+
useIsomorphicLayoutEffect(() => {
|
|
291
|
+
if (activeCell) {
|
|
292
|
+
awareness?.setLocalStateField("activeCell", activeCell);
|
|
293
|
+
}
|
|
294
|
+
}, [awareness, activeCell]);
|
|
295
|
+
// Listen to sheet change
|
|
296
|
+
useIsomorphicLayoutEffect(() => {
|
|
297
|
+
awareness?.setLocalStateField("sheetId", sheetId);
|
|
298
|
+
}, [awareness, sheetId]);
|
|
299
|
+
return {
|
|
300
|
+
onBroadcastPatch,
|
|
301
|
+
users,
|
|
302
|
+
synced,
|
|
303
|
+
};
|
|
304
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"vitest.config.d.ts","sourceRoot":"","sources":["../../vitest.config.ts"],"names":[],"mappings":";AAEA,wBAsBG"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { defineConfig } from "vitest/config";
|
|
2
|
+
export default defineConfig({
|
|
3
|
+
test: {
|
|
4
|
+
globals: true,
|
|
5
|
+
environment: "jsdom",
|
|
6
|
+
// ✅ correct location for deps.inline
|
|
7
|
+
deps: {
|
|
8
|
+
inline: ["konva", "react-konva"],
|
|
9
|
+
},
|
|
10
|
+
},
|
|
11
|
+
// Ensure Vite bundles these for SSR-style test runs
|
|
12
|
+
ssr: {
|
|
13
|
+
noExternal: ["konva", "react-konva"],
|
|
14
|
+
},
|
|
15
|
+
// Prefer the ESM build of react-konva (avoid its CJS wrapper that requires konva)
|
|
16
|
+
resolve: {
|
|
17
|
+
alias: {
|
|
18
|
+
"react-konva": "react-konva/es/ReactKonvaCore.js",
|
|
19
|
+
},
|
|
20
|
+
},
|
|
21
|
+
});
|
package/package.json
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@rowsncolumns/y-spreadsheet",
|
|
3
|
+
"version": "14.0.32",
|
|
4
|
+
"main": "./dist/cjs/index.js",
|
|
5
|
+
"module": "./dist/esm/index.js",
|
|
6
|
+
"exports": {
|
|
7
|
+
".": {
|
|
8
|
+
"import": "./dist/esm/index.js",
|
|
9
|
+
"require": "./dist/cjs/index.js"
|
|
10
|
+
}
|
|
11
|
+
},
|
|
12
|
+
"author": "Vinay M",
|
|
13
|
+
"license": "SEE LICENSE IN LICENSE.md",
|
|
14
|
+
"scripts": {
|
|
15
|
+
"build": "tsc -b ./tsconfig.cjs.json ./tsconfig.esm.json",
|
|
16
|
+
"clean": "rimraf dist",
|
|
17
|
+
"build:watch": "tsc -p ./tsconfig.esm.json --watch",
|
|
18
|
+
"lint": "eslint . --ext .ts,.tsx",
|
|
19
|
+
"test": "vitest --run",
|
|
20
|
+
"types": "tsc -p . --noEmit"
|
|
21
|
+
},
|
|
22
|
+
"files": [
|
|
23
|
+
"dist"
|
|
24
|
+
],
|
|
25
|
+
"repository": {
|
|
26
|
+
"type": "git",
|
|
27
|
+
"url": "https://github.com/rowsncolumns/spreadsheet.git"
|
|
28
|
+
},
|
|
29
|
+
"bugs": {
|
|
30
|
+
"url": "https://github.com/rowsncolumns/spreadsheet/issues"
|
|
31
|
+
},
|
|
32
|
+
"devDependencies": {
|
|
33
|
+
"@hocuspocus/provider": "^3.1.1",
|
|
34
|
+
"@rowsncolumns/spreadsheet": ">=10.2.68",
|
|
35
|
+
"@rowsncolumns/spreadsheet-state": ">=28.0.66",
|
|
36
|
+
"react": "^19.1.0",
|
|
37
|
+
"react-dom": "^19.1.0",
|
|
38
|
+
"y-websocket": "^3.0.0",
|
|
39
|
+
"@types/lodash": "^4.17.17",
|
|
40
|
+
"yjs": "^13.6.27"
|
|
41
|
+
},
|
|
42
|
+
"peerDependencies": {
|
|
43
|
+
"@rowsncolumns/spreadsheet": ">=10.2.68",
|
|
44
|
+
"@rowsncolumns/spreadsheet-state": ">=28.0.66",
|
|
45
|
+
"react": ">=18.0.0",
|
|
46
|
+
"react-dom": ">=18.0.0",
|
|
47
|
+
"y-websocket": ">=2.0.0",
|
|
48
|
+
"yjs": ">=13.0.0"
|
|
49
|
+
},
|
|
50
|
+
"dependencies": {
|
|
51
|
+
"rxjs": "^7.8.2",
|
|
52
|
+
"lodash": "^4.17.21"
|
|
53
|
+
}
|
|
54
|
+
}
|