@tsdraw/react 0.6.1 → 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 +96 -27
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +97 -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]);
|
|
@@ -711,7 +719,7 @@ function useTsdrawCanvasController(options = {}) {
|
|
|
711
719
|
additive: first.shiftKey,
|
|
712
720
|
initialSelection: [...selectedShapeIdsRef.current]
|
|
713
721
|
};
|
|
714
|
-
setSelectionBrush({
|
|
722
|
+
setSelectionBrush(toScreenRect(editor, { minX: x, minY: y, maxX: x, maxY: y }));
|
|
715
723
|
if (!e.shiftKey) {
|
|
716
724
|
setSelectedShapeIds([]);
|
|
717
725
|
selectedShapeIdsRef.current = [];
|
|
@@ -868,6 +876,35 @@ function useTsdrawCanvasController(options = {}) {
|
|
|
868
876
|
}
|
|
869
877
|
editor.endHistoryEntry();
|
|
870
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
|
+
};
|
|
871
908
|
const handleKeyDown = (e) => {
|
|
872
909
|
const isMetaPressed = e.metaKey || e.ctrlKey;
|
|
873
910
|
const loweredKey = e.key.toLowerCase();
|
|
@@ -914,22 +951,42 @@ function useTsdrawCanvasController(options = {}) {
|
|
|
914
951
|
}
|
|
915
952
|
editor.loadHistorySnapshot(loaded.history);
|
|
916
953
|
syncHistoryState();
|
|
954
|
+
if (disposed) return;
|
|
917
955
|
persistenceActive = true;
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
if (isPointerActiveRef.current) {
|
|
927
|
-
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;
|
|
928
964
|
return;
|
|
929
965
|
}
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
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
|
+
}
|
|
933
990
|
} finally {
|
|
934
991
|
if (!disposed) {
|
|
935
992
|
setIsPersistenceReady(true);
|
|
@@ -951,12 +1008,13 @@ function useTsdrawCanvasController(options = {}) {
|
|
|
951
1008
|
canvas.addEventListener("pointerdown", handlePointerDown);
|
|
952
1009
|
window.addEventListener("pointermove", handlePointerMove);
|
|
953
1010
|
window.addEventListener("pointerup", handlePointerUp);
|
|
1011
|
+
window.addEventListener("pointercancel", handlePointerCancel);
|
|
954
1012
|
window.addEventListener("keydown", handleKeyDown);
|
|
955
1013
|
window.addEventListener("keyup", handleKeyUp);
|
|
956
1014
|
void initializePersistence().catch((error) => {
|
|
957
1015
|
console.error("failed to initialize tsdraw persistence", error);
|
|
958
1016
|
});
|
|
959
|
-
disposeMount =
|
|
1017
|
+
disposeMount = onMountRef.current?.({
|
|
960
1018
|
editor,
|
|
961
1019
|
container,
|
|
962
1020
|
canvas,
|
|
@@ -1005,6 +1063,7 @@ function useTsdrawCanvasController(options = {}) {
|
|
|
1005
1063
|
canvas.removeEventListener("pointerdown", handlePointerDown);
|
|
1006
1064
|
window.removeEventListener("pointermove", handlePointerMove);
|
|
1007
1065
|
window.removeEventListener("pointerup", handlePointerUp);
|
|
1066
|
+
window.removeEventListener("pointercancel", handlePointerCancel);
|
|
1008
1067
|
window.removeEventListener("keydown", handleKeyDown);
|
|
1009
1068
|
window.removeEventListener("keyup", handleKeyUp);
|
|
1010
1069
|
isPointerActiveRef.current = false;
|
|
@@ -1016,7 +1075,6 @@ function useTsdrawCanvasController(options = {}) {
|
|
|
1016
1075
|
}, [
|
|
1017
1076
|
getPagePointFromClient,
|
|
1018
1077
|
options.initialTool,
|
|
1019
|
-
options.onMount,
|
|
1020
1078
|
options.persistenceKey,
|
|
1021
1079
|
options.toolDefinitions,
|
|
1022
1080
|
refreshSelectionBounds,
|
|
@@ -1136,6 +1194,8 @@ function useTsdrawCanvasController(options = {}) {
|
|
|
1136
1194
|
};
|
|
1137
1195
|
}
|
|
1138
1196
|
var DEFAULT_TOOLBAR_PARTS = [["undo", "redo"], ["select", "hand", "pen", "eraser"]];
|
|
1197
|
+
var EMPTY_CUSTOM_TOOLS = [];
|
|
1198
|
+
var EMPTY_CUSTOM_ELEMENTS = [];
|
|
1139
1199
|
var DEFAULT_TOOL_LABELS = {
|
|
1140
1200
|
select: "Select",
|
|
1141
1201
|
pen: "Pen",
|
|
@@ -1181,14 +1241,14 @@ function resolvePlacementStyle(placement, fallbackAnchor, fallbackOffsetX, fallb
|
|
|
1181
1241
|
if (offsetY) transforms.push(`translateY(${offsetY}px)`);
|
|
1182
1242
|
}
|
|
1183
1243
|
if (transforms.length > 0) result.transform = transforms.join(" ");
|
|
1184
|
-
return { ...result, ...placement
|
|
1244
|
+
return placement?.style ? { ...result, ...placement.style } : result;
|
|
1185
1245
|
}
|
|
1186
1246
|
function Tsdraw(props) {
|
|
1187
1247
|
const [systemTheme, setSystemTheme] = useState(() => {
|
|
1188
1248
|
if (typeof window === "undefined") return "light";
|
|
1189
1249
|
return window.matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light";
|
|
1190
1250
|
});
|
|
1191
|
-
const customTools = props.customTools ??
|
|
1251
|
+
const customTools = props.customTools ?? EMPTY_CUSTOM_TOOLS;
|
|
1192
1252
|
const toolbarPartIds = props.uiOptions?.toolbar?.parts ?? DEFAULT_TOOLBAR_PARTS;
|
|
1193
1253
|
const customToolMap = useMemo(
|
|
1194
1254
|
() => new Map(customTools.map((customTool) => [customTool.id, customTool])),
|
|
@@ -1295,7 +1355,16 @@ function Tsdraw(props) {
|
|
|
1295
1355
|
}
|
|
1296
1356
|
);
|
|
1297
1357
|
const overlayNode = props.uiOptions?.overlays?.renderToolOverlay?.({ defaultOverlay: defaultToolOverlay, overlayState: toolOverlay, currentTool }) ?? defaultToolOverlay;
|
|
1298
|
-
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]);
|
|
1299
1368
|
const toolbarParts = useMemo(
|
|
1300
1369
|
() => toolbarPartIds.map((toolbarPart, partIndex) => {
|
|
1301
1370
|
const items = toolbarPart.map((item) => {
|
|
@@ -1390,9 +1459,9 @@ function Tsdraw(props) {
|
|
|
1390
1459
|
drawColor,
|
|
1391
1460
|
drawDash,
|
|
1392
1461
|
drawSize,
|
|
1393
|
-
onColorSelect
|
|
1394
|
-
onDashSelect
|
|
1395
|
-
onSizeSelect
|
|
1462
|
+
onColorSelect,
|
|
1463
|
+
onDashSelect,
|
|
1464
|
+
onSizeSelect
|
|
1396
1465
|
}
|
|
1397
1466
|
),
|
|
1398
1467
|
customElements.map((customElement) => /* @__PURE__ */ jsx(
|