@tsdraw/react 0.8.2 → 0.8.3
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 +61 -49
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +61 -49
- package/dist/index.js.map +1 -1
- package/dist/tsdraw.css +46 -10
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -292,6 +292,7 @@ function getCanvasCursor(currentTool, state) {
|
|
|
292
292
|
if (state.isRotatingSelection) return "grabbing";
|
|
293
293
|
if (state.isResizingSelection) return "nwse-resize";
|
|
294
294
|
if (state.isMovingSelection) return "grabbing";
|
|
295
|
+
if (state.isHoveringSelectionBounds) return "move";
|
|
295
296
|
return "default";
|
|
296
297
|
}
|
|
297
298
|
return state.showToolOverlay ? "none" : "crosshair";
|
|
@@ -320,8 +321,7 @@ function createTouchInteractionController(editor, canvas, handlers) {
|
|
|
320
321
|
previousCenter: { x: 0, y: 0 },
|
|
321
322
|
initialCenter: { x: 0, y: 0 },
|
|
322
323
|
previousDistance: 1,
|
|
323
|
-
initialDistance: 1
|
|
324
|
-
previousAngle: 0
|
|
324
|
+
initialDistance: 1
|
|
325
325
|
};
|
|
326
326
|
const isTouchPointer = (event) => event.pointerType === "touch";
|
|
327
327
|
const endTouchCameraGesture = () => {
|
|
@@ -329,7 +329,6 @@ function createTouchInteractionController(editor, canvas, handlers) {
|
|
|
329
329
|
touchCameraState.mode = "not-sure";
|
|
330
330
|
touchCameraState.previousDistance = 1;
|
|
331
331
|
touchCameraState.initialDistance = 1;
|
|
332
|
-
touchCameraState.previousAngle = 0;
|
|
333
332
|
};
|
|
334
333
|
const maybeHandleTouchTapGesture = () => {
|
|
335
334
|
if (activeTouchPoints.size > 0) return;
|
|
@@ -360,14 +359,12 @@ function createTouchInteractionController(editor, canvas, handlers) {
|
|
|
360
359
|
const second = points[1];
|
|
361
360
|
const center = { x: (first.x + second.x) / 2, y: (first.y + second.y) / 2 };
|
|
362
361
|
const distance = Math.hypot(second.x - first.x, second.y - first.y);
|
|
363
|
-
const angle = Math.atan2(second.y - first.y, second.x - first.x);
|
|
364
362
|
touchCameraState.active = true;
|
|
365
363
|
touchCameraState.mode = "not-sure";
|
|
366
364
|
touchCameraState.previousCenter = center;
|
|
367
365
|
touchCameraState.initialCenter = center;
|
|
368
366
|
touchCameraState.previousDistance = Math.max(1, distance);
|
|
369
367
|
touchCameraState.initialDistance = Math.max(1, distance);
|
|
370
|
-
touchCameraState.previousAngle = angle;
|
|
371
368
|
};
|
|
372
369
|
const updateTouchCameraGesture = () => {
|
|
373
370
|
if (!touchCameraState.active) return false;
|
|
@@ -380,7 +377,6 @@ function createTouchInteractionController(editor, canvas, handlers) {
|
|
|
380
377
|
const second = points[1];
|
|
381
378
|
const center = { x: (first.x + second.x) / 2, y: (first.y + second.y) / 2 };
|
|
382
379
|
const distance = Math.max(1, Math.hypot(second.x - first.x, second.y - first.y));
|
|
383
|
-
const angle = Math.atan2(second.y - first.y, second.x - first.x);
|
|
384
380
|
const centerDx = center.x - touchCameraState.previousCenter.x;
|
|
385
381
|
const centerDy = center.y - touchCameraState.previousCenter.y;
|
|
386
382
|
const touchDistance = Math.abs(distance - touchCameraState.initialDistance);
|
|
@@ -396,11 +392,9 @@ function createTouchInteractionController(editor, canvas, handlers) {
|
|
|
396
392
|
if (touchCameraState.mode === "zooming") {
|
|
397
393
|
const zoomFactor = distance / touchCameraState.previousDistance;
|
|
398
394
|
editor.zoomAt(zoomFactor, centerOnCanvasX, centerOnCanvasY);
|
|
399
|
-
editor.rotateAt(angle - touchCameraState.previousAngle, centerOnCanvasX, centerOnCanvasY);
|
|
400
395
|
}
|
|
401
396
|
touchCameraState.previousCenter = center;
|
|
402
397
|
touchCameraState.previousDistance = distance;
|
|
403
|
-
touchCameraState.previousAngle = angle;
|
|
404
398
|
handlers.refreshView();
|
|
405
399
|
return true;
|
|
406
400
|
};
|
|
@@ -681,6 +675,18 @@ function toScreenRect(editor, bounds) {
|
|
|
681
675
|
function resolveDrawColor(colorStyle, theme) {
|
|
682
676
|
return core.resolveThemeColor(colorStyle, theme);
|
|
683
677
|
}
|
|
678
|
+
function getHandlePagePoint(bounds, handle) {
|
|
679
|
+
switch (handle) {
|
|
680
|
+
case "nw":
|
|
681
|
+
return { x: bounds.minX, y: bounds.minY };
|
|
682
|
+
case "ne":
|
|
683
|
+
return { x: bounds.maxX, y: bounds.minY };
|
|
684
|
+
case "sw":
|
|
685
|
+
return { x: bounds.minX, y: bounds.maxY };
|
|
686
|
+
case "se":
|
|
687
|
+
return { x: bounds.maxX, y: bounds.maxY };
|
|
688
|
+
}
|
|
689
|
+
}
|
|
684
690
|
var ZOOM_WHEEL_CAP = 10;
|
|
685
691
|
function useTsdrawCanvasController(options = {}) {
|
|
686
692
|
const onMountRef = react.useRef(options.onMount);
|
|
@@ -702,7 +708,8 @@ function useTsdrawCanvasController(options = {}) {
|
|
|
702
708
|
const resizeRef = react.useRef({
|
|
703
709
|
handle: null,
|
|
704
710
|
startBounds: null,
|
|
705
|
-
startShapes: /* @__PURE__ */ new Map()
|
|
711
|
+
startShapes: /* @__PURE__ */ new Map(),
|
|
712
|
+
cursorHandleOffset: { x: 0, y: 0 }
|
|
706
713
|
});
|
|
707
714
|
const rotateRef = react.useRef({
|
|
708
715
|
center: null,
|
|
@@ -789,33 +796,38 @@ function useTsdrawCanvasController(options = {}) {
|
|
|
789
796
|
setIsResizingSelection(false);
|
|
790
797
|
setIsRotatingSelection(false);
|
|
791
798
|
selectDragRef.current.mode = "none";
|
|
792
|
-
resizeRef.current = { handle: null, startBounds: null, startShapes: /* @__PURE__ */ new Map() };
|
|
793
|
-
rotateRef.current = {
|
|
794
|
-
center: null,
|
|
795
|
-
startAngle: 0,
|
|
796
|
-
startSelectionRotationDeg: selectionRotationRef.current,
|
|
797
|
-
startShapes: /* @__PURE__ */ new Map()
|
|
798
|
-
};
|
|
799
|
+
resizeRef.current = { handle: null, startBounds: null, startShapes: /* @__PURE__ */ new Map(), cursorHandleOffset: { x: 0, y: 0 } };
|
|
800
|
+
rotateRef.current = { center: null, startAngle: 0, startSelectionRotationDeg: 0, startShapes: /* @__PURE__ */ new Map() };
|
|
799
801
|
}, []);
|
|
800
802
|
const handleResizePointerDown = react.useCallback(
|
|
801
803
|
(e, handle) => {
|
|
802
804
|
e.preventDefault();
|
|
803
805
|
e.stopPropagation();
|
|
804
806
|
const editor = editorRef.current;
|
|
805
|
-
|
|
807
|
+
const canvas = canvasRef.current;
|
|
808
|
+
if (!editor || !canvas || selectedShapeIdsRef.current.length === 0) return;
|
|
806
809
|
const bounds = core.getSelectionBoundsPage(editor, selectedShapeIdsRef.current);
|
|
807
810
|
if (!bounds) return;
|
|
811
|
+
const handlePagePoint = getHandlePagePoint(bounds, handle);
|
|
812
|
+
const pointerPage = getPagePointFromClient(editor, e.clientX, e.clientY);
|
|
813
|
+
const cursorOffset = {
|
|
814
|
+
x: pointerPage.x - handlePagePoint.x,
|
|
815
|
+
y: pointerPage.y - handlePagePoint.y
|
|
816
|
+
};
|
|
808
817
|
resizeRef.current = {
|
|
809
818
|
handle,
|
|
810
819
|
startBounds: bounds,
|
|
811
|
-
startShapes: core.buildTransformSnapshots(editor, selectedShapeIdsRef.current)
|
|
820
|
+
startShapes: core.buildTransformSnapshots(editor, selectedShapeIdsRef.current),
|
|
821
|
+
cursorHandleOffset: cursorOffset
|
|
812
822
|
};
|
|
823
|
+
isPointerActiveRef.current = true;
|
|
824
|
+
activePointerIdsRef.current.add(e.pointerId);
|
|
825
|
+
canvas.setPointerCapture(e.pointerId);
|
|
813
826
|
editor.beginHistoryEntry();
|
|
814
827
|
selectDragRef.current.mode = "resize";
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
selectDragRef.current.
|
|
818
|
-
selectDragRef.current.currentPage = p;
|
|
828
|
+
editor.input.pointerDown(handlePagePoint.x, handlePagePoint.y, 0.5, false);
|
|
829
|
+
selectDragRef.current.startPage = handlePagePoint;
|
|
830
|
+
selectDragRef.current.currentPage = handlePagePoint;
|
|
819
831
|
lastPointerClientRef.current = { x: e.clientX, y: e.clientY };
|
|
820
832
|
setIsResizingSelection(true);
|
|
821
833
|
},
|
|
@@ -826,7 +838,8 @@ function useTsdrawCanvasController(options = {}) {
|
|
|
826
838
|
e.preventDefault();
|
|
827
839
|
e.stopPropagation();
|
|
828
840
|
const editor = editorRef.current;
|
|
829
|
-
|
|
841
|
+
const canvas = canvasRef.current;
|
|
842
|
+
if (!editor || !canvas || selectedShapeIdsRef.current.length === 0) return;
|
|
830
843
|
const bounds = core.getSelectionBoundsPage(editor, selectedShapeIdsRef.current);
|
|
831
844
|
if (!bounds) return;
|
|
832
845
|
const center = {
|
|
@@ -840,6 +853,9 @@ function useTsdrawCanvasController(options = {}) {
|
|
|
840
853
|
startSelectionRotationDeg: selectionRotationRef.current,
|
|
841
854
|
startShapes: core.buildTransformSnapshots(editor, selectedShapeIdsRef.current)
|
|
842
855
|
};
|
|
856
|
+
isPointerActiveRef.current = true;
|
|
857
|
+
activePointerIdsRef.current.add(e.pointerId);
|
|
858
|
+
canvas.setPointerCapture(e.pointerId);
|
|
843
859
|
editor.beginHistoryEntry();
|
|
844
860
|
selectDragRef.current.mode = "rotate";
|
|
845
861
|
editor.input.pointerDown(p.x, p.y, 0.5, false);
|
|
@@ -969,7 +985,6 @@ function useTsdrawCanvasController(options = {}) {
|
|
|
969
985
|
setDrawDash(nextDrawStyle.dash);
|
|
970
986
|
setDrawFill(nextDrawStyle.fill);
|
|
971
987
|
setDrawSize(nextDrawStyle.size);
|
|
972
|
-
setSelectionRotationDeg(0);
|
|
973
988
|
render();
|
|
974
989
|
refreshSelectionBounds(editor, nextSelectionIds);
|
|
975
990
|
ignorePersistenceChanges = false;
|
|
@@ -985,7 +1000,6 @@ function useTsdrawCanvasController(options = {}) {
|
|
|
985
1000
|
const applyDocumentChangeResult = (changed) => {
|
|
986
1001
|
if (!changed) return false;
|
|
987
1002
|
reconcileSelectionAfterDocumentLoad();
|
|
988
|
-
setSelectionRotationDeg(0);
|
|
989
1003
|
render();
|
|
990
1004
|
syncHistoryState();
|
|
991
1005
|
return true;
|
|
@@ -1012,8 +1026,7 @@ function useTsdrawCanvasController(options = {}) {
|
|
|
1012
1026
|
setSelectedShapeIds([]);
|
|
1013
1027
|
selectedShapeIdsRef.current = [];
|
|
1014
1028
|
setSelectionBounds(null);
|
|
1015
|
-
|
|
1016
|
-
setSelectionRotationDeg(0);
|
|
1029
|
+
resetSelectUi();
|
|
1017
1030
|
render();
|
|
1018
1031
|
syncHistoryState();
|
|
1019
1032
|
return true;
|
|
@@ -1024,12 +1037,7 @@ function useTsdrawCanvasController(options = {}) {
|
|
|
1024
1037
|
lastPointerClientRef.current = null;
|
|
1025
1038
|
editor.input.pointerUp();
|
|
1026
1039
|
if (currentToolRef.current === "select") {
|
|
1027
|
-
|
|
1028
|
-
if (dragMode === "rotate") setIsRotatingSelection(false);
|
|
1029
|
-
if (dragMode === "resize") setIsResizingSelection(false);
|
|
1030
|
-
if (dragMode === "move") setIsMovingSelection(false);
|
|
1031
|
-
if (dragMode === "marquee") setSelectionBrush(null);
|
|
1032
|
-
selectDragRef.current.mode = "none";
|
|
1040
|
+
resetSelectUi();
|
|
1033
1041
|
} else {
|
|
1034
1042
|
editor.tools.pointerUp();
|
|
1035
1043
|
}
|
|
@@ -1083,7 +1091,13 @@ function useTsdrawCanvasController(options = {}) {
|
|
|
1083
1091
|
if (currentToolRef.current === "select") {
|
|
1084
1092
|
const hit = core.getTopShapeAtPoint(editor, { x, y });
|
|
1085
1093
|
const isHitSelected = !!(hit && selectedShapeIdsRef.current.includes(hit.id));
|
|
1086
|
-
|
|
1094
|
+
const isInsideSelectionBounds = (() => {
|
|
1095
|
+
if (selectedShapeIdsRef.current.length === 0) return false;
|
|
1096
|
+
const pageBounds = core.getSelectionBoundsPage(editor, selectedShapeIdsRef.current);
|
|
1097
|
+
if (!pageBounds) return false;
|
|
1098
|
+
return x >= pageBounds.minX && x <= pageBounds.maxX && y >= pageBounds.minY && y <= pageBounds.maxY;
|
|
1099
|
+
})();
|
|
1100
|
+
if (isHitSelected || isInsideSelectionBounds) {
|
|
1087
1101
|
selectDragRef.current = {
|
|
1088
1102
|
mode: "move",
|
|
1089
1103
|
startPage: { x, y },
|
|
@@ -1154,9 +1168,9 @@ function useTsdrawCanvasController(options = {}) {
|
|
|
1154
1168
|
return;
|
|
1155
1169
|
}
|
|
1156
1170
|
if (mode === "resize") {
|
|
1157
|
-
const { handle, startBounds, startShapes } = resizeRef.current;
|
|
1171
|
+
const { handle, startBounds, startShapes, cursorHandleOffset } = resizeRef.current;
|
|
1158
1172
|
if (!handle || !startBounds) return;
|
|
1159
|
-
core.applyResize(editor, handle, startBounds, startShapes, { x: px, y: py }, e.shiftKey);
|
|
1173
|
+
core.applyResize(editor, handle, startBounds, startShapes, { x: px - cursorHandleOffset.x, y: py - cursorHandleOffset.y }, e.shiftKey);
|
|
1160
1174
|
render();
|
|
1161
1175
|
refreshSelectionBounds(editor);
|
|
1162
1176
|
return;
|
|
@@ -1176,7 +1190,6 @@ function useTsdrawCanvasController(options = {}) {
|
|
|
1176
1190
|
const nextIds = selectDragRef.current.additive ? Array.from(/* @__PURE__ */ new Set([...selectDragRef.current.initialSelection, ...ids])) : ids;
|
|
1177
1191
|
setSelectedShapeIds(nextIds);
|
|
1178
1192
|
selectedShapeIdsRef.current = nextIds;
|
|
1179
|
-
setSelectionRotationDeg(0);
|
|
1180
1193
|
refreshSelectionBounds(editor, nextIds);
|
|
1181
1194
|
return;
|
|
1182
1195
|
}
|
|
@@ -1206,12 +1219,7 @@ function useTsdrawCanvasController(options = {}) {
|
|
|
1206
1219
|
setIsRotatingSelection(false);
|
|
1207
1220
|
selectDragRef.current.mode = "none";
|
|
1208
1221
|
setSelectionRotationDeg(0);
|
|
1209
|
-
rotateRef.current = {
|
|
1210
|
-
center: null,
|
|
1211
|
-
startAngle: 0,
|
|
1212
|
-
startSelectionRotationDeg: selectionRotationRef.current,
|
|
1213
|
-
startShapes: /* @__PURE__ */ new Map()
|
|
1214
|
-
};
|
|
1222
|
+
rotateRef.current = { center: null, startAngle: 0, startSelectionRotationDeg: 0, startShapes: /* @__PURE__ */ new Map() };
|
|
1215
1223
|
render();
|
|
1216
1224
|
refreshSelectionBounds(editor);
|
|
1217
1225
|
editor.endHistoryEntry();
|
|
@@ -1220,7 +1228,7 @@ function useTsdrawCanvasController(options = {}) {
|
|
|
1220
1228
|
if (drag.mode === "resize") {
|
|
1221
1229
|
setIsResizingSelection(false);
|
|
1222
1230
|
selectDragRef.current.mode = "none";
|
|
1223
|
-
resizeRef.current = { handle: null, startBounds: null, startShapes: /* @__PURE__ */ new Map() };
|
|
1231
|
+
resizeRef.current = { handle: null, startBounds: null, startShapes: /* @__PURE__ */ new Map(), cursorHandleOffset: { x: 0, y: 0 } };
|
|
1224
1232
|
render();
|
|
1225
1233
|
refreshSelectionBounds(editor);
|
|
1226
1234
|
editor.endHistoryEntry();
|
|
@@ -1253,7 +1261,6 @@ function useTsdrawCanvasController(options = {}) {
|
|
|
1253
1261
|
}
|
|
1254
1262
|
setSelectedShapeIds(ids);
|
|
1255
1263
|
selectedShapeIdsRef.current = ids;
|
|
1256
|
-
setSelectionRotationDeg(0);
|
|
1257
1264
|
setSelectionBrush(null);
|
|
1258
1265
|
selectDragRef.current.mode = "none";
|
|
1259
1266
|
render();
|
|
@@ -1436,11 +1443,17 @@ function useTsdrawCanvasController(options = {}) {
|
|
|
1436
1443
|
resize();
|
|
1437
1444
|
const ro = new ResizeObserver(resize);
|
|
1438
1445
|
ro.observe(container);
|
|
1446
|
+
const handlePointerLeaveViewport = (e) => {
|
|
1447
|
+
if (e.relatedTarget === null) {
|
|
1448
|
+
setIsPointerInsideCanvas(false);
|
|
1449
|
+
}
|
|
1450
|
+
};
|
|
1439
1451
|
canvas.addEventListener("pointerdown", handlePointerDown);
|
|
1440
1452
|
container.addEventListener("wheel", handleWheel, { passive: false });
|
|
1441
1453
|
document.addEventListener("gesturestart", handleGestureEvent);
|
|
1442
1454
|
document.addEventListener("gesturechange", handleGestureEvent);
|
|
1443
1455
|
document.addEventListener("gestureend", handleGestureEvent);
|
|
1456
|
+
document.documentElement.addEventListener("pointerleave", handlePointerLeaveViewport);
|
|
1444
1457
|
window.addEventListener("pointermove", handlePointerMove);
|
|
1445
1458
|
window.addEventListener("pointerup", handlePointerUp);
|
|
1446
1459
|
window.addEventListener("pointercancel", handlePointerCancel);
|
|
@@ -1464,7 +1477,6 @@ function useTsdrawCanvasController(options = {}) {
|
|
|
1464
1477
|
const changed = editor.undo();
|
|
1465
1478
|
if (!changed) return false;
|
|
1466
1479
|
reconcileSelectionAfterDocumentLoad();
|
|
1467
|
-
setSelectionRotationDeg(0);
|
|
1468
1480
|
render();
|
|
1469
1481
|
syncHistoryState();
|
|
1470
1482
|
return true;
|
|
@@ -1473,7 +1485,6 @@ function useTsdrawCanvasController(options = {}) {
|
|
|
1473
1485
|
const changed = editor.redo();
|
|
1474
1486
|
if (!changed) return false;
|
|
1475
1487
|
reconcileSelectionAfterDocumentLoad();
|
|
1476
|
-
setSelectionRotationDeg(0);
|
|
1477
1488
|
render();
|
|
1478
1489
|
syncHistoryState();
|
|
1479
1490
|
return true;
|
|
@@ -1501,6 +1512,7 @@ function useTsdrawCanvasController(options = {}) {
|
|
|
1501
1512
|
document.removeEventListener("gesturestart", handleGestureEvent);
|
|
1502
1513
|
document.removeEventListener("gesturechange", handleGestureEvent);
|
|
1503
1514
|
document.removeEventListener("gestureend", handleGestureEvent);
|
|
1515
|
+
document.documentElement.removeEventListener("pointerleave", handlePointerLeaveViewport);
|
|
1504
1516
|
window.removeEventListener("pointermove", handlePointerMove);
|
|
1505
1517
|
window.removeEventListener("pointerup", handlePointerUp);
|
|
1506
1518
|
window.removeEventListener("pointercancel", handlePointerCancel);
|
|
@@ -1565,7 +1577,6 @@ function useTsdrawCanvasController(options = {}) {
|
|
|
1565
1577
|
selectedShapeIdsRef.current = nextSelectedShapeIds;
|
|
1566
1578
|
setSelectedShapeIds(nextSelectedShapeIds);
|
|
1567
1579
|
}
|
|
1568
|
-
setSelectionRotationDeg(0);
|
|
1569
1580
|
render();
|
|
1570
1581
|
setCanUndo(editor.canUndo());
|
|
1571
1582
|
setCanRedo(editor.canRedo());
|
|
@@ -1581,17 +1592,18 @@ function useTsdrawCanvasController(options = {}) {
|
|
|
1581
1592
|
selectedShapeIdsRef.current = nextSelectedShapeIds;
|
|
1582
1593
|
setSelectedShapeIds(nextSelectedShapeIds);
|
|
1583
1594
|
}
|
|
1584
|
-
setSelectionRotationDeg(0);
|
|
1585
1595
|
render();
|
|
1586
1596
|
setCanUndo(editor.canUndo());
|
|
1587
1597
|
setCanRedo(editor.canRedo());
|
|
1588
1598
|
return true;
|
|
1589
1599
|
}, [render]);
|
|
1600
|
+
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;
|
|
1590
1601
|
const showToolOverlay = isPointerInsideCanvas && (currentTool === "pen" || currentTool === "eraser");
|
|
1591
1602
|
const canvasCursor = getCanvasCursor(currentTool, {
|
|
1592
1603
|
isMovingSelection,
|
|
1593
1604
|
isResizingSelection,
|
|
1594
1605
|
isRotatingSelection,
|
|
1606
|
+
isHoveringSelectionBounds,
|
|
1595
1607
|
showToolOverlay
|
|
1596
1608
|
});
|
|
1597
1609
|
const cursorContext = {
|