@tsdraw/react 0.6.1 → 0.7.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 +227 -102
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +27 -4
- package/dist/index.d.ts +27 -4
- package/dist/index.js +229 -104
- package/dist/index.js.map +1 -1
- package/dist/tsdraw.css +26 -1
- package/package.json +2 -2
package/dist/index.cjs
CHANGED
|
@@ -100,66 +100,106 @@ function SelectionOverlay({
|
|
|
100
100
|
}
|
|
101
101
|
var STYLE_COLORS = Object.entries(core.DEFAULT_COLORS).filter(([key]) => key !== "white").map(([value]) => ({ value }));
|
|
102
102
|
var STYLE_DASHES = ["draw", "solid", "dashed", "dotted"];
|
|
103
|
+
var STYLE_FILLS = ["none", "blank", "semi", "solid"];
|
|
103
104
|
var STYLE_SIZES = ["s", "m", "l", "xl"];
|
|
104
105
|
function StylePanel({
|
|
105
106
|
visible,
|
|
107
|
+
parts,
|
|
108
|
+
customParts,
|
|
106
109
|
style,
|
|
107
110
|
theme,
|
|
108
111
|
drawColor,
|
|
109
112
|
drawDash,
|
|
113
|
+
drawFill,
|
|
110
114
|
drawSize,
|
|
111
115
|
onColorSelect,
|
|
112
116
|
onDashSelect,
|
|
117
|
+
onFillSelect,
|
|
113
118
|
onSizeSelect
|
|
114
119
|
}) {
|
|
115
|
-
if (!visible) return null;
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
"
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
120
|
+
if (!visible || parts.length === 0) return null;
|
|
121
|
+
const context = {
|
|
122
|
+
drawColor,
|
|
123
|
+
drawDash,
|
|
124
|
+
drawFill,
|
|
125
|
+
drawSize,
|
|
126
|
+
onColorSelect,
|
|
127
|
+
onDashSelect,
|
|
128
|
+
onFillSelect,
|
|
129
|
+
onSizeSelect
|
|
130
|
+
};
|
|
131
|
+
const customPartMap = new Map((customParts ?? []).map((customPart) => [customPart.id, customPart]));
|
|
132
|
+
return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "tsdraw-style-panel", style, "aria-label": "Draw style panel", children: parts.map((part) => {
|
|
133
|
+
if (part === "colors") {
|
|
134
|
+
return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "tsdraw-style-colors", children: STYLE_COLORS.map((item) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
135
|
+
"button",
|
|
136
|
+
{
|
|
137
|
+
type: "button",
|
|
138
|
+
className: "tsdraw-style-color",
|
|
139
|
+
"data-active": drawColor === item.value ? "true" : void 0,
|
|
140
|
+
"aria-label": `Color ${item.value}`,
|
|
141
|
+
title: item.value,
|
|
142
|
+
onClick: () => onColorSelect(item.value),
|
|
143
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
144
|
+
"span",
|
|
145
|
+
{
|
|
146
|
+
className: "tsdraw-style-color-dot",
|
|
147
|
+
style: { background: core.resolveThemeColor(item.value, theme) }
|
|
148
|
+
}
|
|
149
|
+
)
|
|
150
|
+
},
|
|
151
|
+
item.value
|
|
152
|
+
)) }, part);
|
|
153
|
+
}
|
|
154
|
+
if (part === "dashes") {
|
|
155
|
+
return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "tsdraw-style-section", children: STYLE_DASHES.map((dash) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
156
|
+
"button",
|
|
157
|
+
{
|
|
158
|
+
type: "button",
|
|
159
|
+
className: "tsdraw-style-row",
|
|
160
|
+
"data-active": drawDash === dash ? "true" : void 0,
|
|
161
|
+
"aria-label": `Stroke ${dash}`,
|
|
162
|
+
title: dash,
|
|
163
|
+
onClick: () => onDashSelect(dash),
|
|
164
|
+
children: /* @__PURE__ */ jsxRuntime.jsx("span", { className: "tsdraw-style-preview", children: /* @__PURE__ */ jsxRuntime.jsx("span", { className: `tsdraw-style-preview-line tsdraw-style-preview-line--${dash}` }) })
|
|
165
|
+
},
|
|
166
|
+
dash
|
|
167
|
+
)) }, part);
|
|
168
|
+
}
|
|
169
|
+
if (part === "fills") {
|
|
170
|
+
return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "tsdraw-style-section", children: STYLE_FILLS.map((fill) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
171
|
+
"button",
|
|
172
|
+
{
|
|
173
|
+
type: "button",
|
|
174
|
+
className: "tsdraw-style-row",
|
|
175
|
+
"data-active": drawFill === fill ? "true" : void 0,
|
|
176
|
+
"aria-label": `Fill ${fill}`,
|
|
177
|
+
title: fill,
|
|
178
|
+
onClick: () => onFillSelect(fill),
|
|
179
|
+
children: /* @__PURE__ */ jsxRuntime.jsx("span", { className: "tsdraw-style-preview", children: /* @__PURE__ */ jsxRuntime.jsx("span", { className: `tsdraw-style-fill tsdraw-style-fill--${fill}` }) })
|
|
180
|
+
},
|
|
181
|
+
fill
|
|
182
|
+
)) }, part);
|
|
183
|
+
}
|
|
184
|
+
if (part === "sizes") {
|
|
185
|
+
return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "tsdraw-style-section", children: STYLE_SIZES.map((size) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
186
|
+
"button",
|
|
187
|
+
{
|
|
188
|
+
type: "button",
|
|
189
|
+
className: "tsdraw-style-row",
|
|
190
|
+
"data-active": drawSize === size ? "true" : void 0,
|
|
191
|
+
"aria-label": `Thickness ${size}`,
|
|
192
|
+
title: size,
|
|
193
|
+
onClick: () => onSizeSelect(size),
|
|
194
|
+
children: /* @__PURE__ */ jsxRuntime.jsx("span", { className: "tsdraw-style-preview", children: /* @__PURE__ */ jsxRuntime.jsx("span", { className: `tsdraw-style-size tsdraw-style-size--${size}` }) })
|
|
195
|
+
},
|
|
196
|
+
size
|
|
197
|
+
)) }, part);
|
|
198
|
+
}
|
|
199
|
+
const customPart = customPartMap.get(part);
|
|
200
|
+
if (!customPart) return null;
|
|
201
|
+
return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "tsdraw-style-section tsdraw-style-section--custom", children: customPart.render(context) }, part);
|
|
202
|
+
}) });
|
|
163
203
|
}
|
|
164
204
|
function ToolOverlay({
|
|
165
205
|
visible,
|
|
@@ -199,6 +239,8 @@ function ToolOverlay({
|
|
|
199
239
|
function getDefaultToolbarIcon(toolId, isActive) {
|
|
200
240
|
if (toolId === "select") return /* @__PURE__ */ jsxRuntime.jsx(iconsReact.IconPointer, { size: 16, stroke: 1.8, fill: isActive ? "currentColor" : "none" });
|
|
201
241
|
if (toolId === "pen") return /* @__PURE__ */ jsxRuntime.jsx(iconsReact.IconPencil, { size: 16, stroke: 1.8, fill: isActive ? "currentColor" : "none" });
|
|
242
|
+
if (toolId === "square") return /* @__PURE__ */ jsxRuntime.jsx(iconsReact.IconSquare, { size: 16, stroke: 1.8, fill: isActive ? "currentColor" : "none" });
|
|
243
|
+
if (toolId === "circle") return /* @__PURE__ */ jsxRuntime.jsx(iconsReact.IconCircle, { size: 16, stroke: 1.8, fill: isActive ? "currentColor" : "none" });
|
|
202
244
|
if (toolId === "eraser") return /* @__PURE__ */ jsxRuntime.jsx(iconsReact.IconEraser, { size: 16, stroke: 1.8, fill: isActive ? "currentColor" : "none" });
|
|
203
245
|
if (toolId === "hand") return /* @__PURE__ */ jsxRuntime.jsx(iconsReact.IconHandStop, { size: 16, stroke: isActive ? 1 : 1.8, fill: isActive ? "currentColor" : "none", style: isActive ? { stroke: "#000000" } : void 0 });
|
|
204
246
|
return null;
|
|
@@ -365,11 +407,15 @@ function createSessionId() {
|
|
|
365
407
|
}
|
|
366
408
|
function getOrCreateSessionId() {
|
|
367
409
|
if (typeof window === "undefined") return createSessionId();
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
410
|
+
try {
|
|
411
|
+
const existing = window.sessionStorage.getItem(SESSION_STORAGE_KEY);
|
|
412
|
+
if (existing) return existing;
|
|
413
|
+
const newId = createSessionId();
|
|
414
|
+
window.sessionStorage.setItem(SESSION_STORAGE_KEY, newId);
|
|
415
|
+
return newId;
|
|
416
|
+
} catch {
|
|
417
|
+
return createSessionId();
|
|
418
|
+
}
|
|
373
419
|
}
|
|
374
420
|
|
|
375
421
|
// src/canvas/useTsdrawCanvasController.ts
|
|
@@ -386,8 +432,7 @@ function resolveDrawColor(colorStyle, theme) {
|
|
|
386
432
|
return core.resolveThemeColor(colorStyle, theme);
|
|
387
433
|
}
|
|
388
434
|
function useTsdrawCanvasController(options = {}) {
|
|
389
|
-
const
|
|
390
|
-
const stylePanelToolIdsRef = react.useRef(stylePanelToolIds);
|
|
435
|
+
const onMountRef = react.useRef(options.onMount);
|
|
391
436
|
const containerRef = react.useRef(null);
|
|
392
437
|
const canvasRef = react.useRef(null);
|
|
393
438
|
const editorRef = react.useRef(null);
|
|
@@ -421,6 +466,7 @@ function useTsdrawCanvasController(options = {}) {
|
|
|
421
466
|
const [currentTool, setCurrentToolState] = react.useState(options.initialTool ?? "pen");
|
|
422
467
|
const [drawColor, setDrawColor] = react.useState("black");
|
|
423
468
|
const [drawDash, setDrawDash] = react.useState("draw");
|
|
469
|
+
const [drawFill, setDrawFill] = react.useState("none");
|
|
424
470
|
const [drawSize, setDrawSize] = react.useState("m");
|
|
425
471
|
const [selectedShapeIds, setSelectedShapeIds] = react.useState([]);
|
|
426
472
|
const [selectionBrush, setSelectionBrush] = react.useState(null);
|
|
@@ -438,8 +484,8 @@ function useTsdrawCanvasController(options = {}) {
|
|
|
438
484
|
currentToolRef.current = currentTool;
|
|
439
485
|
}, [currentTool]);
|
|
440
486
|
react.useEffect(() => {
|
|
441
|
-
|
|
442
|
-
}, [
|
|
487
|
+
onMountRef.current = options.onMount;
|
|
488
|
+
}, [options.onMount]);
|
|
443
489
|
react.useEffect(() => {
|
|
444
490
|
selectedShapeIdsRef.current = selectedShapeIds;
|
|
445
491
|
}, [selectedShapeIds]);
|
|
@@ -448,7 +494,7 @@ function useTsdrawCanvasController(options = {}) {
|
|
|
448
494
|
}, [selectionRotationDeg]);
|
|
449
495
|
react.useEffect(() => {
|
|
450
496
|
schedulePersistRef.current?.();
|
|
451
|
-
}, [selectedShapeIds, currentTool, drawColor, drawDash, drawSize]);
|
|
497
|
+
}, [selectedShapeIds, currentTool, drawColor, drawDash, drawFill, drawSize]);
|
|
452
498
|
const render = react.useCallback(() => {
|
|
453
499
|
const canvas = canvasRef.current;
|
|
454
500
|
const editor = editorRef.current;
|
|
@@ -584,6 +630,7 @@ function useTsdrawCanvasController(options = {}) {
|
|
|
584
630
|
const initialStyle = editor.getCurrentDrawStyle();
|
|
585
631
|
setDrawColor(initialStyle.color);
|
|
586
632
|
setDrawDash(initialStyle.dash);
|
|
633
|
+
setDrawFill(initialStyle.fill);
|
|
587
634
|
setDrawSize(initialStyle.size);
|
|
588
635
|
const resize = () => {
|
|
589
636
|
const dpr = window.devicePixelRatio ?? 1;
|
|
@@ -665,6 +712,7 @@ function useTsdrawCanvasController(options = {}) {
|
|
|
665
712
|
const nextDrawStyle = editor.getCurrentDrawStyle();
|
|
666
713
|
setDrawColor(nextDrawStyle.color);
|
|
667
714
|
setDrawDash(nextDrawStyle.dash);
|
|
715
|
+
setDrawFill(nextDrawStyle.fill);
|
|
668
716
|
setDrawSize(nextDrawStyle.size);
|
|
669
717
|
setSelectionRotationDeg(0);
|
|
670
718
|
render();
|
|
@@ -713,7 +761,7 @@ function useTsdrawCanvasController(options = {}) {
|
|
|
713
761
|
additive: first.shiftKey,
|
|
714
762
|
initialSelection: [...selectedShapeIdsRef.current]
|
|
715
763
|
};
|
|
716
|
-
setSelectionBrush({
|
|
764
|
+
setSelectionBrush(toScreenRect(editor, { minX: x, minY: y, maxX: x, maxY: y }));
|
|
717
765
|
if (!e.shiftKey) {
|
|
718
766
|
setSelectedShapeIds([]);
|
|
719
767
|
selectedShapeIdsRef.current = [];
|
|
@@ -870,6 +918,35 @@ function useTsdrawCanvasController(options = {}) {
|
|
|
870
918
|
}
|
|
871
919
|
editor.endHistoryEntry();
|
|
872
920
|
};
|
|
921
|
+
const handlePointerCancel = () => {
|
|
922
|
+
if (!isPointerActiveRef.current) return;
|
|
923
|
+
isPointerActiveRef.current = false;
|
|
924
|
+
lastPointerClientRef.current = null;
|
|
925
|
+
editor.input.pointerUp();
|
|
926
|
+
if (currentToolRef.current === "select") {
|
|
927
|
+
const drag = selectDragRef.current;
|
|
928
|
+
if (drag.mode === "rotate") setIsRotatingSelection(false);
|
|
929
|
+
if (drag.mode === "resize") setIsResizingSelection(false);
|
|
930
|
+
if (drag.mode === "move") setIsMovingSelection(false);
|
|
931
|
+
if (drag.mode === "marquee") setSelectionBrush(null);
|
|
932
|
+
if (drag.mode !== "none") {
|
|
933
|
+
selectDragRef.current.mode = "none";
|
|
934
|
+
render();
|
|
935
|
+
refreshSelectionBounds(editor);
|
|
936
|
+
}
|
|
937
|
+
editor.endHistoryEntry();
|
|
938
|
+
} else {
|
|
939
|
+
editor.tools.pointerUp();
|
|
940
|
+
render();
|
|
941
|
+
refreshSelectionBounds(editor);
|
|
942
|
+
editor.endHistoryEntry();
|
|
943
|
+
}
|
|
944
|
+
if (pendingRemoteDocumentRef.current) {
|
|
945
|
+
const pending = pendingRemoteDocumentRef.current;
|
|
946
|
+
pendingRemoteDocumentRef.current = null;
|
|
947
|
+
applyRemoteDocumentSnapshot(pending);
|
|
948
|
+
}
|
|
949
|
+
};
|
|
873
950
|
const handleKeyDown = (e) => {
|
|
874
951
|
const isMetaPressed = e.metaKey || e.ctrlKey;
|
|
875
952
|
const loweredKey = e.key.toLowerCase();
|
|
@@ -916,22 +993,42 @@ function useTsdrawCanvasController(options = {}) {
|
|
|
916
993
|
}
|
|
917
994
|
editor.loadHistorySnapshot(loaded.history);
|
|
918
995
|
syncHistoryState();
|
|
996
|
+
if (disposed) return;
|
|
919
997
|
persistenceActive = true;
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
if (isPointerActiveRef.current) {
|
|
929
|
-
pendingRemoteDocumentRef.current = nextDocument;
|
|
998
|
+
if (typeof BroadcastChannel !== "undefined") {
|
|
999
|
+
persistenceChannel = new BroadcastChannel(`tsdraw:persistence:${persistenceKey}`);
|
|
1000
|
+
let isLoadingRemote = false;
|
|
1001
|
+
let pendingRemoteLoad = false;
|
|
1002
|
+
persistenceChannel.onmessage = () => {
|
|
1003
|
+
if (disposed) return;
|
|
1004
|
+
if (isLoadingRemote) {
|
|
1005
|
+
pendingRemoteLoad = true;
|
|
930
1006
|
return;
|
|
931
1007
|
}
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
1008
|
+
isLoadingRemote = true;
|
|
1009
|
+
const processLoad = async () => {
|
|
1010
|
+
try {
|
|
1011
|
+
do {
|
|
1012
|
+
pendingRemoteLoad = false;
|
|
1013
|
+
if (!persistenceDb || disposed) return;
|
|
1014
|
+
const nextLoaded = await persistenceDb.load(sessionId);
|
|
1015
|
+
if (disposed) return;
|
|
1016
|
+
if (nextLoaded.records.length > 0) {
|
|
1017
|
+
const nextDocument = { records: nextLoaded.records };
|
|
1018
|
+
if (isPointerActiveRef.current) {
|
|
1019
|
+
pendingRemoteDocumentRef.current = nextDocument;
|
|
1020
|
+
return;
|
|
1021
|
+
}
|
|
1022
|
+
applyRemoteDocumentSnapshot(nextDocument);
|
|
1023
|
+
}
|
|
1024
|
+
} while (pendingRemoteLoad && !disposed);
|
|
1025
|
+
} finally {
|
|
1026
|
+
isLoadingRemote = false;
|
|
1027
|
+
}
|
|
1028
|
+
};
|
|
1029
|
+
void processLoad();
|
|
1030
|
+
};
|
|
1031
|
+
}
|
|
935
1032
|
} finally {
|
|
936
1033
|
if (!disposed) {
|
|
937
1034
|
setIsPersistenceReady(true);
|
|
@@ -953,12 +1050,13 @@ function useTsdrawCanvasController(options = {}) {
|
|
|
953
1050
|
canvas.addEventListener("pointerdown", handlePointerDown);
|
|
954
1051
|
window.addEventListener("pointermove", handlePointerMove);
|
|
955
1052
|
window.addEventListener("pointerup", handlePointerUp);
|
|
1053
|
+
window.addEventListener("pointercancel", handlePointerCancel);
|
|
956
1054
|
window.addEventListener("keydown", handleKeyDown);
|
|
957
1055
|
window.addEventListener("keyup", handleKeyUp);
|
|
958
1056
|
void initializePersistence().catch((error) => {
|
|
959
1057
|
console.error("failed to initialize tsdraw persistence", error);
|
|
960
1058
|
});
|
|
961
|
-
disposeMount =
|
|
1059
|
+
disposeMount = onMountRef.current?.({
|
|
962
1060
|
editor,
|
|
963
1061
|
container,
|
|
964
1062
|
canvas,
|
|
@@ -993,6 +1091,7 @@ function useTsdrawCanvasController(options = {}) {
|
|
|
993
1091
|
editor.setCurrentDrawStyle(partial);
|
|
994
1092
|
if (partial.color) setDrawColor(partial.color);
|
|
995
1093
|
if (partial.dash) setDrawDash(partial.dash);
|
|
1094
|
+
if (partial.fill) setDrawFill(partial.fill);
|
|
996
1095
|
if (partial.size) setDrawSize(partial.size);
|
|
997
1096
|
render();
|
|
998
1097
|
}
|
|
@@ -1007,6 +1106,7 @@ function useTsdrawCanvasController(options = {}) {
|
|
|
1007
1106
|
canvas.removeEventListener("pointerdown", handlePointerDown);
|
|
1008
1107
|
window.removeEventListener("pointermove", handlePointerMove);
|
|
1009
1108
|
window.removeEventListener("pointerup", handlePointerUp);
|
|
1109
|
+
window.removeEventListener("pointercancel", handlePointerCancel);
|
|
1010
1110
|
window.removeEventListener("keydown", handleKeyDown);
|
|
1011
1111
|
window.removeEventListener("keyup", handleKeyUp);
|
|
1012
1112
|
isPointerActiveRef.current = false;
|
|
@@ -1018,7 +1118,6 @@ function useTsdrawCanvasController(options = {}) {
|
|
|
1018
1118
|
}, [
|
|
1019
1119
|
getPagePointFromClient,
|
|
1020
1120
|
options.initialTool,
|
|
1021
|
-
options.onMount,
|
|
1022
1121
|
options.persistenceKey,
|
|
1023
1122
|
options.toolDefinitions,
|
|
1024
1123
|
refreshSelectionBounds,
|
|
@@ -1050,6 +1149,7 @@ function useTsdrawCanvasController(options = {}) {
|
|
|
1050
1149
|
editor.setCurrentDrawStyle(partial);
|
|
1051
1150
|
if (partial.color) setDrawColor(partial.color);
|
|
1052
1151
|
if (partial.dash) setDrawDash(partial.dash);
|
|
1152
|
+
if (partial.fill) setDrawFill(partial.fill);
|
|
1053
1153
|
if (partial.size) setDrawSize(partial.size);
|
|
1054
1154
|
render();
|
|
1055
1155
|
},
|
|
@@ -1117,6 +1217,7 @@ function useTsdrawCanvasController(options = {}) {
|
|
|
1117
1217
|
currentTool,
|
|
1118
1218
|
drawColor,
|
|
1119
1219
|
drawDash,
|
|
1220
|
+
drawFill,
|
|
1120
1221
|
drawSize,
|
|
1121
1222
|
selectedShapeIds,
|
|
1122
1223
|
selectionBrush,
|
|
@@ -1126,7 +1227,6 @@ function useTsdrawCanvasController(options = {}) {
|
|
|
1126
1227
|
cursorContext,
|
|
1127
1228
|
toolOverlay,
|
|
1128
1229
|
isPersistenceReady,
|
|
1129
|
-
showStylePanel: stylePanelToolIdsRef.current.includes(currentTool),
|
|
1130
1230
|
canUndo,
|
|
1131
1231
|
canRedo,
|
|
1132
1232
|
undo,
|
|
@@ -1137,10 +1237,21 @@ function useTsdrawCanvasController(options = {}) {
|
|
|
1137
1237
|
handleRotatePointerDown
|
|
1138
1238
|
};
|
|
1139
1239
|
}
|
|
1140
|
-
var DEFAULT_TOOLBAR_PARTS = [["undo", "redo"], ["select", "hand", "pen", "eraser"]];
|
|
1240
|
+
var DEFAULT_TOOLBAR_PARTS = [["undo", "redo"], ["select", "hand", "pen", "eraser", "square", "circle"]];
|
|
1241
|
+
var EMPTY_CUSTOM_TOOLS = [];
|
|
1242
|
+
var EMPTY_CUSTOM_ELEMENTS = [];
|
|
1243
|
+
var EMPTY_STYLE_PANEL_PARTS = [];
|
|
1244
|
+
var EMPTY_STYLE_PANEL_CUSTOM_PARTS = [];
|
|
1245
|
+
var DEFAULT_STYLE_PANEL_PARTS_BY_TOOL = {
|
|
1246
|
+
pen: ["colors", "dashes", "sizes"],
|
|
1247
|
+
square: ["colors", "dashes", "fills", "sizes"],
|
|
1248
|
+
circle: ["colors", "dashes", "fills", "sizes"]
|
|
1249
|
+
};
|
|
1141
1250
|
var DEFAULT_TOOL_LABELS = {
|
|
1142
1251
|
select: "Select",
|
|
1143
1252
|
pen: "Pen",
|
|
1253
|
+
square: "Rectangle",
|
|
1254
|
+
circle: "Ellipse",
|
|
1144
1255
|
eraser: "Eraser",
|
|
1145
1256
|
hand: "Hand"
|
|
1146
1257
|
};
|
|
@@ -1183,14 +1294,14 @@ function resolvePlacementStyle(placement, fallbackAnchor, fallbackOffsetX, fallb
|
|
|
1183
1294
|
if (offsetY) transforms.push(`translateY(${offsetY}px)`);
|
|
1184
1295
|
}
|
|
1185
1296
|
if (transforms.length > 0) result.transform = transforms.join(" ");
|
|
1186
|
-
return { ...result, ...placement
|
|
1297
|
+
return placement?.style ? { ...result, ...placement.style } : result;
|
|
1187
1298
|
}
|
|
1188
1299
|
function Tsdraw(props) {
|
|
1189
1300
|
const [systemTheme, setSystemTheme] = react.useState(() => {
|
|
1190
1301
|
if (typeof window === "undefined") return "light";
|
|
1191
1302
|
return window.matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light";
|
|
1192
1303
|
});
|
|
1193
|
-
const customTools = props.customTools ??
|
|
1304
|
+
const customTools = props.customTools ?? EMPTY_CUSTOM_TOOLS;
|
|
1194
1305
|
const toolbarPartIds = props.uiOptions?.toolbar?.parts ?? DEFAULT_TOOLBAR_PARTS;
|
|
1195
1306
|
const customToolMap = react.useMemo(
|
|
1196
1307
|
() => new Map(customTools.map((customTool) => [customTool.id, customTool])),
|
|
@@ -1212,21 +1323,6 @@ function Tsdraw(props) {
|
|
|
1212
1323
|
() => customTools.filter((customTool) => toolbarToolIds.has(customTool.id)).map((customTool) => customTool.definition),
|
|
1213
1324
|
[customTools, toolbarToolIds]
|
|
1214
1325
|
);
|
|
1215
|
-
const stylePanelToolIds = react.useMemo(
|
|
1216
|
-
() => {
|
|
1217
|
-
const nextToolIds = /* @__PURE__ */ new Set();
|
|
1218
|
-
if (toolbarToolIds.has("pen")) {
|
|
1219
|
-
nextToolIds.add("pen");
|
|
1220
|
-
}
|
|
1221
|
-
for (const customTool of customTools) {
|
|
1222
|
-
if ((customTool.showStylePanel ?? false) && toolbarToolIds.has(customTool.id)) {
|
|
1223
|
-
nextToolIds.add(customTool.id);
|
|
1224
|
-
}
|
|
1225
|
-
}
|
|
1226
|
-
return [...nextToolIds];
|
|
1227
|
-
},
|
|
1228
|
-
[customTools, toolbarToolIds]
|
|
1229
|
-
);
|
|
1230
1326
|
const firstToolbarTool = react.useMemo(() => {
|
|
1231
1327
|
for (const toolbarPart of toolbarPartIds) {
|
|
1232
1328
|
for (const item of toolbarPart) {
|
|
@@ -1255,6 +1351,7 @@ function Tsdraw(props) {
|
|
|
1255
1351
|
currentTool,
|
|
1256
1352
|
drawColor,
|
|
1257
1353
|
drawDash,
|
|
1354
|
+
drawFill,
|
|
1258
1355
|
drawSize,
|
|
1259
1356
|
selectedShapeIds,
|
|
1260
1357
|
selectionBrush,
|
|
@@ -1264,7 +1361,6 @@ function Tsdraw(props) {
|
|
|
1264
1361
|
cursorContext,
|
|
1265
1362
|
toolOverlay,
|
|
1266
1363
|
isPersistenceReady,
|
|
1267
|
-
showStylePanel,
|
|
1268
1364
|
canUndo,
|
|
1269
1365
|
canRedo,
|
|
1270
1366
|
undo,
|
|
@@ -1278,11 +1374,12 @@ function Tsdraw(props) {
|
|
|
1278
1374
|
initialTool,
|
|
1279
1375
|
theme: resolvedTheme,
|
|
1280
1376
|
persistenceKey: props.persistenceKey,
|
|
1281
|
-
stylePanelToolIds,
|
|
1282
1377
|
onMount: props.onMount
|
|
1283
1378
|
});
|
|
1284
1379
|
const toolbarPlacementStyle = resolvePlacementStyle(props.uiOptions?.toolbar?.placement, "bottom-center", 0, 14);
|
|
1285
1380
|
const stylePanelPlacementStyle = resolvePlacementStyle(props.uiOptions?.stylePanel?.placement, "top-right", 8, 8);
|
|
1381
|
+
const isToolbarHidden = props.uiOptions?.toolbar?.hide === true;
|
|
1382
|
+
const isStylePanelHidden = props.uiOptions?.stylePanel?.hide === true;
|
|
1286
1383
|
const canvasCursor = props.uiOptions?.cursor?.getCursor?.(cursorContext) ?? defaultCanvasCursor;
|
|
1287
1384
|
const defaultToolOverlay = /* @__PURE__ */ jsxRuntime.jsx(
|
|
1288
1385
|
ToolOverlay,
|
|
@@ -1297,7 +1394,31 @@ function Tsdraw(props) {
|
|
|
1297
1394
|
}
|
|
1298
1395
|
);
|
|
1299
1396
|
const overlayNode = props.uiOptions?.overlays?.renderToolOverlay?.({ defaultOverlay: defaultToolOverlay, overlayState: toolOverlay, currentTool }) ?? defaultToolOverlay;
|
|
1300
|
-
const customElements = props.uiOptions?.customElements ??
|
|
1397
|
+
const customElements = props.uiOptions?.customElements ?? EMPTY_CUSTOM_ELEMENTS;
|
|
1398
|
+
const onColorSelect = react.useCallback((color) => {
|
|
1399
|
+
applyDrawStyle({ color });
|
|
1400
|
+
}, [applyDrawStyle]);
|
|
1401
|
+
const onDashSelect = react.useCallback((dash) => {
|
|
1402
|
+
applyDrawStyle({ dash });
|
|
1403
|
+
}, [applyDrawStyle]);
|
|
1404
|
+
const onFillSelect = react.useCallback((fill) => {
|
|
1405
|
+
applyDrawStyle({ fill });
|
|
1406
|
+
}, [applyDrawStyle]);
|
|
1407
|
+
const onSizeSelect = react.useCallback((size) => {
|
|
1408
|
+
applyDrawStyle({ size });
|
|
1409
|
+
}, [applyDrawStyle]);
|
|
1410
|
+
const activeCustomTool = customToolMap.get(currentTool);
|
|
1411
|
+
const stylePanelParts = react.useMemo(
|
|
1412
|
+
() => {
|
|
1413
|
+
const fromCustomTool = activeCustomTool?.stylePanel?.parts;
|
|
1414
|
+
if (fromCustomTool && fromCustomTool.length > 0) return fromCustomTool;
|
|
1415
|
+
if (activeCustomTool?.stylePanel?.customParts && activeCustomTool.stylePanel.customParts.length > 0) return activeCustomTool.stylePanel.customParts.map((customPart) => customPart.id);
|
|
1416
|
+
if (currentTool in DEFAULT_STYLE_PANEL_PARTS_BY_TOOL) return DEFAULT_STYLE_PANEL_PARTS_BY_TOOL[currentTool] ?? EMPTY_STYLE_PANEL_PARTS;
|
|
1417
|
+
return EMPTY_STYLE_PANEL_PARTS;
|
|
1418
|
+
},
|
|
1419
|
+
[activeCustomTool, currentTool]
|
|
1420
|
+
);
|
|
1421
|
+
const stylePanelCustomParts = activeCustomTool?.stylePanel?.customParts ?? EMPTY_STYLE_PANEL_CUSTOM_PARTS;
|
|
1301
1422
|
const toolbarParts = react.useMemo(
|
|
1302
1423
|
() => toolbarPartIds.map((toolbarPart, partIndex) => {
|
|
1303
1424
|
const items = toolbarPart.map((item) => {
|
|
@@ -1386,15 +1507,19 @@ function Tsdraw(props) {
|
|
|
1386
1507
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1387
1508
|
StylePanel,
|
|
1388
1509
|
{
|
|
1389
|
-
visible: isPersistenceReady &&
|
|
1510
|
+
visible: !isStylePanelHidden && isPersistenceReady && stylePanelParts.length > 0,
|
|
1511
|
+
parts: stylePanelParts,
|
|
1512
|
+
customParts: stylePanelCustomParts,
|
|
1390
1513
|
style: stylePanelPlacementStyle,
|
|
1391
1514
|
theme: resolvedTheme,
|
|
1392
1515
|
drawColor,
|
|
1393
1516
|
drawDash,
|
|
1517
|
+
drawFill,
|
|
1394
1518
|
drawSize,
|
|
1395
|
-
onColorSelect
|
|
1396
|
-
onDashSelect
|
|
1397
|
-
|
|
1519
|
+
onColorSelect,
|
|
1520
|
+
onDashSelect,
|
|
1521
|
+
onFillSelect,
|
|
1522
|
+
onSizeSelect
|
|
1398
1523
|
}
|
|
1399
1524
|
),
|
|
1400
1525
|
customElements.map((customElement) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
@@ -1410,7 +1535,7 @@ function Tsdraw(props) {
|
|
|
1410
1535
|
},
|
|
1411
1536
|
customElement.id
|
|
1412
1537
|
)),
|
|
1413
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1538
|
+
!isToolbarHidden ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
1414
1539
|
Toolbar,
|
|
1415
1540
|
{
|
|
1416
1541
|
parts: toolbarParts,
|
|
@@ -1419,7 +1544,7 @@ function Tsdraw(props) {
|
|
|
1419
1544
|
onToolChange: setTool,
|
|
1420
1545
|
disabled: !isPersistenceReady
|
|
1421
1546
|
}
|
|
1422
|
-
)
|
|
1547
|
+
) : null
|
|
1423
1548
|
]
|
|
1424
1549
|
}
|
|
1425
1550
|
);
|