@tsdraw/react 0.6.0 → 0.6.2
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 +97 -27
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +98 -28
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { useState, useMemo, useEffect,
|
|
1
|
+
import { useState, useMemo, useEffect, useCallback, useRef } from 'react';
|
|
2
2
|
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
|
|
3
3
|
import { DEFAULT_COLORS, getSelectionBoundsPage, buildTransformSnapshots, Editor, STROKE_WIDTHS, ERASER_MARGIN, resolveThemeColor, getTopShapeAtPoint, buildStartPositions, applyRotation, applyResize, applyMove, normalizeSelectionBounds, getShapesInBounds, isSelectTool } from '@tsdraw/core';
|
|
4
4
|
import { IconPointer, IconPencil, IconEraser, IconHandStop, IconArrowBackUp, IconArrowForwardUp } from '@tabler/icons-react';
|
|
@@ -363,11 +363,15 @@ function createSessionId() {
|
|
|
363
363
|
}
|
|
364
364
|
function getOrCreateSessionId() {
|
|
365
365
|
if (typeof window === "undefined") return createSessionId();
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
366
|
+
try {
|
|
367
|
+
const existing = window.sessionStorage.getItem(SESSION_STORAGE_KEY);
|
|
368
|
+
if (existing) return existing;
|
|
369
|
+
const newId = createSessionId();
|
|
370
|
+
window.sessionStorage.setItem(SESSION_STORAGE_KEY, newId);
|
|
371
|
+
return newId;
|
|
372
|
+
} catch {
|
|
373
|
+
return createSessionId();
|
|
374
|
+
}
|
|
371
375
|
}
|
|
372
376
|
|
|
373
377
|
// src/canvas/useTsdrawCanvasController.ts
|
|
@@ -386,6 +390,7 @@ function resolveDrawColor(colorStyle, theme) {
|
|
|
386
390
|
function useTsdrawCanvasController(options = {}) {
|
|
387
391
|
const stylePanelToolIds = options.stylePanelToolIds ?? ["pen"];
|
|
388
392
|
const stylePanelToolIdsRef = useRef(stylePanelToolIds);
|
|
393
|
+
const onMountRef = useRef(options.onMount);
|
|
389
394
|
const containerRef = useRef(null);
|
|
390
395
|
const canvasRef = useRef(null);
|
|
391
396
|
const editorRef = useRef(null);
|
|
@@ -438,6 +443,9 @@ function useTsdrawCanvasController(options = {}) {
|
|
|
438
443
|
useEffect(() => {
|
|
439
444
|
stylePanelToolIdsRef.current = stylePanelToolIds;
|
|
440
445
|
}, [stylePanelToolIds]);
|
|
446
|
+
useEffect(() => {
|
|
447
|
+
onMountRef.current = options.onMount;
|
|
448
|
+
}, [options.onMount]);
|
|
441
449
|
useEffect(() => {
|
|
442
450
|
selectedShapeIdsRef.current = selectedShapeIds;
|
|
443
451
|
}, [selectedShapeIds]);
|
|
@@ -608,6 +616,7 @@ function useTsdrawCanvasController(options = {}) {
|
|
|
608
616
|
history: editor.getHistorySnapshot(),
|
|
609
617
|
sessionId
|
|
610
618
|
});
|
|
619
|
+
if (disposed) return;
|
|
611
620
|
persistenceChannel?.postMessage({
|
|
612
621
|
type: "tsdraw:persisted",
|
|
613
622
|
senderSessionId: sessionId
|
|
@@ -710,7 +719,7 @@ function useTsdrawCanvasController(options = {}) {
|
|
|
710
719
|
additive: first.shiftKey,
|
|
711
720
|
initialSelection: [...selectedShapeIdsRef.current]
|
|
712
721
|
};
|
|
713
|
-
setSelectionBrush({
|
|
722
|
+
setSelectionBrush(toScreenRect(editor, { minX: x, minY: y, maxX: x, maxY: y }));
|
|
714
723
|
if (!e.shiftKey) {
|
|
715
724
|
setSelectedShapeIds([]);
|
|
716
725
|
selectedShapeIdsRef.current = [];
|
|
@@ -867,6 +876,35 @@ function useTsdrawCanvasController(options = {}) {
|
|
|
867
876
|
}
|
|
868
877
|
editor.endHistoryEntry();
|
|
869
878
|
};
|
|
879
|
+
const handlePointerCancel = () => {
|
|
880
|
+
if (!isPointerActiveRef.current) return;
|
|
881
|
+
isPointerActiveRef.current = false;
|
|
882
|
+
lastPointerClientRef.current = null;
|
|
883
|
+
editor.input.pointerUp();
|
|
884
|
+
if (currentToolRef.current === "select") {
|
|
885
|
+
const drag = selectDragRef.current;
|
|
886
|
+
if (drag.mode === "rotate") setIsRotatingSelection(false);
|
|
887
|
+
if (drag.mode === "resize") setIsResizingSelection(false);
|
|
888
|
+
if (drag.mode === "move") setIsMovingSelection(false);
|
|
889
|
+
if (drag.mode === "marquee") setSelectionBrush(null);
|
|
890
|
+
if (drag.mode !== "none") {
|
|
891
|
+
selectDragRef.current.mode = "none";
|
|
892
|
+
render();
|
|
893
|
+
refreshSelectionBounds(editor);
|
|
894
|
+
}
|
|
895
|
+
editor.endHistoryEntry();
|
|
896
|
+
} else {
|
|
897
|
+
editor.tools.pointerUp();
|
|
898
|
+
render();
|
|
899
|
+
refreshSelectionBounds(editor);
|
|
900
|
+
editor.endHistoryEntry();
|
|
901
|
+
}
|
|
902
|
+
if (pendingRemoteDocumentRef.current) {
|
|
903
|
+
const pending = pendingRemoteDocumentRef.current;
|
|
904
|
+
pendingRemoteDocumentRef.current = null;
|
|
905
|
+
applyRemoteDocumentSnapshot(pending);
|
|
906
|
+
}
|
|
907
|
+
};
|
|
870
908
|
const handleKeyDown = (e) => {
|
|
871
909
|
const isMetaPressed = e.metaKey || e.ctrlKey;
|
|
872
910
|
const loweredKey = e.key.toLowerCase();
|
|
@@ -913,22 +951,42 @@ function useTsdrawCanvasController(options = {}) {
|
|
|
913
951
|
}
|
|
914
952
|
editor.loadHistorySnapshot(loaded.history);
|
|
915
953
|
syncHistoryState();
|
|
954
|
+
if (disposed) return;
|
|
916
955
|
persistenceActive = true;
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
if (isPointerActiveRef.current) {
|
|
926
|
-
pendingRemoteDocumentRef.current = nextDocument;
|
|
956
|
+
if (typeof BroadcastChannel !== "undefined") {
|
|
957
|
+
persistenceChannel = new BroadcastChannel(`tsdraw:persistence:${persistenceKey}`);
|
|
958
|
+
let isLoadingRemote = false;
|
|
959
|
+
let pendingRemoteLoad = false;
|
|
960
|
+
persistenceChannel.onmessage = () => {
|
|
961
|
+
if (disposed) return;
|
|
962
|
+
if (isLoadingRemote) {
|
|
963
|
+
pendingRemoteLoad = true;
|
|
927
964
|
return;
|
|
928
965
|
}
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
966
|
+
isLoadingRemote = true;
|
|
967
|
+
const processLoad = async () => {
|
|
968
|
+
try {
|
|
969
|
+
do {
|
|
970
|
+
pendingRemoteLoad = false;
|
|
971
|
+
if (!persistenceDb || disposed) return;
|
|
972
|
+
const nextLoaded = await persistenceDb.load(sessionId);
|
|
973
|
+
if (disposed) return;
|
|
974
|
+
if (nextLoaded.records.length > 0) {
|
|
975
|
+
const nextDocument = { records: nextLoaded.records };
|
|
976
|
+
if (isPointerActiveRef.current) {
|
|
977
|
+
pendingRemoteDocumentRef.current = nextDocument;
|
|
978
|
+
return;
|
|
979
|
+
}
|
|
980
|
+
applyRemoteDocumentSnapshot(nextDocument);
|
|
981
|
+
}
|
|
982
|
+
} while (pendingRemoteLoad && !disposed);
|
|
983
|
+
} finally {
|
|
984
|
+
isLoadingRemote = false;
|
|
985
|
+
}
|
|
986
|
+
};
|
|
987
|
+
void processLoad();
|
|
988
|
+
};
|
|
989
|
+
}
|
|
932
990
|
} finally {
|
|
933
991
|
if (!disposed) {
|
|
934
992
|
setIsPersistenceReady(true);
|
|
@@ -950,12 +1008,13 @@ function useTsdrawCanvasController(options = {}) {
|
|
|
950
1008
|
canvas.addEventListener("pointerdown", handlePointerDown);
|
|
951
1009
|
window.addEventListener("pointermove", handlePointerMove);
|
|
952
1010
|
window.addEventListener("pointerup", handlePointerUp);
|
|
1011
|
+
window.addEventListener("pointercancel", handlePointerCancel);
|
|
953
1012
|
window.addEventListener("keydown", handleKeyDown);
|
|
954
1013
|
window.addEventListener("keyup", handleKeyUp);
|
|
955
1014
|
void initializePersistence().catch((error) => {
|
|
956
1015
|
console.error("failed to initialize tsdraw persistence", error);
|
|
957
1016
|
});
|
|
958
|
-
disposeMount =
|
|
1017
|
+
disposeMount = onMountRef.current?.({
|
|
959
1018
|
editor,
|
|
960
1019
|
container,
|
|
961
1020
|
canvas,
|
|
@@ -1004,6 +1063,7 @@ function useTsdrawCanvasController(options = {}) {
|
|
|
1004
1063
|
canvas.removeEventListener("pointerdown", handlePointerDown);
|
|
1005
1064
|
window.removeEventListener("pointermove", handlePointerMove);
|
|
1006
1065
|
window.removeEventListener("pointerup", handlePointerUp);
|
|
1066
|
+
window.removeEventListener("pointercancel", handlePointerCancel);
|
|
1007
1067
|
window.removeEventListener("keydown", handleKeyDown);
|
|
1008
1068
|
window.removeEventListener("keyup", handleKeyUp);
|
|
1009
1069
|
isPointerActiveRef.current = false;
|
|
@@ -1015,7 +1075,6 @@ function useTsdrawCanvasController(options = {}) {
|
|
|
1015
1075
|
}, [
|
|
1016
1076
|
getPagePointFromClient,
|
|
1017
1077
|
options.initialTool,
|
|
1018
|
-
options.onMount,
|
|
1019
1078
|
options.persistenceKey,
|
|
1020
1079
|
options.toolDefinitions,
|
|
1021
1080
|
refreshSelectionBounds,
|
|
@@ -1135,6 +1194,8 @@ function useTsdrawCanvasController(options = {}) {
|
|
|
1135
1194
|
};
|
|
1136
1195
|
}
|
|
1137
1196
|
var DEFAULT_TOOLBAR_PARTS = [["undo", "redo"], ["select", "hand", "pen", "eraser"]];
|
|
1197
|
+
var EMPTY_CUSTOM_TOOLS = [];
|
|
1198
|
+
var EMPTY_CUSTOM_ELEMENTS = [];
|
|
1138
1199
|
var DEFAULT_TOOL_LABELS = {
|
|
1139
1200
|
select: "Select",
|
|
1140
1201
|
pen: "Pen",
|
|
@@ -1180,14 +1241,14 @@ function resolvePlacementStyle(placement, fallbackAnchor, fallbackOffsetX, fallb
|
|
|
1180
1241
|
if (offsetY) transforms.push(`translateY(${offsetY}px)`);
|
|
1181
1242
|
}
|
|
1182
1243
|
if (transforms.length > 0) result.transform = transforms.join(" ");
|
|
1183
|
-
return { ...result, ...placement
|
|
1244
|
+
return placement?.style ? { ...result, ...placement.style } : result;
|
|
1184
1245
|
}
|
|
1185
1246
|
function Tsdraw(props) {
|
|
1186
1247
|
const [systemTheme, setSystemTheme] = useState(() => {
|
|
1187
1248
|
if (typeof window === "undefined") return "light";
|
|
1188
1249
|
return window.matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light";
|
|
1189
1250
|
});
|
|
1190
|
-
const customTools = props.customTools ??
|
|
1251
|
+
const customTools = props.customTools ?? EMPTY_CUSTOM_TOOLS;
|
|
1191
1252
|
const toolbarPartIds = props.uiOptions?.toolbar?.parts ?? DEFAULT_TOOLBAR_PARTS;
|
|
1192
1253
|
const customToolMap = useMemo(
|
|
1193
1254
|
() => new Map(customTools.map((customTool) => [customTool.id, customTool])),
|
|
@@ -1294,7 +1355,16 @@ function Tsdraw(props) {
|
|
|
1294
1355
|
}
|
|
1295
1356
|
);
|
|
1296
1357
|
const overlayNode = props.uiOptions?.overlays?.renderToolOverlay?.({ defaultOverlay: defaultToolOverlay, overlayState: toolOverlay, currentTool }) ?? defaultToolOverlay;
|
|
1297
|
-
const customElements = props.uiOptions?.customElements ??
|
|
1358
|
+
const customElements = props.uiOptions?.customElements ?? EMPTY_CUSTOM_ELEMENTS;
|
|
1359
|
+
const onColorSelect = useCallback((color) => {
|
|
1360
|
+
applyDrawStyle({ color });
|
|
1361
|
+
}, [applyDrawStyle]);
|
|
1362
|
+
const onDashSelect = useCallback((dash) => {
|
|
1363
|
+
applyDrawStyle({ dash });
|
|
1364
|
+
}, [applyDrawStyle]);
|
|
1365
|
+
const onSizeSelect = useCallback((size) => {
|
|
1366
|
+
applyDrawStyle({ size });
|
|
1367
|
+
}, [applyDrawStyle]);
|
|
1298
1368
|
const toolbarParts = useMemo(
|
|
1299
1369
|
() => toolbarPartIds.map((toolbarPart, partIndex) => {
|
|
1300
1370
|
const items = toolbarPart.map((item) => {
|
|
@@ -1389,9 +1459,9 @@ function Tsdraw(props) {
|
|
|
1389
1459
|
drawColor,
|
|
1390
1460
|
drawDash,
|
|
1391
1461
|
drawSize,
|
|
1392
|
-
onColorSelect
|
|
1393
|
-
onDashSelect
|
|
1394
|
-
onSizeSelect
|
|
1462
|
+
onColorSelect,
|
|
1463
|
+
onDashSelect,
|
|
1464
|
+
onSizeSelect
|
|
1395
1465
|
}
|
|
1396
1466
|
),
|
|
1397
1467
|
customElements.map((customElement) => /* @__PURE__ */ jsx(
|