tldraw 4.3.0-canary.d8da2a99f394 → 4.3.0-canary.e1766dd4eab3
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 +26 -5
- package/dist-cjs/index.js +2 -1
- package/dist-cjs/index.js.map +2 -2
- package/dist-cjs/lib/bindings/arrow/ArrowBindingUtil.js.map +2 -2
- package/dist-cjs/lib/canvas/TldrawSelectionForeground.js +2 -2
- package/dist-cjs/lib/canvas/TldrawSelectionForeground.js.map +2 -2
- package/dist-cjs/lib/defaultExternalContentHandlers.js.map +2 -2
- package/dist-cjs/lib/shapes/arrow/ArrowShapeUtil.js +9 -12
- package/dist-cjs/lib/shapes/arrow/ArrowShapeUtil.js.map +2 -2
- package/dist-cjs/lib/shapes/arrow/arrow-types.js.map +1 -1
- package/dist-cjs/lib/shapes/arrow/arrowLabel.js.map +2 -2
- package/dist-cjs/lib/shapes/arrow/arrowTargetState.js.map +2 -2
- package/dist-cjs/lib/shapes/arrow/elbow/elbowArrowSnapLines.js.map +2 -2
- package/dist-cjs/lib/shapes/arrow/shared.js.map +2 -2
- package/dist-cjs/lib/shapes/arrow/toolStates/Pointing.js.map +2 -2
- package/dist-cjs/lib/shapes/bookmark/bookmarks.js.map +2 -2
- package/dist-cjs/lib/shapes/draw/DrawShapeUtil.js +3 -3
- package/dist-cjs/lib/shapes/draw/DrawShapeUtil.js.map +2 -2
- package/dist-cjs/lib/shapes/draw/toolStates/Drawing.js.map +2 -2
- package/dist-cjs/lib/shapes/embed/EmbedShapeUtil.js.map +2 -2
- package/dist-cjs/lib/shapes/frame/FrameShapeTool.js.map +1 -1
- package/dist-cjs/lib/shapes/frame/FrameShapeUtil.js +1 -1
- package/dist-cjs/lib/shapes/frame/FrameShapeUtil.js.map +2 -2
- package/dist-cjs/lib/shapes/geo/GeoShapeUtil.js +10 -6
- package/dist-cjs/lib/shapes/geo/GeoShapeUtil.js.map +2 -2
- package/dist-cjs/lib/shapes/geo/toolStates/Pointing.js.map +2 -2
- package/dist-cjs/lib/shapes/highlight/HighlightShapeUtil.js +1 -1
- package/dist-cjs/lib/shapes/highlight/HighlightShapeUtil.js.map +2 -2
- package/dist-cjs/lib/shapes/line/toolStates/Pointing.js.map +2 -2
- package/dist-cjs/lib/shapes/note/NoteShapeUtil.js +5 -5
- package/dist-cjs/lib/shapes/note/NoteShapeUtil.js.map +2 -2
- package/dist-cjs/lib/shapes/note/noteHelpers.js.map +2 -2
- package/dist-cjs/lib/shapes/shared/HyperlinkButton.js +2 -1
- package/dist-cjs/lib/shapes/shared/HyperlinkButton.js.map +2 -2
- package/dist-cjs/lib/shapes/shared/PlainTextLabel.js +14 -2
- package/dist-cjs/lib/shapes/shared/PlainTextLabel.js.map +3 -3
- package/dist-cjs/lib/shapes/shared/RichTextLabel.js +11 -3
- package/dist-cjs/lib/shapes/shared/RichTextLabel.js.map +3 -3
- package/dist-cjs/lib/shapes/shared/ShapeFill.js +2 -2
- package/dist-cjs/lib/shapes/shared/ShapeFill.js.map +2 -2
- package/dist-cjs/lib/shapes/shared/crop.js +1 -0
- package/dist-cjs/lib/shapes/shared/crop.js.map +2 -2
- package/dist-cjs/lib/shapes/shared/useEditablePlainText.js.map +2 -2
- package/dist-cjs/lib/shapes/shared/useEditableRichText.js.map +2 -2
- package/dist-cjs/lib/shapes/shared/{useForceSolid.js → useEfficientZoomThreshold.js} +10 -7
- package/dist-cjs/lib/shapes/shared/useEfficientZoomThreshold.js.map +7 -0
- package/dist-cjs/lib/shapes/shared/useImageOrVideoAsset.js +1 -1
- package/dist-cjs/lib/shapes/shared/useImageOrVideoAsset.js.map +2 -2
- package/dist-cjs/lib/shapes/text/TextShapeUtil.js +5 -2
- package/dist-cjs/lib/shapes/text/TextShapeUtil.js.map +2 -2
- package/dist-cjs/lib/shapes/text/toolStates/Pointing.js.map +2 -2
- package/dist-cjs/lib/shapes/video/VideoShapeUtil.js +1 -1
- package/dist-cjs/lib/shapes/video/VideoShapeUtil.js.map +2 -2
- package/dist-cjs/lib/tools/EraserTool/childStates/Erasing.js.map +2 -2
- package/dist-cjs/lib/tools/EraserTool/childStates/Pointing.js.map +2 -2
- package/dist-cjs/lib/tools/SelectTool/DragAndDropManager.js +1 -4
- package/dist-cjs/lib/tools/SelectTool/DragAndDropManager.js.map +2 -2
- package/dist-cjs/lib/tools/SelectTool/childStates/Brushing.js.map +2 -2
- package/dist-cjs/lib/tools/SelectTool/childStates/Crop/children/Idle.js.map +2 -2
- package/dist-cjs/lib/tools/SelectTool/childStates/DraggingHandle.js +1 -1
- package/dist-cjs/lib/tools/SelectTool/childStates/DraggingHandle.js.map +2 -2
- package/dist-cjs/lib/tools/SelectTool/childStates/EditingShape.js +30 -10
- package/dist-cjs/lib/tools/SelectTool/childStates/EditingShape.js.map +2 -2
- package/dist-cjs/lib/tools/SelectTool/childStates/Idle.js.map +2 -2
- package/dist-cjs/lib/tools/SelectTool/childStates/PointingArrowLabel.js.map +2 -2
- package/dist-cjs/lib/tools/SelectTool/childStates/PointingHandle.js.map +2 -2
- package/dist-cjs/lib/tools/SelectTool/childStates/PointingSelection.js.map +2 -2
- package/dist-cjs/lib/tools/SelectTool/childStates/Resizing.js.map +2 -2
- package/dist-cjs/lib/tools/SelectTool/childStates/ScribbleBrushing.js.map +2 -2
- package/dist-cjs/lib/tools/SelectTool/childStates/Translating.js.map +2 -2
- package/dist-cjs/lib/ui/components/ActionsMenu/DefaultActionsMenuContent.js +3 -9
- package/dist-cjs/lib/ui/components/ActionsMenu/DefaultActionsMenuContent.js.map +2 -2
- package/dist-cjs/lib/ui/components/EditLinkDialog.js +11 -1
- package/dist-cjs/lib/ui/components/EditLinkDialog.js.map +2 -2
- package/dist-cjs/lib/ui/components/Toolbar/AltTextEditor.js.map +2 -2
- package/dist-cjs/lib/ui/components/ZoomMenu/DefaultZoomMenu.js +1 -1
- package/dist-cjs/lib/ui/components/ZoomMenu/DefaultZoomMenu.js.map +2 -2
- package/dist-cjs/lib/ui/components/menu-items.js +3 -1
- package/dist-cjs/lib/ui/components/menu-items.js.map +2 -2
- package/dist-cjs/lib/ui/components/primitives/TldrawUiSlider.js +1 -1
- package/dist-cjs/lib/ui/components/primitives/TldrawUiSlider.js.map +2 -2
- package/dist-cjs/lib/ui/components/primitives/TldrawUiTooltip.js +143 -88
- package/dist-cjs/lib/ui/components/primitives/TldrawUiTooltip.js.map +2 -2
- package/dist-cjs/lib/ui/components/primitives/menus/TldrawUiMenuItem.js +1 -1
- package/dist-cjs/lib/ui/components/primitives/menus/TldrawUiMenuItem.js.map +2 -2
- package/dist-cjs/lib/ui/context/actions.js +1 -2
- package/dist-cjs/lib/ui/context/actions.js.map +2 -2
- package/dist-cjs/lib/ui/hooks/menu-hooks.js.map +2 -2
- package/dist-cjs/lib/ui/hooks/useFlatten.js.map +2 -2
- package/dist-cjs/lib/ui/hooks/useTools.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-cjs/lib/utils/excalidraw/putExcalidrawContent.js +8 -0
- package/dist-cjs/lib/utils/excalidraw/putExcalidrawContent.js.map +2 -2
- package/dist-cjs/lib/utils/export/exportAs.js.map +2 -2
- package/dist-cjs/lib/utils/frames/frames.js.map +2 -2
- package/dist-cjs/lib/utils/text/richText.js +7 -17
- package/dist-cjs/lib/utils/text/richText.js.map +3 -3
- package/dist-cjs/lib/utils/tldr/buildFromV1Document.js.map +2 -2
- package/dist-esm/index.d.mts +26 -5
- package/dist-esm/index.mjs +3 -1
- package/dist-esm/index.mjs.map +2 -2
- package/dist-esm/lib/bindings/arrow/ArrowBindingUtil.mjs.map +2 -2
- package/dist-esm/lib/canvas/TldrawSelectionForeground.mjs +2 -2
- package/dist-esm/lib/canvas/TldrawSelectionForeground.mjs.map +2 -2
- package/dist-esm/lib/defaultExternalContentHandlers.mjs.map +2 -2
- package/dist-esm/lib/shapes/arrow/ArrowShapeUtil.mjs +10 -14
- package/dist-esm/lib/shapes/arrow/ArrowShapeUtil.mjs.map +2 -2
- package/dist-esm/lib/shapes/arrow/arrowLabel.mjs.map +2 -2
- package/dist-esm/lib/shapes/arrow/arrowTargetState.mjs.map +2 -2
- package/dist-esm/lib/shapes/arrow/elbow/elbowArrowSnapLines.mjs.map +2 -2
- package/dist-esm/lib/shapes/arrow/shared.mjs.map +2 -2
- package/dist-esm/lib/shapes/arrow/toolStates/Pointing.mjs.map +2 -2
- package/dist-esm/lib/shapes/bookmark/bookmarks.mjs.map +2 -2
- package/dist-esm/lib/shapes/draw/DrawShapeUtil.mjs +3 -3
- package/dist-esm/lib/shapes/draw/DrawShapeUtil.mjs.map +2 -2
- package/dist-esm/lib/shapes/draw/toolStates/Drawing.mjs.map +2 -2
- package/dist-esm/lib/shapes/embed/EmbedShapeUtil.mjs.map +2 -2
- package/dist-esm/lib/shapes/frame/FrameShapeTool.mjs.map +1 -1
- package/dist-esm/lib/shapes/frame/FrameShapeUtil.mjs +1 -1
- package/dist-esm/lib/shapes/frame/FrameShapeUtil.mjs.map +2 -2
- package/dist-esm/lib/shapes/geo/GeoShapeUtil.mjs +10 -6
- package/dist-esm/lib/shapes/geo/GeoShapeUtil.mjs.map +2 -2
- package/dist-esm/lib/shapes/geo/toolStates/Pointing.mjs.map +2 -2
- package/dist-esm/lib/shapes/highlight/HighlightShapeUtil.mjs +1 -1
- package/dist-esm/lib/shapes/highlight/HighlightShapeUtil.mjs.map +2 -2
- package/dist-esm/lib/shapes/line/toolStates/Pointing.mjs.map +2 -2
- package/dist-esm/lib/shapes/note/NoteShapeUtil.mjs +5 -5
- package/dist-esm/lib/shapes/note/NoteShapeUtil.mjs.map +2 -2
- package/dist-esm/lib/shapes/note/noteHelpers.mjs.map +2 -2
- package/dist-esm/lib/shapes/shared/HyperlinkButton.mjs +3 -2
- package/dist-esm/lib/shapes/shared/HyperlinkButton.mjs.map +2 -2
- package/dist-esm/lib/shapes/shared/PlainTextLabel.mjs +14 -2
- package/dist-esm/lib/shapes/shared/PlainTextLabel.mjs.map +2 -2
- package/dist-esm/lib/shapes/shared/RichTextLabel.mjs +11 -3
- package/dist-esm/lib/shapes/shared/RichTextLabel.mjs.map +2 -2
- package/dist-esm/lib/shapes/shared/ShapeFill.mjs +2 -2
- package/dist-esm/lib/shapes/shared/ShapeFill.mjs.map +2 -2
- package/dist-esm/lib/shapes/shared/crop.mjs +1 -0
- package/dist-esm/lib/shapes/shared/crop.mjs.map +2 -2
- package/dist-esm/lib/shapes/shared/useEditablePlainText.mjs.map +2 -2
- package/dist-esm/lib/shapes/shared/useEditableRichText.mjs.map +2 -2
- package/dist-esm/lib/shapes/shared/useEfficientZoomThreshold.mjs +12 -0
- package/dist-esm/lib/shapes/shared/useEfficientZoomThreshold.mjs.map +7 -0
- package/dist-esm/lib/shapes/shared/useImageOrVideoAsset.mjs +1 -1
- package/dist-esm/lib/shapes/shared/useImageOrVideoAsset.mjs.map +2 -2
- package/dist-esm/lib/shapes/text/TextShapeUtil.mjs +5 -2
- package/dist-esm/lib/shapes/text/TextShapeUtil.mjs.map +2 -2
- package/dist-esm/lib/shapes/text/toolStates/Pointing.mjs.map +2 -2
- package/dist-esm/lib/shapes/video/VideoShapeUtil.mjs +1 -1
- package/dist-esm/lib/shapes/video/VideoShapeUtil.mjs.map +2 -2
- package/dist-esm/lib/tools/EraserTool/childStates/Erasing.mjs.map +2 -2
- package/dist-esm/lib/tools/EraserTool/childStates/Pointing.mjs +1 -4
- package/dist-esm/lib/tools/EraserTool/childStates/Pointing.mjs.map +2 -2
- package/dist-esm/lib/tools/SelectTool/DragAndDropManager.mjs +1 -4
- package/dist-esm/lib/tools/SelectTool/DragAndDropManager.mjs.map +2 -2
- package/dist-esm/lib/tools/SelectTool/childStates/Brushing.mjs.map +2 -2
- package/dist-esm/lib/tools/SelectTool/childStates/Crop/children/Idle.mjs.map +2 -2
- package/dist-esm/lib/tools/SelectTool/childStates/DraggingHandle.mjs +1 -1
- package/dist-esm/lib/tools/SelectTool/childStates/DraggingHandle.mjs.map +2 -2
- package/dist-esm/lib/tools/SelectTool/childStates/EditingShape.mjs +30 -10
- package/dist-esm/lib/tools/SelectTool/childStates/EditingShape.mjs.map +2 -2
- package/dist-esm/lib/tools/SelectTool/childStates/Idle.mjs.map +2 -2
- package/dist-esm/lib/tools/SelectTool/childStates/PointingArrowLabel.mjs.map +2 -2
- package/dist-esm/lib/tools/SelectTool/childStates/PointingHandle.mjs +1 -4
- package/dist-esm/lib/tools/SelectTool/childStates/PointingHandle.mjs.map +2 -2
- package/dist-esm/lib/tools/SelectTool/childStates/PointingSelection.mjs.map +2 -2
- package/dist-esm/lib/tools/SelectTool/childStates/Resizing.mjs.map +2 -2
- package/dist-esm/lib/tools/SelectTool/childStates/ScribbleBrushing.mjs.map +2 -2
- package/dist-esm/lib/tools/SelectTool/childStates/Translating.mjs.map +2 -2
- package/dist-esm/lib/ui/components/ActionsMenu/DefaultActionsMenuContent.mjs +2 -8
- package/dist-esm/lib/ui/components/ActionsMenu/DefaultActionsMenuContent.mjs.map +2 -2
- package/dist-esm/lib/ui/components/EditLinkDialog.mjs +11 -1
- package/dist-esm/lib/ui/components/EditLinkDialog.mjs.map +2 -2
- package/dist-esm/lib/ui/components/Toolbar/AltTextEditor.mjs.map +2 -2
- package/dist-esm/lib/ui/components/ZoomMenu/DefaultZoomMenu.mjs +1 -1
- package/dist-esm/lib/ui/components/ZoomMenu/DefaultZoomMenu.mjs.map +2 -2
- package/dist-esm/lib/ui/components/menu-items.mjs +4 -5
- package/dist-esm/lib/ui/components/menu-items.mjs.map +2 -2
- package/dist-esm/lib/ui/components/primitives/TldrawUiSlider.mjs +2 -2
- package/dist-esm/lib/ui/components/primitives/TldrawUiSlider.mjs.map +2 -2
- package/dist-esm/lib/ui/components/primitives/TldrawUiTooltip.mjs +151 -90
- package/dist-esm/lib/ui/components/primitives/TldrawUiTooltip.mjs.map +2 -2
- package/dist-esm/lib/ui/components/primitives/menus/TldrawUiMenuItem.mjs +2 -2
- package/dist-esm/lib/ui/components/primitives/menus/TldrawUiMenuItem.mjs.map +2 -2
- package/dist-esm/lib/ui/context/actions.mjs +1 -2
- package/dist-esm/lib/ui/context/actions.mjs.map +2 -2
- package/dist-esm/lib/ui/hooks/menu-hooks.mjs +1 -4
- package/dist-esm/lib/ui/hooks/menu-hooks.mjs.map +2 -2
- package/dist-esm/lib/ui/hooks/useFlatten.mjs.map +2 -2
- package/dist-esm/lib/ui/hooks/useTools.mjs.map +2 -2
- package/dist-esm/lib/ui/version.mjs +3 -3
- package/dist-esm/lib/ui/version.mjs.map +1 -1
- package/dist-esm/lib/utils/excalidraw/putExcalidrawContent.mjs +8 -0
- package/dist-esm/lib/utils/excalidraw/putExcalidrawContent.mjs.map +2 -2
- package/dist-esm/lib/utils/export/exportAs.mjs +1 -3
- package/dist-esm/lib/utils/export/exportAs.mjs.map +2 -2
- package/dist-esm/lib/utils/frames/frames.mjs.map +2 -2
- package/dist-esm/lib/utils/text/richText.mjs +3 -3
- package/dist-esm/lib/utils/text/richText.mjs.map +2 -2
- package/dist-esm/lib/utils/tldr/buildFromV1Document.mjs.map +2 -2
- package/package.json +10 -10
- package/src/index.ts +1 -0
- package/src/lib/bindings/arrow/ArrowBindingUtil.ts +1 -1
- package/src/lib/canvas/TldrawSelectionForeground.tsx +6 -11
- package/src/lib/defaultExternalContentHandlers.ts +3 -4
- package/src/lib/shapes/arrow/ArrowShapeUtil.test.ts +2 -2
- package/src/lib/shapes/arrow/ArrowShapeUtil.tsx +11 -13
- package/src/lib/shapes/arrow/arrow-types.ts +2 -0
- package/src/lib/shapes/arrow/arrowLabel.ts +1 -1
- package/src/lib/shapes/arrow/arrowTargetState.ts +1 -1
- package/src/lib/shapes/arrow/elbow/elbowArrowSnapLines.tsx +3 -3
- package/src/lib/shapes/arrow/shared.ts +4 -4
- package/src/lib/shapes/arrow/toolStates/Pointing.tsx +1 -1
- package/src/lib/shapes/bookmark/bookmarks.ts +3 -3
- package/src/lib/shapes/draw/DrawShapeUtil.tsx +3 -3
- package/src/lib/shapes/draw/toolStates/Drawing.ts +4 -4
- package/src/lib/shapes/embed/EmbedShapeUtil.tsx +1 -1
- package/src/lib/shapes/frame/FrameShapeTool.ts +1 -1
- package/src/lib/shapes/frame/FrameShapeUtil.tsx +1 -1
- package/src/lib/shapes/geo/GeoShapeUtil.test.tsx +10 -2
- package/src/lib/shapes/geo/GeoShapeUtil.tsx +9 -4
- package/src/lib/shapes/geo/toolStates/Pointing.ts +3 -3
- package/src/lib/shapes/highlight/HighlightShapeUtil.tsx +1 -1
- package/src/lib/shapes/line/LineShapeTool.test.ts +6 -6
- package/src/lib/shapes/line/LineShapeUtil.test.tsx +5 -5
- package/src/lib/shapes/line/toolStates/Pointing.ts +1 -1
- package/src/lib/shapes/note/NoteShapeTool.test.ts +2 -1
- package/src/lib/shapes/note/NoteShapeUtil.tsx +7 -8
- package/src/lib/shapes/note/noteHelpers.ts +2 -2
- package/src/lib/shapes/shared/HyperlinkButton.tsx +3 -2
- package/src/lib/shapes/shared/PlainTextLabel.tsx +12 -2
- package/src/lib/shapes/shared/RichTextLabel.tsx +13 -3
- package/src/lib/shapes/shared/ShapeFill.tsx +2 -2
- package/src/lib/shapes/shared/crop.ts +1 -0
- package/src/lib/shapes/shared/useEditablePlainText.ts +7 -3
- package/src/lib/shapes/shared/useEditableRichText.ts +7 -3
- package/src/lib/shapes/shared/useEfficientZoomThreshold.ts +10 -0
- package/src/lib/shapes/shared/useImageOrVideoAsset.ts +1 -1
- package/src/lib/shapes/text/TextShapeTool.test.ts +4 -4
- package/src/lib/shapes/text/TextShapeUtil.tsx +5 -0
- package/src/lib/shapes/text/toolStates/Pointing.ts +1 -1
- package/src/lib/shapes/video/VideoShapeUtil.tsx +2 -1
- package/src/lib/tools/EraserTool/childStates/Erasing.ts +3 -5
- package/src/lib/tools/EraserTool/childStates/Pointing.ts +3 -16
- package/src/lib/tools/SelectTool/DragAndDropManager.ts +2 -4
- package/src/lib/tools/SelectTool/childStates/Brushing.ts +2 -6
- package/src/lib/tools/SelectTool/childStates/Crop/children/Idle.ts +2 -3
- package/src/lib/tools/SelectTool/childStates/DraggingHandle.tsx +4 -7
- package/src/lib/tools/SelectTool/childStates/EditingShape.ts +46 -15
- package/src/lib/tools/SelectTool/childStates/Idle.ts +6 -10
- package/src/lib/tools/SelectTool/childStates/PointingArrowLabel.ts +1 -1
- package/src/lib/tools/SelectTool/childStates/PointingHandle.ts +4 -12
- package/src/lib/tools/SelectTool/childStates/PointingSelection.ts +2 -2
- package/src/lib/tools/SelectTool/childStates/Resizing.ts +2 -4
- package/src/lib/tools/SelectTool/childStates/ScribbleBrushing.ts +2 -4
- package/src/lib/tools/SelectTool/childStates/Translating.ts +1 -3
- package/src/lib/ui/components/ActionsMenu/DefaultActionsMenuContent.tsx +1 -9
- package/src/lib/ui/components/EditLinkDialog.tsx +16 -6
- package/src/lib/ui/components/Toolbar/AltTextEditor.tsx +2 -2
- package/src/lib/ui/components/ZoomMenu/DefaultZoomMenu.tsx +1 -1
- package/src/lib/ui/components/menu-items.tsx +9 -15
- package/src/lib/ui/components/primitives/TldrawUiSlider.tsx +2 -2
- package/src/lib/ui/components/primitives/TldrawUiTooltip.tsx +196 -108
- package/src/lib/ui/components/primitives/menus/TldrawUiMenuItem.tsx +2 -2
- package/src/lib/ui/context/actions.tsx +9 -13
- package/src/lib/ui/hooks/menu-hooks.ts +9 -19
- package/src/lib/ui/hooks/useFlatten.ts +1 -2
- package/src/lib/ui/hooks/useTools.tsx +1 -2
- package/src/lib/ui/version.ts +3 -3
- package/src/lib/utils/excalidraw/putExcalidrawContent.ts +8 -0
- package/src/lib/utils/export/exportAs.ts +2 -9
- package/src/lib/utils/frames/frames.ts +1 -1
- package/src/lib/utils/text/richText.ts +3 -3
- package/src/lib/utils/tldr/buildFromV1Document.ts +12 -17
- package/src/test/Editor.test.tsx +38 -12
- package/src/test/SelectTool.test.ts +11 -19
- package/src/test/TestEditor.ts +1 -4
- package/src/test/TldrawEditor.test.tsx +21 -18
- package/src/test/bindings.test.tsx +29 -25
- package/src/test/bindingsIndex.test.tsx +4 -4
- package/src/test/commands/__snapshots__/getSvgString.test.ts.snap +2 -2
- package/src/test/commands/cameraState.test.ts +299 -0
- package/src/test/commands/createShape.test.ts +64 -0
- package/src/test/commands/createShapes.test.ts +15 -1
- package/src/test/commands/getSvgString.test.ts +2 -2
- package/src/test/commands/isShapeOfType.test.ts +44 -0
- package/src/test/commands/putContent.test.ts +80 -1
- package/src/test/commands/updateShape.test.ts +67 -0
- package/src/test/commands/updateShapes.test.ts +21 -5
- package/src/test/custom-clipping.test.ts +36 -35
- package/src/test/customSnapping.test.tsx +77 -62
- package/src/test/duplicate.test.ts +1 -1
- package/src/test/frames.test.ts +2 -2
- package/src/test/getCulledShapes.test.tsx +11 -3
- package/src/test/getShapeAtPoint.test.ts +2 -2
- package/src/test/groups.test.tsx +6 -3
- package/src/test/resizing.test.ts +9 -13
- package/src/test/selection-omnibus.test.ts +11 -11
- package/src/test/shapeutils.test.ts +1 -1
- package/src/test/styles2.test.tsx +1 -1
- package/src/test/styles3.test.ts +5 -5
- package/src/test/test-jsx.tsx +69 -57
- package/src/test/text.test.ts +15 -17
- package/src/test/translating.test.ts +6 -8
- package/tldraw.css +8 -4
- package/dist-cjs/lib/shapes/shared/useForceSolid.js.map +0 -7
- package/dist-esm/lib/shapes/shared/useForceSolid.mjs +0 -9
- package/dist-esm/lib/shapes/shared/useForceSolid.mjs.map +0 -7
- package/src/lib/shapes/shared/useForceSolid.ts +0 -6
|
@@ -6,8 +6,8 @@ import {
|
|
|
6
6
|
BindingOnShapeDeleteOptions,
|
|
7
7
|
BindingOnShapeIsolateOptions,
|
|
8
8
|
BindingUtil,
|
|
9
|
+
TLBinding,
|
|
9
10
|
TLShapeId,
|
|
10
|
-
TLUnknownBinding,
|
|
11
11
|
createBindingId,
|
|
12
12
|
createShapeId,
|
|
13
13
|
} from '@tldraw/editor'
|
|
@@ -40,7 +40,7 @@ const mockOnAfterChangeToShape = vi.fn()
|
|
|
40
40
|
|
|
41
41
|
const calls: string[] = []
|
|
42
42
|
|
|
43
|
-
const registerCall = (method: string, binding:
|
|
43
|
+
const registerCall = (method: string, binding: TLBinding) => {
|
|
44
44
|
calls.push(
|
|
45
45
|
`${method}: ${binding.fromId.slice('shape:'.length)}->${binding.toId.slice('shape:'.length)}`
|
|
46
46
|
)
|
|
@@ -60,62 +60,62 @@ class TestBindingUtil extends BindingUtil {
|
|
|
60
60
|
mockOnOperationComplete()
|
|
61
61
|
}
|
|
62
62
|
|
|
63
|
-
override onBeforeDelete(options: BindingOnDeleteOptions
|
|
63
|
+
override onBeforeDelete(options: BindingOnDeleteOptions): void {
|
|
64
64
|
registerCall('onBeforeDelete', options.binding)
|
|
65
65
|
mockOnBeforeDelete(options)
|
|
66
66
|
}
|
|
67
67
|
|
|
68
|
-
override onAfterDelete(options: BindingOnDeleteOptions
|
|
68
|
+
override onAfterDelete(options: BindingOnDeleteOptions): void {
|
|
69
69
|
registerCall('onAfterDelete', options.binding)
|
|
70
70
|
mockOnAfterDelete(options)
|
|
71
71
|
}
|
|
72
72
|
|
|
73
|
-
override onBeforeDeleteFromShape(options: BindingOnShapeDeleteOptions
|
|
73
|
+
override onBeforeDeleteFromShape(options: BindingOnShapeDeleteOptions): void {
|
|
74
74
|
registerCall('onBeforeDeleteFromShape', options.binding)
|
|
75
75
|
mockOnBeforeFromShapeDelete(options)
|
|
76
76
|
}
|
|
77
77
|
|
|
78
|
-
override onBeforeDeleteToShape(options: BindingOnShapeDeleteOptions
|
|
78
|
+
override onBeforeDeleteToShape(options: BindingOnShapeDeleteOptions): void {
|
|
79
79
|
registerCall('onBeforeDeleteToShape', options.binding)
|
|
80
80
|
mockOnBeforeToShapeDelete(options)
|
|
81
81
|
}
|
|
82
82
|
|
|
83
|
-
override onBeforeIsolateFromShape(options: BindingOnShapeIsolateOptions
|
|
83
|
+
override onBeforeIsolateFromShape(options: BindingOnShapeIsolateOptions): void {
|
|
84
84
|
registerCall('onBeforeIsolateFromShape', options.binding)
|
|
85
85
|
mockOnBeforeFromShapeIsolate(options)
|
|
86
86
|
}
|
|
87
87
|
|
|
88
|
-
override onBeforeIsolateToShape(options: BindingOnShapeIsolateOptions
|
|
88
|
+
override onBeforeIsolateToShape(options: BindingOnShapeIsolateOptions): void {
|
|
89
89
|
registerCall('onBeforeIsolateToShape', options.binding)
|
|
90
90
|
mockOnBeforeToShapeIsolate(options)
|
|
91
91
|
}
|
|
92
92
|
|
|
93
|
-
override onBeforeCreate(options: BindingOnCreateOptions
|
|
93
|
+
override onBeforeCreate(options: BindingOnCreateOptions): void {
|
|
94
94
|
registerCall('onBeforeCreate', options.binding)
|
|
95
95
|
mockOnBeforeCreate(options)
|
|
96
96
|
}
|
|
97
97
|
|
|
98
|
-
override onAfterCreate(options: BindingOnCreateOptions
|
|
98
|
+
override onAfterCreate(options: BindingOnCreateOptions): void {
|
|
99
99
|
registerCall('onAfterCreate', options.binding)
|
|
100
100
|
mockOnAfterCreate(options)
|
|
101
101
|
}
|
|
102
102
|
|
|
103
|
-
override onBeforeChange(options: BindingOnChangeOptions
|
|
103
|
+
override onBeforeChange(options: BindingOnChangeOptions): void {
|
|
104
104
|
registerCall('onBeforeChange', options.bindingAfter)
|
|
105
105
|
mockOnBeforeChange(options)
|
|
106
106
|
}
|
|
107
107
|
|
|
108
|
-
override onAfterChange(options: BindingOnChangeOptions
|
|
108
|
+
override onAfterChange(options: BindingOnChangeOptions): void {
|
|
109
109
|
registerCall('onAfterChange', options.bindingAfter)
|
|
110
110
|
mockOnAfterChange(options)
|
|
111
111
|
}
|
|
112
112
|
|
|
113
|
-
override onAfterChangeFromShape(options: BindingOnShapeChangeOptions
|
|
113
|
+
override onAfterChangeFromShape(options: BindingOnShapeChangeOptions): void {
|
|
114
114
|
registerCall('onAfterChangeFromShape', options.binding)
|
|
115
115
|
mockOnAfterChangeFromShape(options)
|
|
116
116
|
}
|
|
117
117
|
|
|
118
|
-
override onAfterChangeToShape(options: BindingOnShapeChangeOptions
|
|
118
|
+
override onAfterChangeToShape(options: BindingOnShapeChangeOptions): void {
|
|
119
119
|
registerCall('onAfterChangeToShape', options.binding)
|
|
120
120
|
mockOnAfterChangeToShape(options)
|
|
121
121
|
}
|
|
@@ -146,11 +146,19 @@ beforeEach(() => {
|
|
|
146
146
|
mockOnAfterChangeToShape.mockReset()
|
|
147
147
|
})
|
|
148
148
|
|
|
149
|
+
const TEST_TYPE = 'test'
|
|
150
|
+
|
|
151
|
+
declare module '@tldraw/tlschema' {
|
|
152
|
+
export interface TLGlobalBindingPropsMap {
|
|
153
|
+
[TEST_TYPE]: Record<string, never>
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
149
157
|
function bindShapes(fromId: TLShapeId, toId: TLShapeId) {
|
|
150
158
|
const bindingId = createBindingId()
|
|
151
159
|
editor.createBinding({
|
|
152
160
|
id: bindingId,
|
|
153
|
-
type:
|
|
161
|
+
type: TEST_TYPE,
|
|
154
162
|
fromId,
|
|
155
163
|
toId,
|
|
156
164
|
})
|
|
@@ -256,11 +264,9 @@ test('copying the to shape on its own does trigger the unbind operation', () =>
|
|
|
256
264
|
})
|
|
257
265
|
|
|
258
266
|
test('cascading deletes in beforeFromShapeDelete are handled correctly', () => {
|
|
259
|
-
mockOnBeforeFromShapeDelete.mockImplementation(
|
|
260
|
-
(options
|
|
261
|
-
|
|
262
|
-
}
|
|
263
|
-
)
|
|
267
|
+
mockOnBeforeFromShapeDelete.mockImplementation((options: BindingOnShapeDeleteOptions) => {
|
|
268
|
+
editor.deleteShape(options.binding.toId)
|
|
269
|
+
})
|
|
264
270
|
|
|
265
271
|
bindShapes(ids.box1, ids.box2)
|
|
266
272
|
bindShapes(ids.box2, ids.box3)
|
|
@@ -301,11 +307,9 @@ test('cascading deletes in beforeFromShapeDelete are handled correctly', () => {
|
|
|
301
307
|
})
|
|
302
308
|
|
|
303
309
|
test('cascading deletes in beforeToShapeDelete are handled correctly', () => {
|
|
304
|
-
mockOnBeforeToShapeDelete.mockImplementation(
|
|
305
|
-
(options
|
|
306
|
-
|
|
307
|
-
}
|
|
308
|
-
)
|
|
310
|
+
mockOnBeforeToShapeDelete.mockImplementation((options: BindingOnShapeDeleteOptions) => {
|
|
311
|
+
editor.deleteShape(options.binding.fromId)
|
|
312
|
+
})
|
|
309
313
|
|
|
310
314
|
bindShapes(ids.box1, ids.box2)
|
|
311
315
|
bindShapes(ids.box2, ids.box3)
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { TLShapeId, createShapeId } from '@tldraw/editor'
|
|
2
2
|
import { TestEditor } from './TestEditor'
|
|
3
3
|
import { TL } from './test-jsx'
|
|
4
4
|
|
|
@@ -219,7 +219,7 @@ describe('bindingsIndex', () => {
|
|
|
219
219
|
|
|
220
220
|
const [box1Clone, box2Clone] = editor
|
|
221
221
|
.getSelectedShapes()
|
|
222
|
-
.filter((shape) => editor.isShapeOfType
|
|
222
|
+
.filter((shape) => editor.isShapeOfType(shape, 'geo'))
|
|
223
223
|
.sort((a, b) => a.x - b.x)
|
|
224
224
|
|
|
225
225
|
expect(editor.getArrowsBoundTo(box2Clone.id)).toHaveLength(3)
|
|
@@ -248,9 +248,9 @@ describe('bindingsIndex', () => {
|
|
|
248
248
|
|
|
249
249
|
// move arrowA end from box2 to box3
|
|
250
250
|
const binding = editor
|
|
251
|
-
.getBindingsInvolvingShape
|
|
251
|
+
.getBindingsInvolvingShape(ids.box2, 'arrow')
|
|
252
252
|
.find((b) => b.props.terminal === 'end')!
|
|
253
|
-
editor.updateBinding({ ...binding, toId: box3 }
|
|
253
|
+
editor.updateBinding({ ...binding, toId: box3 })
|
|
254
254
|
|
|
255
255
|
expect(editor.getArrowsBoundTo(ids.box2)).toHaveLength(2)
|
|
256
256
|
expect(editor.getArrowsBoundTo(ids.box1)).toHaveLength(3)
|
|
@@ -83,7 +83,7 @@ exports[`Matches a snapshot > Basic SVG 1`] = `
|
|
|
83
83
|
stroke-width="3.5"
|
|
84
84
|
/>
|
|
85
85
|
<foreignobject
|
|
86
|
-
class="tl-export-embed-styles tl-rich-text tl-rich-text-svg"
|
|
86
|
+
class="tl-export-embed-styles tl-rich-text tl-rich-text-svg tl-text__outline"
|
|
87
87
|
height="100"
|
|
88
88
|
width="100"
|
|
89
89
|
x="0"
|
|
@@ -223,7 +223,7 @@ exports[`Returns all shapes when no ids are provided > All shapes 1`] = `
|
|
|
223
223
|
stroke-width="3.5"
|
|
224
224
|
/>
|
|
225
225
|
<foreignobject
|
|
226
|
-
class="tl-export-embed-styles tl-rich-text tl-rich-text-svg"
|
|
226
|
+
class="tl-export-embed-styles tl-rich-text tl-rich-text-svg tl-text__outline"
|
|
227
227
|
height="100"
|
|
228
228
|
width="100"
|
|
229
229
|
x="0"
|
|
@@ -0,0 +1,299 @@
|
|
|
1
|
+
import { Box } from '@tldraw/editor'
|
|
2
|
+
import { TestEditor } from '../TestEditor'
|
|
3
|
+
|
|
4
|
+
let editor: TestEditor
|
|
5
|
+
|
|
6
|
+
beforeEach(() => {
|
|
7
|
+
editor = new TestEditor()
|
|
8
|
+
editor.updateViewportScreenBounds(new Box(0, 0, 1600, 900))
|
|
9
|
+
})
|
|
10
|
+
|
|
11
|
+
describe('getCameraState', () => {
|
|
12
|
+
it('starts as idle', () => {
|
|
13
|
+
expect(editor.getCameraState()).toBe('idle')
|
|
14
|
+
})
|
|
15
|
+
|
|
16
|
+
it('becomes moving when the camera changes via setCamera', () => {
|
|
17
|
+
expect(editor.getCameraState()).toBe('idle')
|
|
18
|
+
editor.setCamera({ x: 100, y: 100, z: 1 })
|
|
19
|
+
expect(editor.getCameraState()).toBe('moving')
|
|
20
|
+
})
|
|
21
|
+
|
|
22
|
+
it('becomes moving when the camera changes via pan', () => {
|
|
23
|
+
expect(editor.getCameraState()).toBe('idle')
|
|
24
|
+
editor.pan({ x: 100, y: 100 })
|
|
25
|
+
expect(editor.getCameraState()).toBe('moving')
|
|
26
|
+
})
|
|
27
|
+
|
|
28
|
+
it('becomes moving when the camera changes via zoomIn', () => {
|
|
29
|
+
expect(editor.getCameraState()).toBe('idle')
|
|
30
|
+
editor.zoomIn(undefined, { immediate: true })
|
|
31
|
+
expect(editor.getCameraState()).toBe('moving')
|
|
32
|
+
})
|
|
33
|
+
|
|
34
|
+
it('returns to idle after the timeout elapses', () => {
|
|
35
|
+
expect(editor.getCameraState()).toBe('idle')
|
|
36
|
+
editor.setCamera({ x: 100, y: 100, z: 1 })
|
|
37
|
+
expect(editor.getCameraState()).toBe('moving')
|
|
38
|
+
|
|
39
|
+
// The default timeout is 64ms (options.cameraMovingTimeoutMs)
|
|
40
|
+
// Each tick is 16ms, so we need ~4 ticks to elapse
|
|
41
|
+
editor.forceTick(5)
|
|
42
|
+
expect(editor.getCameraState()).toBe('idle')
|
|
43
|
+
})
|
|
44
|
+
|
|
45
|
+
it('stays moving while camera continues to change', () => {
|
|
46
|
+
expect(editor.getCameraState()).toBe('idle')
|
|
47
|
+
editor.setCamera({ x: 100, y: 100, z: 1 })
|
|
48
|
+
expect(editor.getCameraState()).toBe('moving')
|
|
49
|
+
|
|
50
|
+
// Move again before timeout elapses
|
|
51
|
+
editor.forceTick(2)
|
|
52
|
+
editor.setCamera({ x: 200, y: 200, z: 1 })
|
|
53
|
+
expect(editor.getCameraState()).toBe('moving')
|
|
54
|
+
|
|
55
|
+
// Move again
|
|
56
|
+
editor.forceTick(2)
|
|
57
|
+
editor.setCamera({ x: 300, y: 300, z: 1 })
|
|
58
|
+
expect(editor.getCameraState()).toBe('moving')
|
|
59
|
+
|
|
60
|
+
// Now let it settle
|
|
61
|
+
editor.forceTick(5)
|
|
62
|
+
expect(editor.getCameraState()).toBe('idle')
|
|
63
|
+
})
|
|
64
|
+
|
|
65
|
+
it('stays idle when camera position does not actually change', () => {
|
|
66
|
+
expect(editor.getCameraState()).toBe('idle')
|
|
67
|
+
|
|
68
|
+
// Setting the same camera position should not trigger moving state
|
|
69
|
+
const currentCamera = editor.getCamera()
|
|
70
|
+
editor.setCamera({ x: currentCamera.x, y: currentCamera.y, z: currentCamera.z })
|
|
71
|
+
expect(editor.getCameraState()).toBe('idle')
|
|
72
|
+
})
|
|
73
|
+
|
|
74
|
+
it('does not add multiple tick listeners when camera changes rapidly', () => {
|
|
75
|
+
// This test verifies the fix: we should not have redundant listeners
|
|
76
|
+
expect(editor.getCameraState()).toBe('idle')
|
|
77
|
+
|
|
78
|
+
// Change camera multiple times rapidly
|
|
79
|
+
editor.setCamera({ x: 100, y: 100, z: 1 })
|
|
80
|
+
editor.setCamera({ x: 200, y: 200, z: 1 })
|
|
81
|
+
editor.setCamera({ x: 300, y: 300, z: 1 })
|
|
82
|
+
|
|
83
|
+
expect(editor.getCameraState()).toBe('moving')
|
|
84
|
+
|
|
85
|
+
// After timeout, should return to idle exactly once
|
|
86
|
+
// If there were multiple listeners, the state might behave unexpectedly
|
|
87
|
+
editor.forceTick(5)
|
|
88
|
+
expect(editor.getCameraState()).toBe('idle')
|
|
89
|
+
})
|
|
90
|
+
|
|
91
|
+
it('resets timeout when camera changes while already moving', () => {
|
|
92
|
+
expect(editor.getCameraState()).toBe('idle')
|
|
93
|
+
editor.setCamera({ x: 100, y: 100, z: 1 })
|
|
94
|
+
expect(editor.getCameraState()).toBe('moving')
|
|
95
|
+
|
|
96
|
+
// Wait almost until timeout
|
|
97
|
+
editor.forceTick(3)
|
|
98
|
+
expect(editor.getCameraState()).toBe('moving')
|
|
99
|
+
|
|
100
|
+
// Change camera again - should reset timeout
|
|
101
|
+
editor.setCamera({ x: 200, y: 200, z: 1 })
|
|
102
|
+
expect(editor.getCameraState()).toBe('moving')
|
|
103
|
+
|
|
104
|
+
// Wait 3 more ticks - would have been idle if timeout wasn't reset
|
|
105
|
+
editor.forceTick(3)
|
|
106
|
+
expect(editor.getCameraState()).toBe('moving')
|
|
107
|
+
|
|
108
|
+
// Now let it fully settle
|
|
109
|
+
editor.forceTick(3)
|
|
110
|
+
expect(editor.getCameraState()).toBe('idle')
|
|
111
|
+
})
|
|
112
|
+
})
|
|
113
|
+
|
|
114
|
+
describe('camera state with zoom', () => {
|
|
115
|
+
it('becomes moving on zoomOut', () => {
|
|
116
|
+
expect(editor.getCameraState()).toBe('idle')
|
|
117
|
+
editor.zoomOut(undefined, { immediate: true })
|
|
118
|
+
expect(editor.getCameraState()).toBe('moving')
|
|
119
|
+
})
|
|
120
|
+
|
|
121
|
+
it('becomes moving on centerOnPoint', () => {
|
|
122
|
+
expect(editor.getCameraState()).toBe('idle')
|
|
123
|
+
editor.centerOnPoint({ x: 500, y: 500 })
|
|
124
|
+
expect(editor.getCameraState()).toBe('moving')
|
|
125
|
+
})
|
|
126
|
+
|
|
127
|
+
it('becomes moving on zoomToFit', () => {
|
|
128
|
+
// Create a shape so zoomToFit has something to fit
|
|
129
|
+
editor.createShape({ type: 'geo', x: 100, y: 100, props: { w: 200, h: 200 } })
|
|
130
|
+
expect(editor.getCameraState()).toBe('idle')
|
|
131
|
+
editor.zoomToFit({ immediate: true })
|
|
132
|
+
expect(editor.getCameraState()).toBe('moving')
|
|
133
|
+
})
|
|
134
|
+
})
|
|
135
|
+
|
|
136
|
+
describe('getDebouncedZoomLevel', () => {
|
|
137
|
+
it('returns the current zoom level when camera is idle', () => {
|
|
138
|
+
expect(editor.getCameraState()).toBe('idle')
|
|
139
|
+
expect(editor.getDebouncedZoomLevel()).toBe(editor.getZoomLevel())
|
|
140
|
+
|
|
141
|
+
// Change zoom and let it settle
|
|
142
|
+
editor.zoomIn(undefined, { immediate: true })
|
|
143
|
+
editor.forceTick(5)
|
|
144
|
+
expect(editor.getCameraState()).toBe('idle')
|
|
145
|
+
expect(editor.getDebouncedZoomLevel()).toBe(editor.getZoomLevel())
|
|
146
|
+
})
|
|
147
|
+
|
|
148
|
+
it('captures zoom when camera starts moving', () => {
|
|
149
|
+
expect(editor.getCameraState()).toBe('idle')
|
|
150
|
+
|
|
151
|
+
// Start zooming - the debounced zoom is captured when movement starts
|
|
152
|
+
editor.zoomIn(undefined, { immediate: true })
|
|
153
|
+
expect(editor.getCameraState()).toBe('moving')
|
|
154
|
+
|
|
155
|
+
// The debounced zoom is captured at the moment movement starts (after first change)
|
|
156
|
+
const capturedZoom = editor.getDebouncedZoomLevel()
|
|
157
|
+
expect(capturedZoom).toBe(editor.getZoomLevel())
|
|
158
|
+
})
|
|
159
|
+
|
|
160
|
+
it('keeps captured zoom during continued camera movement', () => {
|
|
161
|
+
// Start zooming
|
|
162
|
+
editor.zoomIn(undefined, { immediate: true })
|
|
163
|
+
const capturedZoom = editor.getDebouncedZoomLevel()
|
|
164
|
+
expect(editor.getCameraState()).toBe('moving')
|
|
165
|
+
|
|
166
|
+
// Zoom again while still moving - debounced value should stay the same
|
|
167
|
+
editor.zoomIn(undefined, { immediate: true })
|
|
168
|
+
expect(editor.getCameraState()).toBe('moving')
|
|
169
|
+
expect(editor.getDebouncedZoomLevel()).toBe(capturedZoom)
|
|
170
|
+
|
|
171
|
+
// But current zoom should have changed
|
|
172
|
+
expect(editor.getZoomLevel()).not.toBe(capturedZoom)
|
|
173
|
+
})
|
|
174
|
+
|
|
175
|
+
it('updates debounced zoom when camera becomes idle again', () => {
|
|
176
|
+
// Start zooming
|
|
177
|
+
editor.zoomIn(undefined, { immediate: true })
|
|
178
|
+
const capturedZoom = editor.getDebouncedZoomLevel()
|
|
179
|
+
|
|
180
|
+
// Zoom again while moving to change the current zoom
|
|
181
|
+
editor.zoomIn(undefined, { immediate: true })
|
|
182
|
+
expect(editor.getDebouncedZoomLevel()).toBe(capturedZoom)
|
|
183
|
+
|
|
184
|
+
// Let camera settle
|
|
185
|
+
editor.forceTick(5)
|
|
186
|
+
expect(editor.getCameraState()).toBe('idle')
|
|
187
|
+
|
|
188
|
+
// Debounced zoom should now match current zoom
|
|
189
|
+
expect(editor.getDebouncedZoomLevel()).toBe(editor.getZoomLevel())
|
|
190
|
+
})
|
|
191
|
+
|
|
192
|
+
it('captures new zoom at the start of each new movement', () => {
|
|
193
|
+
// First zoom and settle
|
|
194
|
+
editor.zoomIn(undefined, { immediate: true })
|
|
195
|
+
const firstCapturedZoom = editor.getDebouncedZoomLevel()
|
|
196
|
+
editor.forceTick(5)
|
|
197
|
+
expect(editor.getCameraState()).toBe('idle')
|
|
198
|
+
|
|
199
|
+
// Second zoom - should capture new zoom level
|
|
200
|
+
editor.zoomIn(undefined, { immediate: true })
|
|
201
|
+
expect(editor.getCameraState()).toBe('moving')
|
|
202
|
+
// The captured zoom should be different from the first capture
|
|
203
|
+
expect(editor.getDebouncedZoomLevel()).not.toBe(firstCapturedZoom)
|
|
204
|
+
// And it should match the current zoom (since we just started moving)
|
|
205
|
+
expect(editor.getDebouncedZoomLevel()).toBe(editor.getZoomLevel())
|
|
206
|
+
})
|
|
207
|
+
|
|
208
|
+
describe('with debouncedZoom option disabled', () => {
|
|
209
|
+
let editorWithoutDebouncedZoom: TestEditor
|
|
210
|
+
|
|
211
|
+
beforeEach(() => {
|
|
212
|
+
editorWithoutDebouncedZoom = new TestEditor({
|
|
213
|
+
options: { debouncedZoom: false },
|
|
214
|
+
})
|
|
215
|
+
editorWithoutDebouncedZoom.updateViewportScreenBounds(new Box(0, 0, 1600, 900))
|
|
216
|
+
})
|
|
217
|
+
|
|
218
|
+
it('always returns the current zoom level even when camera is moving', () => {
|
|
219
|
+
const initialZoom = editorWithoutDebouncedZoom.getZoomLevel()
|
|
220
|
+
|
|
221
|
+
editorWithoutDebouncedZoom.zoomIn(undefined, { immediate: true })
|
|
222
|
+
expect(editorWithoutDebouncedZoom.getCameraState()).toBe('moving')
|
|
223
|
+
|
|
224
|
+
// Should return the current zoom, not the captured one
|
|
225
|
+
expect(editorWithoutDebouncedZoom.getDebouncedZoomLevel()).toBe(
|
|
226
|
+
editorWithoutDebouncedZoom.getZoomLevel()
|
|
227
|
+
)
|
|
228
|
+
expect(editorWithoutDebouncedZoom.getDebouncedZoomLevel()).not.toBe(initialZoom)
|
|
229
|
+
})
|
|
230
|
+
})
|
|
231
|
+
})
|
|
232
|
+
|
|
233
|
+
describe('getEfficientZoomLevel', () => {
|
|
234
|
+
it('returns current zoom level when below shape threshold', () => {
|
|
235
|
+
// Default threshold is 500 shapes, we have 0
|
|
236
|
+
expect(editor.getZoomLevel()).toBe(editor.getEfficientZoomLevel())
|
|
237
|
+
|
|
238
|
+
// Add a few shapes - still below threshold
|
|
239
|
+
for (let i = 0; i < 10; i++) {
|
|
240
|
+
editor.createShape({ type: 'geo', x: i * 100, y: 0, props: { w: 50, h: 50 } })
|
|
241
|
+
}
|
|
242
|
+
expect(editor.getCurrentPageShapeIds().size).toBe(10)
|
|
243
|
+
|
|
244
|
+
// Start zooming
|
|
245
|
+
editor.zoomIn(undefined, { immediate: true })
|
|
246
|
+
expect(editor.getCameraState()).toBe('moving')
|
|
247
|
+
|
|
248
|
+
// Should still return current zoom because we're below threshold
|
|
249
|
+
expect(editor.getEfficientZoomLevel()).toBe(editor.getZoomLevel())
|
|
250
|
+
})
|
|
251
|
+
|
|
252
|
+
describe('with many shapes above threshold', () => {
|
|
253
|
+
let editorWithManyShapes: TestEditor
|
|
254
|
+
|
|
255
|
+
beforeEach(() => {
|
|
256
|
+
// Use a lower threshold for testing
|
|
257
|
+
editorWithManyShapes = new TestEditor({
|
|
258
|
+
options: { debouncedZoomThreshold: 5 },
|
|
259
|
+
})
|
|
260
|
+
editorWithManyShapes.updateViewportScreenBounds(new Box(0, 0, 1600, 900))
|
|
261
|
+
|
|
262
|
+
// Add shapes above the threshold
|
|
263
|
+
for (let i = 0; i < 10; i++) {
|
|
264
|
+
editorWithManyShapes.createShape({
|
|
265
|
+
type: 'geo',
|
|
266
|
+
x: i * 100,
|
|
267
|
+
y: 0,
|
|
268
|
+
props: { w: 50, h: 50 },
|
|
269
|
+
})
|
|
270
|
+
}
|
|
271
|
+
})
|
|
272
|
+
|
|
273
|
+
it('returns debounced zoom level when above shape threshold and camera is moving', () => {
|
|
274
|
+
// First zoom to capture a debounced value
|
|
275
|
+
editorWithManyShapes.zoomIn(undefined, { immediate: true })
|
|
276
|
+
const capturedZoom = editorWithManyShapes.getEfficientZoomLevel()
|
|
277
|
+
expect(editorWithManyShapes.getCameraState()).toBe('moving')
|
|
278
|
+
|
|
279
|
+
// Zoom again while still moving
|
|
280
|
+
editorWithManyShapes.zoomIn(undefined, { immediate: true })
|
|
281
|
+
expect(editorWithManyShapes.getCameraState()).toBe('moving')
|
|
282
|
+
|
|
283
|
+
// Should return the captured zoom, not the current zoom
|
|
284
|
+
expect(editorWithManyShapes.getEfficientZoomLevel()).toBe(capturedZoom)
|
|
285
|
+
expect(editorWithManyShapes.getEfficientZoomLevel()).not.toBe(
|
|
286
|
+
editorWithManyShapes.getZoomLevel()
|
|
287
|
+
)
|
|
288
|
+
})
|
|
289
|
+
|
|
290
|
+
it('returns current zoom level when above threshold but camera is idle', () => {
|
|
291
|
+
editorWithManyShapes.zoomIn(undefined, { immediate: true })
|
|
292
|
+
editorWithManyShapes.forceTick(5)
|
|
293
|
+
expect(editorWithManyShapes.getCameraState()).toBe('idle')
|
|
294
|
+
|
|
295
|
+
// Should return current zoom because camera is idle
|
|
296
|
+
expect(editorWithManyShapes.getEfficientZoomLevel()).toBe(editorWithManyShapes.getZoomLevel())
|
|
297
|
+
})
|
|
298
|
+
})
|
|
299
|
+
})
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { TLArrowShape, TLGeoShape, createShapeId } from '@tldraw/editor'
|
|
2
|
+
import { TestEditor } from '../TestEditor'
|
|
3
|
+
|
|
4
|
+
let editor: TestEditor
|
|
5
|
+
|
|
6
|
+
const ids = {
|
|
7
|
+
box1: createShapeId('box1'),
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
beforeEach(() => {
|
|
11
|
+
editor = new TestEditor()
|
|
12
|
+
})
|
|
13
|
+
|
|
14
|
+
it('Uses typescript generics', () => {
|
|
15
|
+
expect(() => {
|
|
16
|
+
editor.createShape(
|
|
17
|
+
//@ts-expect-error Yep error because we are giving the wrong props to the shape
|
|
18
|
+
{
|
|
19
|
+
id: ids.box1,
|
|
20
|
+
type: 'geo',
|
|
21
|
+
props: { w: 'OH NO' },
|
|
22
|
+
}
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
// Errors when creating a shape with unknown props
|
|
26
|
+
editor.createShape({
|
|
27
|
+
id: ids.box1,
|
|
28
|
+
type: 'geo',
|
|
29
|
+
props: {
|
|
30
|
+
// @ts-expect-error
|
|
31
|
+
foo: 'bar',
|
|
32
|
+
},
|
|
33
|
+
})
|
|
34
|
+
|
|
35
|
+
// Yep error here because we are giving the wrong props to the shape
|
|
36
|
+
editor.createShape<TLGeoShape>({
|
|
37
|
+
id: ids.box1,
|
|
38
|
+
type: 'geo',
|
|
39
|
+
//@ts-expect-error
|
|
40
|
+
props: { w: 'OH NO' },
|
|
41
|
+
})
|
|
42
|
+
|
|
43
|
+
// Yep error here because we are giving the wrong generic
|
|
44
|
+
editor.createShape<TLArrowShape>({
|
|
45
|
+
id: ids.box1,
|
|
46
|
+
//@ts-expect-error
|
|
47
|
+
type: 'geo',
|
|
48
|
+
//@ts-expect-error
|
|
49
|
+
props: { w: 'OH NO' },
|
|
50
|
+
})
|
|
51
|
+
|
|
52
|
+
// All good, correct match of generic and shape type
|
|
53
|
+
editor.createShape<TLGeoShape>({
|
|
54
|
+
id: ids.box1,
|
|
55
|
+
type: 'geo',
|
|
56
|
+
props: { w: 100 },
|
|
57
|
+
})
|
|
58
|
+
|
|
59
|
+
editor.createShape<TLGeoShape>({
|
|
60
|
+
id: ids.box1,
|
|
61
|
+
type: 'geo',
|
|
62
|
+
})
|
|
63
|
+
}).toThrow()
|
|
64
|
+
})
|
|
@@ -19,14 +19,28 @@ beforeEach(() => {
|
|
|
19
19
|
|
|
20
20
|
it('Uses typescript generics', () => {
|
|
21
21
|
expect(() => {
|
|
22
|
-
//
|
|
22
|
+
// Yep error because we are giving the wrong props to the shape
|
|
23
23
|
editor.createShapes([
|
|
24
|
+
//@ts-expect-error
|
|
24
25
|
{
|
|
25
26
|
id: ids.box1,
|
|
26
27
|
type: 'geo',
|
|
27
28
|
props: { w: 'OH NO' },
|
|
28
29
|
},
|
|
29
30
|
])
|
|
31
|
+
|
|
32
|
+
// Errors when creating shapes with unknown props
|
|
33
|
+
editor.createShapes([
|
|
34
|
+
{
|
|
35
|
+
id: ids.box1,
|
|
36
|
+
type: 'geo',
|
|
37
|
+
props: {
|
|
38
|
+
// @ts-expect-error
|
|
39
|
+
foo: 'bar',
|
|
40
|
+
},
|
|
41
|
+
},
|
|
42
|
+
])
|
|
43
|
+
|
|
30
44
|
// Yep error here because we are giving the wrong props to the shape
|
|
31
45
|
editor.createShapes<TLGeoShape>([
|
|
32
46
|
{
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { DefaultDashStyle,
|
|
1
|
+
import { DefaultDashStyle, createShapeId, toRichText } from '@tldraw/editor'
|
|
2
2
|
import { vi } from 'vitest'
|
|
3
3
|
import { TestEditor } from '../TestEditor'
|
|
4
4
|
|
|
@@ -21,7 +21,7 @@ beforeEach(() => {
|
|
|
21
21
|
editor = new TestEditor()
|
|
22
22
|
editor.setStyleForNextShapes(DefaultDashStyle, 'solid')
|
|
23
23
|
editor.setStyleForSelectedShapes(DefaultDashStyle, 'solid')
|
|
24
|
-
editor.createShapes
|
|
24
|
+
editor.createShapes([
|
|
25
25
|
{
|
|
26
26
|
id: ids.boxA,
|
|
27
27
|
type: 'geo',
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { TLArrowShape, createShapeId } from '@tldraw/editor'
|
|
2
|
+
import { TestEditor } from '../TestEditor'
|
|
3
|
+
|
|
4
|
+
let editor: TestEditor
|
|
5
|
+
|
|
6
|
+
beforeEach(() => {
|
|
7
|
+
editor = new TestEditor()
|
|
8
|
+
})
|
|
9
|
+
|
|
10
|
+
export declare function assertNever(x: never): never
|
|
11
|
+
|
|
12
|
+
it('narrows down the shape type', () => {
|
|
13
|
+
const id = createShapeId('arrow1')
|
|
14
|
+
editor.createShape({ type: 'arrow', id, x: 0, y: 0 })
|
|
15
|
+
|
|
16
|
+
const shape = editor.getShape(id)!
|
|
17
|
+
if (editor.isShapeOfType(shape, 'arrow')) {
|
|
18
|
+
expect(shape.type === 'arrow').toBe(true)
|
|
19
|
+
expect(
|
|
20
|
+
// @ts-expect-error This comparison appears to be unintentional because the types '"arrow"' and '"card"' have no overlap.
|
|
21
|
+
shape.type === 'card'
|
|
22
|
+
).toBe(false)
|
|
23
|
+
}
|
|
24
|
+
})
|
|
25
|
+
|
|
26
|
+
it('narrows down the shape type with generic', () => {
|
|
27
|
+
const id = createShapeId('arrow1')
|
|
28
|
+
editor.createShape({ type: 'arrow', id, x: 0, y: 0 })
|
|
29
|
+
|
|
30
|
+
const shape = editor.getShape(id)!
|
|
31
|
+
if (editor.isShapeOfType<TLArrowShape>(shape, 'arrow')) {
|
|
32
|
+
expect(shape.type === 'arrow').toBe(true)
|
|
33
|
+
|
|
34
|
+
expect(
|
|
35
|
+
// @ts-expect-error This comparison appears to be unintentional because the types '"arrow"' and '"card"' have no overlap.
|
|
36
|
+
shape.type === 'card'
|
|
37
|
+
).toBe(false)
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// @ts-expect-error mismatch between the generic and the shape type
|
|
41
|
+
if (editor.isShapeOfType<TLArrowShape>(shape, 'card')) {
|
|
42
|
+
assertNever(shape)
|
|
43
|
+
}
|
|
44
|
+
})
|