@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.cjs
CHANGED
|
@@ -365,11 +365,15 @@ function createSessionId() {
|
|
|
365
365
|
}
|
|
366
366
|
function getOrCreateSessionId() {
|
|
367
367
|
if (typeof window === "undefined") return createSessionId();
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
368
|
+
try {
|
|
369
|
+
const existing = window.sessionStorage.getItem(SESSION_STORAGE_KEY);
|
|
370
|
+
if (existing) return existing;
|
|
371
|
+
const newId = createSessionId();
|
|
372
|
+
window.sessionStorage.setItem(SESSION_STORAGE_KEY, newId);
|
|
373
|
+
return newId;
|
|
374
|
+
} catch {
|
|
375
|
+
return createSessionId();
|
|
376
|
+
}
|
|
373
377
|
}
|
|
374
378
|
|
|
375
379
|
// src/canvas/useTsdrawCanvasController.ts
|
|
@@ -388,6 +392,7 @@ function resolveDrawColor(colorStyle, theme) {
|
|
|
388
392
|
function useTsdrawCanvasController(options = {}) {
|
|
389
393
|
const stylePanelToolIds = options.stylePanelToolIds ?? ["pen"];
|
|
390
394
|
const stylePanelToolIdsRef = react.useRef(stylePanelToolIds);
|
|
395
|
+
const onMountRef = react.useRef(options.onMount);
|
|
391
396
|
const containerRef = react.useRef(null);
|
|
392
397
|
const canvasRef = react.useRef(null);
|
|
393
398
|
const editorRef = react.useRef(null);
|
|
@@ -440,6 +445,9 @@ function useTsdrawCanvasController(options = {}) {
|
|
|
440
445
|
react.useEffect(() => {
|
|
441
446
|
stylePanelToolIdsRef.current = stylePanelToolIds;
|
|
442
447
|
}, [stylePanelToolIds]);
|
|
448
|
+
react.useEffect(() => {
|
|
449
|
+
onMountRef.current = options.onMount;
|
|
450
|
+
}, [options.onMount]);
|
|
443
451
|
react.useEffect(() => {
|
|
444
452
|
selectedShapeIdsRef.current = selectedShapeIds;
|
|
445
453
|
}, [selectedShapeIds]);
|
|
@@ -610,6 +618,7 @@ function useTsdrawCanvasController(options = {}) {
|
|
|
610
618
|
history: editor.getHistorySnapshot(),
|
|
611
619
|
sessionId
|
|
612
620
|
});
|
|
621
|
+
if (disposed) return;
|
|
613
622
|
persistenceChannel?.postMessage({
|
|
614
623
|
type: "tsdraw:persisted",
|
|
615
624
|
senderSessionId: sessionId
|
|
@@ -712,7 +721,7 @@ function useTsdrawCanvasController(options = {}) {
|
|
|
712
721
|
additive: first.shiftKey,
|
|
713
722
|
initialSelection: [...selectedShapeIdsRef.current]
|
|
714
723
|
};
|
|
715
|
-
setSelectionBrush({
|
|
724
|
+
setSelectionBrush(toScreenRect(editor, { minX: x, minY: y, maxX: x, maxY: y }));
|
|
716
725
|
if (!e.shiftKey) {
|
|
717
726
|
setSelectedShapeIds([]);
|
|
718
727
|
selectedShapeIdsRef.current = [];
|
|
@@ -869,6 +878,35 @@ function useTsdrawCanvasController(options = {}) {
|
|
|
869
878
|
}
|
|
870
879
|
editor.endHistoryEntry();
|
|
871
880
|
};
|
|
881
|
+
const handlePointerCancel = () => {
|
|
882
|
+
if (!isPointerActiveRef.current) return;
|
|
883
|
+
isPointerActiveRef.current = false;
|
|
884
|
+
lastPointerClientRef.current = null;
|
|
885
|
+
editor.input.pointerUp();
|
|
886
|
+
if (currentToolRef.current === "select") {
|
|
887
|
+
const drag = selectDragRef.current;
|
|
888
|
+
if (drag.mode === "rotate") setIsRotatingSelection(false);
|
|
889
|
+
if (drag.mode === "resize") setIsResizingSelection(false);
|
|
890
|
+
if (drag.mode === "move") setIsMovingSelection(false);
|
|
891
|
+
if (drag.mode === "marquee") setSelectionBrush(null);
|
|
892
|
+
if (drag.mode !== "none") {
|
|
893
|
+
selectDragRef.current.mode = "none";
|
|
894
|
+
render();
|
|
895
|
+
refreshSelectionBounds(editor);
|
|
896
|
+
}
|
|
897
|
+
editor.endHistoryEntry();
|
|
898
|
+
} else {
|
|
899
|
+
editor.tools.pointerUp();
|
|
900
|
+
render();
|
|
901
|
+
refreshSelectionBounds(editor);
|
|
902
|
+
editor.endHistoryEntry();
|
|
903
|
+
}
|
|
904
|
+
if (pendingRemoteDocumentRef.current) {
|
|
905
|
+
const pending = pendingRemoteDocumentRef.current;
|
|
906
|
+
pendingRemoteDocumentRef.current = null;
|
|
907
|
+
applyRemoteDocumentSnapshot(pending);
|
|
908
|
+
}
|
|
909
|
+
};
|
|
872
910
|
const handleKeyDown = (e) => {
|
|
873
911
|
const isMetaPressed = e.metaKey || e.ctrlKey;
|
|
874
912
|
const loweredKey = e.key.toLowerCase();
|
|
@@ -915,22 +953,42 @@ function useTsdrawCanvasController(options = {}) {
|
|
|
915
953
|
}
|
|
916
954
|
editor.loadHistorySnapshot(loaded.history);
|
|
917
955
|
syncHistoryState();
|
|
956
|
+
if (disposed) return;
|
|
918
957
|
persistenceActive = true;
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
if (isPointerActiveRef.current) {
|
|
928
|
-
pendingRemoteDocumentRef.current = nextDocument;
|
|
958
|
+
if (typeof BroadcastChannel !== "undefined") {
|
|
959
|
+
persistenceChannel = new BroadcastChannel(`tsdraw:persistence:${persistenceKey}`);
|
|
960
|
+
let isLoadingRemote = false;
|
|
961
|
+
let pendingRemoteLoad = false;
|
|
962
|
+
persistenceChannel.onmessage = () => {
|
|
963
|
+
if (disposed) return;
|
|
964
|
+
if (isLoadingRemote) {
|
|
965
|
+
pendingRemoteLoad = true;
|
|
929
966
|
return;
|
|
930
967
|
}
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
968
|
+
isLoadingRemote = true;
|
|
969
|
+
const processLoad = async () => {
|
|
970
|
+
try {
|
|
971
|
+
do {
|
|
972
|
+
pendingRemoteLoad = false;
|
|
973
|
+
if (!persistenceDb || disposed) return;
|
|
974
|
+
const nextLoaded = await persistenceDb.load(sessionId);
|
|
975
|
+
if (disposed) return;
|
|
976
|
+
if (nextLoaded.records.length > 0) {
|
|
977
|
+
const nextDocument = { records: nextLoaded.records };
|
|
978
|
+
if (isPointerActiveRef.current) {
|
|
979
|
+
pendingRemoteDocumentRef.current = nextDocument;
|
|
980
|
+
return;
|
|
981
|
+
}
|
|
982
|
+
applyRemoteDocumentSnapshot(nextDocument);
|
|
983
|
+
}
|
|
984
|
+
} while (pendingRemoteLoad && !disposed);
|
|
985
|
+
} finally {
|
|
986
|
+
isLoadingRemote = false;
|
|
987
|
+
}
|
|
988
|
+
};
|
|
989
|
+
void processLoad();
|
|
990
|
+
};
|
|
991
|
+
}
|
|
934
992
|
} finally {
|
|
935
993
|
if (!disposed) {
|
|
936
994
|
setIsPersistenceReady(true);
|
|
@@ -952,12 +1010,13 @@ function useTsdrawCanvasController(options = {}) {
|
|
|
952
1010
|
canvas.addEventListener("pointerdown", handlePointerDown);
|
|
953
1011
|
window.addEventListener("pointermove", handlePointerMove);
|
|
954
1012
|
window.addEventListener("pointerup", handlePointerUp);
|
|
1013
|
+
window.addEventListener("pointercancel", handlePointerCancel);
|
|
955
1014
|
window.addEventListener("keydown", handleKeyDown);
|
|
956
1015
|
window.addEventListener("keyup", handleKeyUp);
|
|
957
1016
|
void initializePersistence().catch((error) => {
|
|
958
1017
|
console.error("failed to initialize tsdraw persistence", error);
|
|
959
1018
|
});
|
|
960
|
-
disposeMount =
|
|
1019
|
+
disposeMount = onMountRef.current?.({
|
|
961
1020
|
editor,
|
|
962
1021
|
container,
|
|
963
1022
|
canvas,
|
|
@@ -1006,6 +1065,7 @@ function useTsdrawCanvasController(options = {}) {
|
|
|
1006
1065
|
canvas.removeEventListener("pointerdown", handlePointerDown);
|
|
1007
1066
|
window.removeEventListener("pointermove", handlePointerMove);
|
|
1008
1067
|
window.removeEventListener("pointerup", handlePointerUp);
|
|
1068
|
+
window.removeEventListener("pointercancel", handlePointerCancel);
|
|
1009
1069
|
window.removeEventListener("keydown", handleKeyDown);
|
|
1010
1070
|
window.removeEventListener("keyup", handleKeyUp);
|
|
1011
1071
|
isPointerActiveRef.current = false;
|
|
@@ -1017,7 +1077,6 @@ function useTsdrawCanvasController(options = {}) {
|
|
|
1017
1077
|
}, [
|
|
1018
1078
|
getPagePointFromClient,
|
|
1019
1079
|
options.initialTool,
|
|
1020
|
-
options.onMount,
|
|
1021
1080
|
options.persistenceKey,
|
|
1022
1081
|
options.toolDefinitions,
|
|
1023
1082
|
refreshSelectionBounds,
|
|
@@ -1137,6 +1196,8 @@ function useTsdrawCanvasController(options = {}) {
|
|
|
1137
1196
|
};
|
|
1138
1197
|
}
|
|
1139
1198
|
var DEFAULT_TOOLBAR_PARTS = [["undo", "redo"], ["select", "hand", "pen", "eraser"]];
|
|
1199
|
+
var EMPTY_CUSTOM_TOOLS = [];
|
|
1200
|
+
var EMPTY_CUSTOM_ELEMENTS = [];
|
|
1140
1201
|
var DEFAULT_TOOL_LABELS = {
|
|
1141
1202
|
select: "Select",
|
|
1142
1203
|
pen: "Pen",
|
|
@@ -1182,14 +1243,14 @@ function resolvePlacementStyle(placement, fallbackAnchor, fallbackOffsetX, fallb
|
|
|
1182
1243
|
if (offsetY) transforms.push(`translateY(${offsetY}px)`);
|
|
1183
1244
|
}
|
|
1184
1245
|
if (transforms.length > 0) result.transform = transforms.join(" ");
|
|
1185
|
-
return { ...result, ...placement
|
|
1246
|
+
return placement?.style ? { ...result, ...placement.style } : result;
|
|
1186
1247
|
}
|
|
1187
1248
|
function Tsdraw(props) {
|
|
1188
1249
|
const [systemTheme, setSystemTheme] = react.useState(() => {
|
|
1189
1250
|
if (typeof window === "undefined") return "light";
|
|
1190
1251
|
return window.matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light";
|
|
1191
1252
|
});
|
|
1192
|
-
const customTools = props.customTools ??
|
|
1253
|
+
const customTools = props.customTools ?? EMPTY_CUSTOM_TOOLS;
|
|
1193
1254
|
const toolbarPartIds = props.uiOptions?.toolbar?.parts ?? DEFAULT_TOOLBAR_PARTS;
|
|
1194
1255
|
const customToolMap = react.useMemo(
|
|
1195
1256
|
() => new Map(customTools.map((customTool) => [customTool.id, customTool])),
|
|
@@ -1296,7 +1357,16 @@ function Tsdraw(props) {
|
|
|
1296
1357
|
}
|
|
1297
1358
|
);
|
|
1298
1359
|
const overlayNode = props.uiOptions?.overlays?.renderToolOverlay?.({ defaultOverlay: defaultToolOverlay, overlayState: toolOverlay, currentTool }) ?? defaultToolOverlay;
|
|
1299
|
-
const customElements = props.uiOptions?.customElements ??
|
|
1360
|
+
const customElements = props.uiOptions?.customElements ?? EMPTY_CUSTOM_ELEMENTS;
|
|
1361
|
+
const onColorSelect = react.useCallback((color) => {
|
|
1362
|
+
applyDrawStyle({ color });
|
|
1363
|
+
}, [applyDrawStyle]);
|
|
1364
|
+
const onDashSelect = react.useCallback((dash) => {
|
|
1365
|
+
applyDrawStyle({ dash });
|
|
1366
|
+
}, [applyDrawStyle]);
|
|
1367
|
+
const onSizeSelect = react.useCallback((size) => {
|
|
1368
|
+
applyDrawStyle({ size });
|
|
1369
|
+
}, [applyDrawStyle]);
|
|
1300
1370
|
const toolbarParts = react.useMemo(
|
|
1301
1371
|
() => toolbarPartIds.map((toolbarPart, partIndex) => {
|
|
1302
1372
|
const items = toolbarPart.map((item) => {
|
|
@@ -1391,9 +1461,9 @@ function Tsdraw(props) {
|
|
|
1391
1461
|
drawColor,
|
|
1392
1462
|
drawDash,
|
|
1393
1463
|
drawSize,
|
|
1394
|
-
onColorSelect
|
|
1395
|
-
onDashSelect
|
|
1396
|
-
onSizeSelect
|
|
1464
|
+
onColorSelect,
|
|
1465
|
+
onDashSelect,
|
|
1466
|
+
onSizeSelect
|
|
1397
1467
|
}
|
|
1398
1468
|
),
|
|
1399
1469
|
customElements.map((customElement) => /* @__PURE__ */ jsxRuntime.jsx(
|