@pixerate/editor-react 0.3.13 → 0.4.0
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/dist/index.cjs +184 -50
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +59 -4
- package/dist/index.d.ts +59 -4
- package/dist/index.js +175 -42
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -227,6 +227,127 @@ function useEditor2(options = {}) {
|
|
|
227
227
|
});
|
|
228
228
|
}
|
|
229
229
|
|
|
230
|
+
// src/hooks/useNavigationGuard.ts
|
|
231
|
+
import { useState, useEffect as useEffect2, useCallback, useRef as useRef2 } from "react";
|
|
232
|
+
function useNavigationGuard({
|
|
233
|
+
isDirty,
|
|
234
|
+
enabled = true,
|
|
235
|
+
onNavigate,
|
|
236
|
+
onBlocked
|
|
237
|
+
}) {
|
|
238
|
+
const [showConfirm, setShowConfirm] = useState(false);
|
|
239
|
+
const [pendingUrl, setPendingUrl] = useState(null);
|
|
240
|
+
const onNavigateRef = useRef2(onNavigate);
|
|
241
|
+
onNavigateRef.current = onNavigate;
|
|
242
|
+
const onBlockedRef = useRef2(onBlocked);
|
|
243
|
+
onBlockedRef.current = onBlocked;
|
|
244
|
+
useEffect2(() => {
|
|
245
|
+
if (typeof window === "undefined") return;
|
|
246
|
+
if (!enabled || !isDirty) return;
|
|
247
|
+
const handleBeforeUnload = (e) => {
|
|
248
|
+
e.preventDefault();
|
|
249
|
+
e.returnValue = "";
|
|
250
|
+
};
|
|
251
|
+
window.addEventListener("beforeunload", handleBeforeUnload);
|
|
252
|
+
return () => {
|
|
253
|
+
window.removeEventListener("beforeunload", handleBeforeUnload);
|
|
254
|
+
};
|
|
255
|
+
}, [enabled, isDirty]);
|
|
256
|
+
useEffect2(() => {
|
|
257
|
+
if (typeof document === "undefined" || typeof window === "undefined")
|
|
258
|
+
return;
|
|
259
|
+
if (!enabled || !isDirty) return;
|
|
260
|
+
const handleClick = (e) => {
|
|
261
|
+
const target = e.target;
|
|
262
|
+
const anchor = target?.closest?.("a");
|
|
263
|
+
if (!anchor) return;
|
|
264
|
+
if (anchor.target === "_blank") return;
|
|
265
|
+
const isInternal = anchor.host === window.location.host;
|
|
266
|
+
if (!isInternal) return;
|
|
267
|
+
if (anchor.pathname === window.location.pathname && anchor.search === window.location.search) {
|
|
268
|
+
return;
|
|
269
|
+
}
|
|
270
|
+
e.preventDefault();
|
|
271
|
+
e.stopPropagation();
|
|
272
|
+
const href = anchor.href;
|
|
273
|
+
setPendingUrl(href);
|
|
274
|
+
setShowConfirm(true);
|
|
275
|
+
onBlockedRef.current?.(href);
|
|
276
|
+
};
|
|
277
|
+
document.addEventListener("click", handleClick, { capture: true });
|
|
278
|
+
return () => {
|
|
279
|
+
document.removeEventListener("click", handleClick, { capture: true });
|
|
280
|
+
};
|
|
281
|
+
}, [enabled, isDirty]);
|
|
282
|
+
const confirmLeave = useCallback(() => {
|
|
283
|
+
setShowConfirm(false);
|
|
284
|
+
if (pendingUrl) {
|
|
285
|
+
if (onNavigateRef.current) {
|
|
286
|
+
onNavigateRef.current(pendingUrl);
|
|
287
|
+
} else if (typeof window !== "undefined") {
|
|
288
|
+
window.location.href = pendingUrl;
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
}, [pendingUrl]);
|
|
292
|
+
const cancelLeave = useCallback(() => {
|
|
293
|
+
setShowConfirm(false);
|
|
294
|
+
setPendingUrl(null);
|
|
295
|
+
}, []);
|
|
296
|
+
return {
|
|
297
|
+
showConfirm,
|
|
298
|
+
setShowConfirm,
|
|
299
|
+
pendingUrl,
|
|
300
|
+
confirmLeave,
|
|
301
|
+
cancelLeave
|
|
302
|
+
};
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
// src/hooks/useDismissGuard.ts
|
|
306
|
+
import { useState as useState2, useCallback as useCallback2, useRef as useRef3 } from "react";
|
|
307
|
+
function useDismissGuard({
|
|
308
|
+
isDirty,
|
|
309
|
+
enabled = true,
|
|
310
|
+
onDismiss
|
|
311
|
+
}) {
|
|
312
|
+
const [showConfirm, setShowConfirm] = useState2(false);
|
|
313
|
+
const onDismissRef = useRef3(onDismiss);
|
|
314
|
+
onDismissRef.current = onDismiss;
|
|
315
|
+
const requestDismiss = useCallback2(() => {
|
|
316
|
+
if (enabled && isDirty) {
|
|
317
|
+
setShowConfirm(true);
|
|
318
|
+
} else {
|
|
319
|
+
onDismissRef.current?.();
|
|
320
|
+
}
|
|
321
|
+
}, [enabled, isDirty]);
|
|
322
|
+
const confirmDismiss = useCallback2(() => {
|
|
323
|
+
setShowConfirm(false);
|
|
324
|
+
onDismissRef.current?.();
|
|
325
|
+
}, []);
|
|
326
|
+
const cancelDismiss = useCallback2(() => {
|
|
327
|
+
setShowConfirm(false);
|
|
328
|
+
}, []);
|
|
329
|
+
const handleKeyDown = useCallback2(
|
|
330
|
+
(event) => {
|
|
331
|
+
if (event.key === "Escape") {
|
|
332
|
+
if (event.defaultPrevented) return false;
|
|
333
|
+
event.preventDefault();
|
|
334
|
+
requestDismiss();
|
|
335
|
+
return true;
|
|
336
|
+
}
|
|
337
|
+
return false;
|
|
338
|
+
},
|
|
339
|
+
[requestDismiss]
|
|
340
|
+
);
|
|
341
|
+
return {
|
|
342
|
+
showConfirm,
|
|
343
|
+
setShowConfirm,
|
|
344
|
+
requestDismiss,
|
|
345
|
+
confirmDismiss,
|
|
346
|
+
cancelDismiss,
|
|
347
|
+
handleKeyDown
|
|
348
|
+
};
|
|
349
|
+
}
|
|
350
|
+
|
|
230
351
|
// src/components/EditorContent.tsx
|
|
231
352
|
import {
|
|
232
353
|
EditorContent as TipTapEditorContent
|
|
@@ -241,7 +362,7 @@ var EditorContent = ({
|
|
|
241
362
|
};
|
|
242
363
|
|
|
243
364
|
// src/components/BubbleMenu.tsx
|
|
244
|
-
import { useEffect as
|
|
365
|
+
import { useEffect as useEffect3, useRef as useRef4, useState as useState3 } from "react";
|
|
245
366
|
import { createPortal } from "react-dom";
|
|
246
367
|
import {
|
|
247
368
|
BubbleMenuPlugin
|
|
@@ -255,12 +376,12 @@ var BubbleMenu = ({
|
|
|
255
376
|
pluginKey = "bubbleMenu",
|
|
256
377
|
updateDelay
|
|
257
378
|
}) => {
|
|
258
|
-
const menuEl =
|
|
259
|
-
const [mounted, setMounted] =
|
|
379
|
+
const menuEl = useRef4(null);
|
|
380
|
+
const [mounted, setMounted] = useState3(false);
|
|
260
381
|
if (!menuEl.current && typeof document !== "undefined") {
|
|
261
382
|
menuEl.current = document.createElement("div");
|
|
262
383
|
}
|
|
263
|
-
|
|
384
|
+
useEffect3(() => {
|
|
264
385
|
if (!editor || !menuEl.current || editor.isDestroyed) {
|
|
265
386
|
return;
|
|
266
387
|
}
|
|
@@ -362,13 +483,20 @@ var TemplateRenderer = ({
|
|
|
362
483
|
}) });
|
|
363
484
|
};
|
|
364
485
|
|
|
486
|
+
// src/index.ts
|
|
487
|
+
import {
|
|
488
|
+
DirtyTracker,
|
|
489
|
+
createDirtyTracker,
|
|
490
|
+
defaultIsEqual
|
|
491
|
+
} from "@pixerate/editor";
|
|
492
|
+
|
|
365
493
|
// src/spreadsheet/useSpreadsheetEditor.ts
|
|
366
|
-
import { useState as
|
|
494
|
+
import { useState as useState4, useMemo as useMemo3, useCallback as useCallback3 } from "react";
|
|
367
495
|
import {
|
|
368
496
|
SpreadsheetController
|
|
369
497
|
} from "@pixerate/editor";
|
|
370
498
|
function useSpreadsheetEditor(options = {}) {
|
|
371
|
-
const [doc, setDoc] =
|
|
499
|
+
const [doc, setDoc] = useState4(() => ({
|
|
372
500
|
id: options.document?.id || `sheet_${Date.now()}`,
|
|
373
501
|
name: options.document?.name || "Sheet 1",
|
|
374
502
|
columns: options.document?.columns || [],
|
|
@@ -377,10 +505,10 @@ function useSpreadsheetEditor(options = {}) {
|
|
|
377
505
|
createdAt: options.document?.createdAt || Date.now(),
|
|
378
506
|
updatedAt: options.document?.updatedAt || Date.now()
|
|
379
507
|
}));
|
|
380
|
-
const [activeCell, setActiveCell] =
|
|
381
|
-
const [selectedRange, setSelectedRange] =
|
|
382
|
-
const [editingCell, setEditingCell] =
|
|
383
|
-
const [draftValue, setDraftValue] =
|
|
508
|
+
const [activeCell, setActiveCell] = useState4(null);
|
|
509
|
+
const [selectedRange, setSelectedRange] = useState4(null);
|
|
510
|
+
const [editingCell, setEditingCell] = useState4(null);
|
|
511
|
+
const [draftValue, setDraftValue] = useState4("");
|
|
384
512
|
const controller = useMemo3(() => {
|
|
385
513
|
return new SpreadsheetController({
|
|
386
514
|
...options,
|
|
@@ -398,82 +526,82 @@ function useSpreadsheetEditor(options = {}) {
|
|
|
398
526
|
}
|
|
399
527
|
});
|
|
400
528
|
}, []);
|
|
401
|
-
const setCellValue =
|
|
529
|
+
const setCellValue = useCallback3((rowId, colId, raw) => {
|
|
402
530
|
controller.setCellValue(rowId, colId, raw);
|
|
403
531
|
setDoc({ ...controller.document });
|
|
404
532
|
}, [controller]);
|
|
405
|
-
const selectCell =
|
|
533
|
+
const selectCell = useCallback3((row, col, extendSelection = false) => {
|
|
406
534
|
controller.selectCell(row, col, extendSelection);
|
|
407
535
|
setActiveCell(controller.activeCell);
|
|
408
536
|
setSelectedRange(controller.selectedRange);
|
|
409
537
|
}, [controller]);
|
|
410
|
-
const selectRange =
|
|
538
|
+
const selectRange = useCallback3((range) => {
|
|
411
539
|
controller.selectRange(range);
|
|
412
540
|
setSelectedRange(controller.selectedRange);
|
|
413
541
|
}, [controller]);
|
|
414
|
-
const startEditing =
|
|
542
|
+
const startEditing = useCallback3((row, col, initialVal) => {
|
|
415
543
|
controller.startEditing(row, col, initialVal);
|
|
416
544
|
setActiveCell(controller.activeCell);
|
|
417
545
|
setEditingCell(controller.editingCell);
|
|
418
546
|
setDraftValue(controller.draftValue);
|
|
419
547
|
}, [controller]);
|
|
420
|
-
const commitEditing =
|
|
548
|
+
const commitEditing = useCallback3((val) => {
|
|
421
549
|
controller.commitEditing(val);
|
|
422
550
|
setEditingCell(null);
|
|
423
551
|
setDraftValue("");
|
|
424
552
|
setDoc({ ...controller.document });
|
|
425
553
|
}, [controller]);
|
|
426
|
-
const cancelEditing =
|
|
554
|
+
const cancelEditing = useCallback3(() => {
|
|
427
555
|
controller.cancelEditing();
|
|
428
556
|
setEditingCell(null);
|
|
429
557
|
setDraftValue("");
|
|
430
558
|
}, [controller]);
|
|
431
|
-
const moveCursor =
|
|
559
|
+
const moveCursor = useCallback3((dir, extend = false) => {
|
|
432
560
|
controller.moveCursor(dir, extend);
|
|
433
561
|
setActiveCell(controller.activeCell);
|
|
434
562
|
setSelectedRange(controller.selectedRange);
|
|
435
563
|
}, [controller]);
|
|
436
|
-
const insertColumn =
|
|
564
|
+
const insertColumn = useCallback3((index, config) => {
|
|
437
565
|
controller.insertColumn(index, config);
|
|
438
566
|
setDoc({ ...controller.document });
|
|
439
567
|
}, [controller]);
|
|
440
|
-
const deleteColumn =
|
|
568
|
+
const deleteColumn = useCallback3((colId) => {
|
|
441
569
|
controller.deleteColumn(colId);
|
|
442
570
|
setDoc({ ...controller.document });
|
|
443
571
|
}, [controller]);
|
|
444
|
-
const insertRow =
|
|
572
|
+
const insertRow = useCallback3((index, config) => {
|
|
445
573
|
controller.insertRow(index, config);
|
|
446
574
|
setDoc({ ...controller.document });
|
|
447
575
|
}, [controller]);
|
|
448
|
-
const deleteRow =
|
|
576
|
+
const deleteRow = useCallback3((rowId) => {
|
|
449
577
|
controller.deleteRow(rowId);
|
|
450
578
|
setDoc({ ...controller.document });
|
|
451
579
|
}, [controller]);
|
|
452
|
-
const setColumnWidth =
|
|
580
|
+
const setColumnWidth = useCallback3((colId, width) => {
|
|
453
581
|
controller.setColumnWidth(colId, width);
|
|
454
582
|
setDoc({ ...controller.document });
|
|
455
583
|
}, [controller]);
|
|
456
|
-
const autoFitColumnWidth =
|
|
584
|
+
const autoFitColumnWidth = useCallback3((colId, customWidth) => {
|
|
457
585
|
controller.autoFitColumnWidth(colId, customWidth);
|
|
458
586
|
setDoc({ ...controller.document });
|
|
459
587
|
}, [controller]);
|
|
460
|
-
const registerDataSource =
|
|
588
|
+
const registerDataSource = useCallback3((ds) => {
|
|
461
589
|
controller.registerDataSource(ds);
|
|
462
590
|
setDoc({ ...controller.document });
|
|
463
591
|
}, [controller]);
|
|
464
|
-
const setPrimaryDataSource =
|
|
592
|
+
const setPrimaryDataSource = useCallback3((id) => {
|
|
465
593
|
controller.setPrimaryDataSource(id);
|
|
466
594
|
setDoc({ ...controller.document });
|
|
467
595
|
}, [controller]);
|
|
468
|
-
const recalculateAll =
|
|
596
|
+
const recalculateAll = useCallback3(() => {
|
|
469
597
|
controller.recalculateAll();
|
|
470
598
|
setDoc({ ...controller.document });
|
|
471
599
|
}, [controller]);
|
|
472
|
-
const undo =
|
|
600
|
+
const undo = useCallback3(() => {
|
|
473
601
|
controller.undo();
|
|
474
602
|
setDoc({ ...controller.document });
|
|
475
603
|
}, [controller]);
|
|
476
|
-
const redo =
|
|
604
|
+
const redo = useCallback3(() => {
|
|
477
605
|
controller.redo();
|
|
478
606
|
setDoc({ ...controller.document });
|
|
479
607
|
}, [controller]);
|
|
@@ -526,7 +654,7 @@ function useSpreadsheetEditor(options = {}) {
|
|
|
526
654
|
}
|
|
527
655
|
|
|
528
656
|
// src/spreadsheet/SpreadsheetEditor.tsx
|
|
529
|
-
import { useRef as
|
|
657
|
+
import { useRef as useRef5, useState as useState5, useMemo as useMemo4, useEffect as useEffect4, useCallback as useCallback4 } from "react";
|
|
530
658
|
import {
|
|
531
659
|
indexToColName
|
|
532
660
|
} from "@pixerate/editor";
|
|
@@ -541,11 +669,11 @@ var SpreadsheetEditor = ({
|
|
|
541
669
|
}) => {
|
|
542
670
|
const defaultState = useSpreadsheetEditor();
|
|
543
671
|
const state = externalState || defaultState;
|
|
544
|
-
const gridRef =
|
|
545
|
-
const [resizingColId, setResizingColId] =
|
|
546
|
-
const [hoveredColId, setHoveredColId] =
|
|
547
|
-
const [resizeStartX, setResizeStartX] =
|
|
548
|
-
const [resizeStartWidth, setResizeStartWidth] =
|
|
672
|
+
const gridRef = useRef5(null);
|
|
673
|
+
const [resizingColId, setResizingColId] = useState5(null);
|
|
674
|
+
const [hoveredColId, setHoveredColId] = useState5(null);
|
|
675
|
+
const [resizeStartX, setResizeStartX] = useState5(0);
|
|
676
|
+
const [resizeStartWidth, setResizeStartWidth] = useState5(0);
|
|
549
677
|
const activeRawValue = useMemo4(() => {
|
|
550
678
|
if (!state.activeCell) return "";
|
|
551
679
|
const row = state.document.rows[state.activeCell.row];
|
|
@@ -558,19 +686,19 @@ var SpreadsheetEditor = ({
|
|
|
558
686
|
const colName = indexToColName(state.activeCell.col);
|
|
559
687
|
return `${colName}${state.activeCell.row + 1}`;
|
|
560
688
|
}, [state.activeCell]);
|
|
561
|
-
const handleCellClick =
|
|
689
|
+
const handleCellClick = useCallback4((rIdx, cIdx, e) => {
|
|
562
690
|
if (state.editingCell && (state.editingCell.row !== rIdx || state.editingCell.col !== cIdx)) {
|
|
563
691
|
state.commitEditing();
|
|
564
692
|
}
|
|
565
693
|
state.selectCell(rIdx, cIdx, e.shiftKey);
|
|
566
694
|
}, [state]);
|
|
567
|
-
const handleCellDblClick =
|
|
695
|
+
const handleCellDblClick = useCallback4((rIdx, cIdx) => {
|
|
568
696
|
if (readOnly) return;
|
|
569
697
|
const col = state.document.columns[cIdx];
|
|
570
698
|
if (col?.readOnly) return;
|
|
571
699
|
state.startEditing(rIdx, cIdx);
|
|
572
700
|
}, [readOnly, state]);
|
|
573
|
-
const handleKeyDown =
|
|
701
|
+
const handleKeyDown = useCallback4((e) => {
|
|
574
702
|
if (state.editingCell) {
|
|
575
703
|
if (e.key === "Enter") {
|
|
576
704
|
e.preventDefault();
|
|
@@ -634,7 +762,7 @@ var SpreadsheetEditor = ({
|
|
|
634
762
|
}
|
|
635
763
|
}
|
|
636
764
|
}, [readOnly, state]);
|
|
637
|
-
|
|
765
|
+
useEffect4(() => {
|
|
638
766
|
if (!resizingColId) return;
|
|
639
767
|
if (typeof document !== "undefined") {
|
|
640
768
|
document.body.style.cursor = "col-resize";
|
|
@@ -688,7 +816,7 @@ var SpreadsheetEditor = ({
|
|
|
688
816
|
window.removeEventListener("mouseup", handleMouseUp);
|
|
689
817
|
};
|
|
690
818
|
}, [resizingColId, resizeStartX, resizeStartWidth, state, onColumnResize]);
|
|
691
|
-
const measureElementContentWidth =
|
|
819
|
+
const measureElementContentWidth = useCallback4((el, container) => {
|
|
692
820
|
if (typeof document === "undefined") return 0;
|
|
693
821
|
let maxW = el.scrollWidth || 0;
|
|
694
822
|
const firstChild = el.firstElementChild;
|
|
@@ -736,7 +864,7 @@ var SpreadsheetEditor = ({
|
|
|
736
864
|
}
|
|
737
865
|
return maxW;
|
|
738
866
|
}, []);
|
|
739
|
-
const autoFitColumn =
|
|
867
|
+
const autoFitColumn = useCallback4((colId, colIndex) => {
|
|
740
868
|
const col = state.document.columns.find((c) => c.id === colId);
|
|
741
869
|
if (!col) return;
|
|
742
870
|
let calculatedWidth = 0;
|
|
@@ -773,12 +901,12 @@ var SpreadsheetEditor = ({
|
|
|
773
901
|
onColumnResize(updatedCol.id, updatedCol.width || 130);
|
|
774
902
|
}
|
|
775
903
|
}, [state, measureElementContentWidth, onColumnResize]);
|
|
776
|
-
const handleResizeDblClick =
|
|
904
|
+
const handleResizeDblClick = useCallback4((colId, colIndex, e) => {
|
|
777
905
|
e.preventDefault();
|
|
778
906
|
e.stopPropagation();
|
|
779
907
|
autoFitColumn(colId, colIndex);
|
|
780
908
|
}, [autoFitColumn]);
|
|
781
|
-
const handleHeaderDblClick =
|
|
909
|
+
const handleHeaderDblClick = useCallback4((colId, colIndex, e) => {
|
|
782
910
|
const target = e.currentTarget;
|
|
783
911
|
if (target) {
|
|
784
912
|
const rect = target.getBoundingClientRect();
|
|
@@ -963,10 +1091,15 @@ var SpreadsheetEditor = ({
|
|
|
963
1091
|
};
|
|
964
1092
|
export {
|
|
965
1093
|
BubbleMenu,
|
|
1094
|
+
DirtyTracker,
|
|
966
1095
|
EditorContent,
|
|
967
1096
|
SpreadsheetEditor,
|
|
968
1097
|
TemplateRenderer,
|
|
1098
|
+
createDirtyTracker,
|
|
1099
|
+
defaultIsEqual,
|
|
1100
|
+
useDismissGuard,
|
|
969
1101
|
useEditor2 as useEditor,
|
|
1102
|
+
useNavigationGuard,
|
|
970
1103
|
usePromptEditor,
|
|
971
1104
|
useSpreadsheetEditor
|
|
972
1105
|
};
|