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