tldraw 3.15.1 → 3.15.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-cjs/index.d.ts +28 -0
- package/dist-cjs/index.js +1 -1
- package/dist-cjs/lib/shapes/line/LineShapeUtil.js +15 -1
- package/dist-cjs/lib/shapes/line/LineShapeUtil.js.map +2 -2
- package/dist-cjs/lib/tools/SelectTool/childStates/DraggingHandle.js +39 -22
- package/dist-cjs/lib/tools/SelectTool/childStates/DraggingHandle.js.map +2 -2
- package/dist-cjs/lib/tools/SelectTool/childStates/Resizing.js +8 -0
- package/dist-cjs/lib/tools/SelectTool/childStates/Resizing.js.map +2 -2
- package/dist-cjs/lib/tools/SelectTool/childStates/Rotating.js +8 -0
- package/dist-cjs/lib/tools/SelectTool/childStates/Rotating.js.map +2 -2
- package/dist-cjs/lib/tools/SelectTool/childStates/Translating.js +8 -0
- package/dist-cjs/lib/tools/SelectTool/childStates/Translating.js.map +2 -2
- package/dist-cjs/lib/ui/assetUrls.js +13 -10
- package/dist-cjs/lib/ui/assetUrls.js.map +2 -2
- package/dist-cjs/lib/ui/version.js +2 -2
- package/dist-cjs/lib/ui/version.js.map +1 -1
- package/dist-esm/index.d.mts +28 -0
- package/dist-esm/index.mjs +1 -1
- package/dist-esm/lib/shapes/line/LineShapeUtil.mjs +15 -1
- package/dist-esm/lib/shapes/line/LineShapeUtil.mjs.map +2 -2
- package/dist-esm/lib/tools/SelectTool/childStates/DraggingHandle.mjs +39 -22
- package/dist-esm/lib/tools/SelectTool/childStates/DraggingHandle.mjs.map +2 -2
- package/dist-esm/lib/tools/SelectTool/childStates/Resizing.mjs +8 -0
- package/dist-esm/lib/tools/SelectTool/childStates/Resizing.mjs.map +2 -2
- package/dist-esm/lib/tools/SelectTool/childStates/Rotating.mjs +8 -0
- package/dist-esm/lib/tools/SelectTool/childStates/Rotating.mjs.map +2 -2
- package/dist-esm/lib/tools/SelectTool/childStates/Translating.mjs +8 -0
- package/dist-esm/lib/tools/SelectTool/childStates/Translating.mjs.map +2 -2
- package/dist-esm/lib/ui/assetUrls.mjs +13 -10
- package/dist-esm/lib/ui/assetUrls.mjs.map +2 -2
- package/dist-esm/lib/ui/version.mjs +2 -2
- package/dist-esm/lib/ui/version.mjs.map +1 -1
- package/package.json +3 -3
- package/src/lib/shapes/line/LineShapeUtil.tsx +19 -2
- package/src/lib/tools/SelectTool/childStates/DraggingHandle.tsx +50 -30
- package/src/lib/tools/SelectTool/childStates/Resizing.ts +12 -1
- package/src/lib/tools/SelectTool/childStates/Rotating.ts +11 -0
- package/src/lib/tools/SelectTool/childStates/Translating.ts +11 -0
- package/src/lib/ui/assetUrls.ts +13 -10
- package/src/lib/ui/version.ts +2 -2
- package/src/test/shapeutils.test.ts +394 -45
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../../src/lib/tools/SelectTool/childStates/Resizing.ts"],
|
|
4
|
-
"sourcesContent": ["import {\n\tBox,\n\tHALF_PI,\n\tMat,\n\tPI,\n\tPI2,\n\tSelectionCorner,\n\tSelectionEdge,\n\tStateNode,\n\tTLFrameShape,\n\tTLPointerEventInfo,\n\tTLShape,\n\tTLShapeId,\n\tTLShapePartial,\n\tTLTextShape,\n\tTLTickEventInfo,\n\tVec,\n\tVecLike,\n\tareAnglesCompatible,\n\tcompact,\n\tisAccelKey,\n\tkickoutOccludedShapes,\n} from '@tldraw/editor'\n\nexport type ResizingInfo = TLPointerEventInfo & {\n\ttarget: 'selection'\n\thandle: SelectionEdge | SelectionCorner\n\tisCreating?: boolean\n\tcreatingMarkId?: string\n\tonCreate?(shape: TLShape | null): void\n\tcreationCursorOffset?: VecLike\n\tonInteractionEnd?: string\n}\n\nexport class Resizing extends StateNode {\n\tstatic override id = 'resizing'\n\n\tinfo = {} as ResizingInfo\n\n\tmarkId = ''\n\n\t// A switch to detect when the user is holding ctrl\n\tprivate didHoldCommand = false\n\n\t// we transition into the resizing state from the geo pointing state, which starts with a shape of size w: 1, h: 1,\n\t// so if the user drags x: +50, y: +50 after mouseDown, the shape will be w: 51, h: 51, which is too many pixels, alas\n\t// so we allow passing a further offset into this state to negate such issues\n\tcreationCursorOffset = { x: 0, y: 0 } as VecLike\n\n\tprivate snapshot = {} as any as Snapshot\n\n\toverride onEnter(info: ResizingInfo) {\n\t\tconst { isCreating = false, creatingMarkId, creationCursorOffset = { x: 0, y: 0 } } = info\n\n\t\tthis.info = info\n\t\tthis.didHoldCommand = false\n\n\t\tthis.parent.setCurrentToolIdMask(info.onInteractionEnd)\n\t\tthis.creationCursorOffset = creationCursorOffset\n\n\t\ttry {\n\t\t\t// On rare and mysterious occasions, the user can enter the resizing state with no shapes selected\n\t\t\tthis.snapshot = this._createSnapshot()\n\t\t} catch (e) {\n\t\t\tconsole.error(e)\n\t\t\tthis.cancel()\n\t\t\treturn\n\t\t}\n\n\t\tthis.markId = ''\n\n\t\tif (isCreating) {\n\t\t\tif (creatingMarkId) {\n\t\t\t\tthis.markId = creatingMarkId\n\t\t\t} else {\n\t\t\t\t// handle legacy implicit `creating:{shapeId}` marks\n\t\t\t\tconst markId = this.editor.getMarkIdMatching(\n\t\t\t\t\t`creating:${this.editor.getOnlySelectedShapeId()}`\n\t\t\t\t)\n\t\t\t\tif (markId) {\n\t\t\t\t\tthis.markId = markId\n\t\t\t\t}\n\t\t\t}\n\t\t} else {\n\t\t\tthis.markId = this.editor.markHistoryStoppingPoint('starting resizing')\n\t\t}\n\n\t\tif (isCreating) {\n\t\t\tthis.editor.setCursor({ type: 'cross', rotation: 0 })\n\t\t}\n\n\t\tthis.handleResizeStart()\n\t\tthis.updateShapes()\n\t}\n\n\toverride onTick({ elapsed }: TLTickEventInfo) {\n\t\tconst { editor } = this\n\t\teditor.edgeScrollManager.updateEdgeScrolling(elapsed)\n\t}\n\n\toverride onPointerMove() {\n\t\tthis.updateShapes()\n\t}\n\n\toverride onKeyDown() {\n\t\tthis.updateShapes()\n\t}\n\toverride onKeyUp() {\n\t\tthis.updateShapes()\n\t}\n\n\toverride onPointerUp() {\n\t\tthis.complete()\n\t}\n\n\toverride onComplete() {\n\t\tthis.complete()\n\t}\n\n\toverride onCancel() {\n\t\tthis.cancel()\n\t}\n\n\tprivate cancel() {\n\t\t// Restore initial models\n\t\tthis.editor.bailToMark(this.markId)\n\t\tif (this.info.onInteractionEnd) {\n\t\t\tthis.editor.setCurrentTool(this.info.onInteractionEnd, {})\n\t\t} else {\n\t\t\tthis.parent.transition('idle')\n\t\t}\n\t}\n\n\tprivate complete() {\n\t\tkickoutOccludedShapes(this.editor, this.snapshot.selectedShapeIds)\n\n\t\tthis.handleResizeEnd()\n\n\t\tif (this.info.isCreating && this.info.onCreate) {\n\t\t\tthis.info.onCreate?.(this.editor.getOnlySelectedShape())\n\t\t\treturn\n\t\t}\n\n\t\tif (this.editor.getInstanceState().isToolLocked && this.info.onInteractionEnd) {\n\t\t\tthis.editor.setCurrentTool(this.info.onInteractionEnd, {})\n\t\t\treturn\n\t\t}\n\n\t\tthis.parent.transition('idle')\n\t}\n\n\tprivate handleResizeStart() {\n\t\tconst { shapeSnapshots } = this.snapshot\n\n\t\tconst changes: TLShapePartial[] = []\n\n\t\tshapeSnapshots.forEach(({ shape }) => {\n\t\t\tconst util = this.editor.getShapeUtil(shape)\n\t\t\tconst change = util.onResizeStart?.(shape)\n\t\t\tif (change) {\n\t\t\t\tchanges.push(change)\n\t\t\t}\n\t\t})\n\n\t\tif (changes.length > 0) {\n\t\t\tthis.editor.updateShapes(changes)\n\t\t}\n\t}\n\n\tprivate handleResizeEnd() {\n\t\tconst { shapeSnapshots } = this.snapshot\n\n\t\tconst changes: TLShapePartial[] = []\n\n\t\tshapeSnapshots.forEach(({ shape }) => {\n\t\t\tconst current = this.editor.getShape(shape.id)!\n\t\t\tconst util = this.editor.getShapeUtil(shape)\n\t\t\tconst change = util.onResizeEnd?.(shape, current)\n\t\t\tif (change) {\n\t\t\t\tchanges.push(change)\n\t\t\t}\n\t\t})\n\n\t\tif (changes.length > 0) {\n\t\t\tthis.editor.updateShapes(changes)\n\t\t}\n\t}\n\n\tprivate updateShapes() {\n\t\tconst { altKey, shiftKey } = this.editor.inputs\n\t\tconst {\n\t\t\tframes,\n\t\t\tshapeSnapshots,\n\t\t\tselectionBounds,\n\t\t\tcursorHandleOffset,\n\t\t\tselectedShapeIds,\n\t\t\tselectionRotation,\n\t\t\tcanShapesDeform,\n\t\t} = this.snapshot\n\n\t\tlet isAspectRatioLocked = shiftKey || !canShapesDeform\n\n\t\tif (shapeSnapshots.size === 1) {\n\t\t\tconst onlySnapshot = [...shapeSnapshots.values()][0]!\n\t\t\tif (this.editor.isShapeOfType<TLTextShape>(onlySnapshot.shape, 'text')) {\n\t\t\t\tisAspectRatioLocked = !(this.info.handle === 'left' || this.info.handle === 'right')\n\t\t\t}\n\t\t}\n\n\t\t// first negate the 'cursor handle offset'\n\t\t// we need to do this because we do grid snapping based on the page point of the handle\n\t\t// rather than the page point of the cursor, so it's easier to pretend that the cursor\n\t\t// is really where the handle actually is\n\t\t//\n\t\t// *** Massively zoomed-in diagram of the initial mouseDown ***\n\t\t//\n\t\t//\n\t\t// \u2502\n\t\t// \u2502\n\t\t// \u2502\n\t\t// \u2502\n\t\t// \u2502\n\t\t// \u2502\n\t\t// \u2502\n\t\t// \u2502corner handle\n\t\t// \u250C\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2510\n\t\t// selection \u2502 \u2502\n\t\t// \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524 x\u25C4\u2500\u2500\u253C\u2500\u2500\u2500\u2500 drag handle point \u25B2\n\t\t// \u2502 \u2502 \u2502\n\t\t// \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518 \u251C\u2500 cursorHandleOffset.y\n\t\t// \u2502\n\t\t// originPagePoint\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u25BAx\u2500\u2510 \u25BC\n\t\t// \u2502 \u2514\u2500\u2510\n\t\t// \u2502 \u2514\u2500\u2510\n\t\t// \u2502 \u2502 mouse (sorry)\n\t\t// \u2514\u2500\u2500\u2510 \u250C\u2518\n\t\t// \u2502 \u2502\n\t\t// \u2514\u2500\u2518\n\t\t// \u25C4\u2500\u2500\u252C\u2500\u2500\u25BA\n\t\t// \u2502\n\t\t// cursorHandleOffset.x\n\n\t\tconst isHoldingAccel = isAccelKey(this.editor.inputs)\n\n\t\tconst currentPagePoint = this.editor.inputs.currentPagePoint\n\t\t\t.clone()\n\t\t\t.sub(cursorHandleOffset)\n\t\t\t.sub(this.creationCursorOffset)\n\n\t\tconst originPagePoint = this.editor.inputs.originPagePoint.clone().sub(cursorHandleOffset)\n\n\t\tif (this.editor.getInstanceState().isGridMode && !isHoldingAccel) {\n\t\t\tconst { gridSize } = this.editor.getDocumentSettings()\n\t\t\tcurrentPagePoint.snapToGrid(gridSize)\n\t\t}\n\n\t\tconst dragHandle = this.info.handle as SelectionCorner | SelectionEdge\n\t\tconst scaleOriginHandle = rotateSelectionHandle(dragHandle, Math.PI)\n\n\t\tthis.editor.snaps.clearIndicators()\n\n\t\tconst shouldSnap = this.editor.user.getIsSnapMode() ? !isHoldingAccel : isHoldingAccel\n\n\t\tif (shouldSnap && selectionRotation % HALF_PI === 0) {\n\t\t\tconst { nudge } = this.editor.snaps.shapeBounds.snapResizeShapes({\n\t\t\t\tdragDelta: Vec.Sub(currentPagePoint, originPagePoint),\n\t\t\t\tinitialSelectionPageBounds: this.snapshot.initialSelectionPageBounds,\n\t\t\t\thandle: rotateSelectionHandle(dragHandle, selectionRotation),\n\t\t\t\tisAspectRatioLocked,\n\t\t\t\tisResizingFromCenter: altKey,\n\t\t\t})\n\n\t\t\tcurrentPagePoint.add(nudge)\n\t\t}\n\n\t\t// get the page point of the selection handle opposite to the drag handle\n\t\t// or the center of the selection box if altKey is pressed\n\t\tconst scaleOriginPage = Vec.RotWith(\n\t\t\taltKey ? selectionBounds.center : selectionBounds.getHandlePoint(scaleOriginHandle),\n\t\t\tselectionBounds.point,\n\t\t\tselectionRotation\n\t\t)\n\n\t\t// calculate the scale by measuring the current distance between the drag handle and the scale origin\n\t\t// and dividing by the original distance between the drag handle and the scale origin\n\n\t\t// bug: for edges, the page point doesn't matter, the\n\n\t\tconst distanceFromScaleOriginNow = Vec.Sub(currentPagePoint, scaleOriginPage).rot(\n\t\t\t-selectionRotation\n\t\t)\n\n\t\tconst distanceFromScaleOriginAtStart = Vec.Sub(originPagePoint, scaleOriginPage).rot(\n\t\t\t-selectionRotation\n\t\t)\n\n\t\tconst scale = Vec.DivV(distanceFromScaleOriginNow, distanceFromScaleOriginAtStart)\n\n\t\tif (!Number.isFinite(scale.x)) scale.x = 1\n\t\tif (!Number.isFinite(scale.y)) scale.y = 1\n\n\t\tconst isXLocked = dragHandle === 'top' || dragHandle === 'bottom'\n\t\tconst isYLocked = dragHandle === 'left' || dragHandle === 'right'\n\n\t\t// lock an axis if required\n\t\tif (isAspectRatioLocked) {\n\t\t\tif (isYLocked) {\n\t\t\t\t// holding shift and dragging either the left or the right edge\n\t\t\t\tscale.y = Math.abs(scale.x)\n\t\t\t} else if (isXLocked) {\n\t\t\t\t// holding shift and dragging either the top or the bottom edge\n\t\t\t\tscale.x = Math.abs(scale.y)\n\t\t\t} else if (Math.abs(scale.x) > Math.abs(scale.y)) {\n\t\t\t\t// holding shift and the drag has moved further in the x dimension\n\t\t\t\tscale.y = Math.abs(scale.x) * (scale.y < 0 ? -1 : 1)\n\t\t\t} else {\n\t\t\t\t// holding shift and the drag has moved further in the y dimension\n\t\t\t\tscale.x = Math.abs(scale.y) * (scale.x < 0 ? -1 : 1)\n\t\t\t}\n\t\t} else {\n\t\t\t// not holding shift, but still need to lock axes if dragging an edge\n\t\t\tif (isXLocked) {\n\t\t\t\tscale.x = 1\n\t\t\t}\n\t\t\tif (isYLocked) {\n\t\t\t\tscale.y = 1\n\t\t\t}\n\t\t}\n\n\t\tif (!this.info.isCreating) {\n\t\t\tthis.updateCursor({\n\t\t\t\tdragHandle,\n\t\t\t\tisFlippedX: scale.x < 0,\n\t\t\t\tisFlippedY: scale.y < 0,\n\t\t\t\trotation: selectionRotation,\n\t\t\t})\n\t\t}\n\n\t\tfor (const id of shapeSnapshots.keys()) {\n\t\t\tconst snapshot = shapeSnapshots.get(id)!\n\n\t\t\tthis.editor.resizeShape(id, scale, {\n\t\t\t\tinitialShape: snapshot.shape,\n\t\t\t\tinitialBounds: snapshot.bounds,\n\t\t\t\tinitialPageTransform: snapshot.pageTransform,\n\t\t\t\tdragHandle,\n\t\t\t\tmode:\n\t\t\t\t\tselectedShapeIds.length === 1 && id === selectedShapeIds[0]\n\t\t\t\t\t\t? 'resize_bounds'\n\t\t\t\t\t\t: 'scale_shape',\n\t\t\t\tscaleOrigin: scaleOriginPage,\n\t\t\t\tisAspectRatioLocked,\n\t\t\t\tscaleAxisRotation: selectionRotation,\n\t\t\t\tskipStartAndEndCallbacks: true,\n\t\t\t})\n\t\t}\n\n\t\t// If there's only one shape snapshot and it's a frame and the user is holding ctrl,\n\t\t// then we preserve the position of the frame's children, almost like the user is cropping\n\t\t// the frame rather than resizing it.\n\t\tif (isHoldingAccel) {\n\t\t\tthis.didHoldCommand = true\n\n\t\t\tfor (const { id, children } of frames) {\n\t\t\t\tif (!children.length) continue\n\t\t\t\tconst initial = shapeSnapshots.get(id)!.shape\n\t\t\t\tconst current = this.editor.getShape(id)!\n\t\t\t\tif (!(initial && current)) continue\n\n\t\t\t\tconst dx = current.x - initial.x\n\t\t\t\tconst dy = current.y - initial.y\n\n\t\t\t\tconst delta = new Vec(dx, dy).rot(-initial.rotation)\n\n\t\t\t\tif (delta.x !== 0 || delta.y !== 0) {\n\t\t\t\t\tfor (const child of children) {\n\t\t\t\t\t\tthis.editor.updateShape({\n\t\t\t\t\t\t\tid: child.id,\n\t\t\t\t\t\t\ttype: child.type,\n\t\t\t\t\t\t\tx: child.x - delta.x,\n\t\t\t\t\t\t\ty: child.y - delta.y,\n\t\t\t\t\t\t})\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t} else if (this.didHoldCommand) {\n\t\t\t// If we're no longer holding the command key...\n\t\t\tthis.didHoldCommand = false\n\n\t\t\tfor (const { children } of frames) {\n\t\t\t\tif (!children.length) continue\n\t\t\t\tfor (const child of children) {\n\t\t\t\t\tthis.editor.updateShape({\n\t\t\t\t\t\tid: child.id,\n\t\t\t\t\t\ttype: child.type,\n\t\t\t\t\t\tx: child.x,\n\t\t\t\t\t\ty: child.y,\n\t\t\t\t\t})\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t// ---\n\n\tprivate updateCursor({\n\t\tdragHandle,\n\t\tisFlippedX,\n\t\tisFlippedY,\n\t\trotation,\n\t}: {\n\t\tdragHandle: SelectionCorner | SelectionEdge\n\t\tisFlippedX: boolean\n\t\tisFlippedY: boolean\n\t\trotation: number\n\t}) {\n\t\tconst nextCursor = { ...this.editor.getInstanceState().cursor }\n\n\t\tswitch (dragHandle) {\n\t\t\tcase 'top_left':\n\t\t\tcase 'bottom_right': {\n\t\t\t\tnextCursor.type = 'nwse-resize'\n\t\t\t\tif (isFlippedX !== isFlippedY) {\n\t\t\t\t\tnextCursor.type = 'nesw-resize'\n\t\t\t\t}\n\t\t\t\tbreak\n\t\t\t}\n\t\t\tcase 'top_right':\n\t\t\tcase 'bottom_left': {\n\t\t\t\tnextCursor.type = 'nesw-resize'\n\t\t\t\tif (isFlippedX !== isFlippedY) {\n\t\t\t\t\tnextCursor.type = 'nwse-resize'\n\t\t\t\t}\n\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\n\t\tnextCursor.rotation = rotation\n\n\t\tthis.editor.setCursor(nextCursor)\n\t}\n\n\toverride onExit() {\n\t\tthis.parent.setCurrentToolIdMask(undefined)\n\t\tthis.editor.setCursor({ type: 'default', rotation: 0 })\n\t\tthis.editor.snaps.clearIndicators()\n\t}\n\n\tprivate _createSnapshot() {\n\t\tconst { editor } = this\n\t\tconst selectedShapeIds = editor.getSelectedShapeIds()\n\t\tconst selectionRotation = editor.getSelectionRotation()\n\t\tconst {\n\t\t\tinputs: { originPagePoint },\n\t\t} = editor\n\n\t\tconst selectionBounds = editor.getSelectionRotatedPageBounds()\n\t\tif (!selectionBounds) throw Error('Resizing but nothing is selected')\n\n\t\tconst dragHandlePoint = Vec.RotWith(\n\t\t\tselectionBounds.getHandlePoint(this.info.handle!),\n\t\t\tselectionBounds.point,\n\t\t\tselectionRotation\n\t\t)\n\n\t\tconst cursorHandleOffset = Vec.Sub(originPagePoint, dragHandlePoint)\n\n\t\tconst shapeSnapshots = new Map<\n\t\t\tTLShapeId,\n\t\t\t{\n\t\t\t\tshape: TLShape\n\t\t\t\tbounds: Box\n\t\t\t\tpageTransform: Mat\n\t\t\t\tpageRotation: number\n\t\t\t\tisAspectRatioLocked: boolean\n\t\t\t}\n\t\t>()\n\n\t\tconst frames: { id: TLShapeId; children: TLShape[] }[] = []\n\n\t\tconst populateResizingShapes = (shapeId: TLShapeId): false | undefined => {\n\t\t\tconst shape = editor.getShape(shapeId)\n\t\t\tif (!shape) return false\n\n\t\t\tconst util = editor.getShapeUtil(shape)\n\n\t\t\t// If the shape can resize, add it to the resizing shapes snapshots\n\t\t\tif (util.canResize(shape)) {\n\t\t\t\tconst pageTransform = editor.getShapePageTransform(shape)!\n\t\t\t\tshapeSnapshots.set(shape.id, {\n\t\t\t\t\tshape,\n\t\t\t\t\tbounds: editor.getShapeGeometry(shape).bounds,\n\t\t\t\t\tpageTransform,\n\t\t\t\t\tpageRotation: Mat.Decompose(pageTransform).rotation,\n\t\t\t\t\tisAspectRatioLocked: util.isAspectRatioLocked(shape),\n\t\t\t\t})\n\t\t\t}\n\n\t\t\t// Special case:\n\t\t\t// For frames, we don't want to resize children but we DO want to get a snapshot of their children so that we can restore their\n\t\t\t// positions with the accel key behavior. We could break this further into APIs, for example by collecting snapshots of all\n\t\t\t// descendants (easy) but also flagging with behavior like \"resize\" or \"keep absolute position\" or \"reposition only with accel key\",\n\t\t\t// though I'm not sure where that would be defined; perhaps better handled with onResizeStart / onResize callbacks on the util, and\n\t\t\t// pass `accelKeyIsPressed` as well as `accelKeyWasPressed`?\n\t\t\tif (editor.isShapeOfType<TLFrameShape>(shape, 'frame')) {\n\t\t\t\tframes.push({\n\t\t\t\t\tid: shape.id,\n\t\t\t\t\tchildren: compact(\n\t\t\t\t\t\teditor.getSortedChildIdsForParent(shape).map((id) => editor.getShape(id))\n\t\t\t\t\t),\n\t\t\t\t})\n\t\t\t}\n\n\t\t\t// This will stop the traversal of descendants\n\t\t\tif (!util.canResizeChildren(shape)) return false\n\t\t}\n\n\t\tselectedShapeIds.forEach((shapeId) => {\n\t\t\tconst keepDescending = populateResizingShapes(shapeId)\n\t\t\tif (keepDescending === false) return\n\t\t\teditor.visitDescendants(shapeId, populateResizingShapes)\n\t\t})\n\n\t\tconst canShapesDeform = ![...shapeSnapshots.values()].some(\n\t\t\t(shape) =>\n\t\t\t\t!areAnglesCompatible(shape.pageRotation, selectionRotation) || shape.isAspectRatioLocked\n\t\t)\n\n\t\treturn {\n\t\t\tshapeSnapshots,\n\t\t\tselectionBounds,\n\t\t\tcursorHandleOffset,\n\t\t\tselectionRotation,\n\t\t\tselectedShapeIds,\n\t\t\tcanShapesDeform,\n\t\t\tinitialSelectionPageBounds: this.editor.getSelectionPageBounds()!,\n\t\t\tframes,\n\t\t}\n\t}\n}\n\ntype Snapshot = ReturnType<Resizing['_createSnapshot']>\n\nconst ORDERED_SELECTION_HANDLES: (SelectionEdge | SelectionCorner)[] = [\n\t'top',\n\t'top_right',\n\t'right',\n\t'bottom_right',\n\t'bottom',\n\t'bottom_left',\n\t'left',\n\t'top_left',\n]\n\nexport function rotateSelectionHandle(handle: SelectionEdge | SelectionCorner, rotation: number) {\n\t// first find out how many tau we need to rotate by\n\trotation = rotation % PI2\n\tconst numSteps = Math.round(rotation / (PI / 4))\n\n\tconst currentIndex = ORDERED_SELECTION_HANDLES.indexOf(handle)\n\treturn ORDERED_SELECTION_HANDLES[(currentIndex + numSteps) % ORDERED_SELECTION_HANDLES.length]\n}\n"],
|
|
5
|
-
"mappings": "AAAA;AAAA,EAEC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EAGA;AAAA,EAQA;AAAA,EAEA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACM;AAYA,MAAM,iBAAiB,UAAU;AAAA,EACvC,OAAgB,KAAK;AAAA,EAErB,OAAO,CAAC;AAAA,EAER,SAAS;AAAA;AAAA,EAGD,iBAAiB;AAAA;AAAA;AAAA;AAAA,EAKzB,uBAAuB,EAAE,GAAG,GAAG,GAAG,EAAE;AAAA,EAE5B,WAAW,CAAC;AAAA,EAEX,QAAQ,MAAoB;AACpC,UAAM,EAAE,aAAa,OAAO,gBAAgB,uBAAuB,EAAE,GAAG,GAAG,GAAG,EAAE,EAAE,IAAI;AAEtF,SAAK,OAAO;AACZ,SAAK,iBAAiB;AAEtB,SAAK,OAAO,qBAAqB,KAAK,gBAAgB;AACtD,SAAK,uBAAuB;AAE5B,QAAI;AAEH,WAAK,WAAW,KAAK,gBAAgB;AAAA,IACtC,SAAS,GAAG;AACX,cAAQ,MAAM,CAAC;AACf,WAAK,OAAO;AACZ;AAAA,IACD;AAEA,SAAK,SAAS;AAEd,QAAI,YAAY;AACf,UAAI,gBAAgB;AACnB,aAAK,SAAS;AAAA,MACf,OAAO;AAEN,cAAM,SAAS,KAAK,OAAO;AAAA,UAC1B,YAAY,KAAK,OAAO,uBAAuB,CAAC;AAAA,QACjD;AACA,YAAI,QAAQ;AACX,eAAK,SAAS;AAAA,QACf;AAAA,MACD;AAAA,IACD,OAAO;AACN,WAAK,SAAS,KAAK,OAAO,yBAAyB,mBAAmB;AAAA,IACvE;AAEA,QAAI,YAAY;AACf,WAAK,OAAO,UAAU,EAAE,MAAM,SAAS,UAAU,EAAE,CAAC;AAAA,IACrD;AAEA,SAAK,kBAAkB;AACvB,SAAK,aAAa;AAAA,EACnB;AAAA,EAES,OAAO,EAAE,QAAQ,GAAoB;AAC7C,UAAM,EAAE,OAAO,IAAI;AACnB,WAAO,kBAAkB,oBAAoB,OAAO;AAAA,EACrD;AAAA,EAES,gBAAgB;AACxB,SAAK,aAAa;AAAA,EACnB;AAAA,EAES,YAAY;AACpB,SAAK,aAAa;AAAA,EACnB;AAAA,EACS,UAAU;AAClB,SAAK,aAAa;AAAA,EACnB;AAAA,EAES,cAAc;AACtB,SAAK,SAAS;AAAA,EACf;AAAA,EAES,aAAa;AACrB,SAAK,SAAS;AAAA,EACf;AAAA,EAES,WAAW;AACnB,SAAK,OAAO;AAAA,EACb;AAAA,EAEQ,SAAS;AAEhB,SAAK,OAAO,WAAW,KAAK,MAAM;
|
|
4
|
+
"sourcesContent": ["import {\n\tBox,\n\tHALF_PI,\n\tMat,\n\tPI,\n\tPI2,\n\tSelectionCorner,\n\tSelectionEdge,\n\tStateNode,\n\tTLFrameShape,\n\tTLPointerEventInfo,\n\tTLShape,\n\tTLShapeId,\n\tTLShapePartial,\n\tTLTextShape,\n\tTLTickEventInfo,\n\tVec,\n\tVecLike,\n\tareAnglesCompatible,\n\tcompact,\n\tisAccelKey,\n\tkickoutOccludedShapes,\n} from '@tldraw/editor'\n\nexport type ResizingInfo = TLPointerEventInfo & {\n\ttarget: 'selection'\n\thandle: SelectionEdge | SelectionCorner\n\tisCreating?: boolean\n\tcreatingMarkId?: string\n\tonCreate?(shape: TLShape | null): void\n\tcreationCursorOffset?: VecLike\n\tonInteractionEnd?: string\n}\n\nexport class Resizing extends StateNode {\n\tstatic override id = 'resizing'\n\n\tinfo = {} as ResizingInfo\n\n\tmarkId = ''\n\n\t// A switch to detect when the user is holding ctrl\n\tprivate didHoldCommand = false\n\n\t// we transition into the resizing state from the geo pointing state, which starts with a shape of size w: 1, h: 1,\n\t// so if the user drags x: +50, y: +50 after mouseDown, the shape will be w: 51, h: 51, which is too many pixels, alas\n\t// so we allow passing a further offset into this state to negate such issues\n\tcreationCursorOffset = { x: 0, y: 0 } as VecLike\n\n\tprivate snapshot = {} as any as Snapshot\n\n\toverride onEnter(info: ResizingInfo) {\n\t\tconst { isCreating = false, creatingMarkId, creationCursorOffset = { x: 0, y: 0 } } = info\n\n\t\tthis.info = info\n\t\tthis.didHoldCommand = false\n\n\t\tthis.parent.setCurrentToolIdMask(info.onInteractionEnd)\n\t\tthis.creationCursorOffset = creationCursorOffset\n\n\t\ttry {\n\t\t\t// On rare and mysterious occasions, the user can enter the resizing state with no shapes selected\n\t\t\tthis.snapshot = this._createSnapshot()\n\t\t} catch (e) {\n\t\t\tconsole.error(e)\n\t\t\tthis.cancel()\n\t\t\treturn\n\t\t}\n\n\t\tthis.markId = ''\n\n\t\tif (isCreating) {\n\t\t\tif (creatingMarkId) {\n\t\t\t\tthis.markId = creatingMarkId\n\t\t\t} else {\n\t\t\t\t// handle legacy implicit `creating:{shapeId}` marks\n\t\t\t\tconst markId = this.editor.getMarkIdMatching(\n\t\t\t\t\t`creating:${this.editor.getOnlySelectedShapeId()}`\n\t\t\t\t)\n\t\t\t\tif (markId) {\n\t\t\t\t\tthis.markId = markId\n\t\t\t\t}\n\t\t\t}\n\t\t} else {\n\t\t\tthis.markId = this.editor.markHistoryStoppingPoint('starting resizing')\n\t\t}\n\n\t\tif (isCreating) {\n\t\t\tthis.editor.setCursor({ type: 'cross', rotation: 0 })\n\t\t}\n\n\t\tthis.handleResizeStart()\n\t\tthis.updateShapes()\n\t}\n\n\toverride onTick({ elapsed }: TLTickEventInfo) {\n\t\tconst { editor } = this\n\t\teditor.edgeScrollManager.updateEdgeScrolling(elapsed)\n\t}\n\n\toverride onPointerMove() {\n\t\tthis.updateShapes()\n\t}\n\n\toverride onKeyDown() {\n\t\tthis.updateShapes()\n\t}\n\toverride onKeyUp() {\n\t\tthis.updateShapes()\n\t}\n\n\toverride onPointerUp() {\n\t\tthis.complete()\n\t}\n\n\toverride onComplete() {\n\t\tthis.complete()\n\t}\n\n\toverride onCancel() {\n\t\tthis.cancel()\n\t}\n\n\tprivate cancel() {\n\t\t// Call onResizeCancel callback before resetting\n\t\tconst { shapeSnapshots } = this.snapshot\n\n\t\tshapeSnapshots.forEach(({ shape }) => {\n\t\t\tconst current = this.editor.getShape(shape.id)\n\t\t\tif (current) {\n\t\t\t\tconst util = this.editor.getShapeUtil(shape)\n\t\t\t\tutil.onResizeCancel?.(shape, current)\n\t\t\t}\n\t\t})\n\n\t\tthis.editor.bailToMark(this.markId)\n\n\t\tif (this.info.onInteractionEnd) {\n\t\t\tthis.editor.setCurrentTool(this.info.onInteractionEnd, {})\n\t\t} else {\n\t\t\tthis.parent.transition('idle')\n\t\t}\n\t}\n\n\tprivate complete() {\n\t\tkickoutOccludedShapes(this.editor, this.snapshot.selectedShapeIds)\n\n\t\tthis.handleResizeEnd()\n\n\t\tif (this.info.isCreating && this.info.onCreate) {\n\t\t\tthis.info.onCreate?.(this.editor.getOnlySelectedShape())\n\t\t\treturn\n\t\t}\n\n\t\tif (this.editor.getInstanceState().isToolLocked && this.info.onInteractionEnd) {\n\t\t\tthis.editor.setCurrentTool(this.info.onInteractionEnd, {})\n\t\t\treturn\n\t\t}\n\n\t\tthis.parent.transition('idle')\n\t}\n\n\tprivate handleResizeStart() {\n\t\tconst { shapeSnapshots } = this.snapshot\n\n\t\tconst changes: TLShapePartial[] = []\n\n\t\tshapeSnapshots.forEach(({ shape }) => {\n\t\t\tconst util = this.editor.getShapeUtil(shape)\n\t\t\tconst change = util.onResizeStart?.(shape)\n\t\t\tif (change) {\n\t\t\t\tchanges.push(change)\n\t\t\t}\n\t\t})\n\n\t\tif (changes.length > 0) {\n\t\t\tthis.editor.updateShapes(changes)\n\t\t}\n\t}\n\n\tprivate handleResizeEnd() {\n\t\tconst { shapeSnapshots } = this.snapshot\n\n\t\tconst changes: TLShapePartial[] = []\n\n\t\tshapeSnapshots.forEach(({ shape }) => {\n\t\t\tconst current = this.editor.getShape(shape.id)!\n\t\t\tconst util = this.editor.getShapeUtil(shape)\n\t\t\tconst change = util.onResizeEnd?.(shape, current)\n\t\t\tif (change) {\n\t\t\t\tchanges.push(change)\n\t\t\t}\n\t\t})\n\n\t\tif (changes.length > 0) {\n\t\t\tthis.editor.updateShapes(changes)\n\t\t}\n\t}\n\n\tprivate updateShapes() {\n\t\tconst { altKey, shiftKey } = this.editor.inputs\n\t\tconst {\n\t\t\tframes,\n\t\t\tshapeSnapshots,\n\t\t\tselectionBounds,\n\t\t\tcursorHandleOffset,\n\t\t\tselectedShapeIds,\n\t\t\tselectionRotation,\n\t\t\tcanShapesDeform,\n\t\t} = this.snapshot\n\n\t\tlet isAspectRatioLocked = shiftKey || !canShapesDeform\n\n\t\tif (shapeSnapshots.size === 1) {\n\t\t\tconst onlySnapshot = [...shapeSnapshots.values()][0]!\n\t\t\tif (this.editor.isShapeOfType<TLTextShape>(onlySnapshot.shape, 'text')) {\n\t\t\t\tisAspectRatioLocked = !(this.info.handle === 'left' || this.info.handle === 'right')\n\t\t\t}\n\t\t}\n\n\t\t// first negate the 'cursor handle offset'\n\t\t// we need to do this because we do grid snapping based on the page point of the handle\n\t\t// rather than the page point of the cursor, so it's easier to pretend that the cursor\n\t\t// is really where the handle actually is\n\t\t//\n\t\t// *** Massively zoomed-in diagram of the initial mouseDown ***\n\t\t//\n\t\t//\n\t\t// \u2502\n\t\t// \u2502\n\t\t// \u2502\n\t\t// \u2502\n\t\t// \u2502\n\t\t// \u2502\n\t\t// \u2502\n\t\t// \u2502corner handle\n\t\t// \u250C\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2510\n\t\t// selection \u2502 \u2502\n\t\t// \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524 x\u25C4\u2500\u2500\u253C\u2500\u2500\u2500\u2500 drag handle point \u25B2\n\t\t// \u2502 \u2502 \u2502\n\t\t// \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518 \u251C\u2500 cursorHandleOffset.y\n\t\t// \u2502\n\t\t// originPagePoint\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u25BAx\u2500\u2510 \u25BC\n\t\t// \u2502 \u2514\u2500\u2510\n\t\t// \u2502 \u2514\u2500\u2510\n\t\t// \u2502 \u2502 mouse (sorry)\n\t\t// \u2514\u2500\u2500\u2510 \u250C\u2518\n\t\t// \u2502 \u2502\n\t\t// \u2514\u2500\u2518\n\t\t// \u25C4\u2500\u2500\u252C\u2500\u2500\u25BA\n\t\t// \u2502\n\t\t// cursorHandleOffset.x\n\n\t\tconst isHoldingAccel = isAccelKey(this.editor.inputs)\n\n\t\tconst currentPagePoint = this.editor.inputs.currentPagePoint\n\t\t\t.clone()\n\t\t\t.sub(cursorHandleOffset)\n\t\t\t.sub(this.creationCursorOffset)\n\n\t\tconst originPagePoint = this.editor.inputs.originPagePoint.clone().sub(cursorHandleOffset)\n\n\t\tif (this.editor.getInstanceState().isGridMode && !isHoldingAccel) {\n\t\t\tconst { gridSize } = this.editor.getDocumentSettings()\n\t\t\tcurrentPagePoint.snapToGrid(gridSize)\n\t\t}\n\n\t\tconst dragHandle = this.info.handle as SelectionCorner | SelectionEdge\n\t\tconst scaleOriginHandle = rotateSelectionHandle(dragHandle, Math.PI)\n\n\t\tthis.editor.snaps.clearIndicators()\n\n\t\tconst shouldSnap = this.editor.user.getIsSnapMode() ? !isHoldingAccel : isHoldingAccel\n\n\t\tif (shouldSnap && selectionRotation % HALF_PI === 0) {\n\t\t\tconst { nudge } = this.editor.snaps.shapeBounds.snapResizeShapes({\n\t\t\t\tdragDelta: Vec.Sub(currentPagePoint, originPagePoint),\n\t\t\t\tinitialSelectionPageBounds: this.snapshot.initialSelectionPageBounds,\n\t\t\t\thandle: rotateSelectionHandle(dragHandle, selectionRotation),\n\t\t\t\tisAspectRatioLocked,\n\t\t\t\tisResizingFromCenter: altKey,\n\t\t\t})\n\n\t\t\tcurrentPagePoint.add(nudge)\n\t\t}\n\n\t\t// get the page point of the selection handle opposite to the drag handle\n\t\t// or the center of the selection box if altKey is pressed\n\t\tconst scaleOriginPage = Vec.RotWith(\n\t\t\taltKey ? selectionBounds.center : selectionBounds.getHandlePoint(scaleOriginHandle),\n\t\t\tselectionBounds.point,\n\t\t\tselectionRotation\n\t\t)\n\n\t\t// calculate the scale by measuring the current distance between the drag handle and the scale origin\n\t\t// and dividing by the original distance between the drag handle and the scale origin\n\n\t\t// bug: for edges, the page point doesn't matter, the\n\n\t\tconst distanceFromScaleOriginNow = Vec.Sub(currentPagePoint, scaleOriginPage).rot(\n\t\t\t-selectionRotation\n\t\t)\n\n\t\tconst distanceFromScaleOriginAtStart = Vec.Sub(originPagePoint, scaleOriginPage).rot(\n\t\t\t-selectionRotation\n\t\t)\n\n\t\tconst scale = Vec.DivV(distanceFromScaleOriginNow, distanceFromScaleOriginAtStart)\n\n\t\tif (!Number.isFinite(scale.x)) scale.x = 1\n\t\tif (!Number.isFinite(scale.y)) scale.y = 1\n\n\t\tconst isXLocked = dragHandle === 'top' || dragHandle === 'bottom'\n\t\tconst isYLocked = dragHandle === 'left' || dragHandle === 'right'\n\n\t\t// lock an axis if required\n\t\tif (isAspectRatioLocked) {\n\t\t\tif (isYLocked) {\n\t\t\t\t// holding shift and dragging either the left or the right edge\n\t\t\t\tscale.y = Math.abs(scale.x)\n\t\t\t} else if (isXLocked) {\n\t\t\t\t// holding shift and dragging either the top or the bottom edge\n\t\t\t\tscale.x = Math.abs(scale.y)\n\t\t\t} else if (Math.abs(scale.x) > Math.abs(scale.y)) {\n\t\t\t\t// holding shift and the drag has moved further in the x dimension\n\t\t\t\tscale.y = Math.abs(scale.x) * (scale.y < 0 ? -1 : 1)\n\t\t\t} else {\n\t\t\t\t// holding shift and the drag has moved further in the y dimension\n\t\t\t\tscale.x = Math.abs(scale.y) * (scale.x < 0 ? -1 : 1)\n\t\t\t}\n\t\t} else {\n\t\t\t// not holding shift, but still need to lock axes if dragging an edge\n\t\t\tif (isXLocked) {\n\t\t\t\tscale.x = 1\n\t\t\t}\n\t\t\tif (isYLocked) {\n\t\t\t\tscale.y = 1\n\t\t\t}\n\t\t}\n\n\t\tif (!this.info.isCreating) {\n\t\t\tthis.updateCursor({\n\t\t\t\tdragHandle,\n\t\t\t\tisFlippedX: scale.x < 0,\n\t\t\t\tisFlippedY: scale.y < 0,\n\t\t\t\trotation: selectionRotation,\n\t\t\t})\n\t\t}\n\n\t\tfor (const id of shapeSnapshots.keys()) {\n\t\t\tconst snapshot = shapeSnapshots.get(id)!\n\n\t\t\tthis.editor.resizeShape(id, scale, {\n\t\t\t\tinitialShape: snapshot.shape,\n\t\t\t\tinitialBounds: snapshot.bounds,\n\t\t\t\tinitialPageTransform: snapshot.pageTransform,\n\t\t\t\tdragHandle,\n\t\t\t\tmode:\n\t\t\t\t\tselectedShapeIds.length === 1 && id === selectedShapeIds[0]\n\t\t\t\t\t\t? 'resize_bounds'\n\t\t\t\t\t\t: 'scale_shape',\n\t\t\t\tscaleOrigin: scaleOriginPage,\n\t\t\t\tisAspectRatioLocked,\n\t\t\t\tscaleAxisRotation: selectionRotation,\n\t\t\t\tskipStartAndEndCallbacks: true,\n\t\t\t})\n\t\t}\n\n\t\t// If there's only one shape snapshot and it's a frame and the user is holding ctrl,\n\t\t// then we preserve the position of the frame's children, almost like the user is cropping\n\t\t// the frame rather than resizing it.\n\t\tif (isHoldingAccel) {\n\t\t\tthis.didHoldCommand = true\n\n\t\t\tfor (const { id, children } of frames) {\n\t\t\t\tif (!children.length) continue\n\t\t\t\tconst initial = shapeSnapshots.get(id)!.shape\n\t\t\t\tconst current = this.editor.getShape(id)!\n\t\t\t\tif (!(initial && current)) continue\n\n\t\t\t\tconst dx = current.x - initial.x\n\t\t\t\tconst dy = current.y - initial.y\n\n\t\t\t\tconst delta = new Vec(dx, dy).rot(-initial.rotation)\n\n\t\t\t\tif (delta.x !== 0 || delta.y !== 0) {\n\t\t\t\t\tfor (const child of children) {\n\t\t\t\t\t\tthis.editor.updateShape({\n\t\t\t\t\t\t\tid: child.id,\n\t\t\t\t\t\t\ttype: child.type,\n\t\t\t\t\t\t\tx: child.x - delta.x,\n\t\t\t\t\t\t\ty: child.y - delta.y,\n\t\t\t\t\t\t})\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t} else if (this.didHoldCommand) {\n\t\t\t// If we're no longer holding the command key...\n\t\t\tthis.didHoldCommand = false\n\n\t\t\tfor (const { children } of frames) {\n\t\t\t\tif (!children.length) continue\n\t\t\t\tfor (const child of children) {\n\t\t\t\t\tthis.editor.updateShape({\n\t\t\t\t\t\tid: child.id,\n\t\t\t\t\t\ttype: child.type,\n\t\t\t\t\t\tx: child.x,\n\t\t\t\t\t\ty: child.y,\n\t\t\t\t\t})\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t// ---\n\n\tprivate updateCursor({\n\t\tdragHandle,\n\t\tisFlippedX,\n\t\tisFlippedY,\n\t\trotation,\n\t}: {\n\t\tdragHandle: SelectionCorner | SelectionEdge\n\t\tisFlippedX: boolean\n\t\tisFlippedY: boolean\n\t\trotation: number\n\t}) {\n\t\tconst nextCursor = { ...this.editor.getInstanceState().cursor }\n\n\t\tswitch (dragHandle) {\n\t\t\tcase 'top_left':\n\t\t\tcase 'bottom_right': {\n\t\t\t\tnextCursor.type = 'nwse-resize'\n\t\t\t\tif (isFlippedX !== isFlippedY) {\n\t\t\t\t\tnextCursor.type = 'nesw-resize'\n\t\t\t\t}\n\t\t\t\tbreak\n\t\t\t}\n\t\t\tcase 'top_right':\n\t\t\tcase 'bottom_left': {\n\t\t\t\tnextCursor.type = 'nesw-resize'\n\t\t\t\tif (isFlippedX !== isFlippedY) {\n\t\t\t\t\tnextCursor.type = 'nwse-resize'\n\t\t\t\t}\n\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\n\t\tnextCursor.rotation = rotation\n\n\t\tthis.editor.setCursor(nextCursor)\n\t}\n\n\toverride onExit() {\n\t\tthis.parent.setCurrentToolIdMask(undefined)\n\t\tthis.editor.setCursor({ type: 'default', rotation: 0 })\n\t\tthis.editor.snaps.clearIndicators()\n\t}\n\n\tprivate _createSnapshot() {\n\t\tconst { editor } = this\n\t\tconst selectedShapeIds = editor.getSelectedShapeIds()\n\t\tconst selectionRotation = editor.getSelectionRotation()\n\t\tconst {\n\t\t\tinputs: { originPagePoint },\n\t\t} = editor\n\n\t\tconst selectionBounds = editor.getSelectionRotatedPageBounds()\n\t\tif (!selectionBounds) throw Error('Resizing but nothing is selected')\n\n\t\tconst dragHandlePoint = Vec.RotWith(\n\t\t\tselectionBounds.getHandlePoint(this.info.handle!),\n\t\t\tselectionBounds.point,\n\t\t\tselectionRotation\n\t\t)\n\n\t\tconst cursorHandleOffset = Vec.Sub(originPagePoint, dragHandlePoint)\n\n\t\tconst shapeSnapshots = new Map<\n\t\t\tTLShapeId,\n\t\t\t{\n\t\t\t\tshape: TLShape\n\t\t\t\tbounds: Box\n\t\t\t\tpageTransform: Mat\n\t\t\t\tpageRotation: number\n\t\t\t\tisAspectRatioLocked: boolean\n\t\t\t}\n\t\t>()\n\n\t\tconst frames: { id: TLShapeId; children: TLShape[] }[] = []\n\n\t\tconst populateResizingShapes = (shapeId: TLShapeId): false | undefined => {\n\t\t\tconst shape = editor.getShape(shapeId)\n\t\t\tif (!shape) return false\n\n\t\t\tconst util = editor.getShapeUtil(shape)\n\n\t\t\t// If the shape can resize, add it to the resizing shapes snapshots\n\t\t\tif (util.canResize(shape)) {\n\t\t\t\tconst pageTransform = editor.getShapePageTransform(shape)!\n\t\t\t\tshapeSnapshots.set(shape.id, {\n\t\t\t\t\tshape,\n\t\t\t\t\tbounds: editor.getShapeGeometry(shape).bounds,\n\t\t\t\t\tpageTransform,\n\t\t\t\t\tpageRotation: Mat.Decompose(pageTransform).rotation,\n\t\t\t\t\tisAspectRatioLocked: util.isAspectRatioLocked(shape),\n\t\t\t\t})\n\t\t\t}\n\n\t\t\t// Special case:\n\t\t\t// For frames, we don't want to resize children but we DO want to get a snapshot of their children so that we can restore their\n\t\t\t// positions with the accel key behavior. We could break this further into APIs, for example by collecting snapshots of all\n\t\t\t// descendants (easy) but also flagging with behavior like \"resize\" or \"keep absolute position\" or \"reposition only with accel key\",\n\t\t\t// though I'm not sure where that would be defined; perhaps better handled with onResizeStart / onResize callbacks on the util, and\n\t\t\t// pass `accelKeyIsPressed` as well as `accelKeyWasPressed`?\n\t\t\tif (editor.isShapeOfType<TLFrameShape>(shape, 'frame')) {\n\t\t\t\tframes.push({\n\t\t\t\t\tid: shape.id,\n\t\t\t\t\tchildren: compact(\n\t\t\t\t\t\teditor.getSortedChildIdsForParent(shape).map((id) => editor.getShape(id))\n\t\t\t\t\t),\n\t\t\t\t})\n\t\t\t}\n\n\t\t\t// This will stop the traversal of descendants\n\t\t\tif (!util.canResizeChildren(shape)) return false\n\t\t}\n\n\t\tselectedShapeIds.forEach((shapeId) => {\n\t\t\tconst keepDescending = populateResizingShapes(shapeId)\n\t\t\tif (keepDescending === false) return\n\t\t\teditor.visitDescendants(shapeId, populateResizingShapes)\n\t\t})\n\n\t\tconst canShapesDeform = ![...shapeSnapshots.values()].some(\n\t\t\t(shape) =>\n\t\t\t\t!areAnglesCompatible(shape.pageRotation, selectionRotation) || shape.isAspectRatioLocked\n\t\t)\n\n\t\treturn {\n\t\t\tshapeSnapshots,\n\t\t\tselectionBounds,\n\t\t\tcursorHandleOffset,\n\t\t\tselectionRotation,\n\t\t\tselectedShapeIds,\n\t\t\tcanShapesDeform,\n\t\t\tinitialSelectionPageBounds: this.editor.getSelectionPageBounds()!,\n\t\t\tframes,\n\t\t}\n\t}\n}\n\ntype Snapshot = ReturnType<Resizing['_createSnapshot']>\n\nconst ORDERED_SELECTION_HANDLES: (SelectionEdge | SelectionCorner)[] = [\n\t'top',\n\t'top_right',\n\t'right',\n\t'bottom_right',\n\t'bottom',\n\t'bottom_left',\n\t'left',\n\t'top_left',\n]\n\nexport function rotateSelectionHandle(handle: SelectionEdge | SelectionCorner, rotation: number) {\n\t// first find out how many tau we need to rotate by\n\trotation = rotation % PI2\n\tconst numSteps = Math.round(rotation / (PI / 4))\n\n\tconst currentIndex = ORDERED_SELECTION_HANDLES.indexOf(handle)\n\treturn ORDERED_SELECTION_HANDLES[(currentIndex + numSteps) % ORDERED_SELECTION_HANDLES.length]\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -78,6 +78,14 @@ class Rotating extends StateNode {
|
|
|
78
78
|
});
|
|
79
79
|
}
|
|
80
80
|
cancel() {
|
|
81
|
+
const { shapeSnapshots } = this.snapshot;
|
|
82
|
+
shapeSnapshots.forEach(({ shape }) => {
|
|
83
|
+
const current = this.editor.getShape(shape.id);
|
|
84
|
+
if (current) {
|
|
85
|
+
const util = this.editor.getShapeUtil(shape);
|
|
86
|
+
util.onRotateCancel?.(shape, current);
|
|
87
|
+
}
|
|
88
|
+
});
|
|
81
89
|
this.editor.bailToMark(this.markId);
|
|
82
90
|
if (this.info.onInteractionEnd) {
|
|
83
91
|
this.editor.setCurrentTool(this.info.onInteractionEnd, this.info);
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../../src/lib/tools/SelectTool/childStates/Rotating.ts"],
|
|
4
|
-
"sourcesContent": ["import {\n\tRotateCorner,\n\tStateNode,\n\tTLPointerEventInfo,\n\tTLRotationSnapshot,\n\tapplyRotationToSnapshotShapes,\n\tdegreesToRadians,\n\tgetRotationSnapshot,\n\tkickoutOccludedShapes,\n\tshortAngleDist,\n\tsnapAngle,\n} from '@tldraw/editor'\nimport { CursorTypeMap } from './PointingResizeHandle'\n\nconst ONE_DEGREE = Math.PI / 180\n\nexport class Rotating extends StateNode {\n\tstatic override id = 'rotating'\n\n\tsnapshot = {} as TLRotationSnapshot\n\n\tinfo = {} as Extract<TLPointerEventInfo, { target: 'selection' }> & { onInteractionEnd?: string }\n\n\tmarkId = ''\n\n\toverride onEnter(info: TLPointerEventInfo & { target: 'selection'; onInteractionEnd?: string }) {\n\t\t// Store the event information\n\t\tthis.info = info\n\t\tthis.parent.setCurrentToolIdMask(info.onInteractionEnd)\n\n\t\tthis.markId = this.editor.markHistoryStoppingPoint('rotate start')\n\n\t\tconst snapshot = getRotationSnapshot({\n\t\t\teditor: this.editor,\n\t\t\tids: this.editor.getSelectedShapeIds(),\n\t\t})\n\t\tif (!snapshot) return this.parent.transition('idle', this.info)\n\t\tthis.snapshot = snapshot\n\n\t\t// Trigger a pointer move\n\t\tconst newSelectionRotation = this._getRotationFromPointerPosition({\n\t\t\tsnapToNearestDegree: false,\n\t\t})\n\n\t\tapplyRotationToSnapshotShapes({\n\t\t\teditor: this.editor,\n\t\t\tdelta: this._getRotationFromPointerPosition({ snapToNearestDegree: false }),\n\t\t\tsnapshot: this.snapshot,\n\t\t\tstage: 'start',\n\t\t})\n\n\t\t// Update cursor\n\t\tthis.editor.setCursor({\n\t\t\ttype: CursorTypeMap[this.info.handle as RotateCorner],\n\t\t\trotation: newSelectionRotation + this.snapshot.initialShapesRotation,\n\t\t})\n\t}\n\n\toverride onExit() {\n\t\tthis.editor.setCursor({ type: 'default', rotation: 0 })\n\t\tthis.parent.setCurrentToolIdMask(undefined)\n\n\t\tthis.snapshot = {} as TLRotationSnapshot\n\t}\n\n\toverride onPointerMove() {\n\t\tthis.update()\n\t}\n\n\toverride onKeyDown() {\n\t\tthis.update()\n\t}\n\n\toverride onKeyUp() {\n\t\tthis.update()\n\t}\n\n\toverride onPointerUp() {\n\t\tthis.complete()\n\t}\n\n\toverride onComplete() {\n\t\tthis.complete()\n\t}\n\n\toverride onCancel() {\n\t\tthis.cancel()\n\t}\n\n\t// ---\n\n\tprivate update() {\n\t\tconst newSelectionRotation = this._getRotationFromPointerPosition({\n\t\t\tsnapToNearestDegree: false,\n\t\t})\n\n\t\tapplyRotationToSnapshotShapes({\n\t\t\teditor: this.editor,\n\t\t\tdelta: newSelectionRotation,\n\t\t\tsnapshot: this.snapshot,\n\t\t\tstage: 'update',\n\t\t})\n\n\t\t// Update cursor\n\t\tthis.editor.setCursor({\n\t\t\ttype: CursorTypeMap[this.info.handle as RotateCorner],\n\t\t\trotation: newSelectionRotation + this.snapshot.initialShapesRotation,\n\t\t})\n\t}\n\n\tprivate cancel() {\n\t\tthis.editor.bailToMark(this.markId)\n\t\tif (this.info.onInteractionEnd) {\n\t\t\tthis.editor.setCurrentTool(this.info.onInteractionEnd, this.info)\n\t\t} else {\n\t\t\tthis.parent.transition('idle', this.info)\n\t\t}\n\t}\n\n\tprivate complete() {\n\t\tapplyRotationToSnapshotShapes({\n\t\t\teditor: this.editor,\n\t\t\tdelta: this._getRotationFromPointerPosition({ snapToNearestDegree: true }),\n\t\t\tsnapshot: this.snapshot,\n\t\t\tstage: 'end',\n\t\t})\n\t\tkickoutOccludedShapes(\n\t\t\tthis.editor,\n\t\t\tthis.snapshot.shapeSnapshots.map((s) => s.shape.id)\n\t\t)\n\t\tif (this.info.onInteractionEnd) {\n\t\t\tthis.editor.setCurrentTool(this.info.onInteractionEnd, this.info)\n\t\t} else {\n\t\t\tthis.parent.transition('idle', this.info)\n\t\t}\n\t}\n\n\t_getRotationFromPointerPosition({ snapToNearestDegree }: { snapToNearestDegree: boolean }) {\n\t\tconst {\n\t\t\tinputs: { shiftKey, currentPagePoint },\n\t\t} = this.editor\n\t\tconst { initialCursorAngle, initialShapesRotation, initialPageCenter } = this.snapshot\n\n\t\t// The delta is the difference between the current angle and the initial angle\n\t\tconst preSnapRotationDelta = initialPageCenter.angle(currentPagePoint) - initialCursorAngle\n\t\tlet newSelectionRotation = initialShapesRotation + preSnapRotationDelta\n\n\t\tif (shiftKey) {\n\t\t\tnewSelectionRotation = snapAngle(newSelectionRotation, 24)\n\t\t} else if (snapToNearestDegree) {\n\t\t\tnewSelectionRotation = Math.round(newSelectionRotation / ONE_DEGREE) * ONE_DEGREE\n\n\t\t\tif (this.editor.getInstanceState().isCoarsePointer) {\n\t\t\t\tconst snappedToRightAngle = snapAngle(newSelectionRotation, 4)\n\t\t\t\tconst angleToRightAngle = shortAngleDist(newSelectionRotation, snappedToRightAngle)\n\t\t\t\tif (Math.abs(angleToRightAngle) < degreesToRadians(5)) {\n\t\t\t\t\tnewSelectionRotation = snappedToRightAngle\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn newSelectionRotation - initialShapesRotation\n\t}\n}\n"],
|
|
5
|
-
"mappings": "AAAA;AAAA,EAEC;AAAA,EAGA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACM;AACP,SAAS,qBAAqB;AAE9B,MAAM,aAAa,KAAK,KAAK;AAEtB,MAAM,iBAAiB,UAAU;AAAA,EACvC,OAAgB,KAAK;AAAA,EAErB,WAAW,CAAC;AAAA,EAEZ,OAAO,CAAC;AAAA,EAER,SAAS;AAAA,EAEA,QAAQ,MAA+E;AAE/F,SAAK,OAAO;AACZ,SAAK,OAAO,qBAAqB,KAAK,gBAAgB;AAEtD,SAAK,SAAS,KAAK,OAAO,yBAAyB,cAAc;AAEjE,UAAM,WAAW,oBAAoB;AAAA,MACpC,QAAQ,KAAK;AAAA,MACb,KAAK,KAAK,OAAO,oBAAoB;AAAA,IACtC,CAAC;AACD,QAAI,CAAC,SAAU,QAAO,KAAK,OAAO,WAAW,QAAQ,KAAK,IAAI;AAC9D,SAAK,WAAW;AAGhB,UAAM,uBAAuB,KAAK,gCAAgC;AAAA,MACjE,qBAAqB;AAAA,IACtB,CAAC;AAED,kCAA8B;AAAA,MAC7B,QAAQ,KAAK;AAAA,MACb,OAAO,KAAK,gCAAgC,EAAE,qBAAqB,MAAM,CAAC;AAAA,MAC1E,UAAU,KAAK;AAAA,MACf,OAAO;AAAA,IACR,CAAC;AAGD,SAAK,OAAO,UAAU;AAAA,MACrB,MAAM,cAAc,KAAK,KAAK,MAAsB;AAAA,MACpD,UAAU,uBAAuB,KAAK,SAAS;AAAA,IAChD,CAAC;AAAA,EACF;AAAA,EAES,SAAS;AACjB,SAAK,OAAO,UAAU,EAAE,MAAM,WAAW,UAAU,EAAE,CAAC;AACtD,SAAK,OAAO,qBAAqB,MAAS;AAE1C,SAAK,WAAW,CAAC;AAAA,EAClB;AAAA,EAES,gBAAgB;AACxB,SAAK,OAAO;AAAA,EACb;AAAA,EAES,YAAY;AACpB,SAAK,OAAO;AAAA,EACb;AAAA,EAES,UAAU;AAClB,SAAK,OAAO;AAAA,EACb;AAAA,EAES,cAAc;AACtB,SAAK,SAAS;AAAA,EACf;AAAA,EAES,aAAa;AACrB,SAAK,SAAS;AAAA,EACf;AAAA,EAES,WAAW;AACnB,SAAK,OAAO;AAAA,EACb;AAAA;AAAA,EAIQ,SAAS;AAChB,UAAM,uBAAuB,KAAK,gCAAgC;AAAA,MACjE,qBAAqB;AAAA,IACtB,CAAC;AAED,kCAA8B;AAAA,MAC7B,QAAQ,KAAK;AAAA,MACb,OAAO;AAAA,MACP,UAAU,KAAK;AAAA,MACf,OAAO;AAAA,IACR,CAAC;AAGD,SAAK,OAAO,UAAU;AAAA,MACrB,MAAM,cAAc,KAAK,KAAK,MAAsB;AAAA,MACpD,UAAU,uBAAuB,KAAK,SAAS;AAAA,IAChD,CAAC;AAAA,EACF;AAAA,EAEQ,SAAS;
|
|
4
|
+
"sourcesContent": ["import {\n\tRotateCorner,\n\tStateNode,\n\tTLPointerEventInfo,\n\tTLRotationSnapshot,\n\tapplyRotationToSnapshotShapes,\n\tdegreesToRadians,\n\tgetRotationSnapshot,\n\tkickoutOccludedShapes,\n\tshortAngleDist,\n\tsnapAngle,\n} from '@tldraw/editor'\nimport { CursorTypeMap } from './PointingResizeHandle'\n\nconst ONE_DEGREE = Math.PI / 180\n\nexport class Rotating extends StateNode {\n\tstatic override id = 'rotating'\n\n\tsnapshot = {} as TLRotationSnapshot\n\n\tinfo = {} as Extract<TLPointerEventInfo, { target: 'selection' }> & { onInteractionEnd?: string }\n\n\tmarkId = ''\n\n\toverride onEnter(info: TLPointerEventInfo & { target: 'selection'; onInteractionEnd?: string }) {\n\t\t// Store the event information\n\t\tthis.info = info\n\t\tthis.parent.setCurrentToolIdMask(info.onInteractionEnd)\n\n\t\tthis.markId = this.editor.markHistoryStoppingPoint('rotate start')\n\n\t\tconst snapshot = getRotationSnapshot({\n\t\t\teditor: this.editor,\n\t\t\tids: this.editor.getSelectedShapeIds(),\n\t\t})\n\t\tif (!snapshot) return this.parent.transition('idle', this.info)\n\t\tthis.snapshot = snapshot\n\n\t\t// Trigger a pointer move\n\t\tconst newSelectionRotation = this._getRotationFromPointerPosition({\n\t\t\tsnapToNearestDegree: false,\n\t\t})\n\n\t\tapplyRotationToSnapshotShapes({\n\t\t\teditor: this.editor,\n\t\t\tdelta: this._getRotationFromPointerPosition({ snapToNearestDegree: false }),\n\t\t\tsnapshot: this.snapshot,\n\t\t\tstage: 'start',\n\t\t})\n\n\t\t// Update cursor\n\t\tthis.editor.setCursor({\n\t\t\ttype: CursorTypeMap[this.info.handle as RotateCorner],\n\t\t\trotation: newSelectionRotation + this.snapshot.initialShapesRotation,\n\t\t})\n\t}\n\n\toverride onExit() {\n\t\tthis.editor.setCursor({ type: 'default', rotation: 0 })\n\t\tthis.parent.setCurrentToolIdMask(undefined)\n\n\t\tthis.snapshot = {} as TLRotationSnapshot\n\t}\n\n\toverride onPointerMove() {\n\t\tthis.update()\n\t}\n\n\toverride onKeyDown() {\n\t\tthis.update()\n\t}\n\n\toverride onKeyUp() {\n\t\tthis.update()\n\t}\n\n\toverride onPointerUp() {\n\t\tthis.complete()\n\t}\n\n\toverride onComplete() {\n\t\tthis.complete()\n\t}\n\n\toverride onCancel() {\n\t\tthis.cancel()\n\t}\n\n\t// ---\n\n\tprivate update() {\n\t\tconst newSelectionRotation = this._getRotationFromPointerPosition({\n\t\t\tsnapToNearestDegree: false,\n\t\t})\n\n\t\tapplyRotationToSnapshotShapes({\n\t\t\teditor: this.editor,\n\t\t\tdelta: newSelectionRotation,\n\t\t\tsnapshot: this.snapshot,\n\t\t\tstage: 'update',\n\t\t})\n\n\t\t// Update cursor\n\t\tthis.editor.setCursor({\n\t\t\ttype: CursorTypeMap[this.info.handle as RotateCorner],\n\t\t\trotation: newSelectionRotation + this.snapshot.initialShapesRotation,\n\t\t})\n\t}\n\n\tprivate cancel() {\n\t\t// Call onRotateCancel callback before bailing to mark\n\t\tconst { shapeSnapshots } = this.snapshot\n\n\t\tshapeSnapshots.forEach(({ shape }) => {\n\t\t\tconst current = this.editor.getShape(shape.id)\n\t\t\tif (current) {\n\t\t\t\tconst util = this.editor.getShapeUtil(shape)\n\t\t\t\tutil.onRotateCancel?.(shape, current)\n\t\t\t}\n\t\t})\n\n\t\tthis.editor.bailToMark(this.markId)\n\t\tif (this.info.onInteractionEnd) {\n\t\t\tthis.editor.setCurrentTool(this.info.onInteractionEnd, this.info)\n\t\t} else {\n\t\t\tthis.parent.transition('idle', this.info)\n\t\t}\n\t}\n\n\tprivate complete() {\n\t\tapplyRotationToSnapshotShapes({\n\t\t\teditor: this.editor,\n\t\t\tdelta: this._getRotationFromPointerPosition({ snapToNearestDegree: true }),\n\t\t\tsnapshot: this.snapshot,\n\t\t\tstage: 'end',\n\t\t})\n\t\tkickoutOccludedShapes(\n\t\t\tthis.editor,\n\t\t\tthis.snapshot.shapeSnapshots.map((s) => s.shape.id)\n\t\t)\n\t\tif (this.info.onInteractionEnd) {\n\t\t\tthis.editor.setCurrentTool(this.info.onInteractionEnd, this.info)\n\t\t} else {\n\t\t\tthis.parent.transition('idle', this.info)\n\t\t}\n\t}\n\n\t_getRotationFromPointerPosition({ snapToNearestDegree }: { snapToNearestDegree: boolean }) {\n\t\tconst {\n\t\t\tinputs: { shiftKey, currentPagePoint },\n\t\t} = this.editor\n\t\tconst { initialCursorAngle, initialShapesRotation, initialPageCenter } = this.snapshot\n\n\t\t// The delta is the difference between the current angle and the initial angle\n\t\tconst preSnapRotationDelta = initialPageCenter.angle(currentPagePoint) - initialCursorAngle\n\t\tlet newSelectionRotation = initialShapesRotation + preSnapRotationDelta\n\n\t\tif (shiftKey) {\n\t\t\tnewSelectionRotation = snapAngle(newSelectionRotation, 24)\n\t\t} else if (snapToNearestDegree) {\n\t\t\tnewSelectionRotation = Math.round(newSelectionRotation / ONE_DEGREE) * ONE_DEGREE\n\n\t\t\tif (this.editor.getInstanceState().isCoarsePointer) {\n\t\t\t\tconst snappedToRightAngle = snapAngle(newSelectionRotation, 4)\n\t\t\t\tconst angleToRightAngle = shortAngleDist(newSelectionRotation, snappedToRightAngle)\n\t\t\t\tif (Math.abs(angleToRightAngle) < degreesToRadians(5)) {\n\t\t\t\t\tnewSelectionRotation = snappedToRightAngle\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn newSelectionRotation - initialShapesRotation\n\t}\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -196,6 +196,14 @@ class Translating extends (_a = StateNode, _updateParentTransforms_dec = [bind],
|
|
|
196
196
|
}
|
|
197
197
|
}
|
|
198
198
|
cancel() {
|
|
199
|
+
const { movingShapes } = this.snapshot;
|
|
200
|
+
movingShapes.forEach((shape) => {
|
|
201
|
+
const current = this.editor.getShape(shape.id);
|
|
202
|
+
if (current) {
|
|
203
|
+
const util = this.editor.getShapeUtil(shape);
|
|
204
|
+
util.onTranslateCancel?.(shape, current);
|
|
205
|
+
}
|
|
206
|
+
});
|
|
199
207
|
this.reset();
|
|
200
208
|
if (this.info.onInteractionEnd) {
|
|
201
209
|
this.editor.setCurrentTool(this.info.onInteractionEnd);
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../../src/lib/tools/SelectTool/childStates/Translating.ts"],
|
|
4
|
-
"sourcesContent": ["import {\n\tBoundsSnapPoint,\n\tEditor,\n\tMat,\n\tMatModel,\n\tPageRecordType,\n\tStateNode,\n\tTLNoteShape,\n\tTLPointerEventInfo,\n\tTLShape,\n\tTLShapePartial,\n\tTLTickEventInfo,\n\tVec,\n\tbind,\n\tcompact,\n\tisPageId,\n\tkickoutOccludedShapes,\n} from '@tldraw/editor'\nimport {\n\tNOTE_ADJACENT_POSITION_SNAP_RADIUS,\n\tNOTE_CENTER_OFFSET,\n\tgetAvailableNoteAdjacentPositions,\n} from '../../../shapes/note/noteHelpers'\nimport { DragAndDropManager } from '../DragAndDropManager'\n\nexport type TranslatingInfo = TLPointerEventInfo & {\n\ttarget: 'shape'\n\tisCreating?: boolean\n\tcreatingMarkId?: string\n\tonCreate?(): void\n\tdidStartInPit?: boolean\n\tonInteractionEnd?: string\n}\n\nexport class Translating extends StateNode {\n\tstatic override id = 'translating'\n\n\tinfo = {} as TranslatingInfo\n\n\tselectionSnapshot: TranslatingSnapshot = {} as any\n\n\tsnapshot: TranslatingSnapshot = {} as any\n\n\tmarkId = ''\n\n\tisCloning = false\n\tisCreating = false\n\tonCreate(_shape: TLShape | null): void {\n\t\treturn\n\t}\n\n\tdragAndDropManager = new DragAndDropManager(this.editor)\n\n\toverride onEnter(info: TranslatingInfo) {\n\t\tconst { isCreating = false, creatingMarkId, onCreate = () => void null } = info\n\n\t\tif (!this.editor.getSelectedShapeIds()?.length) {\n\t\t\tthis.parent.transition('idle')\n\t\t\treturn\n\t\t}\n\n\t\tthis.info = info\n\t\tthis.parent.setCurrentToolIdMask(info.onInteractionEnd)\n\t\tthis.isCreating = isCreating\n\n\t\tthis.markId = ''\n\n\t\tif (isCreating) {\n\t\t\tif (creatingMarkId) {\n\t\t\t\tthis.markId = creatingMarkId\n\t\t\t} else {\n\t\t\t\t// handle legacy implicit `creating:{shapeId}` marks\n\t\t\t\tconst markId = this.editor.getMarkIdMatching(\n\t\t\t\t\t`creating:${this.editor.getOnlySelectedShapeId()}`\n\t\t\t\t)\n\t\t\t\tif (markId) {\n\t\t\t\t\tthis.markId = markId\n\t\t\t\t}\n\t\t\t}\n\t\t} else {\n\t\t\tthis.markId = this.editor.markHistoryStoppingPoint('translating')\n\t\t}\n\n\t\tthis.onCreate = onCreate\n\n\t\tthis.isCloning = false\n\t\tthis.info = info\n\n\t\tthis.editor.setCursor({ type: 'move', rotation: 0 })\n\t\tthis.selectionSnapshot = getTranslatingSnapshot(this.editor)\n\n\t\t// Don't clone on create; otherwise clone on altKey\n\t\tif (!this.isCreating) {\n\t\t\tif (this.editor.inputs.altKey) {\n\t\t\t\tthis.startCloning()\n\t\t\t\tif (this.isCloning) return\n\t\t\t}\n\t\t}\n\n\t\tthis.snapshot = this.selectionSnapshot\n\t\tthis.handleStart()\n\t\tthis.updateShapes()\n\t}\n\n\toverride onExit() {\n\t\tthis.parent.setCurrentToolIdMask(undefined)\n\t\tthis.selectionSnapshot = {} as any\n\t\tthis.snapshot = {} as any\n\t\tthis.editor.snaps.clearIndicators()\n\t\tthis.editor.setCursor({ type: 'default', rotation: 0 })\n\t\tthis.dragAndDropManager.clear()\n\t}\n\n\toverride onTick({ elapsed }: TLTickEventInfo) {\n\t\tconst { editor } = this\n\t\teditor.edgeScrollManager.updateEdgeScrolling(elapsed)\n\t}\n\n\toverride onPointerMove() {\n\t\tthis.updateShapes()\n\t}\n\n\toverride onKeyDown() {\n\t\tif (this.editor.inputs.altKey && !this.isCloning) {\n\t\t\tthis.startCloning()\n\t\t\tif (this.isCloning) return\n\t\t}\n\n\t\t// need to update in case user pressed a different modifier key\n\t\tthis.updateShapes()\n\t}\n\n\toverride onKeyUp() {\n\t\tif (!this.editor.inputs.altKey && this.isCloning) {\n\t\t\tthis.stopCloning()\n\t\t\treturn\n\t\t}\n\n\t\t// need to update in case user pressed a different modifier key\n\t\tthis.updateShapes()\n\t}\n\n\toverride onPointerUp() {\n\t\tthis.complete()\n\t}\n\n\toverride onComplete() {\n\t\tthis.complete()\n\t}\n\n\toverride onCancel() {\n\t\tthis.cancel()\n\t}\n\n\tprotected startCloning() {\n\t\tif (this.isCreating) return\n\t\tconst shapeIds = Array.from(this.editor.getSelectedShapeIds())\n\n\t\t// If we can't create the shapes, don't even start cloning\n\t\tif (!this.editor.canCreateShapes(shapeIds)) return\n\n\t\tthis.isCloning = true\n\t\tthis.reset()\n\t\tthis.markId = this.editor.markHistoryStoppingPoint('translate cloning')\n\n\t\tthis.editor.duplicateShapes(Array.from(this.editor.getSelectedShapeIds()))\n\n\t\tthis.snapshot = getTranslatingSnapshot(this.editor)\n\t\tthis.handleStart()\n\t\tthis.updateShapes()\n\t}\n\n\tprotected stopCloning() {\n\t\tthis.isCloning = false\n\t\tthis.snapshot = this.selectionSnapshot\n\t\tthis.reset()\n\t\tthis.markId = this.editor.markHistoryStoppingPoint('translate')\n\t\tthis.updateShapes()\n\t}\n\n\treset() {\n\t\tthis.editor.bailToMark(this.markId)\n\t}\n\n\tprotected complete() {\n\t\tthis.updateShapes()\n\t\tthis.dragAndDropManager.dropShapes(this.snapshot.movingShapes)\n\t\tthis.handleEnd()\n\t\tkickoutOccludedShapes(\n\t\t\tthis.editor,\n\t\t\tthis.snapshot.movingShapes.map((s) => s.id)\n\t\t)\n\n\t\tif (this.editor.getInstanceState().isToolLocked && this.info.onInteractionEnd) {\n\t\t\tthis.editor.setCurrentTool(this.info.onInteractionEnd)\n\t\t} else {\n\t\t\tif (this.isCreating) {\n\t\t\t\tthis.onCreate?.(this.editor.getOnlySelectedShape())\n\t\t\t} else {\n\t\t\t\tthis.parent.transition('idle')\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate cancel() {\n\t\tthis.reset()\n\t\tif (this.info.onInteractionEnd) {\n\t\t\tthis.editor.setCurrentTool(this.info.onInteractionEnd)\n\t\t} else {\n\t\t\tthis.parent.transition('idle', this.info)\n\t\t}\n\t}\n\n\tprotected handleStart() {\n\t\tconst { movingShapes } = this.snapshot\n\n\t\tconst changes: TLShapePartial[] = []\n\n\t\tmovingShapes.forEach((shape) => {\n\t\t\tconst util = this.editor.getShapeUtil(shape)\n\t\t\tconst change = util.onTranslateStart?.(shape)\n\t\t\tif (change) {\n\t\t\t\tchanges.push(change)\n\t\t\t}\n\t\t})\n\n\t\tif (changes.length > 0) {\n\t\t\tthis.editor.updateShapes(changes)\n\t\t}\n\n\t\tthis.dragAndDropManager.startDraggingShapes(\n\t\t\t// Get fresh shapes from the snapshot, in case onTranslateStart mutates the shape\n\t\t\tcompact(this.snapshot.movingShapes.map((s) => this.editor.getShape(s.id))),\n\t\t\t// Start from the place where the user started dragging\n\t\t\tthis.editor.inputs.originPagePoint,\n\t\t\tthis.updateParentTransforms\n\t\t)\n\n\t\tthis.editor.setHoveredShape(null)\n\t}\n\n\tprotected handleEnd() {\n\t\tconst { movingShapes } = this.snapshot\n\n\t\tif (this.isCloning && movingShapes.length > 0) {\n\t\t\tconst currentAveragePagePoint = Vec.Average(\n\t\t\t\tmovingShapes.map((s) => this.editor.getShapePageTransform(s.id)!.point())\n\t\t\t)\n\t\t\tconst offset = Vec.Sub(currentAveragePagePoint, this.selectionSnapshot.averagePagePoint)\n\t\t\tif (!Vec.IsNaN(offset)) {\n\t\t\t\tthis.editor.updateInstanceState({\n\t\t\t\t\tduplicateProps: {\n\t\t\t\t\t\tshapeIds: movingShapes.map((s) => s.id),\n\t\t\t\t\t\toffset: { x: offset.x, y: offset.y },\n\t\t\t\t\t},\n\t\t\t\t})\n\t\t\t}\n\t\t}\n\n\t\tconst changes: TLShapePartial[] = []\n\n\t\tmovingShapes.forEach((shape) => {\n\t\t\tconst current = this.editor.getShape(shape.id)!\n\t\t\tconst util = this.editor.getShapeUtil(shape)\n\t\t\tconst change = util.onTranslateEnd?.(shape, current)\n\t\t\tif (change) {\n\t\t\t\tchanges.push(change)\n\t\t\t}\n\t\t})\n\n\t\tif (changes.length > 0) {\n\t\t\tthis.editor.updateShapes(changes)\n\t\t}\n\t}\n\n\tprotected updateShapes() {\n\t\tconst { snapshot } = this\n\n\t\t// We should have started already, but hey\n\t\tthis.dragAndDropManager.startDraggingShapes(\n\t\t\tsnapshot.movingShapes,\n\t\t\tthis.editor.inputs.originPagePoint,\n\t\t\tthis.updateParentTransforms\n\t\t)\n\n\t\tmoveShapesToPoint({\n\t\t\teditor: this.editor,\n\t\t\tsnapshot,\n\t\t})\n\n\t\tconst { movingShapes } = snapshot\n\n\t\tconst changes: TLShapePartial[] = []\n\n\t\tmovingShapes.forEach((shape) => {\n\t\t\tconst current = this.editor.getShape(shape.id)!\n\t\t\tconst util = this.editor.getShapeUtil(shape)\n\t\t\tconst change = util.onTranslate?.(shape, current)\n\t\t\tif (change) {\n\t\t\t\tchanges.push(change)\n\t\t\t}\n\t\t})\n\n\t\tif (changes.length > 0) {\n\t\t\tthis.editor.updateShapes(changes)\n\t\t}\n\t}\n\n\t@bind\n\tprotected updateParentTransforms() {\n\t\tconst {\n\t\t\teditor,\n\t\t\tsnapshot: { shapeSnapshots },\n\t\t} = this\n\t\tconst movingShapes: TLShape[] = []\n\n\t\tshapeSnapshots.forEach((shapeSnapshot) => {\n\t\t\tconst shape = editor.getShape(shapeSnapshot.shape.id)\n\t\t\tif (!shape) return null\n\t\t\tmovingShapes.push(shape)\n\n\t\t\tconst parentTransform = isPageId(shape.parentId)\n\t\t\t\t? null\n\t\t\t\t: Mat.Inverse(editor.getShapePageTransform(shape.parentId)!)\n\n\t\t\tshapeSnapshot.parentTransform = parentTransform\n\t\t})\n\t}\n}\n\nfunction getTranslatingSnapshot(editor: Editor) {\n\tconst movingShapes: TLShape[] = []\n\tconst pagePoints: Vec[] = []\n\n\tconst selectedShapeIds = editor.getSelectedShapeIds()\n\tconst shapeSnapshots = compact(\n\t\tselectedShapeIds.map((id): null | MovingShapeSnapshot => {\n\t\t\tconst shape = editor.getShape(id)\n\t\t\tif (!shape) return null\n\t\t\tmovingShapes.push(shape)\n\n\t\t\tconst pageTransform = editor.getShapePageTransform(id)\n\t\t\tconst pagePoint = pageTransform.point()\n\t\t\tconst pageRotation = pageTransform.rotation()\n\n\t\t\tpagePoints.push(pagePoint)\n\n\t\t\tconst parentTransform = PageRecordType.isId(shape.parentId)\n\t\t\t\t? null\n\t\t\t\t: Mat.Inverse(editor.getShapePageTransform(shape.parentId)!)\n\n\t\t\treturn {\n\t\t\t\tshape,\n\t\t\t\tpagePoint,\n\t\t\t\tpageRotation,\n\t\t\t\tparentTransform,\n\t\t\t}\n\t\t})\n\t)\n\n\tconst onlySelectedShape = editor.getOnlySelectedShape()\n\n\tlet initialSnapPoints: BoundsSnapPoint[] = []\n\n\tif (onlySelectedShape) {\n\t\tinitialSnapPoints = editor.snaps.shapeBounds.getSnapPoints(onlySelectedShape.id)!\n\t} else {\n\t\tconst selectionPageBounds = editor.getSelectionPageBounds()\n\t\tif (selectionPageBounds) {\n\t\t\tinitialSnapPoints = selectionPageBounds.cornersAndCenter.map((p, i) => ({\n\t\t\t\tid: 'selection:' + i,\n\t\t\t\tx: p.x,\n\t\t\t\ty: p.y,\n\t\t\t}))\n\t\t}\n\t}\n\n\tlet noteAdjacentPositions: Vec[] | undefined\n\tlet noteSnapshot: (MovingShapeSnapshot & { shape: TLNoteShape }) | undefined\n\n\tconst { originPagePoint } = editor.inputs\n\n\tconst allHoveredNotes = shapeSnapshots.filter(\n\t\t(s) =>\n\t\t\teditor.isShapeOfType<TLNoteShape>(s.shape, 'note') &&\n\t\t\teditor.isPointInShape(s.shape, originPagePoint)\n\t) as (MovingShapeSnapshot & { shape: TLNoteShape })[]\n\n\tif (allHoveredNotes.length === 0) {\n\t\t// noop\n\t} else if (allHoveredNotes.length === 1) {\n\t\t// just one, easy\n\t\tnoteSnapshot = allHoveredNotes[0]\n\t} else {\n\t\t// More than one under the cursor, so we need to find the highest shape in z-order\n\t\tconst allShapesSorted = editor.getCurrentPageShapesSorted()\n\t\tnoteSnapshot = allHoveredNotes\n\t\t\t.map((s) => ({\n\t\t\t\tsnapshot: s,\n\t\t\t\tindex: allShapesSorted.findIndex((shape) => shape.id === s.shape.id),\n\t\t\t}))\n\t\t\t.sort((a, b) => b.index - a.index)[0]?.snapshot // highest up first\n\t}\n\n\tif (noteSnapshot) {\n\t\tnoteAdjacentPositions = getAvailableNoteAdjacentPositions(\n\t\t\teditor,\n\t\t\tnoteSnapshot.pageRotation,\n\t\t\tnoteSnapshot.shape.props.scale,\n\t\t\tnoteSnapshot.shape.props.growY ?? 0\n\t\t)\n\t}\n\n\treturn {\n\t\taveragePagePoint: Vec.Average(pagePoints),\n\t\tmovingShapes,\n\t\tshapeSnapshots,\n\t\tinitialPageBounds: editor.getSelectionPageBounds()!,\n\t\tinitialSnapPoints,\n\t\tnoteAdjacentPositions,\n\t\tnoteSnapshot,\n\t}\n}\n\nexport type TranslatingSnapshot = ReturnType<typeof getTranslatingSnapshot>\n\nexport interface MovingShapeSnapshot {\n\tshape: TLShape\n\tpagePoint: Vec\n\tpageRotation: number\n\tparentTransform: MatModel | null\n}\n\nexport function moveShapesToPoint({\n\teditor,\n\tsnapshot,\n}: {\n\teditor: Editor\n\tsnapshot: TranslatingSnapshot\n}) {\n\tconst { inputs } = editor\n\n\tconst {\n\t\tnoteSnapshot,\n\t\tnoteAdjacentPositions,\n\t\tinitialPageBounds,\n\t\tinitialSnapPoints,\n\t\tshapeSnapshots,\n\t\taveragePagePoint,\n\t} = snapshot\n\n\tconst isGridMode = editor.getInstanceState().isGridMode\n\n\tconst gridSize = editor.getDocumentSettings().gridSize\n\n\tconst delta = Vec.Sub(inputs.currentPagePoint, inputs.originPagePoint)\n\n\tconst flatten: 'x' | 'y' | null = editor.inputs.shiftKey\n\t\t? Math.abs(delta.x) < Math.abs(delta.y)\n\t\t\t? 'x'\n\t\t\t: 'y'\n\t\t: null\n\n\tif (flatten === 'x') {\n\t\tdelta.x = 0\n\t} else if (flatten === 'y') {\n\t\tdelta.y = 0\n\t}\n\n\t// Provisional snapping\n\teditor.snaps.clearIndicators()\n\n\t// If the user isn't moving super quick\n\tconst isSnapping = editor.user.getIsSnapMode() ? !inputs.ctrlKey : inputs.ctrlKey\n\tlet snappedToPit = false\n\tif (isSnapping && editor.inputs.pointerVelocity.len() < 0.5) {\n\t\t// snapping\n\t\tconst { nudge } = editor.snaps.shapeBounds.snapTranslateShapes({\n\t\t\tdragDelta: delta,\n\t\t\tinitialSelectionPageBounds: initialPageBounds,\n\t\t\tlockedAxis: flatten,\n\t\t\tinitialSelectionSnapPoints: initialSnapPoints,\n\t\t})\n\n\t\tdelta.add(nudge)\n\t} else {\n\t\t// for sticky notes, snap to grid position next to other notes\n\t\tif (noteSnapshot && noteAdjacentPositions) {\n\t\t\tconst { scale } = noteSnapshot.shape.props\n\t\t\tconst pageCenter = noteSnapshot.pagePoint\n\t\t\t\t.clone()\n\t\t\t\t.add(delta)\n\t\t\t\t// use the middle of the note, disregarding extra height\n\t\t\t\t.add(NOTE_CENTER_OFFSET.clone().mul(scale).rot(noteSnapshot.pageRotation))\n\n\t\t\t// Find the pit with the center closest to the put center\n\t\t\tlet min = NOTE_ADJACENT_POSITION_SNAP_RADIUS / editor.getZoomLevel() // in screen space\n\t\t\tlet offset = new Vec(0, 0)\n\t\t\tfor (const pit of noteAdjacentPositions) {\n\t\t\t\t// We've already filtered pits with the same page rotation\n\t\t\t\tconst deltaToPit = Vec.Sub(pageCenter, pit)\n\t\t\t\tconst dist = deltaToPit.len()\n\t\t\t\tif (dist < min) {\n\t\t\t\t\tsnappedToPit = true\n\t\t\t\t\tmin = dist\n\t\t\t\t\toffset = deltaToPit\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tdelta.sub(offset)\n\t\t}\n\t}\n\n\tconst averageSnappedPoint = Vec.Add(averagePagePoint, delta)\n\n\t// we don't want to snap to the grid if we're holding the ctrl key, if we've already snapped into a pit, or if we're showing snapping indicators\n\tconst snapIndicators = editor.snaps.getIndicators()\n\tif (isGridMode && !inputs.ctrlKey && !snappedToPit && snapIndicators.length === 0) {\n\t\taverageSnappedPoint.snapToGrid(gridSize)\n\t}\n\n\tconst averageSnap = Vec.Sub(averageSnappedPoint, averagePagePoint)\n\n\teditor.updateShapes(\n\t\tcompact(\n\t\t\tshapeSnapshots.map(({ shape, pagePoint, parentTransform }): TLShapePartial | null => {\n\t\t\t\tconst newPagePoint = Vec.Add(pagePoint, averageSnap)\n\n\t\t\t\tconst newLocalPoint = parentTransform\n\t\t\t\t\t? Mat.applyToPoint(parentTransform, newPagePoint)\n\t\t\t\t\t: newPagePoint\n\n\t\t\t\treturn {\n\t\t\t\t\tid: shape.id,\n\t\t\t\t\ttype: shape.type,\n\t\t\t\t\tx: newLocalPoint.x,\n\t\t\t\t\ty: newLocalPoint.y,\n\t\t\t\t}\n\t\t\t})\n\t\t)\n\t)\n}\n"],
|
|
5
|
-
"mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA,EAGC;AAAA,EAEA;AAAA,EACA;AAAA,EAMA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACM;AACP;AAAA,EACC;AAAA,EACA;AAAA,EACA;AAAA,OACM;AACP,SAAS,0BAA0B;AAW5B,MAAM,qBAAoB,
|
|
4
|
+
"sourcesContent": ["import {\n\tBoundsSnapPoint,\n\tEditor,\n\tMat,\n\tMatModel,\n\tPageRecordType,\n\tStateNode,\n\tTLNoteShape,\n\tTLPointerEventInfo,\n\tTLShape,\n\tTLShapePartial,\n\tTLTickEventInfo,\n\tVec,\n\tbind,\n\tcompact,\n\tisPageId,\n\tkickoutOccludedShapes,\n} from '@tldraw/editor'\nimport {\n\tNOTE_ADJACENT_POSITION_SNAP_RADIUS,\n\tNOTE_CENTER_OFFSET,\n\tgetAvailableNoteAdjacentPositions,\n} from '../../../shapes/note/noteHelpers'\nimport { DragAndDropManager } from '../DragAndDropManager'\n\nexport type TranslatingInfo = TLPointerEventInfo & {\n\ttarget: 'shape'\n\tisCreating?: boolean\n\tcreatingMarkId?: string\n\tonCreate?(): void\n\tdidStartInPit?: boolean\n\tonInteractionEnd?: string\n}\n\nexport class Translating extends StateNode {\n\tstatic override id = 'translating'\n\n\tinfo = {} as TranslatingInfo\n\n\tselectionSnapshot: TranslatingSnapshot = {} as any\n\n\tsnapshot: TranslatingSnapshot = {} as any\n\n\tmarkId = ''\n\n\tisCloning = false\n\tisCreating = false\n\tonCreate(_shape: TLShape | null): void {\n\t\treturn\n\t}\n\n\tdragAndDropManager = new DragAndDropManager(this.editor)\n\n\toverride onEnter(info: TranslatingInfo) {\n\t\tconst { isCreating = false, creatingMarkId, onCreate = () => void null } = info\n\n\t\tif (!this.editor.getSelectedShapeIds()?.length) {\n\t\t\tthis.parent.transition('idle')\n\t\t\treturn\n\t\t}\n\n\t\tthis.info = info\n\t\tthis.parent.setCurrentToolIdMask(info.onInteractionEnd)\n\t\tthis.isCreating = isCreating\n\n\t\tthis.markId = ''\n\n\t\tif (isCreating) {\n\t\t\tif (creatingMarkId) {\n\t\t\t\tthis.markId = creatingMarkId\n\t\t\t} else {\n\t\t\t\t// handle legacy implicit `creating:{shapeId}` marks\n\t\t\t\tconst markId = this.editor.getMarkIdMatching(\n\t\t\t\t\t`creating:${this.editor.getOnlySelectedShapeId()}`\n\t\t\t\t)\n\t\t\t\tif (markId) {\n\t\t\t\t\tthis.markId = markId\n\t\t\t\t}\n\t\t\t}\n\t\t} else {\n\t\t\tthis.markId = this.editor.markHistoryStoppingPoint('translating')\n\t\t}\n\n\t\tthis.onCreate = onCreate\n\n\t\tthis.isCloning = false\n\t\tthis.info = info\n\n\t\tthis.editor.setCursor({ type: 'move', rotation: 0 })\n\t\tthis.selectionSnapshot = getTranslatingSnapshot(this.editor)\n\n\t\t// Don't clone on create; otherwise clone on altKey\n\t\tif (!this.isCreating) {\n\t\t\tif (this.editor.inputs.altKey) {\n\t\t\t\tthis.startCloning()\n\t\t\t\tif (this.isCloning) return\n\t\t\t}\n\t\t}\n\n\t\tthis.snapshot = this.selectionSnapshot\n\t\tthis.handleStart()\n\t\tthis.updateShapes()\n\t}\n\n\toverride onExit() {\n\t\tthis.parent.setCurrentToolIdMask(undefined)\n\t\tthis.selectionSnapshot = {} as any\n\t\tthis.snapshot = {} as any\n\t\tthis.editor.snaps.clearIndicators()\n\t\tthis.editor.setCursor({ type: 'default', rotation: 0 })\n\t\tthis.dragAndDropManager.clear()\n\t}\n\n\toverride onTick({ elapsed }: TLTickEventInfo) {\n\t\tconst { editor } = this\n\t\teditor.edgeScrollManager.updateEdgeScrolling(elapsed)\n\t}\n\n\toverride onPointerMove() {\n\t\tthis.updateShapes()\n\t}\n\n\toverride onKeyDown() {\n\t\tif (this.editor.inputs.altKey && !this.isCloning) {\n\t\t\tthis.startCloning()\n\t\t\tif (this.isCloning) return\n\t\t}\n\n\t\t// need to update in case user pressed a different modifier key\n\t\tthis.updateShapes()\n\t}\n\n\toverride onKeyUp() {\n\t\tif (!this.editor.inputs.altKey && this.isCloning) {\n\t\t\tthis.stopCloning()\n\t\t\treturn\n\t\t}\n\n\t\t// need to update in case user pressed a different modifier key\n\t\tthis.updateShapes()\n\t}\n\n\toverride onPointerUp() {\n\t\tthis.complete()\n\t}\n\n\toverride onComplete() {\n\t\tthis.complete()\n\t}\n\n\toverride onCancel() {\n\t\tthis.cancel()\n\t}\n\n\tprotected startCloning() {\n\t\tif (this.isCreating) return\n\t\tconst shapeIds = Array.from(this.editor.getSelectedShapeIds())\n\n\t\t// If we can't create the shapes, don't even start cloning\n\t\tif (!this.editor.canCreateShapes(shapeIds)) return\n\n\t\tthis.isCloning = true\n\t\tthis.reset()\n\t\tthis.markId = this.editor.markHistoryStoppingPoint('translate cloning')\n\n\t\tthis.editor.duplicateShapes(Array.from(this.editor.getSelectedShapeIds()))\n\n\t\tthis.snapshot = getTranslatingSnapshot(this.editor)\n\t\tthis.handleStart()\n\t\tthis.updateShapes()\n\t}\n\n\tprotected stopCloning() {\n\t\tthis.isCloning = false\n\t\tthis.snapshot = this.selectionSnapshot\n\t\tthis.reset()\n\t\tthis.markId = this.editor.markHistoryStoppingPoint('translate')\n\t\tthis.updateShapes()\n\t}\n\n\treset() {\n\t\tthis.editor.bailToMark(this.markId)\n\t}\n\n\tprotected complete() {\n\t\tthis.updateShapes()\n\t\tthis.dragAndDropManager.dropShapes(this.snapshot.movingShapes)\n\t\tthis.handleEnd()\n\t\tkickoutOccludedShapes(\n\t\t\tthis.editor,\n\t\t\tthis.snapshot.movingShapes.map((s) => s.id)\n\t\t)\n\n\t\tif (this.editor.getInstanceState().isToolLocked && this.info.onInteractionEnd) {\n\t\t\tthis.editor.setCurrentTool(this.info.onInteractionEnd)\n\t\t} else {\n\t\t\tif (this.isCreating) {\n\t\t\t\tthis.onCreate?.(this.editor.getOnlySelectedShape())\n\t\t\t} else {\n\t\t\t\tthis.parent.transition('idle')\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate cancel() {\n\t\t// Call onTranslateCancel callback before resetting\n\t\tconst { movingShapes } = this.snapshot\n\n\t\tmovingShapes.forEach((shape) => {\n\t\t\tconst current = this.editor.getShape(shape.id)\n\t\t\tif (current) {\n\t\t\t\tconst util = this.editor.getShapeUtil(shape)\n\t\t\t\tutil.onTranslateCancel?.(shape, current)\n\t\t\t}\n\t\t})\n\n\t\tthis.reset()\n\t\tif (this.info.onInteractionEnd) {\n\t\t\tthis.editor.setCurrentTool(this.info.onInteractionEnd)\n\t\t} else {\n\t\t\tthis.parent.transition('idle', this.info)\n\t\t}\n\t}\n\n\tprotected handleStart() {\n\t\tconst { movingShapes } = this.snapshot\n\n\t\tconst changes: TLShapePartial[] = []\n\n\t\tmovingShapes.forEach((shape) => {\n\t\t\tconst util = this.editor.getShapeUtil(shape)\n\t\t\tconst change = util.onTranslateStart?.(shape)\n\t\t\tif (change) {\n\t\t\t\tchanges.push(change)\n\t\t\t}\n\t\t})\n\n\t\tif (changes.length > 0) {\n\t\t\tthis.editor.updateShapes(changes)\n\t\t}\n\n\t\tthis.dragAndDropManager.startDraggingShapes(\n\t\t\t// Get fresh shapes from the snapshot, in case onTranslateStart mutates the shape\n\t\t\tcompact(this.snapshot.movingShapes.map((s) => this.editor.getShape(s.id))),\n\t\t\t// Start from the place where the user started dragging\n\t\t\tthis.editor.inputs.originPagePoint,\n\t\t\tthis.updateParentTransforms\n\t\t)\n\n\t\tthis.editor.setHoveredShape(null)\n\t}\n\n\tprotected handleEnd() {\n\t\tconst { movingShapes } = this.snapshot\n\n\t\tif (this.isCloning && movingShapes.length > 0) {\n\t\t\tconst currentAveragePagePoint = Vec.Average(\n\t\t\t\tmovingShapes.map((s) => this.editor.getShapePageTransform(s.id)!.point())\n\t\t\t)\n\t\t\tconst offset = Vec.Sub(currentAveragePagePoint, this.selectionSnapshot.averagePagePoint)\n\t\t\tif (!Vec.IsNaN(offset)) {\n\t\t\t\tthis.editor.updateInstanceState({\n\t\t\t\t\tduplicateProps: {\n\t\t\t\t\t\tshapeIds: movingShapes.map((s) => s.id),\n\t\t\t\t\t\toffset: { x: offset.x, y: offset.y },\n\t\t\t\t\t},\n\t\t\t\t})\n\t\t\t}\n\t\t}\n\n\t\tconst changes: TLShapePartial[] = []\n\n\t\tmovingShapes.forEach((shape) => {\n\t\t\tconst current = this.editor.getShape(shape.id)!\n\t\t\tconst util = this.editor.getShapeUtil(shape)\n\t\t\tconst change = util.onTranslateEnd?.(shape, current)\n\t\t\tif (change) {\n\t\t\t\tchanges.push(change)\n\t\t\t}\n\t\t})\n\n\t\tif (changes.length > 0) {\n\t\t\tthis.editor.updateShapes(changes)\n\t\t}\n\t}\n\n\tprotected updateShapes() {\n\t\tconst { snapshot } = this\n\n\t\t// We should have started already, but hey\n\t\tthis.dragAndDropManager.startDraggingShapes(\n\t\t\tsnapshot.movingShapes,\n\t\t\tthis.editor.inputs.originPagePoint,\n\t\t\tthis.updateParentTransforms\n\t\t)\n\n\t\tmoveShapesToPoint({\n\t\t\teditor: this.editor,\n\t\t\tsnapshot,\n\t\t})\n\n\t\tconst { movingShapes } = snapshot\n\n\t\tconst changes: TLShapePartial[] = []\n\n\t\tmovingShapes.forEach((shape) => {\n\t\t\tconst current = this.editor.getShape(shape.id)!\n\t\t\tconst util = this.editor.getShapeUtil(shape)\n\t\t\tconst change = util.onTranslate?.(shape, current)\n\t\t\tif (change) {\n\t\t\t\tchanges.push(change)\n\t\t\t}\n\t\t})\n\n\t\tif (changes.length > 0) {\n\t\t\tthis.editor.updateShapes(changes)\n\t\t}\n\t}\n\n\t@bind\n\tprotected updateParentTransforms() {\n\t\tconst {\n\t\t\teditor,\n\t\t\tsnapshot: { shapeSnapshots },\n\t\t} = this\n\t\tconst movingShapes: TLShape[] = []\n\n\t\tshapeSnapshots.forEach((shapeSnapshot) => {\n\t\t\tconst shape = editor.getShape(shapeSnapshot.shape.id)\n\t\t\tif (!shape) return null\n\t\t\tmovingShapes.push(shape)\n\n\t\t\tconst parentTransform = isPageId(shape.parentId)\n\t\t\t\t? null\n\t\t\t\t: Mat.Inverse(editor.getShapePageTransform(shape.parentId)!)\n\n\t\t\tshapeSnapshot.parentTransform = parentTransform\n\t\t})\n\t}\n}\n\nfunction getTranslatingSnapshot(editor: Editor) {\n\tconst movingShapes: TLShape[] = []\n\tconst pagePoints: Vec[] = []\n\n\tconst selectedShapeIds = editor.getSelectedShapeIds()\n\tconst shapeSnapshots = compact(\n\t\tselectedShapeIds.map((id): null | MovingShapeSnapshot => {\n\t\t\tconst shape = editor.getShape(id)\n\t\t\tif (!shape) return null\n\t\t\tmovingShapes.push(shape)\n\n\t\t\tconst pageTransform = editor.getShapePageTransform(id)\n\t\t\tconst pagePoint = pageTransform.point()\n\t\t\tconst pageRotation = pageTransform.rotation()\n\n\t\t\tpagePoints.push(pagePoint)\n\n\t\t\tconst parentTransform = PageRecordType.isId(shape.parentId)\n\t\t\t\t? null\n\t\t\t\t: Mat.Inverse(editor.getShapePageTransform(shape.parentId)!)\n\n\t\t\treturn {\n\t\t\t\tshape,\n\t\t\t\tpagePoint,\n\t\t\t\tpageRotation,\n\t\t\t\tparentTransform,\n\t\t\t}\n\t\t})\n\t)\n\n\tconst onlySelectedShape = editor.getOnlySelectedShape()\n\n\tlet initialSnapPoints: BoundsSnapPoint[] = []\n\n\tif (onlySelectedShape) {\n\t\tinitialSnapPoints = editor.snaps.shapeBounds.getSnapPoints(onlySelectedShape.id)!\n\t} else {\n\t\tconst selectionPageBounds = editor.getSelectionPageBounds()\n\t\tif (selectionPageBounds) {\n\t\t\tinitialSnapPoints = selectionPageBounds.cornersAndCenter.map((p, i) => ({\n\t\t\t\tid: 'selection:' + i,\n\t\t\t\tx: p.x,\n\t\t\t\ty: p.y,\n\t\t\t}))\n\t\t}\n\t}\n\n\tlet noteAdjacentPositions: Vec[] | undefined\n\tlet noteSnapshot: (MovingShapeSnapshot & { shape: TLNoteShape }) | undefined\n\n\tconst { originPagePoint } = editor.inputs\n\n\tconst allHoveredNotes = shapeSnapshots.filter(\n\t\t(s) =>\n\t\t\teditor.isShapeOfType<TLNoteShape>(s.shape, 'note') &&\n\t\t\teditor.isPointInShape(s.shape, originPagePoint)\n\t) as (MovingShapeSnapshot & { shape: TLNoteShape })[]\n\n\tif (allHoveredNotes.length === 0) {\n\t\t// noop\n\t} else if (allHoveredNotes.length === 1) {\n\t\t// just one, easy\n\t\tnoteSnapshot = allHoveredNotes[0]\n\t} else {\n\t\t// More than one under the cursor, so we need to find the highest shape in z-order\n\t\tconst allShapesSorted = editor.getCurrentPageShapesSorted()\n\t\tnoteSnapshot = allHoveredNotes\n\t\t\t.map((s) => ({\n\t\t\t\tsnapshot: s,\n\t\t\t\tindex: allShapesSorted.findIndex((shape) => shape.id === s.shape.id),\n\t\t\t}))\n\t\t\t.sort((a, b) => b.index - a.index)[0]?.snapshot // highest up first\n\t}\n\n\tif (noteSnapshot) {\n\t\tnoteAdjacentPositions = getAvailableNoteAdjacentPositions(\n\t\t\teditor,\n\t\t\tnoteSnapshot.pageRotation,\n\t\t\tnoteSnapshot.shape.props.scale,\n\t\t\tnoteSnapshot.shape.props.growY ?? 0\n\t\t)\n\t}\n\n\treturn {\n\t\taveragePagePoint: Vec.Average(pagePoints),\n\t\tmovingShapes,\n\t\tshapeSnapshots,\n\t\tinitialPageBounds: editor.getSelectionPageBounds()!,\n\t\tinitialSnapPoints,\n\t\tnoteAdjacentPositions,\n\t\tnoteSnapshot,\n\t}\n}\n\nexport type TranslatingSnapshot = ReturnType<typeof getTranslatingSnapshot>\n\nexport interface MovingShapeSnapshot {\n\tshape: TLShape\n\tpagePoint: Vec\n\tpageRotation: number\n\tparentTransform: MatModel | null\n}\n\nexport function moveShapesToPoint({\n\teditor,\n\tsnapshot,\n}: {\n\teditor: Editor\n\tsnapshot: TranslatingSnapshot\n}) {\n\tconst { inputs } = editor\n\n\tconst {\n\t\tnoteSnapshot,\n\t\tnoteAdjacentPositions,\n\t\tinitialPageBounds,\n\t\tinitialSnapPoints,\n\t\tshapeSnapshots,\n\t\taveragePagePoint,\n\t} = snapshot\n\n\tconst isGridMode = editor.getInstanceState().isGridMode\n\n\tconst gridSize = editor.getDocumentSettings().gridSize\n\n\tconst delta = Vec.Sub(inputs.currentPagePoint, inputs.originPagePoint)\n\n\tconst flatten: 'x' | 'y' | null = editor.inputs.shiftKey\n\t\t? Math.abs(delta.x) < Math.abs(delta.y)\n\t\t\t? 'x'\n\t\t\t: 'y'\n\t\t: null\n\n\tif (flatten === 'x') {\n\t\tdelta.x = 0\n\t} else if (flatten === 'y') {\n\t\tdelta.y = 0\n\t}\n\n\t// Provisional snapping\n\teditor.snaps.clearIndicators()\n\n\t// If the user isn't moving super quick\n\tconst isSnapping = editor.user.getIsSnapMode() ? !inputs.ctrlKey : inputs.ctrlKey\n\tlet snappedToPit = false\n\tif (isSnapping && editor.inputs.pointerVelocity.len() < 0.5) {\n\t\t// snapping\n\t\tconst { nudge } = editor.snaps.shapeBounds.snapTranslateShapes({\n\t\t\tdragDelta: delta,\n\t\t\tinitialSelectionPageBounds: initialPageBounds,\n\t\t\tlockedAxis: flatten,\n\t\t\tinitialSelectionSnapPoints: initialSnapPoints,\n\t\t})\n\n\t\tdelta.add(nudge)\n\t} else {\n\t\t// for sticky notes, snap to grid position next to other notes\n\t\tif (noteSnapshot && noteAdjacentPositions) {\n\t\t\tconst { scale } = noteSnapshot.shape.props\n\t\t\tconst pageCenter = noteSnapshot.pagePoint\n\t\t\t\t.clone()\n\t\t\t\t.add(delta)\n\t\t\t\t// use the middle of the note, disregarding extra height\n\t\t\t\t.add(NOTE_CENTER_OFFSET.clone().mul(scale).rot(noteSnapshot.pageRotation))\n\n\t\t\t// Find the pit with the center closest to the put center\n\t\t\tlet min = NOTE_ADJACENT_POSITION_SNAP_RADIUS / editor.getZoomLevel() // in screen space\n\t\t\tlet offset = new Vec(0, 0)\n\t\t\tfor (const pit of noteAdjacentPositions) {\n\t\t\t\t// We've already filtered pits with the same page rotation\n\t\t\t\tconst deltaToPit = Vec.Sub(pageCenter, pit)\n\t\t\t\tconst dist = deltaToPit.len()\n\t\t\t\tif (dist < min) {\n\t\t\t\t\tsnappedToPit = true\n\t\t\t\t\tmin = dist\n\t\t\t\t\toffset = deltaToPit\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tdelta.sub(offset)\n\t\t}\n\t}\n\n\tconst averageSnappedPoint = Vec.Add(averagePagePoint, delta)\n\n\t// we don't want to snap to the grid if we're holding the ctrl key, if we've already snapped into a pit, or if we're showing snapping indicators\n\tconst snapIndicators = editor.snaps.getIndicators()\n\tif (isGridMode && !inputs.ctrlKey && !snappedToPit && snapIndicators.length === 0) {\n\t\taverageSnappedPoint.snapToGrid(gridSize)\n\t}\n\n\tconst averageSnap = Vec.Sub(averageSnappedPoint, averagePagePoint)\n\n\teditor.updateShapes(\n\t\tcompact(\n\t\t\tshapeSnapshots.map(({ shape, pagePoint, parentTransform }): TLShapePartial | null => {\n\t\t\t\tconst newPagePoint = Vec.Add(pagePoint, averageSnap)\n\n\t\t\t\tconst newLocalPoint = parentTransform\n\t\t\t\t\t? Mat.applyToPoint(parentTransform, newPagePoint)\n\t\t\t\t\t: newPagePoint\n\n\t\t\t\treturn {\n\t\t\t\t\tid: shape.id,\n\t\t\t\t\ttype: shape.type,\n\t\t\t\t\tx: newLocalPoint.x,\n\t\t\t\t\ty: newLocalPoint.y,\n\t\t\t\t}\n\t\t\t})\n\t\t)\n\t)\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { LANGUAGES, getDefaultCdnBaseUrl } from "@tldraw/editor";
|
|
2
|
+
import { useMemo } from "react";
|
|
2
3
|
import { DEFAULT_EMBED_DEFINITIONS } from "../defaultEmbedDefinitions.mjs";
|
|
3
4
|
import { defaultEditorAssetUrls } from "../utils/static-assets/assetUrls.mjs";
|
|
4
5
|
import { iconTypes } from "./icon-types.mjs";
|
|
@@ -24,16 +25,18 @@ function setDefaultUiAssetUrls(urls) {
|
|
|
24
25
|
defaultUiAssetUrls = urls;
|
|
25
26
|
}
|
|
26
27
|
function useDefaultUiAssetUrlsWithOverrides(overrides) {
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
28
|
+
return useMemo(() => {
|
|
29
|
+
if (!overrides) return defaultUiAssetUrls;
|
|
30
|
+
return {
|
|
31
|
+
fonts: Object.assign({ ...defaultUiAssetUrls.fonts }, { ...overrides?.fonts }),
|
|
32
|
+
icons: Object.assign({ ...defaultUiAssetUrls.icons }, { ...overrides?.icons }),
|
|
33
|
+
embedIcons: Object.assign({ ...defaultUiAssetUrls.embedIcons }, { ...overrides?.embedIcons }),
|
|
34
|
+
translations: Object.assign(
|
|
35
|
+
{ ...defaultUiAssetUrls.translations },
|
|
36
|
+
{ ...overrides?.translations }
|
|
37
|
+
)
|
|
38
|
+
};
|
|
39
|
+
}, [overrides]);
|
|
37
40
|
}
|
|
38
41
|
export {
|
|
39
42
|
defaultUiAssetUrls,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../src/lib/ui/assetUrls.ts"],
|
|
4
|
-
"sourcesContent": ["import { LANGUAGES, RecursivePartial, getDefaultCdnBaseUrl } from '@tldraw/editor'\nimport { DEFAULT_EMBED_DEFINITIONS } from '../defaultEmbedDefinitions'\nimport { TLEditorAssetUrls, defaultEditorAssetUrls } from '../utils/static-assets/assetUrls'\nimport { TLUiIconType, iconTypes } from './icon-types'\n\n/** @public */\nexport interface TLUiAssetUrls extends TLEditorAssetUrls {\n\ticons: Record<TLUiIconType | Exclude<string, TLUiIconType>, string>\n\ttranslations: Record<(typeof LANGUAGES)[number]['locale'], string>\n\tembedIcons: Partial<Record<(typeof DEFAULT_EMBED_DEFINITIONS)[number]['type'], string>>\n}\n\n/** @public */\nexport type TLUiAssetUrlOverrides = RecursivePartial<TLUiAssetUrls>\n\nexport let defaultUiAssetUrls: TLUiAssetUrls = {\n\t...defaultEditorAssetUrls,\n\ticons: Object.fromEntries(\n\t\ticonTypes.map((name) => [name, `${getDefaultCdnBaseUrl()}/icons/icon/0_merged.svg#${name}`])\n\t) as Record<TLUiIconType, string>,\n\ttranslations: Object.fromEntries(\n\t\tLANGUAGES.map((lang) => [\n\t\t\tlang.locale,\n\t\t\t`${getDefaultCdnBaseUrl()}/translations/${lang.locale}.json`,\n\t\t])\n\t) as Record<(typeof LANGUAGES)[number]['locale'], string>,\n\tembedIcons: Object.fromEntries(\n\t\tDEFAULT_EMBED_DEFINITIONS.map((def) => [\n\t\t\tdef.type,\n\t\t\t`${getDefaultCdnBaseUrl()}/embed-icons/${def.type}.png`,\n\t\t])\n\t) as Record<(typeof DEFAULT_EMBED_DEFINITIONS)[number]['type'], string>,\n}\n\n/** @internal */\nexport function setDefaultUiAssetUrls(urls: TLUiAssetUrls) {\n\tdefaultUiAssetUrls = urls\n}\n\n/** @internal */\nexport function useDefaultUiAssetUrlsWithOverrides(\n\toverrides?: TLUiAssetUrlOverrides\n): TLUiAssetUrls {\n\tif (!overrides) return defaultUiAssetUrls\n\n\treturn {\n\t\tfonts: Object.assign({ ...defaultUiAssetUrls.fonts }, { ...overrides?.fonts }),\n\t\ticons: Object.assign({ ...defaultUiAssetUrls.icons }, { ...overrides?.icons }),\n\t\tembedIcons: Object.assign({ ...defaultUiAssetUrls.embedIcons }, { ...overrides?.embedIcons }),\n\t\ttranslations: Object.assign(\n\t\t\t{ ...defaultUiAssetUrls.translations },\n\t\t\t{ ...overrides?.translations }\n\t\t),\n\t}\n}\n"],
|
|
5
|
-
"mappings": "AAAA,SAAS,WAA6B,4BAA4B;AAClE,SAAS,iCAAiC;AAC1C,SAA4B,8BAA8B;AAC1D,SAAuB,iBAAiB;AAYjC,IAAI,qBAAoC;AAAA,EAC9C,GAAG;AAAA,EACH,OAAO,OAAO;AAAA,IACb,UAAU,IAAI,CAAC,SAAS,CAAC,MAAM,GAAG,qBAAqB,CAAC,4BAA4B,IAAI,EAAE,CAAC;AAAA,EAC5F;AAAA,EACA,cAAc,OAAO;AAAA,IACpB,UAAU,IAAI,CAAC,SAAS;AAAA,MACvB,KAAK;AAAA,MACL,GAAG,qBAAqB,CAAC,iBAAiB,KAAK,MAAM;AAAA,IACtD,CAAC;AAAA,EACF;AAAA,EACA,YAAY,OAAO;AAAA,IAClB,0BAA0B,IAAI,CAAC,QAAQ;AAAA,MACtC,IAAI;AAAA,MACJ,GAAG,qBAAqB,CAAC,gBAAgB,IAAI,IAAI;AAAA,IAClD,CAAC;AAAA,EACF;AACD;AAGO,SAAS,sBAAsB,MAAqB;AAC1D,uBAAqB;AACtB;AAGO,SAAS,mCACf,WACgB;AAChB,
|
|
4
|
+
"sourcesContent": ["import { LANGUAGES, RecursivePartial, getDefaultCdnBaseUrl } from '@tldraw/editor'\nimport { useMemo } from 'react'\nimport { DEFAULT_EMBED_DEFINITIONS } from '../defaultEmbedDefinitions'\nimport { TLEditorAssetUrls, defaultEditorAssetUrls } from '../utils/static-assets/assetUrls'\nimport { TLUiIconType, iconTypes } from './icon-types'\n\n/** @public */\nexport interface TLUiAssetUrls extends TLEditorAssetUrls {\n\ticons: Record<TLUiIconType | Exclude<string, TLUiIconType>, string>\n\ttranslations: Record<(typeof LANGUAGES)[number]['locale'], string>\n\tembedIcons: Partial<Record<(typeof DEFAULT_EMBED_DEFINITIONS)[number]['type'], string>>\n}\n\n/** @public */\nexport type TLUiAssetUrlOverrides = RecursivePartial<TLUiAssetUrls>\n\nexport let defaultUiAssetUrls: TLUiAssetUrls = {\n\t...defaultEditorAssetUrls,\n\ticons: Object.fromEntries(\n\t\ticonTypes.map((name) => [name, `${getDefaultCdnBaseUrl()}/icons/icon/0_merged.svg#${name}`])\n\t) as Record<TLUiIconType, string>,\n\ttranslations: Object.fromEntries(\n\t\tLANGUAGES.map((lang) => [\n\t\t\tlang.locale,\n\t\t\t`${getDefaultCdnBaseUrl()}/translations/${lang.locale}.json`,\n\t\t])\n\t) as Record<(typeof LANGUAGES)[number]['locale'], string>,\n\tembedIcons: Object.fromEntries(\n\t\tDEFAULT_EMBED_DEFINITIONS.map((def) => [\n\t\t\tdef.type,\n\t\t\t`${getDefaultCdnBaseUrl()}/embed-icons/${def.type}.png`,\n\t\t])\n\t) as Record<(typeof DEFAULT_EMBED_DEFINITIONS)[number]['type'], string>,\n}\n\n/** @internal */\nexport function setDefaultUiAssetUrls(urls: TLUiAssetUrls) {\n\tdefaultUiAssetUrls = urls\n}\n\n/** @internal */\nexport function useDefaultUiAssetUrlsWithOverrides(\n\toverrides?: TLUiAssetUrlOverrides\n): TLUiAssetUrls {\n\treturn useMemo(() => {\n\t\tif (!overrides) return defaultUiAssetUrls\n\n\t\treturn {\n\t\t\tfonts: Object.assign({ ...defaultUiAssetUrls.fonts }, { ...overrides?.fonts }),\n\t\t\ticons: Object.assign({ ...defaultUiAssetUrls.icons }, { ...overrides?.icons }),\n\t\t\tembedIcons: Object.assign({ ...defaultUiAssetUrls.embedIcons }, { ...overrides?.embedIcons }),\n\t\t\ttranslations: Object.assign(\n\t\t\t\t{ ...defaultUiAssetUrls.translations },\n\t\t\t\t{ ...overrides?.translations }\n\t\t\t),\n\t\t}\n\t}, [overrides])\n}\n"],
|
|
5
|
+
"mappings": "AAAA,SAAS,WAA6B,4BAA4B;AAClE,SAAS,eAAe;AACxB,SAAS,iCAAiC;AAC1C,SAA4B,8BAA8B;AAC1D,SAAuB,iBAAiB;AAYjC,IAAI,qBAAoC;AAAA,EAC9C,GAAG;AAAA,EACH,OAAO,OAAO;AAAA,IACb,UAAU,IAAI,CAAC,SAAS,CAAC,MAAM,GAAG,qBAAqB,CAAC,4BAA4B,IAAI,EAAE,CAAC;AAAA,EAC5F;AAAA,EACA,cAAc,OAAO;AAAA,IACpB,UAAU,IAAI,CAAC,SAAS;AAAA,MACvB,KAAK;AAAA,MACL,GAAG,qBAAqB,CAAC,iBAAiB,KAAK,MAAM;AAAA,IACtD,CAAC;AAAA,EACF;AAAA,EACA,YAAY,OAAO;AAAA,IAClB,0BAA0B,IAAI,CAAC,QAAQ;AAAA,MACtC,IAAI;AAAA,MACJ,GAAG,qBAAqB,CAAC,gBAAgB,IAAI,IAAI;AAAA,IAClD,CAAC;AAAA,EACF;AACD;AAGO,SAAS,sBAAsB,MAAqB;AAC1D,uBAAqB;AACtB;AAGO,SAAS,mCACf,WACgB;AAChB,SAAO,QAAQ,MAAM;AACpB,QAAI,CAAC,UAAW,QAAO;AAEvB,WAAO;AAAA,MACN,OAAO,OAAO,OAAO,EAAE,GAAG,mBAAmB,MAAM,GAAG,EAAE,GAAG,WAAW,MAAM,CAAC;AAAA,MAC7E,OAAO,OAAO,OAAO,EAAE,GAAG,mBAAmB,MAAM,GAAG,EAAE,GAAG,WAAW,MAAM,CAAC;AAAA,MAC7E,YAAY,OAAO,OAAO,EAAE,GAAG,mBAAmB,WAAW,GAAG,EAAE,GAAG,WAAW,WAAW,CAAC;AAAA,MAC5F,cAAc,OAAO;AAAA,QACpB,EAAE,GAAG,mBAAmB,aAAa;AAAA,QACrC,EAAE,GAAG,WAAW,aAAa;AAAA,MAC9B;AAAA,IACD;AAAA,EACD,GAAG,CAAC,SAAS,CAAC;AACf;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../src/lib/ui/version.ts"],
|
|
4
|
-
"sourcesContent": ["// This file is automatically generated by internal/scripts/refresh-assets.ts.\n// Do not edit manually. Or do, I'm a comment, not a cop.\n\nexport const version = '3.15.
|
|
4
|
+
"sourcesContent": ["// This file is automatically generated by internal/scripts/refresh-assets.ts.\n// Do not edit manually. Or do, I'm a comment, not a cop.\n\nexport const version = '3.15.3'\nexport const publishDates = {\n\tmajor: '2024-09-13T14:36:29.063Z',\n\tminor: '2025-07-30T09:07:27.887Z',\n\tpatch: '2025-08-22T10:35:44.376Z',\n}\n"],
|
|
5
5
|
"mappings": "AAGO,MAAM,UAAU;AAChB,MAAM,eAAe;AAAA,EAC3B,OAAO;AAAA,EACP,OAAO;AAAA,EACP,OAAO;AACR;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "tldraw",
|
|
3
3
|
"description": "A tiny little drawing editor.",
|
|
4
|
-
"version": "3.15.
|
|
4
|
+
"version": "3.15.3",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "tldraw Inc.",
|
|
7
7
|
"email": "hello@tldraw.com"
|
|
@@ -54,8 +54,8 @@
|
|
|
54
54
|
"@tiptap/pm": "^2.9.1",
|
|
55
55
|
"@tiptap/react": "^2.9.1",
|
|
56
56
|
"@tiptap/starter-kit": "^2.9.1",
|
|
57
|
-
"@tldraw/editor": "3.15.
|
|
58
|
-
"@tldraw/store": "3.15.
|
|
57
|
+
"@tldraw/editor": "3.15.3",
|
|
58
|
+
"@tldraw/store": "3.15.3",
|
|
59
59
|
"classnames": "^2.5.1",
|
|
60
60
|
"hotkeys-js": "^3.13.9",
|
|
61
61
|
"idb": "^7.1.1",
|
|
@@ -145,8 +145,6 @@ export class LineShapeUtil extends ShapeUtil<TLLineShape> {
|
|
|
145
145
|
}
|
|
146
146
|
|
|
147
147
|
override onHandleDrag(shape: TLLineShape, { handle }: TLHandleDragInfo<TLLineShape>) {
|
|
148
|
-
// we should only ever be dragging vertex handles
|
|
149
|
-
if (handle.type !== 'vertex') return
|
|
150
148
|
const newPoint = maybeSnapToGrid(new Vec(handle.x, handle.y), this.editor)
|
|
151
149
|
return {
|
|
152
150
|
...shape,
|
|
@@ -160,6 +158,25 @@ export class LineShapeUtil extends ShapeUtil<TLLineShape> {
|
|
|
160
158
|
}
|
|
161
159
|
}
|
|
162
160
|
|
|
161
|
+
override onHandleDragStart(shape: TLLineShape, { handle }: TLHandleDragInfo<TLLineShape>) {
|
|
162
|
+
// For line shapes, if we're dragging a "create" handle, then
|
|
163
|
+
// create a new vertex handle at that point; and make this handle
|
|
164
|
+
// the handle that we're dragging.
|
|
165
|
+
if (handle.type === 'create') {
|
|
166
|
+
return {
|
|
167
|
+
...shape,
|
|
168
|
+
props: {
|
|
169
|
+
...shape.props,
|
|
170
|
+
points: {
|
|
171
|
+
...shape.props.points,
|
|
172
|
+
[handle.index]: { id: handle.index, index: handle.index, x: handle.x, y: handle.y },
|
|
173
|
+
},
|
|
174
|
+
},
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
return
|
|
178
|
+
}
|
|
179
|
+
|
|
163
180
|
component(shape: TLLineShape) {
|
|
164
181
|
return (
|
|
165
182
|
<SVGContainer style={{ minWidth: 50, minHeight: 50 }}>
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import {
|
|
2
|
+
Mat,
|
|
2
3
|
StateNode,
|
|
3
4
|
TLArrowShape,
|
|
4
5
|
TLHandle,
|
|
@@ -26,20 +27,20 @@ export type DraggingHandleInfo = TLPointerEventInfo & {
|
|
|
26
27
|
export class DraggingHandle extends StateNode {
|
|
27
28
|
static override id = 'dragging_handle'
|
|
28
29
|
|
|
29
|
-
shapeId
|
|
30
|
-
initialHandle
|
|
31
|
-
initialAdjacentHandle
|
|
32
|
-
initialPagePoint
|
|
30
|
+
shapeId!: TLShapeId
|
|
31
|
+
initialHandle!: TLHandle
|
|
32
|
+
initialAdjacentHandle!: TLHandle | null
|
|
33
|
+
initialPagePoint!: Vec
|
|
33
34
|
|
|
34
|
-
markId
|
|
35
|
-
initialPageTransform
|
|
36
|
-
initialPageRotation
|
|
35
|
+
markId!: string
|
|
36
|
+
initialPageTransform!: Mat
|
|
37
|
+
initialPageRotation!: number
|
|
37
38
|
|
|
38
|
-
info
|
|
39
|
+
info!: DraggingHandleInfo
|
|
39
40
|
|
|
40
41
|
isPrecise = false
|
|
41
|
-
isPreciseId
|
|
42
|
-
pointingId
|
|
42
|
+
isPreciseId: TLShapeId | null = null
|
|
43
|
+
pointingId: TLShapeId | null = null
|
|
43
44
|
|
|
44
45
|
override onEnter(info: DraggingHandleInfo) {
|
|
45
46
|
const { shape, isCreating, creatingMarkId, handle } = info
|
|
@@ -66,26 +67,6 @@ export class DraggingHandle extends StateNode {
|
|
|
66
67
|
|
|
67
68
|
this.initialHandle = structuredClone(handle)
|
|
68
69
|
|
|
69
|
-
if (this.editor.isShapeOfType<TLLineShape>(shape, 'line')) {
|
|
70
|
-
// For line shapes, if we're dragging a "create" handle, then
|
|
71
|
-
// create a new vertex handle at that point; and make this handle
|
|
72
|
-
// the handle that we're dragging.
|
|
73
|
-
if (this.initialHandle.type === 'create') {
|
|
74
|
-
this.editor.updateShape({
|
|
75
|
-
...shape,
|
|
76
|
-
props: {
|
|
77
|
-
points: {
|
|
78
|
-
...shape.props.points,
|
|
79
|
-
[handle.index]: { id: handle.index, index: handle.index, x: handle.x, y: handle.y },
|
|
80
|
-
},
|
|
81
|
-
},
|
|
82
|
-
})
|
|
83
|
-
const handlesAfter = this.editor.getShapeHandles(shape)!
|
|
84
|
-
const handleAfter = handlesAfter.find((h) => h.index === handle.index)!
|
|
85
|
-
this.initialHandle = structuredClone(handleAfter)
|
|
86
|
-
}
|
|
87
|
-
}
|
|
88
|
-
|
|
89
70
|
this.initialPageTransform = this.editor.getShapePageTransform(shape)!
|
|
90
71
|
this.initialPageRotation = this.initialPageTransform.rotation()
|
|
91
72
|
this.initialPagePoint = this.editor.inputs.originPagePoint.clone()
|
|
@@ -135,6 +116,18 @@ export class DraggingHandle extends StateNode {
|
|
|
135
116
|
}
|
|
136
117
|
// -->
|
|
137
118
|
|
|
119
|
+
// Call onHandleDragStart callback
|
|
120
|
+
const handleDragInfo = {
|
|
121
|
+
handle: this.initialHandle,
|
|
122
|
+
isPrecise: this.isPrecise,
|
|
123
|
+
initial: shape,
|
|
124
|
+
}
|
|
125
|
+
const util = this.editor.getShapeUtil(shape)
|
|
126
|
+
const startChanges = util.onHandleDragStart?.(shape, handleDragInfo)
|
|
127
|
+
if (startChanges) {
|
|
128
|
+
this.editor.updateShapes([{ ...startChanges, id: shape.id, type: shape.type }])
|
|
129
|
+
}
|
|
130
|
+
|
|
138
131
|
this.update()
|
|
139
132
|
|
|
140
133
|
this.editor.select(this.shapeId)
|
|
@@ -204,6 +197,21 @@ export class DraggingHandle extends StateNode {
|
|
|
204
197
|
this.editor.snaps.clearIndicators()
|
|
205
198
|
kickoutOccludedShapes(this.editor, [this.shapeId])
|
|
206
199
|
|
|
200
|
+
// Call onHandleDragEnd callback before state transitions
|
|
201
|
+
const shape = this.editor.getShape(this.shapeId)
|
|
202
|
+
if (shape) {
|
|
203
|
+
const util = this.editor.getShapeUtil(shape)
|
|
204
|
+
const handleDragInfo = {
|
|
205
|
+
handle: this.initialHandle,
|
|
206
|
+
isPrecise: this.isPrecise,
|
|
207
|
+
initial: this.info.shape,
|
|
208
|
+
}
|
|
209
|
+
const endChanges = util.onHandleDragEnd?.(shape, handleDragInfo)
|
|
210
|
+
if (endChanges) {
|
|
211
|
+
this.editor.updateShapes([{ ...endChanges, id: shape.id, type: shape.type }])
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
|
|
207
215
|
const { onInteractionEnd } = this.info
|
|
208
216
|
if (this.editor.getInstanceState().isToolLocked && onInteractionEnd) {
|
|
209
217
|
// Return to the tool that was active before this one,
|
|
@@ -216,6 +224,18 @@ export class DraggingHandle extends StateNode {
|
|
|
216
224
|
}
|
|
217
225
|
|
|
218
226
|
private cancel() {
|
|
227
|
+
// Call onHandleDragCancel callback before bailing to mark
|
|
228
|
+
const shape = this.editor.getShape(this.shapeId)
|
|
229
|
+
if (shape) {
|
|
230
|
+
const util = this.editor.getShapeUtil(shape)
|
|
231
|
+
const handleDragInfo = {
|
|
232
|
+
handle: this.initialHandle,
|
|
233
|
+
isPrecise: this.isPrecise,
|
|
234
|
+
initial: this.info.shape,
|
|
235
|
+
}
|
|
236
|
+
util.onHandleDragCancel?.(shape, handleDragInfo)
|
|
237
|
+
}
|
|
238
|
+
|
|
219
239
|
this.editor.bailToMark(this.markId)
|
|
220
240
|
this.editor.snaps.clearIndicators()
|
|
221
241
|
|
|
@@ -122,8 +122,19 @@ export class Resizing extends StateNode {
|
|
|
122
122
|
}
|
|
123
123
|
|
|
124
124
|
private cancel() {
|
|
125
|
-
//
|
|
125
|
+
// Call onResizeCancel callback before resetting
|
|
126
|
+
const { shapeSnapshots } = this.snapshot
|
|
127
|
+
|
|
128
|
+
shapeSnapshots.forEach(({ shape }) => {
|
|
129
|
+
const current = this.editor.getShape(shape.id)
|
|
130
|
+
if (current) {
|
|
131
|
+
const util = this.editor.getShapeUtil(shape)
|
|
132
|
+
util.onResizeCancel?.(shape, current)
|
|
133
|
+
}
|
|
134
|
+
})
|
|
135
|
+
|
|
126
136
|
this.editor.bailToMark(this.markId)
|
|
137
|
+
|
|
127
138
|
if (this.info.onInteractionEnd) {
|
|
128
139
|
this.editor.setCurrentTool(this.info.onInteractionEnd, {})
|
|
129
140
|
} else {
|