tldraw 5.4.0-next.ef99ab47e5ce → 5.4.0
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/DOCS.md +2826 -3226
- package/RELEASE_NOTES.md +190 -2
- package/dist-cjs/index.d.ts +264 -275
- package/dist-cjs/index.js +1 -1
- package/dist-cjs/lib/defaultExternalContentHandlers.js +32 -16
- package/dist-cjs/lib/defaultExternalContentHandlers.js.map +2 -2
- package/dist-cjs/lib/shapes/arrow/curved-arrow.js +2 -2
- package/dist-cjs/lib/shapes/arrow/curved-arrow.js.map +2 -2
- package/dist-cjs/lib/shapes/arrow/elbow/getElbowArrowInfo.js +2 -2
- package/dist-cjs/lib/shapes/arrow/elbow/getElbowArrowInfo.js.map +2 -2
- package/dist-cjs/lib/shapes/arrow/getArrowInfo.js +1 -1
- package/dist-cjs/lib/shapes/arrow/getArrowInfo.js.map +2 -2
- package/dist-cjs/lib/shapes/arrow/straight-arrow.js +2 -2
- package/dist-cjs/lib/shapes/arrow/straight-arrow.js.map +2 -2
- package/dist-cjs/lib/shapes/arrow/toolStates/Idle.js +2 -3
- package/dist-cjs/lib/shapes/arrow/toolStates/Idle.js.map +2 -2
- package/dist-cjs/lib/shapes/arrow/toolStates/Pointing.js +7 -5
- package/dist-cjs/lib/shapes/arrow/toolStates/Pointing.js.map +2 -2
- package/dist-cjs/lib/shapes/draw/toolStates/Drawing.js +8 -6
- package/dist-cjs/lib/shapes/draw/toolStates/Drawing.js.map +2 -2
- package/dist-cjs/lib/shapes/geo/getGeoShapePath.js +3 -1
- package/dist-cjs/lib/shapes/geo/getGeoShapePath.js.map +2 -2
- package/dist-cjs/lib/shapes/geo/toolStates/Idle.js +2 -3
- package/dist-cjs/lib/shapes/geo/toolStates/Idle.js.map +2 -2
- package/dist-cjs/lib/shapes/geo/toolStates/Pointing.js +1 -1
- package/dist-cjs/lib/shapes/geo/toolStates/Pointing.js.map +2 -2
- package/dist-cjs/lib/shapes/line/toolStates/Pointing.js +2 -6
- package/dist-cjs/lib/shapes/line/toolStates/Pointing.js.map +2 -2
- package/dist-cjs/lib/shapes/note/NoteShapeUtil.js +4 -4
- package/dist-cjs/lib/shapes/note/NoteShapeUtil.js.map +2 -2
- package/dist-cjs/lib/shapes/note/noteHelpers.js +6 -5
- package/dist-cjs/lib/shapes/note/noteHelpers.js.map +2 -2
- package/dist-cjs/lib/shapes/note/toolStates/Pointing.js +1 -1
- package/dist-cjs/lib/shapes/note/toolStates/Pointing.js.map +2 -2
- package/dist-cjs/lib/shapes/shared/crop.js +1 -1
- package/dist-cjs/lib/shapes/shared/crop.js.map +2 -2
- package/dist-cjs/lib/shapes/text/toolStates/Idle.js +1 -1
- package/dist-cjs/lib/shapes/text/toolStates/Idle.js.map +2 -2
- package/dist-cjs/lib/shapes/text/toolStates/Pointing.js +1 -0
- package/dist-cjs/lib/shapes/text/toolStates/Pointing.js.map +2 -2
- package/dist-cjs/lib/shapes/video/VideoShapeUtil.js +3 -2
- package/dist-cjs/lib/shapes/video/VideoShapeUtil.js.map +2 -2
- package/dist-cjs/lib/tools/EraserTool/childStates/Erasing.js +2 -0
- package/dist-cjs/lib/tools/EraserTool/childStates/Erasing.js.map +2 -2
- package/dist-cjs/lib/tools/HandTool/childStates/OneFingerZooming.js +6 -0
- package/dist-cjs/lib/tools/HandTool/childStates/OneFingerZooming.js.map +2 -2
- package/dist-cjs/lib/tools/SelectTool/SelectTool.js +3 -0
- package/dist-cjs/lib/tools/SelectTool/SelectTool.js.map +2 -2
- package/dist-cjs/lib/tools/SelectTool/childStates/Brushing.js +1 -0
- package/dist-cjs/lib/tools/SelectTool/childStates/Brushing.js.map +2 -2
- package/dist-cjs/lib/tools/SelectTool/childStates/Crop/Crop.js +1 -0
- package/dist-cjs/lib/tools/SelectTool/childStates/Crop/Crop.js.map +2 -2
- package/dist-cjs/lib/tools/SelectTool/childStates/DraggingHandle.js +1 -3
- package/dist-cjs/lib/tools/SelectTool/childStates/DraggingHandle.js.map +2 -2
- package/dist-cjs/lib/tools/SelectTool/childStates/Idle.js +6 -3
- package/dist-cjs/lib/tools/SelectTool/childStates/Idle.js.map +2 -2
- package/dist-cjs/lib/tools/SelectTool/childStates/PointingArrowLabel.js +1 -1
- package/dist-cjs/lib/tools/SelectTool/childStates/PointingArrowLabel.js.map +2 -2
- package/dist-cjs/lib/tools/SelectTool/childStates/PointingCanvas.js +3 -0
- package/dist-cjs/lib/tools/SelectTool/childStates/PointingCanvas.js.map +2 -2
- package/dist-cjs/lib/tools/SelectTool/childStates/PointingHandle.js +1 -0
- package/dist-cjs/lib/tools/SelectTool/childStates/PointingHandle.js.map +2 -2
- package/dist-cjs/lib/tools/SelectTool/childStates/PointingShape.js +1 -1
- package/dist-cjs/lib/tools/SelectTool/childStates/PointingShape.js.map +2 -2
- package/dist-cjs/lib/tools/SelectTool/childStates/Resizing.js +22 -30
- package/dist-cjs/lib/tools/SelectTool/childStates/Resizing.js.map +2 -2
- package/dist-cjs/lib/tools/SelectTool/childStates/ScribbleBrushing.js +1 -3
- package/dist-cjs/lib/tools/SelectTool/childStates/ScribbleBrushing.js.map +2 -2
- package/dist-cjs/lib/tools/SelectTool/childStates/Translating.js +4 -1
- package/dist-cjs/lib/tools/SelectTool/childStates/Translating.js.map +2 -2
- package/dist-cjs/lib/tools/SelectTool/selectHelpers.js +10 -0
- package/dist-cjs/lib/tools/SelectTool/selectHelpers.js.map +2 -2
- package/dist-cjs/lib/ui/TldrawUi.js +2 -3
- package/dist-cjs/lib/ui/TldrawUi.js.map +2 -2
- package/dist-cjs/lib/ui/components/ActionsMenu/DefaultActionsMenu.js +1 -11
- package/dist-cjs/lib/ui/components/ActionsMenu/DefaultActionsMenu.js.map +2 -2
- package/dist-cjs/lib/ui/components/ContextMenu/DefaultContextMenu.js +6 -1
- package/dist-cjs/lib/ui/components/ContextMenu/DefaultContextMenu.js.map +2 -2
- package/dist-cjs/lib/ui/components/ContextMenu/DefaultContextMenuContent.js +1 -1
- package/dist-cjs/lib/ui/components/ContextMenu/DefaultContextMenuContent.js.map +2 -2
- package/dist-cjs/lib/ui/components/Dialogs.js +8 -0
- package/dist-cjs/lib/ui/components/Dialogs.js.map +2 -2
- package/dist-cjs/lib/ui/components/EditLinkDialog.js +5 -5
- package/dist-cjs/lib/ui/components/EditLinkDialog.js.map +2 -2
- package/dist-cjs/lib/ui/components/Minimap/DefaultMinimap.js +4 -20
- package/dist-cjs/lib/ui/components/Minimap/DefaultMinimap.js.map +2 -2
- package/dist-cjs/lib/ui/components/SharePanel/DefaultPeopleMenuItem.js +1 -2
- package/dist-cjs/lib/ui/components/SharePanel/DefaultPeopleMenuItem.js.map +2 -2
- package/dist-cjs/lib/ui/components/StylePanel/StylePanelDoubleDropdownPicker.js +2 -0
- package/dist-cjs/lib/ui/components/StylePanel/StylePanelDoubleDropdownPicker.js.map +2 -2
- package/dist-cjs/lib/ui/components/Toolbar/AltTextEditor.js +1 -0
- package/dist-cjs/lib/ui/components/Toolbar/AltTextEditor.js.map +2 -2
- package/dist-cjs/lib/ui/components/Toolbar/DefaultImageToolbarContent.js +10 -8
- package/dist-cjs/lib/ui/components/Toolbar/DefaultImageToolbarContent.js.map +2 -2
- package/dist-cjs/lib/ui/components/primitives/TldrawUiSlider.js +20 -1
- package/dist-cjs/lib/ui/components/primitives/TldrawUiSlider.js.map +2 -2
- package/dist-cjs/lib/ui/context/actions.js +8 -8
- package/dist-cjs/lib/ui/context/actions.js.map +2 -2
- package/dist-cjs/lib/ui/hooks/useClipboardEvents.js +2 -0
- package/dist-cjs/lib/ui/hooks/useClipboardEvents.js.map +2 -2
- package/dist-cjs/lib/ui/hooks/useCommentingEnabled.js +1 -1
- package/dist-cjs/lib/ui/hooks/useCommentingEnabled.js.map +2 -2
- package/dist-cjs/lib/ui/hooks/useFlatten.js +42 -23
- package/dist-cjs/lib/ui/hooks/useFlatten.js.map +2 -2
- package/dist-cjs/lib/ui/version.js +3 -3
- package/dist-cjs/lib/ui/version.js.map +1 -1
- package/dist-esm/index.d.mts +264 -275
- package/dist-esm/index.mjs +1 -1
- package/dist-esm/lib/defaultExternalContentHandlers.mjs +32 -16
- package/dist-esm/lib/defaultExternalContentHandlers.mjs.map +2 -2
- package/dist-esm/lib/shapes/arrow/curved-arrow.mjs +2 -2
- package/dist-esm/lib/shapes/arrow/curved-arrow.mjs.map +2 -2
- package/dist-esm/lib/shapes/arrow/elbow/getElbowArrowInfo.mjs +2 -2
- package/dist-esm/lib/shapes/arrow/elbow/getElbowArrowInfo.mjs.map +2 -2
- package/dist-esm/lib/shapes/arrow/getArrowInfo.mjs +1 -1
- package/dist-esm/lib/shapes/arrow/getArrowInfo.mjs.map +2 -2
- package/dist-esm/lib/shapes/arrow/straight-arrow.mjs +2 -2
- package/dist-esm/lib/shapes/arrow/straight-arrow.mjs.map +2 -2
- package/dist-esm/lib/shapes/arrow/toolStates/Idle.mjs +3 -4
- package/dist-esm/lib/shapes/arrow/toolStates/Idle.mjs.map +2 -2
- package/dist-esm/lib/shapes/arrow/toolStates/Pointing.mjs +7 -5
- package/dist-esm/lib/shapes/arrow/toolStates/Pointing.mjs.map +2 -2
- package/dist-esm/lib/shapes/draw/toolStates/Drawing.mjs +8 -6
- package/dist-esm/lib/shapes/draw/toolStates/Drawing.mjs.map +2 -2
- package/dist-esm/lib/shapes/geo/getGeoShapePath.mjs +3 -1
- package/dist-esm/lib/shapes/geo/getGeoShapePath.mjs.map +2 -2
- package/dist-esm/lib/shapes/geo/toolStates/Idle.mjs +3 -4
- package/dist-esm/lib/shapes/geo/toolStates/Idle.mjs.map +2 -2
- package/dist-esm/lib/shapes/geo/toolStates/Pointing.mjs +1 -1
- package/dist-esm/lib/shapes/geo/toolStates/Pointing.mjs.map +2 -2
- package/dist-esm/lib/shapes/line/toolStates/Pointing.mjs +2 -7
- package/dist-esm/lib/shapes/line/toolStates/Pointing.mjs.map +2 -2
- package/dist-esm/lib/shapes/note/NoteShapeUtil.mjs +4 -4
- package/dist-esm/lib/shapes/note/NoteShapeUtil.mjs.map +2 -2
- package/dist-esm/lib/shapes/note/noteHelpers.mjs +6 -5
- package/dist-esm/lib/shapes/note/noteHelpers.mjs.map +2 -2
- package/dist-esm/lib/shapes/note/toolStates/Pointing.mjs +1 -1
- package/dist-esm/lib/shapes/note/toolStates/Pointing.mjs.map +2 -2
- package/dist-esm/lib/shapes/shared/crop.mjs +1 -1
- package/dist-esm/lib/shapes/shared/crop.mjs.map +2 -2
- package/dist-esm/lib/shapes/text/toolStates/Idle.mjs +2 -2
- package/dist-esm/lib/shapes/text/toolStates/Idle.mjs.map +2 -2
- package/dist-esm/lib/shapes/text/toolStates/Pointing.mjs +1 -0
- package/dist-esm/lib/shapes/text/toolStates/Pointing.mjs.map +2 -2
- package/dist-esm/lib/shapes/video/VideoShapeUtil.mjs +3 -2
- package/dist-esm/lib/shapes/video/VideoShapeUtil.mjs.map +2 -2
- package/dist-esm/lib/tools/EraserTool/childStates/Erasing.mjs +2 -0
- package/dist-esm/lib/tools/EraserTool/childStates/Erasing.mjs.map +2 -2
- package/dist-esm/lib/tools/HandTool/childStates/OneFingerZooming.mjs +6 -0
- package/dist-esm/lib/tools/HandTool/childStates/OneFingerZooming.mjs.map +2 -2
- package/dist-esm/lib/tools/SelectTool/SelectTool.mjs +3 -0
- package/dist-esm/lib/tools/SelectTool/SelectTool.mjs.map +2 -2
- package/dist-esm/lib/tools/SelectTool/childStates/Brushing.mjs +1 -0
- package/dist-esm/lib/tools/SelectTool/childStates/Brushing.mjs.map +2 -2
- package/dist-esm/lib/tools/SelectTool/childStates/Crop/Crop.mjs +1 -0
- package/dist-esm/lib/tools/SelectTool/childStates/Crop/Crop.mjs.map +2 -2
- package/dist-esm/lib/tools/SelectTool/childStates/DraggingHandle.mjs +1 -3
- package/dist-esm/lib/tools/SelectTool/childStates/DraggingHandle.mjs.map +2 -2
- package/dist-esm/lib/tools/SelectTool/childStates/Idle.mjs +6 -3
- package/dist-esm/lib/tools/SelectTool/childStates/Idle.mjs.map +2 -2
- package/dist-esm/lib/tools/SelectTool/childStates/PointingArrowLabel.mjs +1 -1
- package/dist-esm/lib/tools/SelectTool/childStates/PointingArrowLabel.mjs.map +2 -2
- package/dist-esm/lib/tools/SelectTool/childStates/PointingCanvas.mjs +3 -0
- package/dist-esm/lib/tools/SelectTool/childStates/PointingCanvas.mjs.map +2 -2
- package/dist-esm/lib/tools/SelectTool/childStates/PointingHandle.mjs +5 -1
- package/dist-esm/lib/tools/SelectTool/childStates/PointingHandle.mjs.map +2 -2
- package/dist-esm/lib/tools/SelectTool/childStates/PointingShape.mjs +1 -1
- package/dist-esm/lib/tools/SelectTool/childStates/PointingShape.mjs.map +2 -2
- package/dist-esm/lib/tools/SelectTool/childStates/Resizing.mjs +22 -31
- package/dist-esm/lib/tools/SelectTool/childStates/Resizing.mjs.map +2 -2
- package/dist-esm/lib/tools/SelectTool/childStates/ScribbleBrushing.mjs +1 -3
- package/dist-esm/lib/tools/SelectTool/childStates/ScribbleBrushing.mjs.map +2 -2
- package/dist-esm/lib/tools/SelectTool/childStates/Translating.mjs +4 -1
- package/dist-esm/lib/tools/SelectTool/childStates/Translating.mjs.map +2 -2
- package/dist-esm/lib/tools/SelectTool/selectHelpers.mjs +10 -0
- package/dist-esm/lib/tools/SelectTool/selectHelpers.mjs.map +2 -2
- package/dist-esm/lib/ui/TldrawUi.mjs +2 -3
- package/dist-esm/lib/ui/TldrawUi.mjs.map +2 -2
- package/dist-esm/lib/ui/components/ActionsMenu/DefaultActionsMenu.mjs +2 -12
- package/dist-esm/lib/ui/components/ActionsMenu/DefaultActionsMenu.mjs.map +2 -2
- package/dist-esm/lib/ui/components/ContextMenu/DefaultContextMenu.mjs +7 -2
- package/dist-esm/lib/ui/components/ContextMenu/DefaultContextMenu.mjs.map +2 -2
- package/dist-esm/lib/ui/components/ContextMenu/DefaultContextMenuContent.mjs +1 -1
- package/dist-esm/lib/ui/components/ContextMenu/DefaultContextMenuContent.mjs.map +2 -2
- package/dist-esm/lib/ui/components/Dialogs.mjs +9 -1
- package/dist-esm/lib/ui/components/Dialogs.mjs.map +2 -2
- package/dist-esm/lib/ui/components/EditLinkDialog.mjs +5 -5
- package/dist-esm/lib/ui/components/EditLinkDialog.mjs.map +2 -2
- package/dist-esm/lib/ui/components/Minimap/DefaultMinimap.mjs +5 -22
- package/dist-esm/lib/ui/components/Minimap/DefaultMinimap.mjs.map +2 -2
- package/dist-esm/lib/ui/components/SharePanel/DefaultPeopleMenuItem.mjs +1 -2
- package/dist-esm/lib/ui/components/SharePanel/DefaultPeopleMenuItem.mjs.map +2 -2
- package/dist-esm/lib/ui/components/StylePanel/StylePanelDoubleDropdownPicker.mjs +2 -0
- package/dist-esm/lib/ui/components/StylePanel/StylePanelDoubleDropdownPicker.mjs.map +2 -2
- package/dist-esm/lib/ui/components/Toolbar/AltTextEditor.mjs +1 -0
- package/dist-esm/lib/ui/components/Toolbar/AltTextEditor.mjs.map +2 -2
- package/dist-esm/lib/ui/components/Toolbar/DefaultImageToolbarContent.mjs +10 -8
- package/dist-esm/lib/ui/components/Toolbar/DefaultImageToolbarContent.mjs.map +2 -2
- package/dist-esm/lib/ui/components/primitives/TldrawUiSlider.mjs +20 -1
- package/dist-esm/lib/ui/components/primitives/TldrawUiSlider.mjs.map +2 -2
- package/dist-esm/lib/ui/context/actions.mjs +8 -8
- package/dist-esm/lib/ui/context/actions.mjs.map +2 -2
- package/dist-esm/lib/ui/hooks/useClipboardEvents.mjs +2 -0
- package/dist-esm/lib/ui/hooks/useClipboardEvents.mjs.map +2 -2
- package/dist-esm/lib/ui/hooks/useCommentingEnabled.mjs +2 -2
- package/dist-esm/lib/ui/hooks/useCommentingEnabled.mjs.map +2 -2
- package/dist-esm/lib/ui/hooks/useFlatten.mjs +42 -23
- package/dist-esm/lib/ui/hooks/useFlatten.mjs.map +2 -2
- package/dist-esm/lib/ui/version.mjs +3 -3
- package/dist-esm/lib/ui/version.mjs.map +1 -1
- package/package.json +12 -6
- package/src/lib/defaultExternalContentHandlers.test.ts +43 -0
- package/src/lib/defaultExternalContentHandlers.ts +40 -18
- package/src/lib/shapes/arrow/ArrowShapeTool.test.ts +17 -0
- package/src/lib/shapes/arrow/curved-arrow.ts +2 -2
- package/src/lib/shapes/arrow/elbow/getElbowArrowInfo.tsx +5 -3
- package/src/lib/shapes/arrow/getArrowInfo.ts +2 -1
- package/src/lib/shapes/arrow/straight-arrow.ts +2 -2
- package/src/lib/shapes/arrow/toolStates/Idle.tsx +3 -4
- package/src/lib/shapes/arrow/toolStates/Pointing.tsx +10 -6
- package/src/lib/shapes/draw/DrawShapeTool.test.ts +19 -0
- package/src/lib/shapes/draw/toolStates/Drawing.ts +12 -6
- package/src/lib/shapes/frame/FrameShapeTool.test.ts +17 -0
- package/src/lib/shapes/geo/GeoShapeTool.test.ts +28 -0
- package/src/lib/shapes/geo/getGeoShapePath.ts +3 -1
- package/src/lib/shapes/geo/toolStates/Idle.ts +3 -4
- package/src/lib/shapes/geo/toolStates/Pointing.ts +2 -1
- package/src/lib/shapes/line/LineShapeTool.test.ts +30 -1
- package/src/lib/shapes/line/toolStates/Pointing.ts +6 -7
- package/src/lib/shapes/note/NoteShapeTool.test.ts +45 -0
- package/src/lib/shapes/note/NoteShapeUtil.tsx +7 -4
- package/src/lib/shapes/note/noteHelpers.ts +18 -8
- package/src/lib/shapes/note/toolStates/Pointing.ts +2 -1
- package/src/lib/shapes/shared/crop.ts +7 -1
- package/src/lib/shapes/text/TextShapeTool.test.ts +33 -0
- package/src/lib/shapes/text/toolStates/Idle.ts +2 -2
- package/src/lib/shapes/text/toolStates/Pointing.ts +3 -0
- package/src/lib/shapes/video/VideoShapeUtil.tsx +4 -2
- package/src/lib/tools/EraserTool/childStates/Erasing.ts +5 -0
- package/src/lib/tools/HandTool/childStates/OneFingerZooming.ts +10 -0
- package/src/lib/tools/SelectTool/SelectTool.ts +4 -0
- package/src/lib/tools/SelectTool/childStates/Brushing.ts +4 -0
- package/src/lib/tools/SelectTool/childStates/Crop/Crop.ts +4 -0
- package/src/lib/tools/SelectTool/childStates/DraggingHandle.tsx +3 -4
- package/src/lib/tools/SelectTool/childStates/Idle.ts +10 -3
- package/src/lib/tools/SelectTool/childStates/PointingArrowLabel.ts +1 -1
- package/src/lib/tools/SelectTool/childStates/PointingCanvas.ts +4 -0
- package/src/lib/tools/SelectTool/childStates/PointingHandle.ts +6 -1
- package/src/lib/tools/SelectTool/childStates/PointingShape.ts +4 -1
- package/src/lib/tools/SelectTool/childStates/Resizing.ts +30 -33
- package/src/lib/tools/SelectTool/childStates/ScribbleBrushing.ts +3 -3
- package/src/lib/tools/SelectTool/childStates/Translating.ts +6 -0
- package/src/lib/tools/SelectTool/selectHelpers.ts +21 -0
- package/src/lib/ui/TldrawUi.tsx +1 -2
- package/src/lib/ui/components/ActionsMenu/DefaultActionsMenu.tsx +4 -14
- package/src/lib/ui/components/ContextMenu/DefaultContextMenu.tsx +8 -2
- package/src/lib/ui/components/ContextMenu/DefaultContextMenuContent.tsx +5 -1
- package/src/lib/ui/components/Dialogs.tsx +17 -1
- package/src/lib/ui/components/EditLinkDialog.tsx +8 -7
- package/src/lib/ui/components/Minimap/DefaultMinimap.tsx +5 -23
- package/src/lib/ui/components/SharePanel/DefaultPeopleMenuItem.tsx +2 -3
- package/src/lib/ui/components/StylePanel/StylePanelDoubleDropdownPicker.tsx +2 -0
- package/src/lib/ui/components/Toolbar/AltTextEditor.tsx +1 -0
- package/src/lib/ui/components/Toolbar/DefaultImageToolbarContent.tsx +13 -8
- package/src/lib/ui/components/primitives/TldrawUiSlider.test.tsx +92 -0
- package/src/lib/ui/components/primitives/TldrawUiSlider.tsx +25 -1
- package/src/lib/ui/context/actions.tsx +25 -8
- package/src/lib/ui/hooks/useClipboardEvents.ts +2 -0
- package/src/lib/ui/hooks/useCommentingEnabled.ts +5 -3
- package/src/lib/ui/hooks/useFlatten.ts +56 -23
- package/src/lib/ui/version.ts +3 -3
- package/src/lib/ui.css +7 -16
- package/src/test/ClickManager.test.ts +18 -0
- package/src/test/Editor.test.tsx +3 -0
- package/src/test/EraserTool.test.ts +18 -2
- package/src/test/HandTool.test.ts +21 -0
- package/src/test/LaserTool.test.ts +46 -0
- package/src/test/ScribbleManager.test.ts +19 -0
- package/src/test/SelectTool.test.ts +110 -0
- package/src/test/TldrawEditor.test.tsx +18 -0
- package/src/test/VideoShapeUtil.test.tsx +126 -0
- package/src/test/actions-menu-readonly.test.tsx +37 -0
- package/src/test/bookmark-shapes.test.ts +110 -0
- package/src/test/commands/__snapshots__/packShapes.test.ts.snap +6 -6
- package/src/test/commands/clipboard.test.ts +36 -1
- package/src/test/commands/lockShapes.test.ts +11 -0
- package/src/test/commands/packShapes.test.ts +20 -0
- package/src/test/commands/putContent.test.ts +35 -1
- package/src/test/commands/setCamera.test.ts +69 -0
- package/src/test/commands/stackShapes.test.ts +12 -0
- package/src/test/commands/updateViewportPageBounds.test.ts +8 -0
- package/src/test/cropping.test.ts +162 -0
- package/src/test/customSnapping.test.tsx +32 -0
- package/src/test/defaultHandleExternalFileContent.test.ts +86 -0
- package/src/test/drawing.test.ts +60 -1
- package/src/test/duplicate.test.ts +22 -0
- package/src/test/flatten.test.ts +135 -0
- package/src/test/frames.test.ts +51 -0
- package/src/test/getShapeAtPoint.test.ts +44 -1
- package/src/test/handleDeepLink.test.tsx +15 -0
- package/src/test/minimap-wheel.test.tsx +48 -0
- package/src/test/resizing.test.ts +46 -0
- package/src/test/selection-omnibus.test.ts +50 -0
- package/src/test/spacebarPanning.test.ts +58 -0
- package/src/test/translating.test.ts +42 -0
- package/src/test/ui/AltTextEditor.test.tsx +51 -0
- package/src/test/ui/ContextMenu.test.tsx +61 -2
- package/src/test/ui/EditLinkDialog.test.tsx +77 -0
- package/src/test/ui/ImageToolbar.test.tsx +58 -0
- package/src/test/ui/StylePanel.test.tsx +33 -2
- package/src/test/ui/keyboardShortcuts.test.tsx +27 -0
- package/src/test/ui/presenceActivity.test.tsx +44 -0
- package/src/test/ui/unlockAll.test.tsx +55 -0
- package/src/test/ui-outside-editor.test.tsx +74 -0
- package/src/test/viewport-following.test.ts +29 -1
- package/tldraw.css +7 -16
|
@@ -3,6 +3,7 @@ import {
|
|
|
3
3
|
IndexKey,
|
|
4
4
|
TLNoteShape,
|
|
5
5
|
TLShape,
|
|
6
|
+
TLShapeId,
|
|
6
7
|
Vec,
|
|
7
8
|
compact,
|
|
8
9
|
createShapeId,
|
|
@@ -94,29 +95,34 @@ export interface AvailableNoteAdjacentPositionsOpts {
|
|
|
94
95
|
extraHeight: number
|
|
95
96
|
noteWidth: number
|
|
96
97
|
noteHeight: number
|
|
98
|
+
/**
|
|
99
|
+
* Notes whose adjacent positions should not be offered, e.g. the notes being dragged,
|
|
100
|
+
* which would otherwise snap to their own slots.
|
|
101
|
+
*/
|
|
102
|
+
excludeShapeIds?: TLShapeId[]
|
|
97
103
|
}
|
|
98
104
|
|
|
99
105
|
/**
|
|
100
|
-
* Get all of the available note adjacent positions, excluding
|
|
106
|
+
* Get all of the available note adjacent positions, excluding those around `excludeShapeIds`.
|
|
101
107
|
*
|
|
102
108
|
* @internal */
|
|
103
109
|
export function getAvailableNoteAdjacentPositions(
|
|
104
110
|
editor: Editor,
|
|
105
111
|
opts: AvailableNoteAdjacentPositionsOpts
|
|
106
112
|
) {
|
|
107
|
-
const { rotation, scale, extraHeight, noteWidth, noteHeight } = opts
|
|
108
|
-
const
|
|
113
|
+
const { rotation, scale, extraHeight, noteWidth, noteHeight, excludeShapeIds = [] } = opts
|
|
114
|
+
const excludedShapeIds = new Set(excludeShapeIds)
|
|
109
115
|
const minSize =
|
|
110
116
|
(Math.max(noteWidth, noteHeight) + editor.options.adjacentShapeMargin + extraHeight) ** 2
|
|
111
117
|
const allCenters = new Map<TLNoteShape, Vec>()
|
|
112
118
|
const positions: (Vec | undefined)[] = []
|
|
113
119
|
|
|
114
|
-
// Get all the positions that are adjacent to the
|
|
120
|
+
// Get all the positions that are adjacent to the candidate note shapes
|
|
115
121
|
for (const shape of editor.getCurrentPageShapes()) {
|
|
116
122
|
if (
|
|
117
123
|
!editor.isShapeOfType(shape, 'note') ||
|
|
118
124
|
scale !== shape.props.scale ||
|
|
119
|
-
|
|
125
|
+
excludedShapeIds.has(shape.id)
|
|
120
126
|
) {
|
|
121
127
|
continue
|
|
122
128
|
}
|
|
@@ -126,7 +132,7 @@ export function getAvailableNoteAdjacentPositions(
|
|
|
126
132
|
// If the note has a different rotation, we can't use its adjacent positions
|
|
127
133
|
if (rotation !== transform.rotation()) continue
|
|
128
134
|
|
|
129
|
-
// Save the
|
|
135
|
+
// Save the candidate note shape's center
|
|
130
136
|
allCenters.set(shape, editor.getShapePageBounds(shape)!.center)
|
|
131
137
|
|
|
132
138
|
// And push its position to the positions array
|
|
@@ -135,7 +141,8 @@ export function getAvailableNoteAdjacentPositions(
|
|
|
135
141
|
getNoteAdjacentPositions(editor, {
|
|
136
142
|
pagePoint: transform.point(),
|
|
137
143
|
pageRotation: rotation,
|
|
138
|
-
|
|
144
|
+
// page units, like the base positions (see PointingHandle)
|
|
145
|
+
growY: shape.props.growY * shape.props.scale,
|
|
139
146
|
extraHeight,
|
|
140
147
|
scale,
|
|
141
148
|
noteWidth,
|
|
@@ -191,8 +198,11 @@ export function getNoteShapeForAdjacentPosition(
|
|
|
191
198
|
// Start from the top of the stack, and work our way down
|
|
192
199
|
const allShapesOnPage = editor.getCurrentPageShapesSorted()
|
|
193
200
|
|
|
201
|
+
// Squared, in page units, to compare against Dist2 below. A neighbour that has grown
|
|
202
|
+
// (growY) sits further from the ideal slot centre, so the radius is a full note plus margin.
|
|
194
203
|
const minDistance =
|
|
195
|
-
(Math.max(noteWidth, noteHeight) + editor.options.adjacentShapeMargin
|
|
204
|
+
((Math.max(noteWidth, noteHeight) + editor.options.adjacentShapeMargin) * shape.props.scale) **
|
|
205
|
+
2
|
|
196
206
|
|
|
197
207
|
for (let i = allShapesOnPage.length - 1; i >= 0; i--) {
|
|
198
208
|
const otherNote = allShapesOnPage[i]
|
|
@@ -551,7 +551,13 @@ export function getCroppedImageDataForReplacedImage(
|
|
|
551
551
|
let crop = defaultCrop
|
|
552
552
|
let newDisplayW = origDisplayW
|
|
553
553
|
let newDisplayH = origDisplayH
|
|
554
|
-
|
|
554
|
+
// Compare the bounds, not the whole object: an explicit `isCircle: false` must still count
|
|
555
|
+
// as the original crop.
|
|
556
|
+
const isOriginalCrop =
|
|
557
|
+
!!imageShape.props.crop &&
|
|
558
|
+
isEqual(imageShape.props.crop.topLeft, defaultCrop.topLeft) &&
|
|
559
|
+
isEqual(imageShape.props.crop.bottomRight, defaultCrop.bottomRight) &&
|
|
560
|
+
!imageShape.props.crop.isCircle
|
|
555
561
|
|
|
556
562
|
if (isOriginalCrop) {
|
|
557
563
|
newDisplayW = origDisplayW
|
|
@@ -55,6 +55,24 @@ describe(TextShapeTool, () => {
|
|
|
55
55
|
props: { richText: toRichText('Hello') },
|
|
56
56
|
})
|
|
57
57
|
})
|
|
58
|
+
|
|
59
|
+
it('Records the deletion of an empty text shape so redo cannot resurrect it', () => {
|
|
60
|
+
editor.setCurrentTool('text')
|
|
61
|
+
editor.pointerDown(0, 0)
|
|
62
|
+
editor.pointerUp()
|
|
63
|
+
editor.expectToBeIn('select.editing_shape')
|
|
64
|
+
expect(editor.getCurrentPageShapes().length).toBe(1)
|
|
65
|
+
|
|
66
|
+
// Exiting without typing deletes the empty shape (TextShapeUtil.onEditEnd)
|
|
67
|
+
editor.cancel()
|
|
68
|
+
expect(editor.getCurrentPageShapes().length).toBe(0)
|
|
69
|
+
|
|
70
|
+
// The creation and the deletion squash to nothing, so neither undo nor redo brings it back
|
|
71
|
+
editor.undo()
|
|
72
|
+
expect(editor.getCurrentPageShapes().length).toBe(0)
|
|
73
|
+
editor.redo()
|
|
74
|
+
expect(editor.getCurrentPageShapes().length).toBe(0)
|
|
75
|
+
})
|
|
58
76
|
})
|
|
59
77
|
|
|
60
78
|
describe('When selecting the tool', () => {
|
|
@@ -280,6 +298,21 @@ describe('When resizing', () => {
|
|
|
280
298
|
expect(editor.getCurrentPageShapes().length).toBe(1)
|
|
281
299
|
})
|
|
282
300
|
|
|
301
|
+
it('removes the pending text shape when another tool is selected mid-drag', () => {
|
|
302
|
+
editor.setCurrentTool('text')
|
|
303
|
+
editor.pointerDown(0, 0)
|
|
304
|
+
vi.advanceTimersByTime(200)
|
|
305
|
+
editor.pointerMove(100, 100)
|
|
306
|
+
editor.expectToBeIn('select.resizing')
|
|
307
|
+
expect(editor.getCurrentPageShapes().length).toBe(1)
|
|
308
|
+
editor.setCurrentTool('geo')
|
|
309
|
+
editor.expectToBeIn('geo.idle')
|
|
310
|
+
expect(editor.getCurrentPageShapes().length).toBe(0)
|
|
311
|
+
expect(editor.getSelectedShapeIds()).toEqual([])
|
|
312
|
+
editor.pointerUp(100, 100)
|
|
313
|
+
expect(editor.getCurrentPageShapes().length).toBe(0)
|
|
314
|
+
})
|
|
315
|
+
|
|
283
316
|
it('preserves the top left when the text has a fixed width', () => {
|
|
284
317
|
editor.setCurrentTool('text')
|
|
285
318
|
const x = 0
|
|
@@ -3,7 +3,7 @@ import {
|
|
|
3
3
|
cancelUpdateHoveredShapeId,
|
|
4
4
|
updateHoveredShapeId,
|
|
5
5
|
} from '../../../tools/selection-logic/updateHoveredShapeId'
|
|
6
|
-
import {
|
|
6
|
+
import { startEditingShape } from '../../../tools/SelectTool/selectHelpers'
|
|
7
7
|
|
|
8
8
|
export class Idle extends StateNode {
|
|
9
9
|
static override id = 'idle'
|
|
@@ -34,7 +34,7 @@ export class Idle extends StateNode {
|
|
|
34
34
|
const onlySelectedShape = this.editor.getOnlySelectedShape()
|
|
35
35
|
if (!this.editor.canEditShape(onlySelectedShape)) return
|
|
36
36
|
this.editor.setCurrentTool('select')
|
|
37
|
-
|
|
37
|
+
startEditingShape(this.editor, onlySelectedShape, { info })
|
|
38
38
|
}
|
|
39
39
|
}
|
|
40
40
|
|
|
@@ -21,6 +21,9 @@ export class Pointing extends StateNode {
|
|
|
21
21
|
enterTime = 0
|
|
22
22
|
override onEnter(): void {
|
|
23
23
|
this.enterTime = Date.now()
|
|
24
|
+
// Only the drag-to-create path sets a mark. Without this reset, a later cancel
|
|
25
|
+
// would bail to a mark left over from a previous text shape and undo unrelated work.
|
|
26
|
+
this.markId = ''
|
|
24
27
|
}
|
|
25
28
|
|
|
26
29
|
override onExit() {
|
|
@@ -175,14 +175,16 @@ const VideoShape = memo(function VideoShape({ shape }: { shape: TLVideoShape })
|
|
|
175
175
|
}
|
|
176
176
|
}, [isEditing, isLoaded])
|
|
177
177
|
|
|
178
|
+
const hasVideo = !!asset?.props.src && !!url && isLoaded
|
|
179
|
+
|
|
178
180
|
return (
|
|
179
181
|
<>
|
|
180
182
|
<HTMLContainer
|
|
181
183
|
id={shape.id}
|
|
182
184
|
style={{
|
|
183
185
|
color: 'var(--tl-color-text-3)',
|
|
184
|
-
backgroundColor:
|
|
185
|
-
border:
|
|
186
|
+
backgroundColor: hasVideo ? 'transparent' : 'var(--tl-color-low)',
|
|
187
|
+
border: hasVideo ? 'none' : '1px solid var(--tl-color-low-border)',
|
|
186
188
|
}}
|
|
187
189
|
>
|
|
188
190
|
<div className="tl-counter-scaled">
|
|
@@ -16,12 +16,17 @@ export class Erasing extends StateNode {
|
|
|
16
16
|
this.info = info
|
|
17
17
|
|
|
18
18
|
const originPagePoint = this.editor.inputs.getOriginPagePoint()
|
|
19
|
+
const pressedShapeIds = new Set(this.editor.getErasingShapeIds())
|
|
19
20
|
this.excludedShapeIds = new Set(
|
|
20
21
|
this.editor
|
|
21
22
|
.getCurrentPageShapes()
|
|
22
23
|
.filter((shape) => {
|
|
23
24
|
//If the shape is locked, we shouldn't erase it
|
|
24
25
|
if (this.editor.isShapeOrAncestorLocked(shape)) return true
|
|
26
|
+
// A frame the press hit on its outline stays marked: a press just inside the outline
|
|
27
|
+
// is within the frame's bounds, so the containment check below would otherwise
|
|
28
|
+
// unmark it on the first move while a press just outside would erase it
|
|
29
|
+
if (this.editor.isShapeFrameLike(shape) && pressedShapeIds.has(shape.id)) return false
|
|
25
30
|
//If the shape is a group or frame-like, check we're inside it when we start erasing
|
|
26
31
|
if (this.editor.isShapeOfType(shape, 'group') || this.editor.isShapeFrameLike(shape)) {
|
|
27
32
|
const pointInShapeShape = this.editor.getPointInShapeSpace(shape, originPagePoint)
|
|
@@ -47,6 +47,16 @@ export class OneFingerZooming extends StateNode {
|
|
|
47
47
|
this.complete()
|
|
48
48
|
}
|
|
49
49
|
|
|
50
|
+
override onPointerDown() {
|
|
51
|
+
// A second touch resets the input origin to the new pointer, so continuing
|
|
52
|
+
// the zoom would jump the camera. Yield before the pinch begins.
|
|
53
|
+
this.parent.transition('idle')
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
override onComplete() {
|
|
57
|
+
this.parent.transition('idle')
|
|
58
|
+
}
|
|
59
|
+
|
|
50
60
|
override onCancel() {
|
|
51
61
|
this.parent.transition('idle')
|
|
52
62
|
}
|
|
@@ -84,5 +84,9 @@ export class SelectTool extends StateNode {
|
|
|
84
84
|
if (this.editor.getCurrentPageState().editingShapeId) {
|
|
85
85
|
this.editor.setEditingShape(null)
|
|
86
86
|
}
|
|
87
|
+
// Leaving crop mode for another tool would otherwise keep the image in its cropping state
|
|
88
|
+
if (this.editor.getCroppingShapeId()) {
|
|
89
|
+
this.editor.setCroppingShape(null)
|
|
90
|
+
}
|
|
87
91
|
}
|
|
88
92
|
}
|
|
@@ -36,6 +36,10 @@ export class Brushing extends StateNode {
|
|
|
36
36
|
|
|
37
37
|
this.isWrapMode = editor.user.getIsWrapMode()
|
|
38
38
|
|
|
39
|
+
// Accel-pressing a resize or rotate handle brushes instead of resizing or rotating,
|
|
40
|
+
// and the handle's hover cursor would otherwise stay up for the whole brush.
|
|
41
|
+
editor.setCursor({ type: 'default', rotation: 0 })
|
|
42
|
+
|
|
39
43
|
this.viewportDidChange = false
|
|
40
44
|
|
|
41
45
|
let isInitialCheck = true
|
|
@@ -28,6 +28,10 @@ export class Crop extends StateNode {
|
|
|
28
28
|
}
|
|
29
29
|
}
|
|
30
30
|
override onCancel() {
|
|
31
|
+
// Parents handle events before children, so a cancel during a child's drag would unwind
|
|
32
|
+
// the whole session here before the child bails to its own mark. Only idle's escape ends
|
|
33
|
+
// the session; mid-drag children revert just their own change and return to idle.
|
|
34
|
+
if (this.getCurrent()?.id !== 'idle') return
|
|
31
35
|
if (!this.didExit) {
|
|
32
36
|
this.didExit = true
|
|
33
37
|
this.editor.bailToMark(this.markId)
|
|
@@ -333,13 +333,12 @@ export class DraggingHandle extends StateNode {
|
|
|
333
333
|
|
|
334
334
|
if (canSnap && (isSnapMode ? !ctrlKey : ctrlKey)) {
|
|
335
335
|
// We're snapping
|
|
336
|
-
const pageTransform = editor.getShapePageTransform(shape.id)
|
|
337
|
-
if (!pageTransform) throw Error('Expected a page transform')
|
|
338
|
-
|
|
339
336
|
const snap = snaps.handles.snapHandle({ currentShapeId: shapeId, handle: nextHandle })
|
|
340
337
|
|
|
341
338
|
if (snap) {
|
|
342
|
-
|
|
339
|
+
// The nudge is in page space and `point` is in the shape's own space, so the shape's
|
|
340
|
+
// full page rotation (not just its parent's) has to come off
|
|
341
|
+
snap.nudge.rot(-editor.getShapePageTransform(shape.id).rotation())
|
|
343
342
|
point.add(snap.nudge)
|
|
344
343
|
nextHandle = { ...initialHandle, x: point.x, y: point.y }
|
|
345
344
|
}
|
|
@@ -440,6 +440,7 @@ export class Idle extends StateNode {
|
|
|
440
440
|
// Call the shape's double click handler
|
|
441
441
|
const change = util.onDoubleClick?.(shape)
|
|
442
442
|
if (change) {
|
|
443
|
+
this.editor.markHistoryStoppingPoint('double click shape')
|
|
443
444
|
this.editor.updateShapes([change])
|
|
444
445
|
return
|
|
445
446
|
}
|
|
@@ -472,6 +473,7 @@ export class Idle extends StateNode {
|
|
|
472
473
|
const changes = util.onDoubleClickHandle?.(shape, handle)
|
|
473
474
|
|
|
474
475
|
if (changes) {
|
|
476
|
+
this.editor.markHistoryStoppingPoint('double click handle')
|
|
475
477
|
this.editor.updateShapes([changes])
|
|
476
478
|
} else {
|
|
477
479
|
// If the shape's double click handler has not created a change,
|
|
@@ -589,7 +591,7 @@ export class Idle extends StateNode {
|
|
|
589
591
|
}
|
|
590
592
|
return
|
|
591
593
|
}
|
|
592
|
-
this.nudgeSelectedShapes(false)
|
|
594
|
+
this.nudgeSelectedShapes(info, false)
|
|
593
595
|
return
|
|
594
596
|
}
|
|
595
597
|
}
|
|
@@ -635,7 +637,7 @@ export class Idle extends StateNode {
|
|
|
635
637
|
)
|
|
636
638
|
return
|
|
637
639
|
}
|
|
638
|
-
this.nudgeSelectedShapes(true)
|
|
640
|
+
this.nudgeSelectedShapes(info, true)
|
|
639
641
|
break
|
|
640
642
|
}
|
|
641
643
|
case 'Tab': {
|
|
@@ -754,13 +756,18 @@ export class Idle extends StateNode {
|
|
|
754
756
|
startEditingShapeWithRichText(this.editor, id, { info })
|
|
755
757
|
}
|
|
756
758
|
|
|
757
|
-
private nudgeSelectedShapes(ephemeral = false) {
|
|
759
|
+
private nudgeSelectedShapes(info: TLKeyboardEventInfo, ephemeral = false) {
|
|
758
760
|
const {
|
|
759
761
|
editor: {
|
|
760
762
|
inputs: { keys },
|
|
761
763
|
},
|
|
762
764
|
} = this
|
|
763
765
|
|
|
766
|
+
// Space+arrow pages the camera and Alt+arrow is the change-page shortcut; both
|
|
767
|
+
// events still reach this state, so without this guard the selection also
|
|
768
|
+
// moves one unit (#10397)
|
|
769
|
+
if (info.altKey || this.editor.inputs.getIsSpacebarPanning()) return
|
|
770
|
+
|
|
764
771
|
// We want to use the "actual" shift key state,
|
|
765
772
|
// not the one that's in the editor.inputs.shiftKey,
|
|
766
773
|
// because that one uses a short timeout on release
|
|
@@ -7,7 +7,10 @@ import {
|
|
|
7
7
|
TLPointerEventInfo,
|
|
8
8
|
Vec,
|
|
9
9
|
} from '@tldraw/editor'
|
|
10
|
-
import {
|
|
10
|
+
import {
|
|
11
|
+
clearArrowTargetState,
|
|
12
|
+
updateArrowTargetState,
|
|
13
|
+
} from '../../../shapes/arrow/arrowTargetState'
|
|
11
14
|
import { getArrowBindings } from '../../../shapes/arrow/shared'
|
|
12
15
|
import {
|
|
13
16
|
getNoteAdjacentPositions,
|
|
@@ -55,6 +58,8 @@ export class PointingHandle extends StateNode {
|
|
|
55
58
|
|
|
56
59
|
override onExit() {
|
|
57
60
|
this.editor.setHintingShapes([])
|
|
61
|
+
// onEnter shows the arrow's binding target; a click without a drag would leave it showing
|
|
62
|
+
clearArrowTargetState(this.editor)
|
|
58
63
|
this.editor.setCursor({ type: 'default', rotation: 0 })
|
|
59
64
|
}
|
|
60
65
|
|
|
@@ -65,7 +65,10 @@ export class PointingShape extends StateNode {
|
|
|
65
65
|
const focusedGroupId = this.editor.getFocusedGroupId()
|
|
66
66
|
const currentPagePoint = this.editor.inputs.getCurrentPagePoint()
|
|
67
67
|
|
|
68
|
-
|
|
68
|
+
// Alt suppresses additive selection here as it does on enter and in
|
|
69
|
+
// selectOnCanvasPointerUp; otherwise shift+alt or ctrl+alt releasing over
|
|
70
|
+
// a selected shape would toggle it out of the selection.
|
|
71
|
+
const additiveSelectionKey = (info.shiftKey || info.accelKey) && !info.altKey
|
|
69
72
|
|
|
70
73
|
const hitShape =
|
|
71
74
|
this.editor.getShapeAtPoint(currentPagePoint, {
|
|
@@ -2,8 +2,6 @@ import {
|
|
|
2
2
|
Box,
|
|
3
3
|
HALF_PI,
|
|
4
4
|
Mat,
|
|
5
|
-
PI,
|
|
6
|
-
PI2,
|
|
7
5
|
SelectionCorner,
|
|
8
6
|
SelectionEdge,
|
|
9
7
|
StateNode,
|
|
@@ -19,6 +17,7 @@ import {
|
|
|
19
17
|
isAccelKey,
|
|
20
18
|
isShapeId,
|
|
21
19
|
kickoutOccludedShapes,
|
|
20
|
+
rotateSelectionHandle,
|
|
22
21
|
} from '@tldraw/editor'
|
|
23
22
|
import { getEnclosedShapeIds } from '../../../shapes/frame/FrameShapeTool'
|
|
24
23
|
import { batchMeasureGeoLabels, setBatchLabelSizeCache } from '../../../shapes/geo/GeoShapeUtil'
|
|
@@ -44,6 +43,9 @@ export class Resizing extends StateNode {
|
|
|
44
43
|
// A switch to detect when the user is holding ctrl
|
|
45
44
|
private didHoldCommand = false
|
|
46
45
|
|
|
46
|
+
// Set by complete() and cancel(); see onExit for what happens when neither ran
|
|
47
|
+
private didFinish = false
|
|
48
|
+
|
|
47
49
|
// we transition into the resizing state from the geo pointing state, which starts with a shape of size w: 1, h: 1,
|
|
48
50
|
// 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
|
|
49
51
|
// so we allow passing a further offset into this state to negate such issues
|
|
@@ -56,6 +58,7 @@ export class Resizing extends StateNode {
|
|
|
56
58
|
|
|
57
59
|
this.info = info
|
|
58
60
|
this.didHoldCommand = false
|
|
61
|
+
this.didFinish = false
|
|
59
62
|
|
|
60
63
|
if (typeof info.onInteractionEnd === 'string') {
|
|
61
64
|
this.parent.setCurrentToolIdMask(info.onInteractionEnd)
|
|
@@ -127,6 +130,8 @@ export class Resizing extends StateNode {
|
|
|
127
130
|
}
|
|
128
131
|
|
|
129
132
|
private cancel() {
|
|
133
|
+
this.didFinish = true
|
|
134
|
+
|
|
130
135
|
// Call onResizeCancel callback before resetting
|
|
131
136
|
const { shapeSnapshots } = this.snapshot
|
|
132
137
|
|
|
@@ -134,6 +139,7 @@ export class Resizing extends StateNode {
|
|
|
134
139
|
const current = this.editor.getShape(shape.id)
|
|
135
140
|
if (current) {
|
|
136
141
|
const util = this.editor.getShapeUtil(shape)
|
|
142
|
+
if (!util.canResize(shape)) return
|
|
137
143
|
util.onResizeCancel?.(shape, current)
|
|
138
144
|
}
|
|
139
145
|
})
|
|
@@ -153,6 +159,8 @@ export class Resizing extends StateNode {
|
|
|
153
159
|
}
|
|
154
160
|
|
|
155
161
|
private complete() {
|
|
162
|
+
this.didFinish = true
|
|
163
|
+
|
|
156
164
|
kickoutOccludedShapes(this.editor, this.snapshot.selectedShapeIds)
|
|
157
165
|
|
|
158
166
|
this.handleResizeEnd()
|
|
@@ -179,12 +187,15 @@ export class Resizing extends StateNode {
|
|
|
179
187
|
}
|
|
180
188
|
|
|
181
189
|
private handleResizeStart() {
|
|
190
|
+
// Non-resizable shapes are in the snapshot only to be repositioned, so they don't get the
|
|
191
|
+
// resize lifecycle callbacks (matching Editor.resizeShape).
|
|
182
192
|
const { shapeSnapshots } = this.snapshot
|
|
183
193
|
|
|
184
194
|
const changes: TLShapePartial[] = []
|
|
185
195
|
|
|
186
196
|
shapeSnapshots.forEach(({ shape }) => {
|
|
187
197
|
const util = this.editor.getShapeUtil(shape)
|
|
198
|
+
if (!util.canResize(shape)) return
|
|
188
199
|
const change = util.onResizeStart?.(shape)
|
|
189
200
|
if (change) {
|
|
190
201
|
changes.push(change)
|
|
@@ -204,6 +215,7 @@ export class Resizing extends StateNode {
|
|
|
204
215
|
shapeSnapshots.forEach(({ shape }) => {
|
|
205
216
|
const current = this.editor.getShape(shape.id)!
|
|
206
217
|
const util = this.editor.getShapeUtil(shape)
|
|
218
|
+
if (!util.canResize(shape)) return
|
|
207
219
|
const change = util.onResizeEnd?.(shape, current)
|
|
208
220
|
if (change) {
|
|
209
221
|
changes.push(change)
|
|
@@ -545,6 +557,12 @@ export class Resizing extends StateNode {
|
|
|
545
557
|
if (this.info.isCreating && this.editor.getHintingShapeIds().length > 0) {
|
|
546
558
|
this.editor.setHintingShapes([])
|
|
547
559
|
}
|
|
560
|
+
// Exited mid-creation without complete() or cancel(), e.g. a tool shortcut pressed while
|
|
561
|
+
// dragging out a text box: the shape created at the drag threshold was never committed and
|
|
562
|
+
// would otherwise be left behind, empty and invisible (#10401)
|
|
563
|
+
if (this.info.isCreating && !this.didFinish) {
|
|
564
|
+
this.editor.bailToMark(this.markId)
|
|
565
|
+
}
|
|
548
566
|
}
|
|
549
567
|
|
|
550
568
|
private _createSnapshot() {
|
|
@@ -583,17 +601,16 @@ export class Resizing extends StateNode {
|
|
|
583
601
|
|
|
584
602
|
const util = editor.getShapeUtil(shape)
|
|
585
603
|
|
|
586
|
-
//
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
}
|
|
604
|
+
// Shapes that can't resize are snapshotted too: getResizeShapePartial repositions them
|
|
605
|
+
// so they keep their place in the selection instead of being left behind.
|
|
606
|
+
const pageTransform = editor.getShapePageTransform(shape)!
|
|
607
|
+
shapeSnapshots.set(shape.id, {
|
|
608
|
+
shape,
|
|
609
|
+
bounds: editor.getShapeGeometry(shape).bounds,
|
|
610
|
+
pageTransform,
|
|
611
|
+
pageRotation: Mat.Decompose(pageTransform).rotation,
|
|
612
|
+
isAspectRatioLocked: util.isAspectRatioLocked(shape),
|
|
613
|
+
})
|
|
597
614
|
|
|
598
615
|
// Special case:
|
|
599
616
|
// 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
|
|
@@ -670,23 +687,3 @@ export class Resizing extends StateNode {
|
|
|
670
687
|
}
|
|
671
688
|
|
|
672
689
|
type Snapshot = ReturnType<Resizing['_createSnapshot']>
|
|
673
|
-
|
|
674
|
-
const ORDERED_SELECTION_HANDLES: (SelectionEdge | SelectionCorner)[] = [
|
|
675
|
-
'top',
|
|
676
|
-
'top_right',
|
|
677
|
-
'right',
|
|
678
|
-
'bottom_right',
|
|
679
|
-
'bottom',
|
|
680
|
-
'bottom_left',
|
|
681
|
-
'left',
|
|
682
|
-
'top_left',
|
|
683
|
-
]
|
|
684
|
-
|
|
685
|
-
export function rotateSelectionHandle(handle: SelectionEdge | SelectionCorner, rotation: number) {
|
|
686
|
-
// first find out how many tau we need to rotate by
|
|
687
|
-
rotation = rotation % PI2
|
|
688
|
-
const numSteps = Math.round(rotation / (PI / 4))
|
|
689
|
-
|
|
690
|
-
const currentIndex = ORDERED_SELECTION_HANDLES.indexOf(handle)
|
|
691
|
-
return ORDERED_SELECTION_HANDLES[(currentIndex + numSteps) % ORDERED_SELECTION_HANDLES.length]
|
|
692
|
-
}
|
|
@@ -23,9 +23,9 @@ export class ScribbleBrushing extends StateNode {
|
|
|
23
23
|
newlySelectedShapeIds = new Set<TLShapeId>()
|
|
24
24
|
|
|
25
25
|
override onEnter() {
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
)
|
|
26
|
+
// Captured regardless of shift so cancel() can restore it like the rectangle
|
|
27
|
+
// brush does (#10429); shift only decides whether it merges into the live selection.
|
|
28
|
+
this.initialSelectedShapeIds = new Set<TLShapeId>(this.editor.getSelectedShapeIds())
|
|
29
29
|
this.newlySelectedShapeIds = new Set<TLShapeId>()
|
|
30
30
|
this.size = 0
|
|
31
31
|
this.hits.clear()
|
|
@@ -170,6 +170,9 @@ export class Translating extends StateNode {
|
|
|
170
170
|
this.editor.duplicateShapes(Array.from(this.editor.getSelectedShapeIds()))
|
|
171
171
|
|
|
172
172
|
this.snapshot = getTranslatingSnapshot(this.editor)
|
|
173
|
+
// The manager ignores startDraggingShapes while it is running, so it has to be
|
|
174
|
+
// cleared or it keeps reparenting the originals instead of the clones
|
|
175
|
+
this.dragAndDropManager.clear()
|
|
173
176
|
this.handleStart()
|
|
174
177
|
this.updateShapes()
|
|
175
178
|
}
|
|
@@ -179,6 +182,8 @@ export class Translating extends StateNode {
|
|
|
179
182
|
this.snapshot = this.selectionSnapshot
|
|
180
183
|
this.reset()
|
|
181
184
|
this.markId = this.editor.markHistoryStoppingPoint('translate')
|
|
185
|
+
// Same as in startCloning: the manager is still tracking the (now deleted) clones
|
|
186
|
+
this.dragAndDropManager.clear()
|
|
182
187
|
this.updateShapes()
|
|
183
188
|
}
|
|
184
189
|
|
|
@@ -439,6 +444,7 @@ function getTranslatingSnapshot(editor: Editor) {
|
|
|
439
444
|
extraHeight: noteSnapshot.shape.props.growY ?? 0,
|
|
440
445
|
noteWidth: dv.noteWidth,
|
|
441
446
|
noteHeight: dv.noteHeight,
|
|
447
|
+
excludeShapeIds: selectedShapeIds,
|
|
442
448
|
})
|
|
443
449
|
noteCenterOffset = new Vec(dv.noteWidth / 2, dv.noteHeight / 2)
|
|
444
450
|
}
|
|
@@ -48,3 +48,24 @@ export function startEditingShapeWithRichText(
|
|
|
48
48
|
editor.emit('select-all-text', { shapeId: shape.id })
|
|
49
49
|
}
|
|
50
50
|
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Start editing any editable shape. Shapes with rich text go through
|
|
54
|
+
* `startEditingShapeWithRichText`; editable shapes without it (frame, video, embed) would
|
|
55
|
+
* make that helper throw, so they enter the editing state directly.
|
|
56
|
+
*
|
|
57
|
+
* @internal
|
|
58
|
+
*/
|
|
59
|
+
export function startEditingShape(
|
|
60
|
+
editor: Editor,
|
|
61
|
+
shape: TLShape,
|
|
62
|
+
options: { selectAll?: boolean; info?: TLEventInfo } = {}
|
|
63
|
+
) {
|
|
64
|
+
if (!editor.canEditShape(shape)) return
|
|
65
|
+
if (hasRichText(shape)) {
|
|
66
|
+
startEditingShapeWithRichText(editor, shape, options)
|
|
67
|
+
return
|
|
68
|
+
}
|
|
69
|
+
editor.setEditingShape(shape)
|
|
70
|
+
editor.setCurrentTool('select.editing_shape', { ...options.info, target: 'shape', shape })
|
|
71
|
+
}
|
package/src/lib/ui/TldrawUi.tsx
CHANGED
|
@@ -75,7 +75,6 @@ interface TldrawUiContentProps {
|
|
|
75
75
|
const TldrawUiInner = React.memo(function TldrawUiInner({
|
|
76
76
|
children,
|
|
77
77
|
hideUi,
|
|
78
|
-
...rest
|
|
79
78
|
}: TldrawUiContentProps & { children: ReactNode }) {
|
|
80
79
|
// The hideUi prop should prevent the UI from mounting.
|
|
81
80
|
// If we ever need want the UI to mount and preserve state, then
|
|
@@ -89,7 +88,7 @@ const TldrawUiInner = React.memo(function TldrawUiInner({
|
|
|
89
88
|
return (
|
|
90
89
|
<>
|
|
91
90
|
{children}
|
|
92
|
-
{hideUi ? null : <TldrawUiContent
|
|
91
|
+
{hideUi ? null : <TldrawUiContent />}
|
|
93
92
|
</>
|
|
94
93
|
)
|
|
95
94
|
})
|