@tsdraw/react 0.8.3 → 0.8.4
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 +93 -19
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +94 -20
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -297,8 +297,6 @@ function getCanvasCursor(currentTool, state) {
|
|
|
297
297
|
}
|
|
298
298
|
return state.showToolOverlay ? "none" : "crosshair";
|
|
299
299
|
}
|
|
300
|
-
|
|
301
|
-
// src/canvas/touchInteractions.ts
|
|
302
300
|
var TAP_MAX_DURATION_MS = 100;
|
|
303
301
|
var DOUBLE_TAP_INTERVAL_MS = 100;
|
|
304
302
|
var TAP_MOVE_TOLERANCE = 14;
|
|
@@ -320,15 +318,28 @@ function createTouchInteractionController(editor, canvas, handlers) {
|
|
|
320
318
|
mode: "not-sure",
|
|
321
319
|
previousCenter: { x: 0, y: 0 },
|
|
322
320
|
initialCenter: { x: 0, y: 0 },
|
|
323
|
-
|
|
324
|
-
|
|
321
|
+
initialDistance: 1,
|
|
322
|
+
initialZoom: 1
|
|
325
323
|
};
|
|
324
|
+
let fingerPanPointerId = null;
|
|
325
|
+
let fingerPanSession = null;
|
|
326
|
+
let fingerPanSlide = null;
|
|
326
327
|
const isTouchPointer = (event) => event.pointerType === "touch";
|
|
328
|
+
const stopFingerPanSlide = () => {
|
|
329
|
+
if (fingerPanSlide) {
|
|
330
|
+
fingerPanSlide.stop();
|
|
331
|
+
fingerPanSlide = null;
|
|
332
|
+
}
|
|
333
|
+
};
|
|
334
|
+
const endFingerPan = () => {
|
|
335
|
+
fingerPanPointerId = null;
|
|
336
|
+
fingerPanSession = null;
|
|
337
|
+
};
|
|
327
338
|
const endTouchCameraGesture = () => {
|
|
328
339
|
touchCameraState.active = false;
|
|
329
340
|
touchCameraState.mode = "not-sure";
|
|
330
|
-
touchCameraState.previousDistance = 1;
|
|
331
341
|
touchCameraState.initialDistance = 1;
|
|
342
|
+
touchCameraState.initialZoom = 1;
|
|
332
343
|
};
|
|
333
344
|
const maybeHandleTouchTapGesture = () => {
|
|
334
345
|
if (activeTouchPoints.size > 0) return;
|
|
@@ -363,8 +374,8 @@ function createTouchInteractionController(editor, canvas, handlers) {
|
|
|
363
374
|
touchCameraState.mode = "not-sure";
|
|
364
375
|
touchCameraState.previousCenter = center;
|
|
365
376
|
touchCameraState.initialCenter = center;
|
|
366
|
-
touchCameraState.previousDistance = Math.max(1, distance);
|
|
367
377
|
touchCameraState.initialDistance = Math.max(1, distance);
|
|
378
|
+
touchCameraState.initialZoom = editor.getZoomLevel();
|
|
368
379
|
};
|
|
369
380
|
const updateTouchCameraGesture = () => {
|
|
370
381
|
if (!touchCameraState.active) return false;
|
|
@@ -388,18 +399,27 @@ function createTouchInteractionController(editor, canvas, handlers) {
|
|
|
388
399
|
const canvasRect = canvas.getBoundingClientRect();
|
|
389
400
|
const centerOnCanvasX = center.x - canvasRect.left;
|
|
390
401
|
const centerOnCanvasY = center.y - canvasRect.top;
|
|
391
|
-
editor.panBy(centerDx, centerDy);
|
|
392
402
|
if (touchCameraState.mode === "zooming") {
|
|
393
|
-
const
|
|
394
|
-
editor.
|
|
403
|
+
const targetZoom = Math.max(0.1, Math.min(4, touchCameraState.initialZoom * (distance / touchCameraState.initialDistance)));
|
|
404
|
+
const pannedX = editor.viewport.x + centerDx;
|
|
405
|
+
const pannedY = editor.viewport.y + centerDy;
|
|
406
|
+
const pageAtCenterX = (centerOnCanvasX - pannedX) / editor.viewport.zoom;
|
|
407
|
+
const pageAtCenterY = (centerOnCanvasY - pannedY) / editor.viewport.zoom;
|
|
408
|
+
editor.setViewport({
|
|
409
|
+
x: centerOnCanvasX - pageAtCenterX * targetZoom,
|
|
410
|
+
y: centerOnCanvasY - pageAtCenterY * targetZoom,
|
|
411
|
+
zoom: targetZoom
|
|
412
|
+
});
|
|
413
|
+
} else {
|
|
414
|
+
editor.panBy(centerDx, centerDy);
|
|
395
415
|
}
|
|
396
416
|
touchCameraState.previousCenter = center;
|
|
397
|
-
touchCameraState.previousDistance = distance;
|
|
398
417
|
handlers.refreshView();
|
|
399
418
|
return true;
|
|
400
419
|
};
|
|
401
420
|
const handlePointerDown = (event) => {
|
|
402
421
|
if (!isTouchPointer(event)) return false;
|
|
422
|
+
stopFingerPanSlide();
|
|
403
423
|
activeTouchPoints.set(event.pointerId, { x: event.clientX, y: event.clientY });
|
|
404
424
|
if (!touchTapState.active) {
|
|
405
425
|
touchTapState.active = true;
|
|
@@ -412,9 +432,16 @@ function createTouchInteractionController(editor, canvas, handlers) {
|
|
|
412
432
|
}
|
|
413
433
|
touchTapState.startPoints.set(event.pointerId, { x: event.clientX, y: event.clientY });
|
|
414
434
|
if (activeTouchPoints.size === 2) {
|
|
435
|
+
endFingerPan();
|
|
415
436
|
beginTouchCameraGesture();
|
|
416
437
|
return true;
|
|
417
438
|
}
|
|
439
|
+
if (handlers.isPenModeActive() && activeTouchPoints.size === 1) {
|
|
440
|
+
handlers.cancelActivePointerInteraction();
|
|
441
|
+
fingerPanPointerId = event.pointerId;
|
|
442
|
+
fingerPanSession = core.beginCameraPan(editor.viewport, event.clientX, event.clientY);
|
|
443
|
+
return true;
|
|
444
|
+
}
|
|
418
445
|
return false;
|
|
419
446
|
};
|
|
420
447
|
const handlePointerMove = (event) => {
|
|
@@ -425,16 +452,34 @@ function createTouchInteractionController(editor, canvas, handlers) {
|
|
|
425
452
|
const moved = Math.hypot(event.clientX - tapStart.x, event.clientY - tapStart.y);
|
|
426
453
|
if (moved > TAP_MOVE_TOLERANCE) touchTapState.moved = true;
|
|
427
454
|
}
|
|
455
|
+
if (fingerPanPointerId === event.pointerId && fingerPanSession) {
|
|
456
|
+
const target = core.moveCameraPan(fingerPanSession, event.clientX, event.clientY);
|
|
457
|
+
editor.setViewport({ x: target.x, y: target.y });
|
|
458
|
+
handlers.refreshView();
|
|
459
|
+
return true;
|
|
460
|
+
}
|
|
428
461
|
return updateTouchCameraGesture();
|
|
429
462
|
};
|
|
430
463
|
const handlePointerUpOrCancel = (event) => {
|
|
431
464
|
if (!isTouchPointer(event)) return false;
|
|
432
465
|
const wasCameraGestureActive = touchCameraState.active;
|
|
466
|
+
const wasFingerPan = fingerPanPointerId === event.pointerId;
|
|
467
|
+
const releasedPanSession = wasFingerPan ? fingerPanSession : null;
|
|
433
468
|
activeTouchPoints.delete(event.pointerId);
|
|
434
469
|
touchTapState.startPoints.delete(event.pointerId);
|
|
435
470
|
if (activeTouchPoints.size < 2) endTouchCameraGesture();
|
|
471
|
+
if (wasFingerPan) {
|
|
472
|
+
endFingerPan();
|
|
473
|
+
if (releasedPanSession) {
|
|
474
|
+
fingerPanSlide = core.startCameraSlide(
|
|
475
|
+
releasedPanSession,
|
|
476
|
+
(dx, dy) => editor.panBy(dx, dy),
|
|
477
|
+
() => handlers.refreshView()
|
|
478
|
+
);
|
|
479
|
+
}
|
|
480
|
+
}
|
|
436
481
|
maybeHandleTouchTapGesture();
|
|
437
|
-
return wasCameraGestureActive;
|
|
482
|
+
return wasCameraGestureActive || wasFingerPan;
|
|
438
483
|
};
|
|
439
484
|
let gestureLastScale = 1;
|
|
440
485
|
let gestureActive = false;
|
|
@@ -468,6 +513,8 @@ function createTouchInteractionController(editor, canvas, handlers) {
|
|
|
468
513
|
touchTapState.active = false;
|
|
469
514
|
touchTapState.startPoints.clear();
|
|
470
515
|
endTouchCameraGesture();
|
|
516
|
+
endFingerPan();
|
|
517
|
+
stopFingerPanSlide();
|
|
471
518
|
};
|
|
472
519
|
return {
|
|
473
520
|
handlePointerDown,
|
|
@@ -476,6 +523,7 @@ function createTouchInteractionController(editor, canvas, handlers) {
|
|
|
476
523
|
handleGestureEvent,
|
|
477
524
|
reset,
|
|
478
525
|
isCameraGestureActive: () => touchCameraState.active,
|
|
526
|
+
isFingerPanActive: () => fingerPanPointerId !== null,
|
|
479
527
|
isTrackpadZoomActive: () => gestureActive
|
|
480
528
|
};
|
|
481
529
|
}
|
|
@@ -704,6 +752,7 @@ function useTsdrawCanvasController(options = {}) {
|
|
|
704
752
|
const schedulePersistRef = react.useRef(null);
|
|
705
753
|
const isPointerActiveRef = react.useRef(false);
|
|
706
754
|
const pendingRemoteDocumentRef = react.useRef(null);
|
|
755
|
+
const activeCameraSlideRef = react.useRef(null);
|
|
707
756
|
const selectionRotationRef = react.useRef(0);
|
|
708
757
|
const resizeRef = react.useRef({
|
|
709
758
|
handle: null,
|
|
@@ -1052,12 +1101,19 @@ function useTsdrawCanvasController(options = {}) {
|
|
|
1052
1101
|
refreshSelectionBounds(editor);
|
|
1053
1102
|
},
|
|
1054
1103
|
runUndo: () => applyDocumentChangeResult(editor.undo()),
|
|
1055
|
-
runRedo: () => applyDocumentChangeResult(editor.redo())
|
|
1104
|
+
runRedo: () => applyDocumentChangeResult(editor.redo()),
|
|
1105
|
+
isPenModeActive: () => penModeRef.current
|
|
1056
1106
|
});
|
|
1057
|
-
const isDrawingTool = (tool) => tool !== "select" && tool !== "hand";
|
|
1058
1107
|
const hasRealPressure = (pressure) => pressure != null && pressure > 0 && pressure !== 0.5;
|
|
1108
|
+
const stopActiveSlide = () => {
|
|
1109
|
+
if (activeCameraSlideRef.current) {
|
|
1110
|
+
activeCameraSlideRef.current.stop();
|
|
1111
|
+
activeCameraSlideRef.current = null;
|
|
1112
|
+
}
|
|
1113
|
+
};
|
|
1059
1114
|
const handlePointerDown = (e) => {
|
|
1060
1115
|
if (!canvas.contains(e.target)) return;
|
|
1116
|
+
stopActiveSlide();
|
|
1061
1117
|
if (!penDetectedRef.current && (e.pointerType === "pen" || hasRealPressure(e.pressure))) {
|
|
1062
1118
|
penDetectedRef.current = true;
|
|
1063
1119
|
penModeRef.current = true;
|
|
@@ -1065,15 +1121,15 @@ function useTsdrawCanvasController(options = {}) {
|
|
|
1065
1121
|
lastPointerDownWithRef.current = e.pointerType;
|
|
1066
1122
|
activePointerIdsRef.current.add(e.pointerId);
|
|
1067
1123
|
const startedCameraGesture = touchInteractions.handlePointerDown(e);
|
|
1068
|
-
if (startedCameraGesture || touchInteractions.isCameraGestureActive()) {
|
|
1124
|
+
if (startedCameraGesture || touchInteractions.isCameraGestureActive() || touchInteractions.isFingerPanActive()) {
|
|
1069
1125
|
e.preventDefault();
|
|
1070
1126
|
if (!canvas.hasPointerCapture(e.pointerId)) {
|
|
1071
1127
|
canvas.setPointerCapture(e.pointerId);
|
|
1072
1128
|
}
|
|
1073
1129
|
return;
|
|
1074
1130
|
}
|
|
1075
|
-
const
|
|
1076
|
-
if (
|
|
1131
|
+
const isTouchBlockedByPenMode = penModeRef.current && e.pointerType === "touch";
|
|
1132
|
+
if (isTouchBlockedByPenMode) {
|
|
1077
1133
|
return;
|
|
1078
1134
|
}
|
|
1079
1135
|
if (activePointerIdsRef.current.size > 1) {
|
|
@@ -1128,7 +1184,7 @@ function useTsdrawCanvasController(options = {}) {
|
|
|
1128
1184
|
}
|
|
1129
1185
|
editor.input.pointerDown(x, y, pressure, isPen);
|
|
1130
1186
|
editor.input.setModifiers(first.shiftKey, first.ctrlKey, first.metaKey);
|
|
1131
|
-
editor.tools.pointerDown({ point: { x, y, z: pressure } });
|
|
1187
|
+
editor.tools.pointerDown({ point: { x, y, z: pressure }, screenX: e.clientX, screenY: e.clientY });
|
|
1132
1188
|
render();
|
|
1133
1189
|
refreshSelectionBounds(editor);
|
|
1134
1190
|
};
|
|
@@ -1141,7 +1197,7 @@ function useTsdrawCanvasController(options = {}) {
|
|
|
1141
1197
|
e.preventDefault();
|
|
1142
1198
|
return;
|
|
1143
1199
|
}
|
|
1144
|
-
if (penModeRef.current && e.pointerType === "touch" &&
|
|
1200
|
+
if (penModeRef.current && e.pointerType === "touch" && !isPointerActiveRef.current) return;
|
|
1145
1201
|
if (activePointerIdsRef.current.size > 1) return;
|
|
1146
1202
|
updatePointerPreview(e.clientX, e.clientY);
|
|
1147
1203
|
const prevClient = lastPointerClientRef.current;
|
|
@@ -1195,7 +1251,7 @@ function useTsdrawCanvasController(options = {}) {
|
|
|
1195
1251
|
}
|
|
1196
1252
|
}
|
|
1197
1253
|
editor.input.setModifiers(e.shiftKey, e.ctrlKey, e.metaKey);
|
|
1198
|
-
editor.tools.pointerMove({ screenDeltaX: dx, screenDeltaY: dy });
|
|
1254
|
+
editor.tools.pointerMove({ screenDeltaX: dx, screenDeltaY: dy, screenX: e.clientX, screenY: e.clientY });
|
|
1199
1255
|
render();
|
|
1200
1256
|
refreshSelectionBounds(editor);
|
|
1201
1257
|
};
|
|
@@ -1274,9 +1330,26 @@ function useTsdrawCanvasController(options = {}) {
|
|
|
1274
1330
|
return;
|
|
1275
1331
|
}
|
|
1276
1332
|
}
|
|
1333
|
+
let handPanSession = null;
|
|
1334
|
+
if (currentToolRef.current === "hand") {
|
|
1335
|
+
const currentState = editor.tools.getCurrentState();
|
|
1336
|
+
if (currentState instanceof core.HandDraggingState) {
|
|
1337
|
+
handPanSession = currentState.getPanSession();
|
|
1338
|
+
}
|
|
1339
|
+
}
|
|
1277
1340
|
editor.tools.pointerUp();
|
|
1278
1341
|
render();
|
|
1279
1342
|
refreshSelectionBounds(editor);
|
|
1343
|
+
if (handPanSession) {
|
|
1344
|
+
activeCameraSlideRef.current = core.startCameraSlide(
|
|
1345
|
+
handPanSession,
|
|
1346
|
+
(slideDx, slideDy) => editor.panBy(slideDx, slideDy),
|
|
1347
|
+
() => {
|
|
1348
|
+
render();
|
|
1349
|
+
refreshSelectionBounds(editor);
|
|
1350
|
+
}
|
|
1351
|
+
);
|
|
1352
|
+
}
|
|
1280
1353
|
if (pendingRemoteDocumentRef.current) {
|
|
1281
1354
|
const pendingRemoteDocument = pendingRemoteDocumentRef.current;
|
|
1282
1355
|
pendingRemoteDocumentRef.current = null;
|
|
@@ -1521,6 +1594,7 @@ function useTsdrawCanvasController(options = {}) {
|
|
|
1521
1594
|
isPointerActiveRef.current = false;
|
|
1522
1595
|
activePointerIdsRef.current.clear();
|
|
1523
1596
|
pendingRemoteDocumentRef.current = null;
|
|
1597
|
+
stopActiveSlide();
|
|
1524
1598
|
touchInteractions.reset();
|
|
1525
1599
|
persistenceChannel?.close();
|
|
1526
1600
|
void persistenceDb?.close();
|