@tsdraw/react 0.9.3 → 0.9.5
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 +144 -13
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +4 -2
- package/dist/index.d.ts +4 -2
- package/dist/index.js +145 -14
- package/dist/index.js.map +1 -1
- package/dist/tsdraw.css +32 -10
- package/package.json +3 -3
package/dist/index.d.cts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
2
|
import { ReactNode, CSSProperties } from 'react';
|
|
3
|
-
import { ZoomRange, ToolId, ColorStyle, DashStyle, FillStyle, SizeStyle, Editor, ToolDefinition, TsdrawBackgroundOptions, TsdrawEditorSnapshot, TsdrawDocumentSnapshot, Viewport } from '@tsdraw/core';
|
|
4
|
-
export { TsdrawBackgroundCustom, TsdrawBackgroundOptions, TsdrawBackgroundPreset, TsdrawBackgroundType } from '@tsdraw/core';
|
|
3
|
+
import { ZoomRange, ToolId, ColorStyle, DashStyle, FillStyle, SizeStyle, Editor, ToolDefinition, AutoShapeOptions, TsdrawBackgroundOptions, TsdrawEditorSnapshot, TsdrawDocumentSnapshot, Viewport } from '@tsdraw/core';
|
|
4
|
+
export { AutoShapeKind, AutoShapeOptions, AutoShapeThresholds, TsdrawBackgroundCustom, TsdrawBackgroundOptions, TsdrawBackgroundPreset, TsdrawBackgroundType } from '@tsdraw/core';
|
|
5
5
|
|
|
6
6
|
interface TsdrawCameraOptions {
|
|
7
7
|
panSpeed?: number;
|
|
@@ -60,6 +60,7 @@ interface TsdrawCursorContext {
|
|
|
60
60
|
isMovingSelection: boolean;
|
|
61
61
|
isResizingSelection: boolean;
|
|
62
62
|
isRotatingSelection: boolean;
|
|
63
|
+
isDraggingVertex: boolean;
|
|
63
64
|
}
|
|
64
65
|
interface TsdrawToolOverlayState {
|
|
65
66
|
visible: boolean;
|
|
@@ -156,6 +157,7 @@ interface TsdrawProps {
|
|
|
156
157
|
touchOptions?: TsdrawTouchOptions;
|
|
157
158
|
keyboardShortcuts?: TsdrawKeyboardShortcutOptions;
|
|
158
159
|
penOptions?: TsdrawPenOptions;
|
|
160
|
+
autoShape?: boolean | AutoShapeOptions;
|
|
159
161
|
background?: TsdrawBackgroundOptions;
|
|
160
162
|
readOnly?: boolean;
|
|
161
163
|
autoFocus?: boolean;
|
package/dist/index.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
2
|
import { ReactNode, CSSProperties } from 'react';
|
|
3
|
-
import { ZoomRange, ToolId, ColorStyle, DashStyle, FillStyle, SizeStyle, Editor, ToolDefinition, TsdrawBackgroundOptions, TsdrawEditorSnapshot, TsdrawDocumentSnapshot, Viewport } from '@tsdraw/core';
|
|
4
|
-
export { TsdrawBackgroundCustom, TsdrawBackgroundOptions, TsdrawBackgroundPreset, TsdrawBackgroundType } from '@tsdraw/core';
|
|
3
|
+
import { ZoomRange, ToolId, ColorStyle, DashStyle, FillStyle, SizeStyle, Editor, ToolDefinition, AutoShapeOptions, TsdrawBackgroundOptions, TsdrawEditorSnapshot, TsdrawDocumentSnapshot, Viewport } from '@tsdraw/core';
|
|
4
|
+
export { AutoShapeKind, AutoShapeOptions, AutoShapeThresholds, TsdrawBackgroundCustom, TsdrawBackgroundOptions, TsdrawBackgroundPreset, TsdrawBackgroundType } from '@tsdraw/core';
|
|
5
5
|
|
|
6
6
|
interface TsdrawCameraOptions {
|
|
7
7
|
panSpeed?: number;
|
|
@@ -60,6 +60,7 @@ interface TsdrawCursorContext {
|
|
|
60
60
|
isMovingSelection: boolean;
|
|
61
61
|
isResizingSelection: boolean;
|
|
62
62
|
isRotatingSelection: boolean;
|
|
63
|
+
isDraggingVertex: boolean;
|
|
63
64
|
}
|
|
64
65
|
interface TsdrawToolOverlayState {
|
|
65
66
|
visible: boolean;
|
|
@@ -156,6 +157,7 @@ interface TsdrawProps {
|
|
|
156
157
|
touchOptions?: TsdrawTouchOptions;
|
|
157
158
|
keyboardShortcuts?: TsdrawKeyboardShortcutOptions;
|
|
158
159
|
penOptions?: TsdrawPenOptions;
|
|
160
|
+
autoShape?: boolean | AutoShapeOptions;
|
|
159
161
|
background?: TsdrawBackgroundOptions;
|
|
160
162
|
readOnly?: boolean;
|
|
161
163
|
autoFocus?: boolean;
|
package/dist/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { useState, useMemo, useEffect, useCallback, useRef, useLayoutEffect } from 'react';
|
|
2
2
|
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
|
|
3
|
-
import { DEFAULT_COLORS, renderCanvasBackground, getSelectionBoundsPage, buildTransformSnapshots, Editor, STROKE_WIDTHS, ERASER_MARGIN, resolveThemeColor,
|
|
3
|
+
import { DEFAULT_COLORS, renderCanvasBackground, getSelectionBoundsPage, buildTransformSnapshots, Editor, getVertexHandlePagePositions, pageToScreen, STROKE_WIDTHS, ERASER_MARGIN, resolveThemeColor, findVertexHit, getTopShapeAtPoint, buildStartPositions, applyRotation, applyResize, applyMove, applyVertexDrag, normalizeSelectionBounds, getShapesInBounds, HandDraggingState, startCameraSlide, isSelectTool, beginCameraPan, moveCameraPan } from '@tsdraw/core';
|
|
4
4
|
import { IconPointer, IconPencil, IconSquare, IconCircle, IconEraser, IconHandStop, IconArrowBackUp, IconArrowForwardUp } from '@tabler/icons-react';
|
|
5
5
|
|
|
6
6
|
// src/components/TsdrawCanvas.tsx
|
|
@@ -8,6 +8,7 @@ function SelectionOverlay({
|
|
|
8
8
|
selectionBrush,
|
|
9
9
|
selectionBounds,
|
|
10
10
|
selectionRotationDeg,
|
|
11
|
+
vertexHandleScreenPositions,
|
|
11
12
|
currentTool,
|
|
12
13
|
selectedCount,
|
|
13
14
|
onRotatePointerDown,
|
|
@@ -35,7 +36,8 @@ function SelectionOverlay({
|
|
|
35
36
|
top: selectionBounds.top,
|
|
36
37
|
width: selectionBounds.width,
|
|
37
38
|
height: selectionBounds.height,
|
|
38
|
-
transform: `rotate(${selectionRotationDeg}deg)
|
|
39
|
+
transform: `rotate(${selectionRotationDeg}deg)`,
|
|
40
|
+
transformOrigin: "center center"
|
|
39
41
|
},
|
|
40
42
|
children: [
|
|
41
43
|
/* @__PURE__ */ jsx("div", { className: "tsdraw-selection-bounds" }),
|
|
@@ -93,7 +95,15 @@ function SelectionOverlay({
|
|
|
93
95
|
] })
|
|
94
96
|
]
|
|
95
97
|
}
|
|
96
|
-
)
|
|
98
|
+
),
|
|
99
|
+
currentTool === "select" && vertexHandleScreenPositions.map((pos, index) => /* @__PURE__ */ jsx(
|
|
100
|
+
"div",
|
|
101
|
+
{
|
|
102
|
+
className: "tsdraw-vertex-handle",
|
|
103
|
+
style: { position: "absolute", left: pos.left, top: pos.top, transform: "translate(-50%, -50%)", pointerEvents: "none", zIndex: 95 }
|
|
104
|
+
},
|
|
105
|
+
`vertex-${index.toString()}`
|
|
106
|
+
))
|
|
97
107
|
] });
|
|
98
108
|
}
|
|
99
109
|
function parseAnchor(anchor) {
|
|
@@ -452,6 +462,7 @@ function getCanvasCursor(currentTool, state) {
|
|
|
452
462
|
if (state.isRotatingSelection) return "grabbing";
|
|
453
463
|
if (state.isResizingSelection) return "nwse-resize";
|
|
454
464
|
if (state.isMovingSelection) return "grabbing";
|
|
465
|
+
if (state.isDraggingVertex) return "grabbing";
|
|
455
466
|
if (state.isHoveringSelectionBounds) return "move";
|
|
456
467
|
return "default";
|
|
457
468
|
}
|
|
@@ -894,6 +905,16 @@ function toScreenRect(editor, bounds) {
|
|
|
894
905
|
height: Math.max(0, maxY - minY)
|
|
895
906
|
};
|
|
896
907
|
}
|
|
908
|
+
function selectionCenteredOnRotation(editor, startBoundsPage, centerPage) {
|
|
909
|
+
const startScreen = toScreenRect(editor, startBoundsPage);
|
|
910
|
+
const centerScreen = pageToScreen(editor.viewport, centerPage.x, centerPage.y);
|
|
911
|
+
return {
|
|
912
|
+
left: centerScreen.x - startScreen.width / 2,
|
|
913
|
+
top: centerScreen.y - startScreen.height / 2,
|
|
914
|
+
width: startScreen.width,
|
|
915
|
+
height: startScreen.height
|
|
916
|
+
};
|
|
917
|
+
}
|
|
897
918
|
function resolveDrawColor(colorStyle, theme) {
|
|
898
919
|
return resolveThemeColor(colorStyle, theme);
|
|
899
920
|
}
|
|
@@ -909,6 +930,11 @@ function getHandlePagePoint(bounds, handle) {
|
|
|
909
930
|
return { x: bounds.maxX, y: bounds.maxY };
|
|
910
931
|
}
|
|
911
932
|
}
|
|
933
|
+
function resolveAutoShapeOption(input) {
|
|
934
|
+
if (input === false) return { enabled: false };
|
|
935
|
+
if (input === true || input == null) return { enabled: true };
|
|
936
|
+
return { enabled: input.enabled ?? true, ...input };
|
|
937
|
+
}
|
|
912
938
|
var ZOOM_WHEEL_CAP = 10;
|
|
913
939
|
var VIEW_ONLY_TOOLS = /* @__PURE__ */ new Set(["select", "hand"]);
|
|
914
940
|
function useTsdrawCanvasController(options = {}) {
|
|
@@ -920,6 +946,7 @@ function useTsdrawCanvasController(options = {}) {
|
|
|
920
946
|
const touchOptionsRef = useRef(options.touchOptions);
|
|
921
947
|
const keyboardShortcutsRef = useRef(options.keyboardShortcuts);
|
|
922
948
|
const penOptionsRef = useRef(options.penOptions);
|
|
949
|
+
const autoShapeRef = useRef(options.autoShape);
|
|
923
950
|
const backgroundRef = useRef(options.background);
|
|
924
951
|
const readOnlyRef = useRef(options.readOnly ?? false);
|
|
925
952
|
const containerRef = useRef(null);
|
|
@@ -948,7 +975,8 @@ function useTsdrawCanvasController(options = {}) {
|
|
|
948
975
|
center: null,
|
|
949
976
|
startAngle: 0,
|
|
950
977
|
startSelectionRotationDeg: 0,
|
|
951
|
-
startShapes: /* @__PURE__ */ new Map()
|
|
978
|
+
startShapes: /* @__PURE__ */ new Map(),
|
|
979
|
+
startBoundsPage: null
|
|
952
980
|
});
|
|
953
981
|
const selectDragRef = useRef({
|
|
954
982
|
mode: "none",
|
|
@@ -970,6 +998,8 @@ function useTsdrawCanvasController(options = {}) {
|
|
|
970
998
|
const [isMovingSelection, setIsMovingSelection] = useState(false);
|
|
971
999
|
const [isResizingSelection, setIsResizingSelection] = useState(false);
|
|
972
1000
|
const [isRotatingSelection, setIsRotatingSelection] = useState(false);
|
|
1001
|
+
const [isDraggingVertex, setIsDraggingVertex] = useState(false);
|
|
1002
|
+
const [vertexHandleScreenPositions, setVertexHandleScreenPositions] = useState([]);
|
|
973
1003
|
const [canUndo, setCanUndo] = useState(false);
|
|
974
1004
|
const [canRedo, setCanRedo] = useState(false);
|
|
975
1005
|
const [isPersistenceReady, setIsPersistenceReady] = useState(!options.persistenceKey);
|
|
@@ -1002,6 +1032,11 @@ function useTsdrawCanvasController(options = {}) {
|
|
|
1002
1032
|
useEffect(() => {
|
|
1003
1033
|
penOptionsRef.current = options.penOptions;
|
|
1004
1034
|
}, [options.penOptions]);
|
|
1035
|
+
useEffect(() => {
|
|
1036
|
+
autoShapeRef.current = options.autoShape;
|
|
1037
|
+
const editor = editorRef.current;
|
|
1038
|
+
if (editor) editor.setAutoShape(resolveAutoShapeOption(options.autoShape));
|
|
1039
|
+
}, [options.autoShape]);
|
|
1005
1040
|
useEffect(() => {
|
|
1006
1041
|
backgroundRef.current = options.background;
|
|
1007
1042
|
}, [options.background]);
|
|
@@ -1058,9 +1093,12 @@ function useTsdrawCanvasController(options = {}) {
|
|
|
1058
1093
|
setIsMovingSelection(false);
|
|
1059
1094
|
setIsResizingSelection(false);
|
|
1060
1095
|
setIsRotatingSelection(false);
|
|
1096
|
+
setIsDraggingVertex(false);
|
|
1061
1097
|
selectDragRef.current.mode = "none";
|
|
1098
|
+
selectDragRef.current.vertexRefs = void 0;
|
|
1099
|
+
selectDragRef.current.vertexSnapshots = void 0;
|
|
1062
1100
|
resizeRef.current = { handle: null, startBounds: null, startShapes: /* @__PURE__ */ new Map(), cursorHandleOffset: { x: 0, y: 0 } };
|
|
1063
|
-
rotateRef.current = { center: null, startAngle: 0, startSelectionRotationDeg: 0, startShapes: /* @__PURE__ */ new Map() };
|
|
1101
|
+
rotateRef.current = { center: null, startAngle: 0, startSelectionRotationDeg: 0, startShapes: /* @__PURE__ */ new Map(), startBoundsPage: null };
|
|
1064
1102
|
}, []);
|
|
1065
1103
|
const handleResizePointerDown = useCallback(
|
|
1066
1104
|
(e, handle) => {
|
|
@@ -1114,8 +1152,10 @@ function useTsdrawCanvasController(options = {}) {
|
|
|
1114
1152
|
center,
|
|
1115
1153
|
startAngle: Math.atan2(p.y - center.y, p.x - center.x),
|
|
1116
1154
|
startSelectionRotationDeg: selectionRotationRef.current,
|
|
1117
|
-
startShapes: buildTransformSnapshots(editor, selectedShapeIdsRef.current)
|
|
1155
|
+
startShapes: buildTransformSnapshots(editor, selectedShapeIdsRef.current),
|
|
1156
|
+
startBoundsPage: bounds
|
|
1118
1157
|
};
|
|
1158
|
+
setSelectionBounds(selectionCenteredOnRotation(editor, bounds, center));
|
|
1119
1159
|
isPointerActiveRef.current = true;
|
|
1120
1160
|
activePointerIdsRef.current.add(e.pointerId);
|
|
1121
1161
|
canvas.setPointerCapture(e.pointerId);
|
|
@@ -1140,7 +1180,8 @@ function useTsdrawCanvasController(options = {}) {
|
|
|
1140
1180
|
const editor = new Editor({
|
|
1141
1181
|
toolDefinitions: options.toolDefinitions,
|
|
1142
1182
|
initialToolId: initialTool,
|
|
1143
|
-
zoomRange: cameraOpts?.zoomRange
|
|
1183
|
+
zoomRange: cameraOpts?.zoomRange,
|
|
1184
|
+
autoShape: resolveAutoShapeOption(autoShapeRef.current)
|
|
1144
1185
|
});
|
|
1145
1186
|
editor.renderer.setTheme(options.theme ?? "light");
|
|
1146
1187
|
if (!editor.tools.hasTool(initialTool)) {
|
|
@@ -1380,6 +1421,30 @@ function useTsdrawCanvasController(options = {}) {
|
|
|
1380
1421
|
const pressure = (first.pressure ?? 0.5) * pressureSensitivity;
|
|
1381
1422
|
const isPen = first.pointerType === "pen" || hasRealPressure(first.pressure);
|
|
1382
1423
|
if (currentToolRef.current === "select") {
|
|
1424
|
+
if (!readOnlyRef.current) {
|
|
1425
|
+
const zoom = editor.viewport.zoom;
|
|
1426
|
+
const marginPage = 12 / zoom;
|
|
1427
|
+
const clusterTolerance = 20 / zoom;
|
|
1428
|
+
const vertexHit = findVertexHit(editor, { x, y }, marginPage, clusterTolerance);
|
|
1429
|
+
if (vertexHit) {
|
|
1430
|
+
const involvedIds = [...new Set(vertexHit.refs.map((r) => r.shapeId))];
|
|
1431
|
+
selectDragRef.current = {
|
|
1432
|
+
mode: "vertex",
|
|
1433
|
+
startPage: { x, y },
|
|
1434
|
+
currentPage: { x, y },
|
|
1435
|
+
startPositions: /* @__PURE__ */ new Map(),
|
|
1436
|
+
additive: false,
|
|
1437
|
+
initialSelection: [...selectedShapeIdsRef.current],
|
|
1438
|
+
vertexRefs: vertexHit.refs,
|
|
1439
|
+
vertexSnapshots: vertexHit.snapshots
|
|
1440
|
+
};
|
|
1441
|
+
setIsDraggingVertex(true);
|
|
1442
|
+
setSelectedShapeIds(involvedIds);
|
|
1443
|
+
selectedShapeIdsRef.current = involvedIds;
|
|
1444
|
+
refreshSelectionBounds(editor, involvedIds);
|
|
1445
|
+
return;
|
|
1446
|
+
}
|
|
1447
|
+
}
|
|
1383
1448
|
const hit = getTopShapeAtPoint(editor, { x, y });
|
|
1384
1449
|
const isHitSelected = !!(hit && selectedShapeIdsRef.current.includes(hit.id));
|
|
1385
1450
|
const isInsideSelectionBounds = (() => {
|
|
@@ -1395,7 +1460,8 @@ function useTsdrawCanvasController(options = {}) {
|
|
|
1395
1460
|
currentPage: { x, y },
|
|
1396
1461
|
startPositions: buildStartPositions(editor, selectedShapeIdsRef.current),
|
|
1397
1462
|
additive: false,
|
|
1398
|
-
initialSelection: [...selectedShapeIdsRef.current]
|
|
1463
|
+
initialSelection: [...selectedShapeIdsRef.current],
|
|
1464
|
+
moveFromEmptyInsideBounds: isInsideSelectionBounds && !hit
|
|
1399
1465
|
};
|
|
1400
1466
|
setIsMovingSelection(true);
|
|
1401
1467
|
return;
|
|
@@ -1451,13 +1517,14 @@ function useTsdrawCanvasController(options = {}) {
|
|
|
1451
1517
|
const mode = selectDragRef.current.mode;
|
|
1452
1518
|
const { x: px, y: py } = editor.input.getCurrentPagePoint();
|
|
1453
1519
|
if (mode === "rotate") {
|
|
1454
|
-
const { center, startAngle, startSelectionRotationDeg, startShapes } = rotateRef.current;
|
|
1455
|
-
if (!center) return;
|
|
1520
|
+
const { center, startAngle, startSelectionRotationDeg, startShapes, startBoundsPage } = rotateRef.current;
|
|
1521
|
+
if (!center || !startBoundsPage) return;
|
|
1456
1522
|
const angle = Math.atan2(py - center.y, px - center.x);
|
|
1457
1523
|
const delta = angle - startAngle;
|
|
1458
1524
|
setSelectionRotationDeg(startSelectionRotationDeg + delta * 180 / Math.PI);
|
|
1459
1525
|
applyRotation(editor, startShapes, center, delta);
|
|
1460
1526
|
render();
|
|
1527
|
+
setSelectionBounds(selectionCenteredOnRotation(editor, startBoundsPage, center));
|
|
1461
1528
|
return;
|
|
1462
1529
|
}
|
|
1463
1530
|
if (mode === "resize") {
|
|
@@ -1475,6 +1542,20 @@ function useTsdrawCanvasController(options = {}) {
|
|
|
1475
1542
|
refreshSelectionBounds(editor);
|
|
1476
1543
|
return;
|
|
1477
1544
|
}
|
|
1545
|
+
if (mode === "vertex") {
|
|
1546
|
+
const drag = selectDragRef.current;
|
|
1547
|
+
const refs = drag.vertexRefs;
|
|
1548
|
+
const snapshots = drag.vertexSnapshots;
|
|
1549
|
+
if (refs && snapshots) {
|
|
1550
|
+
applyVertexDrag(editor, snapshots, refs, {
|
|
1551
|
+
x: px - drag.startPage.x,
|
|
1552
|
+
y: py - drag.startPage.y
|
|
1553
|
+
});
|
|
1554
|
+
render();
|
|
1555
|
+
refreshSelectionBounds(editor);
|
|
1556
|
+
}
|
|
1557
|
+
return;
|
|
1558
|
+
}
|
|
1478
1559
|
if (mode === "marquee") {
|
|
1479
1560
|
selectDragRef.current.currentPage = { x: px, y: py };
|
|
1480
1561
|
const pageRect = normalizeSelectionBounds(selectDragRef.current.startPage, selectDragRef.current.currentPage);
|
|
@@ -1512,7 +1593,7 @@ function useTsdrawCanvasController(options = {}) {
|
|
|
1512
1593
|
setIsRotatingSelection(false);
|
|
1513
1594
|
selectDragRef.current.mode = "none";
|
|
1514
1595
|
setSelectionRotationDeg(0);
|
|
1515
|
-
rotateRef.current = { center: null, startAngle: 0, startSelectionRotationDeg: 0, startShapes: /* @__PURE__ */ new Map() };
|
|
1596
|
+
rotateRef.current = { center: null, startAngle: 0, startSelectionRotationDeg: 0, startShapes: /* @__PURE__ */ new Map(), startBoundsPage: null };
|
|
1516
1597
|
render();
|
|
1517
1598
|
refreshSelectionBounds(editor);
|
|
1518
1599
|
editor.endHistoryEntry();
|
|
@@ -1530,6 +1611,24 @@ function useTsdrawCanvasController(options = {}) {
|
|
|
1530
1611
|
if (drag.mode === "move") {
|
|
1531
1612
|
setIsMovingSelection(false);
|
|
1532
1613
|
selectDragRef.current.mode = "none";
|
|
1614
|
+
const distSq = (x - drag.startPage.x) ** 2 + (y - drag.startPage.y) ** 2;
|
|
1615
|
+
if (drag.moveFromEmptyInsideBounds && distSq < 4) {
|
|
1616
|
+
setSelectedShapeIds([]);
|
|
1617
|
+
selectedShapeIdsRef.current = [];
|
|
1618
|
+
setSelectionBounds(null);
|
|
1619
|
+
} else {
|
|
1620
|
+
refreshSelectionBounds(editor);
|
|
1621
|
+
}
|
|
1622
|
+
selectDragRef.current.moveFromEmptyInsideBounds = void 0;
|
|
1623
|
+
render();
|
|
1624
|
+
editor.endHistoryEntry();
|
|
1625
|
+
return;
|
|
1626
|
+
}
|
|
1627
|
+
if (drag.mode === "vertex") {
|
|
1628
|
+
setIsDraggingVertex(false);
|
|
1629
|
+
selectDragRef.current.mode = "none";
|
|
1630
|
+
selectDragRef.current.vertexRefs = void 0;
|
|
1631
|
+
selectDragRef.current.vertexSnapshots = void 0;
|
|
1533
1632
|
render();
|
|
1534
1633
|
refreshSelectionBounds(editor);
|
|
1535
1634
|
editor.endHistoryEntry();
|
|
@@ -1608,9 +1707,21 @@ function useTsdrawCanvasController(options = {}) {
|
|
|
1608
1707
|
editor.input.pointerUp();
|
|
1609
1708
|
if (currentToolRef.current === "select") {
|
|
1610
1709
|
const drag = selectDragRef.current;
|
|
1611
|
-
if (drag.mode === "rotate")
|
|
1710
|
+
if (drag.mode === "rotate") {
|
|
1711
|
+
setIsRotatingSelection(false);
|
|
1712
|
+
setSelectionRotationDeg(0);
|
|
1713
|
+
rotateRef.current = { center: null, startAngle: 0, startSelectionRotationDeg: 0, startShapes: /* @__PURE__ */ new Map(), startBoundsPage: null };
|
|
1714
|
+
}
|
|
1612
1715
|
if (drag.mode === "resize") setIsResizingSelection(false);
|
|
1613
|
-
if (drag.mode === "move")
|
|
1716
|
+
if (drag.mode === "move") {
|
|
1717
|
+
setIsMovingSelection(false);
|
|
1718
|
+
selectDragRef.current.moveFromEmptyInsideBounds = void 0;
|
|
1719
|
+
}
|
|
1720
|
+
if (drag.mode === "vertex") {
|
|
1721
|
+
setIsDraggingVertex(false);
|
|
1722
|
+
selectDragRef.current.vertexRefs = void 0;
|
|
1723
|
+
selectDragRef.current.vertexSnapshots = void 0;
|
|
1724
|
+
}
|
|
1614
1725
|
if (drag.mode === "marquee") setSelectionBrush(null);
|
|
1615
1726
|
if (drag.mode !== "none") {
|
|
1616
1727
|
selectDragRef.current.mode = "none";
|
|
@@ -1947,10 +2058,25 @@ function useTsdrawCanvasController(options = {}) {
|
|
|
1947
2058
|
}, [render]);
|
|
1948
2059
|
const isHoveringSelectionBounds = isPointerInsideCanvas && currentTool === "select" && selectedShapeIds.length > 0 && selectionBounds != null && pointerScreenPoint.x >= selectionBounds.left && pointerScreenPoint.x <= selectionBounds.left + selectionBounds.width && pointerScreenPoint.y >= selectionBounds.top && pointerScreenPoint.y <= selectionBounds.top + selectionBounds.height;
|
|
1949
2060
|
const showToolOverlay = isPointerInsideCanvas && (currentTool === "pen" || currentTool === "eraser");
|
|
2061
|
+
useEffect(() => {
|
|
2062
|
+
const editor = editorRef.current;
|
|
2063
|
+
if (!editor || currentTool !== "select" || selectedShapeIds.length === 0) {
|
|
2064
|
+
setVertexHandleScreenPositions([]);
|
|
2065
|
+
return;
|
|
2066
|
+
}
|
|
2067
|
+
const pagePts = getVertexHandlePagePositions(editor, selectedShapeIds);
|
|
2068
|
+
setVertexHandleScreenPositions(
|
|
2069
|
+
pagePts.map((pg) => {
|
|
2070
|
+
const s = pageToScreen(editor.viewport, pg.x, pg.y);
|
|
2071
|
+
return { left: s.x, top: s.y };
|
|
2072
|
+
})
|
|
2073
|
+
);
|
|
2074
|
+
}, [currentTool, selectedShapeIds, selectionBounds, selectionRotationDeg, isPersistenceReady]);
|
|
1950
2075
|
const canvasCursor = getCanvasCursor(currentTool, {
|
|
1951
2076
|
isMovingSelection,
|
|
1952
2077
|
isResizingSelection,
|
|
1953
2078
|
isRotatingSelection,
|
|
2079
|
+
isDraggingVertex,
|
|
1954
2080
|
isHoveringSelectionBounds,
|
|
1955
2081
|
showToolOverlay
|
|
1956
2082
|
});
|
|
@@ -1960,7 +2086,8 @@ function useTsdrawCanvasController(options = {}) {
|
|
|
1960
2086
|
showToolOverlay,
|
|
1961
2087
|
isMovingSelection,
|
|
1962
2088
|
isResizingSelection,
|
|
1963
|
-
isRotatingSelection
|
|
2089
|
+
isRotatingSelection,
|
|
2090
|
+
isDraggingVertex
|
|
1964
2091
|
};
|
|
1965
2092
|
const toolOverlay = {
|
|
1966
2093
|
visible: showToolOverlay,
|
|
@@ -1983,6 +2110,7 @@ function useTsdrawCanvasController(options = {}) {
|
|
|
1983
2110
|
selectionBrush,
|
|
1984
2111
|
selectionBounds,
|
|
1985
2112
|
selectionRotationDeg,
|
|
2113
|
+
vertexHandleScreenPositions,
|
|
1986
2114
|
canvasCursor,
|
|
1987
2115
|
cursorContext,
|
|
1988
2116
|
toolOverlay,
|
|
@@ -2093,6 +2221,7 @@ function Tsdraw(props) {
|
|
|
2093
2221
|
selectionBrush,
|
|
2094
2222
|
selectionBounds,
|
|
2095
2223
|
selectionRotationDeg,
|
|
2224
|
+
vertexHandleScreenPositions,
|
|
2096
2225
|
canvasCursor: defaultCanvasCursor,
|
|
2097
2226
|
cursorContext,
|
|
2098
2227
|
toolOverlay,
|
|
@@ -2115,6 +2244,7 @@ function Tsdraw(props) {
|
|
|
2115
2244
|
touchOptions: props.touchOptions,
|
|
2116
2245
|
keyboardShortcuts: props.keyboardShortcuts,
|
|
2117
2246
|
penOptions: props.penOptions,
|
|
2247
|
+
autoShape: props.autoShape,
|
|
2118
2248
|
background: props.background,
|
|
2119
2249
|
readOnly: props.readOnly,
|
|
2120
2250
|
autoFocus: props.autoFocus,
|
|
@@ -2273,6 +2403,7 @@ function Tsdraw(props) {
|
|
|
2273
2403
|
selectionBrush,
|
|
2274
2404
|
selectionBounds,
|
|
2275
2405
|
selectionRotationDeg,
|
|
2406
|
+
vertexHandleScreenPositions,
|
|
2276
2407
|
currentTool,
|
|
2277
2408
|
selectedCount: selectedShapeIds.length,
|
|
2278
2409
|
onRotatePointerDown: handleRotatePointerDown,
|