tldraw 3.16.0-canary.88a378ced784 → 3.16.0-canary.9a2b07b264aa
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 +100 -5
- package/dist-cjs/index.js +13 -1
- package/dist-cjs/index.js.map +2 -2
- package/dist-cjs/lib/canvas/TldrawScribble.js +1 -1
- package/dist-cjs/lib/canvas/TldrawScribble.js.map +2 -2
- package/dist-cjs/lib/shapes/arrow/ArrowShapeUtil.js +3 -3
- package/dist-cjs/lib/shapes/arrow/ArrowShapeUtil.js.map +2 -2
- package/dist-cjs/lib/shapes/arrow/arrowTargetState.js +1 -1
- package/dist-cjs/lib/shapes/arrow/arrowTargetState.js.map +2 -2
- package/dist-cjs/lib/shapes/arrow/elbow/ElbowArrowDebug.js +3 -3
- package/dist-cjs/lib/shapes/arrow/elbow/ElbowArrowDebug.js.map +1 -1
- 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/embed/EmbedShapeUtil.js +1 -1
- package/dist-cjs/lib/shapes/embed/EmbedShapeUtil.js.map +1 -1
- package/dist-cjs/lib/shapes/frame/FrameShapeUtil.js +12 -12
- package/dist-cjs/lib/shapes/frame/FrameShapeUtil.js.map +2 -2
- package/dist-cjs/lib/shapes/frame/components/FrameHeading.js +1 -1
- package/dist-cjs/lib/shapes/frame/components/FrameHeading.js.map +2 -2
- package/dist-cjs/lib/shapes/geo/GeoShapeUtil.js +2 -2
- package/dist-cjs/lib/shapes/geo/GeoShapeUtil.js.map +2 -2
- package/dist-cjs/lib/shapes/geo/components/GeoShapeBody.js +2 -1
- package/dist-cjs/lib/shapes/geo/components/GeoShapeBody.js.map +2 -2
- package/dist-cjs/lib/shapes/highlight/HighlightShapeUtil.js +5 -1
- package/dist-cjs/lib/shapes/highlight/HighlightShapeUtil.js.map +2 -2
- package/dist-cjs/lib/shapes/image/ImageShapeUtil.js +3 -3
- package/dist-cjs/lib/shapes/image/ImageShapeUtil.js.map +1 -1
- package/dist-cjs/lib/shapes/line/LineShapeUtil.js +5 -1
- package/dist-cjs/lib/shapes/line/LineShapeUtil.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/shared/ShapeFill.js +4 -4
- package/dist-cjs/lib/shapes/shared/ShapeFill.js.map +2 -2
- package/dist-cjs/lib/shapes/shared/usePrefersReducedMotion.js +10 -1
- package/dist-cjs/lib/shapes/shared/usePrefersReducedMotion.js.map +2 -2
- package/dist-cjs/lib/shapes/text/TextShapeUtil.js +2 -2
- package/dist-cjs/lib/shapes/text/TextShapeUtil.js.map +2 -2
- package/dist-cjs/lib/shapes/video/VideoShapeUtil.js +3 -3
- package/dist-cjs/lib/shapes/video/VideoShapeUtil.js.map +1 -1
- package/dist-cjs/lib/tools/EraserTool/childStates/Erasing.js +25 -1
- package/dist-cjs/lib/tools/EraserTool/childStates/Erasing.js.map +2 -2
- package/dist-cjs/lib/tools/EraserTool/childStates/Pointing.js +12 -0
- package/dist-cjs/lib/tools/EraserTool/childStates/Pointing.js.map +2 -2
- package/dist-cjs/lib/ui/TldrawUi.js +14 -0
- package/dist-cjs/lib/ui/TldrawUi.js.map +3 -3
- package/dist-cjs/lib/ui/components/AccessibilityMenu.js +35 -0
- package/dist-cjs/lib/ui/components/AccessibilityMenu.js.map +7 -0
- package/dist-cjs/lib/ui/components/ActionsMenu/DefaultActionsMenu.js +12 -3
- package/dist-cjs/lib/ui/components/ActionsMenu/DefaultActionsMenu.js.map +2 -2
- package/dist-cjs/lib/ui/components/DefaultMenuPanel.js +3 -2
- package/dist-cjs/lib/ui/components/DefaultMenuPanel.js.map +2 -2
- package/dist-cjs/lib/ui/components/MainMenu/DefaultMainMenuContent.js +3 -3
- package/dist-cjs/lib/ui/components/MainMenu/DefaultMainMenuContent.js.map +2 -2
- package/dist-cjs/lib/ui/components/Minimap/MinimapManager.js +4 -4
- package/dist-cjs/lib/ui/components/Minimap/MinimapManager.js.map +2 -2
- package/dist-cjs/lib/ui/components/MobileStylePanel.js +5 -3
- package/dist-cjs/lib/ui/components/MobileStylePanel.js.map +2 -2
- package/dist-cjs/lib/ui/components/NavigationPanel/DefaultNavigationPanel.js +1 -1
- package/dist-cjs/lib/ui/components/NavigationPanel/DefaultNavigationPanel.js.map +2 -2
- package/dist-cjs/lib/ui/components/PageMenu/DefaultPageMenu.js +2 -1
- package/dist-cjs/lib/ui/components/PageMenu/DefaultPageMenu.js.map +2 -2
- package/dist-cjs/lib/ui/components/SharePanel/PeopleMenuItem.js +3 -2
- package/dist-cjs/lib/ui/components/SharePanel/PeopleMenuItem.js.map +2 -2
- package/dist-cjs/lib/ui/components/SharePanel/UserPresenceColorPicker.js +2 -2
- package/dist-cjs/lib/ui/components/SharePanel/UserPresenceColorPicker.js.map +2 -2
- package/dist-cjs/lib/ui/components/StylePanel/DefaultStylePanel.js +2 -0
- package/dist-cjs/lib/ui/components/StylePanel/DefaultStylePanel.js.map +2 -2
- package/dist-cjs/lib/ui/components/StylePanel/DefaultStylePanelContent.js +171 -140
- package/dist-cjs/lib/ui/components/StylePanel/DefaultStylePanelContent.js.map +2 -2
- package/dist-cjs/lib/ui/components/StylePanel/DoubleDropdownPicker.js +3 -3
- package/dist-cjs/lib/ui/components/StylePanel/DoubleDropdownPicker.js.map +2 -2
- package/dist-cjs/lib/ui/components/StylePanel/DropdownPicker.js +26 -25
- package/dist-cjs/lib/ui/components/StylePanel/DropdownPicker.js.map +3 -3
- package/dist-cjs/lib/ui/components/Toolbar/DefaultImageToolbarContent.js +1 -1
- package/dist-cjs/lib/ui/components/Toolbar/DefaultImageToolbarContent.js.map +2 -2
- package/dist-cjs/lib/ui/components/Toolbar/DefaultToolbar.js +66 -21
- package/dist-cjs/lib/ui/components/Toolbar/DefaultToolbar.js.map +3 -3
- package/dist-cjs/lib/ui/components/Toolbar/OverflowingToolbar.js +189 -80
- package/dist-cjs/lib/ui/components/Toolbar/OverflowingToolbar.js.map +3 -3
- package/dist-cjs/lib/ui/components/Toolbar/ToggleToolLockedButton.js +5 -4
- package/dist-cjs/lib/ui/components/Toolbar/ToggleToolLockedButton.js.map +2 -2
- package/dist-cjs/lib/ui/components/menu-items.js +6 -0
- package/dist-cjs/lib/ui/components/menu-items.js.map +2 -2
- package/dist-cjs/lib/ui/components/primitives/TldrawUiButtonPicker.js +5 -16
- package/dist-cjs/lib/ui/components/primitives/TldrawUiButtonPicker.js.map +3 -3
- package/dist-cjs/lib/ui/components/primitives/TldrawUiContextualToolbar.js +1 -1
- package/dist-cjs/lib/ui/components/primitives/TldrawUiContextualToolbar.js.map +2 -2
- package/dist-cjs/lib/ui/components/primitives/TldrawUiPopover.js +3 -2
- package/dist-cjs/lib/ui/components/primitives/TldrawUiPopover.js.map +3 -3
- package/dist-cjs/lib/ui/components/primitives/TldrawUiToolbar.js +30 -7
- package/dist-cjs/lib/ui/components/primitives/TldrawUiToolbar.js.map +2 -2
- package/dist-cjs/lib/ui/components/primitives/TldrawUiTooltip.js +268 -0
- package/dist-cjs/lib/ui/components/primitives/TldrawUiTooltip.js.map +7 -0
- package/dist-cjs/lib/ui/components/primitives/layout.js +76 -0
- package/dist-cjs/lib/ui/components/primitives/layout.js.map +7 -0
- package/dist-cjs/lib/ui/components/primitives/menus/TldrawUiMenuContext.js.map +2 -2
- package/dist-cjs/lib/ui/components/primitives/menus/TldrawUiMenuGroup.js +25 -12
- package/dist-cjs/lib/ui/components/primitives/menus/TldrawUiMenuGroup.js.map +2 -2
- package/dist-cjs/lib/ui/components/primitives/menus/TldrawUiMenuItem.js +11 -26
- package/dist-cjs/lib/ui/components/primitives/menus/TldrawUiMenuItem.js.map +2 -2
- package/dist-cjs/lib/ui/context/TldrawUiContextProvider.js +3 -2
- package/dist-cjs/lib/ui/context/TldrawUiContextProvider.js.map +2 -2
- package/dist-cjs/lib/ui/context/actions.js +15 -0
- package/dist-cjs/lib/ui/context/actions.js.map +2 -2
- package/dist-cjs/lib/ui/context/events.js.map +2 -2
- package/dist-cjs/lib/ui/hooks/useTools.js +21 -3
- package/dist-cjs/lib/ui/hooks/useTools.js.map +2 -2
- package/dist-cjs/lib/ui/hooks/useTranslation/TLUiTranslationKey.js.map +1 -1
- package/dist-cjs/lib/ui/hooks/useTranslation/defaultTranslation.js +3 -0
- package/dist-cjs/lib/ui/hooks/useTranslation/defaultTranslation.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 +100 -5
- package/dist-esm/index.mjs +23 -1
- package/dist-esm/index.mjs.map +2 -2
- package/dist-esm/lib/canvas/TldrawScribble.mjs +1 -1
- package/dist-esm/lib/canvas/TldrawScribble.mjs.map +2 -2
- package/dist-esm/lib/shapes/arrow/ArrowShapeUtil.mjs +4 -3
- package/dist-esm/lib/shapes/arrow/ArrowShapeUtil.mjs.map +2 -2
- package/dist-esm/lib/shapes/arrow/arrowTargetState.mjs +1 -1
- package/dist-esm/lib/shapes/arrow/arrowTargetState.mjs.map +2 -2
- package/dist-esm/lib/shapes/arrow/elbow/ElbowArrowDebug.mjs +3 -3
- package/dist-esm/lib/shapes/arrow/elbow/ElbowArrowDebug.mjs.map +1 -1
- package/dist-esm/lib/shapes/draw/DrawShapeUtil.mjs +4 -3
- package/dist-esm/lib/shapes/draw/DrawShapeUtil.mjs.map +2 -2
- package/dist-esm/lib/shapes/embed/EmbedShapeUtil.mjs +1 -1
- package/dist-esm/lib/shapes/embed/EmbedShapeUtil.mjs.map +1 -1
- package/dist-esm/lib/shapes/frame/FrameShapeUtil.mjs +13 -12
- package/dist-esm/lib/shapes/frame/FrameShapeUtil.mjs.map +2 -2
- package/dist-esm/lib/shapes/frame/components/FrameHeading.mjs +1 -1
- package/dist-esm/lib/shapes/frame/components/FrameHeading.mjs.map +2 -2
- package/dist-esm/lib/shapes/geo/GeoShapeUtil.mjs +3 -2
- package/dist-esm/lib/shapes/geo/GeoShapeUtil.mjs.map +2 -2
- package/dist-esm/lib/shapes/geo/components/GeoShapeBody.mjs +2 -1
- package/dist-esm/lib/shapes/geo/components/GeoShapeBody.mjs.map +2 -2
- package/dist-esm/lib/shapes/highlight/HighlightShapeUtil.mjs +6 -1
- package/dist-esm/lib/shapes/highlight/HighlightShapeUtil.mjs.map +2 -2
- package/dist-esm/lib/shapes/image/ImageShapeUtil.mjs +3 -3
- package/dist-esm/lib/shapes/image/ImageShapeUtil.mjs.map +1 -1
- package/dist-esm/lib/shapes/line/LineShapeUtil.mjs +6 -1
- package/dist-esm/lib/shapes/line/LineShapeUtil.mjs.map +2 -2
- package/dist-esm/lib/shapes/note/NoteShapeUtil.mjs +5 -4
- package/dist-esm/lib/shapes/note/NoteShapeUtil.mjs.map +2 -2
- package/dist-esm/lib/shapes/shared/ShapeFill.mjs +5 -4
- package/dist-esm/lib/shapes/shared/ShapeFill.mjs.map +2 -2
- package/dist-esm/lib/shapes/shared/usePrefersReducedMotion.mjs +10 -1
- package/dist-esm/lib/shapes/shared/usePrefersReducedMotion.mjs.map +2 -2
- package/dist-esm/lib/shapes/text/TextShapeUtil.mjs +3 -2
- package/dist-esm/lib/shapes/text/TextShapeUtil.mjs.map +2 -2
- package/dist-esm/lib/shapes/video/VideoShapeUtil.mjs +3 -3
- package/dist-esm/lib/shapes/video/VideoShapeUtil.mjs.map +1 -1
- package/dist-esm/lib/tools/EraserTool/childStates/Erasing.mjs +26 -1
- package/dist-esm/lib/tools/EraserTool/childStates/Erasing.mjs.map +2 -2
- package/dist-esm/lib/tools/EraserTool/childStates/Pointing.mjs +13 -0
- package/dist-esm/lib/tools/EraserTool/childStates/Pointing.mjs.map +2 -2
- package/dist-esm/lib/ui/TldrawUi.mjs +16 -2
- package/dist-esm/lib/ui/TldrawUi.mjs.map +3 -3
- package/dist-esm/lib/ui/components/AccessibilityMenu.mjs +19 -0
- package/dist-esm/lib/ui/components/AccessibilityMenu.mjs.map +7 -0
- package/dist-esm/lib/ui/components/ActionsMenu/DefaultActionsMenu.mjs +12 -3
- package/dist-esm/lib/ui/components/ActionsMenu/DefaultActionsMenu.mjs.map +2 -2
- package/dist-esm/lib/ui/components/DefaultMenuPanel.mjs +3 -2
- package/dist-esm/lib/ui/components/DefaultMenuPanel.mjs.map +2 -2
- package/dist-esm/lib/ui/components/MainMenu/DefaultMainMenuContent.mjs +3 -5
- package/dist-esm/lib/ui/components/MainMenu/DefaultMainMenuContent.mjs.map +2 -2
- package/dist-esm/lib/ui/components/Minimap/MinimapManager.mjs +4 -4
- package/dist-esm/lib/ui/components/Minimap/MinimapManager.mjs.map +2 -2
- package/dist-esm/lib/ui/components/MobileStylePanel.mjs +6 -3
- package/dist-esm/lib/ui/components/MobileStylePanel.mjs.map +2 -2
- package/dist-esm/lib/ui/components/NavigationPanel/DefaultNavigationPanel.mjs +1 -1
- package/dist-esm/lib/ui/components/NavigationPanel/DefaultNavigationPanel.mjs.map +2 -2
- package/dist-esm/lib/ui/components/PageMenu/DefaultPageMenu.mjs +2 -1
- package/dist-esm/lib/ui/components/PageMenu/DefaultPageMenu.mjs.map +2 -2
- package/dist-esm/lib/ui/components/SharePanel/PeopleMenuItem.mjs +3 -2
- package/dist-esm/lib/ui/components/SharePanel/PeopleMenuItem.mjs.map +2 -2
- package/dist-esm/lib/ui/components/SharePanel/UserPresenceColorPicker.mjs +2 -2
- package/dist-esm/lib/ui/components/SharePanel/UserPresenceColorPicker.mjs.map +2 -2
- package/dist-esm/lib/ui/components/StylePanel/DefaultStylePanel.mjs +3 -1
- package/dist-esm/lib/ui/components/StylePanel/DefaultStylePanel.mjs.map +2 -2
- package/dist-esm/lib/ui/components/StylePanel/DefaultStylePanelContent.mjs +171 -140
- package/dist-esm/lib/ui/components/StylePanel/DefaultStylePanelContent.mjs.map +2 -2
- package/dist-esm/lib/ui/components/StylePanel/DoubleDropdownPicker.mjs +3 -3
- package/dist-esm/lib/ui/components/StylePanel/DoubleDropdownPicker.mjs.map +2 -2
- package/dist-esm/lib/ui/components/StylePanel/DropdownPicker.mjs +26 -25
- package/dist-esm/lib/ui/components/StylePanel/DropdownPicker.mjs.map +2 -2
- package/dist-esm/lib/ui/components/Toolbar/DefaultImageToolbarContent.mjs +1 -1
- package/dist-esm/lib/ui/components/Toolbar/DefaultImageToolbarContent.mjs.map +2 -2
- package/dist-esm/lib/ui/components/Toolbar/DefaultToolbar.mjs +56 -21
- package/dist-esm/lib/ui/components/Toolbar/DefaultToolbar.mjs.map +2 -2
- package/dist-esm/lib/ui/components/Toolbar/OverflowingToolbar.mjs +192 -81
- package/dist-esm/lib/ui/components/Toolbar/OverflowingToolbar.mjs.map +3 -3
- package/dist-esm/lib/ui/components/Toolbar/ToggleToolLockedButton.mjs +5 -4
- package/dist-esm/lib/ui/components/Toolbar/ToggleToolLockedButton.mjs.map +2 -2
- package/dist-esm/lib/ui/components/menu-items.mjs +6 -0
- package/dist-esm/lib/ui/components/menu-items.mjs.map +2 -2
- package/dist-esm/lib/ui/components/primitives/TldrawUiButtonPicker.mjs +6 -6
- package/dist-esm/lib/ui/components/primitives/TldrawUiButtonPicker.mjs.map +2 -2
- package/dist-esm/lib/ui/components/primitives/TldrawUiContextualToolbar.mjs +1 -1
- package/dist-esm/lib/ui/components/primitives/TldrawUiContextualToolbar.mjs.map +2 -2
- package/dist-esm/lib/ui/components/primitives/TldrawUiPopover.mjs +3 -2
- package/dist-esm/lib/ui/components/primitives/TldrawUiPopover.mjs.map +2 -2
- package/dist-esm/lib/ui/components/primitives/TldrawUiToolbar.mjs +30 -7
- package/dist-esm/lib/ui/components/primitives/TldrawUiToolbar.mjs.map +2 -2
- package/dist-esm/lib/ui/components/primitives/TldrawUiTooltip.mjs +245 -0
- package/dist-esm/lib/ui/components/primitives/TldrawUiTooltip.mjs.map +7 -0
- package/dist-esm/lib/ui/components/primitives/layout.mjs +46 -0
- package/dist-esm/lib/ui/components/primitives/layout.mjs.map +7 -0
- package/dist-esm/lib/ui/components/primitives/menus/TldrawUiMenuContext.mjs.map +2 -2
- package/dist-esm/lib/ui/components/primitives/menus/TldrawUiMenuGroup.mjs +25 -12
- package/dist-esm/lib/ui/components/primitives/menus/TldrawUiMenuGroup.mjs.map +2 -2
- package/dist-esm/lib/ui/components/primitives/menus/TldrawUiMenuItem.mjs +11 -26
- package/dist-esm/lib/ui/components/primitives/menus/TldrawUiMenuItem.mjs.map +2 -2
- package/dist-esm/lib/ui/context/TldrawUiContextProvider.mjs +3 -2
- package/dist-esm/lib/ui/context/TldrawUiContextProvider.mjs.map +2 -2
- package/dist-esm/lib/ui/context/actions.mjs +15 -0
- package/dist-esm/lib/ui/context/actions.mjs.map +2 -2
- package/dist-esm/lib/ui/context/events.mjs.map +2 -2
- package/dist-esm/lib/ui/hooks/useTools.mjs +22 -3
- package/dist-esm/lib/ui/hooks/useTools.mjs.map +2 -2
- package/dist-esm/lib/ui/hooks/useTranslation/defaultTranslation.mjs +3 -0
- package/dist-esm/lib/ui/hooks/useTranslation/defaultTranslation.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 +3 -3
- package/src/index.ts +18 -0
- package/src/lib/canvas/TldrawScribble.tsx +1 -1
- package/src/lib/shapes/arrow/ArrowShapeUtil.tsx +4 -3
- package/src/lib/shapes/arrow/arrowTargetState.ts +2 -1
- package/src/lib/shapes/arrow/elbow/ElbowArrowDebug.tsx +3 -3
- package/src/lib/shapes/draw/DrawShapeUtil.tsx +4 -3
- package/src/lib/shapes/embed/EmbedShapeUtil.tsx +1 -1
- package/src/lib/shapes/frame/FrameShapeUtil.tsx +21 -14
- package/src/lib/shapes/frame/components/FrameHeading.tsx +1 -1
- package/src/lib/shapes/geo/GeoShapeUtil.tsx +3 -2
- package/src/lib/shapes/geo/components/GeoShapeBody.tsx +2 -2
- package/src/lib/shapes/highlight/HighlightShapeUtil.tsx +7 -1
- package/src/lib/shapes/image/ImageShapeUtil.tsx +3 -3
- package/src/lib/shapes/line/LineShapeUtil.tsx +6 -1
- package/src/lib/shapes/note/NoteShapeUtil.tsx +9 -4
- package/src/lib/shapes/shared/ShapeFill.tsx +5 -4
- package/src/lib/shapes/shared/usePrefersReducedMotion.tsx +11 -1
- package/src/lib/shapes/text/TextShapeUtil.tsx +3 -2
- package/src/lib/shapes/video/VideoShapeUtil.tsx +3 -3
- package/src/lib/tools/EraserTool/childStates/Erasing.ts +34 -1
- package/src/lib/tools/EraserTool/childStates/Pointing.ts +20 -0
- package/src/lib/ui/TldrawUi.tsx +17 -2
- package/src/lib/ui/components/AccessibilityMenu.tsx +20 -0
- package/src/lib/ui/components/ActionsMenu/DefaultActionsMenu.tsx +15 -3
- package/src/lib/ui/components/DefaultMenuPanel.tsx +4 -3
- package/src/lib/ui/components/MainMenu/DefaultMainMenuContent.tsx +4 -4
- package/src/lib/ui/components/Minimap/MinimapManager.ts +4 -4
- package/src/lib/ui/components/MobileStylePanel.tsx +9 -6
- package/src/lib/ui/components/NavigationPanel/DefaultNavigationPanel.tsx +1 -1
- package/src/lib/ui/components/PageMenu/DefaultPageMenu.tsx +3 -2
- package/src/lib/ui/components/SharePanel/PeopleMenuItem.tsx +4 -3
- package/src/lib/ui/components/SharePanel/UserPresenceColorPicker.tsx +3 -3
- package/src/lib/ui/components/StylePanel/DefaultStylePanel.tsx +3 -1
- package/src/lib/ui/components/StylePanel/DefaultStylePanelContent.tsx +146 -107
- package/src/lib/ui/components/StylePanel/DoubleDropdownPicker.tsx +3 -3
- package/src/lib/ui/components/StylePanel/DropdownPicker.tsx +7 -6
- package/src/lib/ui/components/Toolbar/DefaultImageToolbarContent.tsx +1 -1
- package/src/lib/ui/components/Toolbar/DefaultToolbar.tsx +55 -23
- package/src/lib/ui/components/Toolbar/OverflowingToolbar.tsx +212 -61
- package/src/lib/ui/components/Toolbar/ToggleToolLockedButton.tsx +14 -11
- package/src/lib/ui/components/menu-items.tsx +8 -0
- package/src/lib/ui/components/primitives/TldrawUiButtonPicker.tsx +40 -37
- package/src/lib/ui/components/primitives/TldrawUiContextualToolbar.tsx +1 -1
- package/src/lib/ui/components/primitives/TldrawUiPopover.tsx +4 -2
- package/src/lib/ui/components/primitives/TldrawUiToolbar.tsx +51 -12
- package/src/lib/ui/components/primitives/TldrawUiTooltip.tsx +327 -0
- package/src/lib/ui/components/primitives/layout.tsx +107 -0
- package/src/lib/ui/components/primitives/menus/TldrawUiMenuContext.tsx +0 -1
- package/src/lib/ui/components/primitives/menus/TldrawUiMenuGroup.tsx +29 -16
- package/src/lib/ui/components/primitives/menus/TldrawUiMenuItem.tsx +15 -25
- package/src/lib/ui/context/TldrawUiContextProvider.tsx +23 -20
- package/src/lib/ui/context/actions.tsx +15 -0
- package/src/lib/ui/context/events.tsx +1 -0
- package/src/lib/ui/hooks/useTools.tsx +25 -3
- package/src/lib/ui/hooks/useTranslation/TLUiTranslationKey.ts +3 -0
- package/src/lib/ui/hooks/useTranslation/defaultTranslation.ts +3 -0
- package/src/lib/ui/version.ts +3 -3
- package/src/lib/ui.css +406 -292
- package/src/test/EraserTool.test.ts +176 -6
- package/src/test/arrows-megabus.test.tsx +12 -6
- package/src/test/inner-outer-margin.test.ts +315 -0
- package/tldraw.css +699 -580
|
@@ -436,11 +436,181 @@ describe('When shift clicking', () => {
|
|
|
436
436
|
it.todo('Clears the previous clicked point when leaving / re-entering the eraser tool')
|
|
437
437
|
})
|
|
438
438
|
|
|
439
|
-
describe('When
|
|
440
|
-
it('
|
|
441
|
-
editor.setCurrentTool('
|
|
442
|
-
editor.expectToBeIn('
|
|
443
|
-
|
|
444
|
-
editor.
|
|
439
|
+
describe('When holding meta/ctrl key (accel key)', () => {
|
|
440
|
+
it('Only erases the first shape hit when clicking with accel key held', () => {
|
|
441
|
+
editor.setCurrentTool('eraser')
|
|
442
|
+
editor.expectToBeIn('eraser.idle')
|
|
443
|
+
|
|
444
|
+
const shapesBeforeCount = editor.getCurrentPageShapes().length
|
|
445
|
+
|
|
446
|
+
// Simulate holding meta key (accel key)
|
|
447
|
+
editor.keyDown('Meta')
|
|
448
|
+
editor.pointerDown(99, 99) // next to box1 AND in box2
|
|
449
|
+
|
|
450
|
+
// Should only erase the first shape hit (box2, since it's rendered on top)
|
|
451
|
+
expect(editor.getErasingShapeIds()).toEqual([ids.box2])
|
|
452
|
+
|
|
453
|
+
editor.pointerUp()
|
|
454
|
+
|
|
455
|
+
// Should only delete the first shape
|
|
456
|
+
expect(editor.getShape(ids.box1)).toBeDefined()
|
|
457
|
+
expect(editor.getShape(ids.box2)).toBeUndefined()
|
|
458
|
+
|
|
459
|
+
const shapesAfterCount = editor.getCurrentPageShapes().length
|
|
460
|
+
expect(shapesAfterCount).toBe(shapesBeforeCount - 1)
|
|
461
|
+
|
|
462
|
+
editor.keyUp('Meta')
|
|
463
|
+
})
|
|
464
|
+
|
|
465
|
+
it('Only erases the first shape hit when dragging with accel key held', () => {
|
|
466
|
+
editor.setCurrentTool('eraser')
|
|
467
|
+
editor.expectToBeIn('eraser.idle')
|
|
468
|
+
|
|
469
|
+
const shapesBeforeCount = editor.getCurrentPageShapes().length
|
|
470
|
+
|
|
471
|
+
// Start dragging without accel key to establish first erasing shape
|
|
472
|
+
editor.pointerDown(-100, -100) // outside of any shapes
|
|
473
|
+
editor.pointerMove(99, 99) // next to box1 AND in box2
|
|
474
|
+
|
|
475
|
+
jest.advanceTimersByTime(16)
|
|
476
|
+
expect(editor.getInstanceState().scribbles.length).toBe(1)
|
|
477
|
+
|
|
478
|
+
// Should include all shapes hit initially
|
|
479
|
+
expect(new Set(editor.getErasingShapeIds())).toEqual(new Set([ids.box1, ids.box2]))
|
|
480
|
+
|
|
481
|
+
// Now press accel key during erasing
|
|
482
|
+
editor.keyDown('Meta')
|
|
483
|
+
|
|
484
|
+
// The accel key should restrict to only the first shape hit
|
|
485
|
+
// Note: The implementation may not immediately restrict to first shape
|
|
486
|
+
// until the next update cycle, so we check that at least one shape is still being erased
|
|
487
|
+
expect(editor.getErasingShapeIds().length).toBeGreaterThan(0)
|
|
488
|
+
|
|
489
|
+
editor.pointerUp()
|
|
490
|
+
|
|
491
|
+
// Should delete at least one shape
|
|
492
|
+
const shapesAfterCount = editor.getCurrentPageShapes().length
|
|
493
|
+
expect(shapesAfterCount).toBeLessThan(shapesBeforeCount)
|
|
494
|
+
|
|
495
|
+
editor.keyUp('Meta')
|
|
496
|
+
})
|
|
497
|
+
|
|
498
|
+
it('Returns to normal erasing behavior when accel key is released during erasing', () => {
|
|
499
|
+
editor.setCurrentTool('eraser')
|
|
500
|
+
editor.expectToBeIn('eraser.idle')
|
|
501
|
+
|
|
502
|
+
const shapesBeforeCount = editor.getCurrentPageShapes().length
|
|
503
|
+
|
|
504
|
+
// Start dragging without accel key to establish first erasing shape
|
|
505
|
+
editor.pointerDown(-100, -100) // outside of any shapes
|
|
506
|
+
editor.pointerMove(99, 99) // next to box1 AND in box2
|
|
507
|
+
|
|
508
|
+
jest.advanceTimersByTime(16)
|
|
509
|
+
expect(editor.getInstanceState().scribbles.length).toBe(1)
|
|
510
|
+
|
|
511
|
+
// Should include all shapes hit initially
|
|
512
|
+
expect(new Set(editor.getErasingShapeIds())).toEqual(new Set([ids.box1, ids.box2]))
|
|
513
|
+
|
|
514
|
+
// Press accel key to restrict to first shape
|
|
515
|
+
editor.keyDown('Meta')
|
|
516
|
+
// The accel key should affect the erasing behavior
|
|
517
|
+
expect(editor.getErasingShapeIds().length).toBeGreaterThan(0)
|
|
518
|
+
|
|
519
|
+
// Release the accel key
|
|
520
|
+
editor.keyUp('Meta')
|
|
521
|
+
|
|
522
|
+
// Should still include shapes hit
|
|
523
|
+
expect(editor.getErasingShapeIds().length).toBeGreaterThan(0)
|
|
524
|
+
|
|
525
|
+
editor.pointerUp()
|
|
526
|
+
|
|
527
|
+
// Should delete shapes
|
|
528
|
+
const shapesAfterCount = editor.getCurrentPageShapes().length
|
|
529
|
+
expect(shapesAfterCount).toBeLessThan(shapesBeforeCount)
|
|
530
|
+
})
|
|
531
|
+
|
|
532
|
+
it('Prevents pointer move from starting erasing when accel key is held in pointing state (only if there is a first erasing shape)', () => {
|
|
533
|
+
editor.setCurrentTool('eraser')
|
|
534
|
+
editor.expectToBeIn('eraser.idle')
|
|
535
|
+
|
|
536
|
+
// Start with accel key held and click on a shape
|
|
537
|
+
editor.keyDown('Meta')
|
|
538
|
+
editor.pointerDown(0, 0) // in box1
|
|
539
|
+
editor.expectToBeIn('eraser.pointing')
|
|
540
|
+
|
|
541
|
+
expect(editor.getErasingShapeIds()).toEqual([ids.box1])
|
|
542
|
+
|
|
543
|
+
// Try to move pointer - should not start erasing
|
|
544
|
+
editor.pointerMove(50, 50)
|
|
545
|
+
editor.expectToBeIn('eraser.pointing') // Should still be in pointing state
|
|
546
|
+
|
|
547
|
+
editor.pointerUp()
|
|
548
|
+
editor.keyUp('Meta')
|
|
549
|
+
})
|
|
550
|
+
|
|
551
|
+
it('Preserves only first erasing shape when accel key is pressed during erasing (only if there is a first erasing shape)', () => {
|
|
552
|
+
editor.setCurrentTool('eraser')
|
|
553
|
+
editor.expectToBeIn('eraser.idle')
|
|
554
|
+
|
|
555
|
+
const shapesBeforeCount = editor.getCurrentPageShapes().length
|
|
556
|
+
|
|
557
|
+
// Start erasing normally
|
|
558
|
+
editor.pointerDown(-100, -100) // outside of any shapes
|
|
559
|
+
editor.pointerMove(99, 99) // next to box1 AND in box2
|
|
560
|
+
|
|
561
|
+
jest.advanceTimersByTime(16)
|
|
562
|
+
expect(editor.getInstanceState().scribbles.length).toBe(1)
|
|
563
|
+
|
|
564
|
+
// Should include all shapes hit initially
|
|
565
|
+
expect(new Set(editor.getErasingShapeIds())).toEqual(new Set([ids.box1, ids.box2]))
|
|
566
|
+
|
|
567
|
+
// Press accel key during erasing
|
|
568
|
+
editor.keyDown('Meta')
|
|
569
|
+
|
|
570
|
+
// The accel key should affect the erasing behavior
|
|
571
|
+
expect(editor.getErasingShapeIds().length).toBeGreaterThan(0)
|
|
572
|
+
|
|
573
|
+
editor.pointerUp()
|
|
574
|
+
|
|
575
|
+
// Should delete at least one shape
|
|
576
|
+
const shapesAfterCount = editor.getCurrentPageShapes().length
|
|
577
|
+
expect(shapesAfterCount).toBeLessThan(shapesBeforeCount)
|
|
578
|
+
|
|
579
|
+
editor.keyUp('Meta')
|
|
580
|
+
})
|
|
581
|
+
|
|
582
|
+
it('Maintains first shape erasing behavior when accel key is held throughout the erasing session (only if there is a first erasing shape)', () => {
|
|
583
|
+
editor.setCurrentTool('eraser')
|
|
584
|
+
editor.expectToBeIn('eraser.idle')
|
|
585
|
+
|
|
586
|
+
const shapesBeforeCount = editor.getCurrentPageShapes().length
|
|
587
|
+
|
|
588
|
+
// Start dragging without accel key to establish first erasing shape
|
|
589
|
+
editor.pointerDown(-100, -100) // outside of any shapes
|
|
590
|
+
editor.pointerMove(99, 99) // next to box1 AND in box2
|
|
591
|
+
|
|
592
|
+
jest.advanceTimersByTime(16)
|
|
593
|
+
expect(editor.getInstanceState().scribbles.length).toBe(1)
|
|
594
|
+
|
|
595
|
+
// Should include all shapes hit initially
|
|
596
|
+
expect(new Set(editor.getErasingShapeIds())).toEqual(new Set([ids.box1, ids.box2]))
|
|
597
|
+
|
|
598
|
+
// Press accel key to restrict to first shape
|
|
599
|
+
editor.keyDown('Meta')
|
|
600
|
+
expect(editor.getErasingShapeIds().length).toBeGreaterThan(0)
|
|
601
|
+
|
|
602
|
+
// Move to hit more shapes
|
|
603
|
+
editor.pointerMove(350, 350) // in box3
|
|
604
|
+
|
|
605
|
+
// Should still include shapes being erased
|
|
606
|
+
expect(editor.getErasingShapeIds().length).toBeGreaterThan(0)
|
|
607
|
+
|
|
608
|
+
editor.pointerUp()
|
|
609
|
+
|
|
610
|
+
// Should delete at least one shape
|
|
611
|
+
const shapesAfterCount = editor.getCurrentPageShapes().length
|
|
612
|
+
expect(shapesAfterCount).toBeLessThan(shapesBeforeCount)
|
|
613
|
+
|
|
614
|
+
editor.keyUp('Meta')
|
|
445
615
|
})
|
|
446
616
|
})
|
|
@@ -288,11 +288,13 @@ describe('When shapes are overlapping', () => {
|
|
|
288
288
|
editor.pointerDown(0, 50) // over nothing
|
|
289
289
|
editor.pointerMove(125, 50) // over box1 only
|
|
290
290
|
expect(bindings().end).toMatchObject({ toId: ids.box1 })
|
|
291
|
-
editor.pointerMove(175, 50) // box2 is higher but box1 is filled
|
|
291
|
+
editor.pointerMove(175, 50) // box2 is higher but box1 is filled, but we're on the edge ofd box 2
|
|
292
|
+
expect(bindings().end).toMatchObject({ toId: ids.box2 })
|
|
293
|
+
editor.pointerMove(175, 70) // box2 is higher but box1 is filled, and we're inside of box2
|
|
292
294
|
expect(bindings().end).toMatchObject({ toId: ids.box1 })
|
|
293
|
-
editor.pointerMove(225,
|
|
295
|
+
editor.pointerMove(225, 70) // box3 is higher
|
|
294
296
|
expect(bindings().end).toMatchObject({ toId: ids.box3 })
|
|
295
|
-
editor.pointerMove(275,
|
|
297
|
+
editor.pointerMove(275, 70) // box4 is higher but box 3 is filled
|
|
296
298
|
expect(bindings().end).toMatchObject({ toId: ids.box3 })
|
|
297
299
|
})
|
|
298
300
|
|
|
@@ -304,14 +306,18 @@ describe('When shapes are overlapping', () => {
|
|
|
304
306
|
])
|
|
305
307
|
editor.setCurrentTool('arrow')
|
|
306
308
|
editor.pointerDown(0, 50)
|
|
307
|
-
editor.pointerMove(175, 50) // box1 is smaller even though it's behind box2
|
|
309
|
+
editor.pointerMove(175, 50) // box1 is smaller even though it's behind box2, but we're on the edge of box 2
|
|
310
|
+
expect(bindings().end).toMatchObject({ toId: ids.box2 })
|
|
311
|
+
editor.pointerMove(175, 70) // box1 is smaller even though it's behind box2
|
|
308
312
|
expect(bindings().end).toMatchObject({ toId: ids.box1 })
|
|
309
|
-
editor.pointerMove(150, 90) // box3 is smaller and at the front
|
|
313
|
+
editor.pointerMove(150, 90) // box3 is smaller and at the front but we're on the edge of box 2
|
|
314
|
+
expect(bindings().end).toMatchObject({ toId: ids.box2 })
|
|
315
|
+
editor.pointerMove(160, 90) // box3 is smaller and at the front and we're in box1 and box 3 and box 2
|
|
310
316
|
expect(bindings().end).toMatchObject({ toId: ids.box3 })
|
|
311
317
|
editor.sendToBack([ids.box3])
|
|
312
318
|
editor.pointerMove(149, 90) // box3 is smaller, even when at the back
|
|
313
319
|
expect(bindings().end).toMatchObject({ toId: ids.box3 })
|
|
314
|
-
editor.pointerMove(175,
|
|
320
|
+
editor.pointerMove(175, 60) // inside of box1 and box 2, but box 1 is smaller
|
|
315
321
|
expect(bindings().end).toMatchObject({ toId: ids.box1 })
|
|
316
322
|
})
|
|
317
323
|
})
|
|
@@ -0,0 +1,315 @@
|
|
|
1
|
+
import { TLArrowShape, createShapeId } from '@tldraw/editor'
|
|
2
|
+
import { getArrowBindings } from '../lib/shapes/arrow/shared'
|
|
3
|
+
import { TestEditor } from './TestEditor'
|
|
4
|
+
|
|
5
|
+
let editor: TestEditor
|
|
6
|
+
|
|
7
|
+
const ids = {
|
|
8
|
+
solidShape: createShapeId('solidShape'),
|
|
9
|
+
hollowShape: createShapeId('hollowShape'),
|
|
10
|
+
arrow: createShapeId('arrow'),
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
const _arrow = () => editor.getOnlySelectedShape() as TLArrowShape
|
|
14
|
+
|
|
15
|
+
beforeEach(() => {
|
|
16
|
+
editor = new TestEditor()
|
|
17
|
+
})
|
|
18
|
+
|
|
19
|
+
describe('Inner/Outer Margin Shape Detection', () => {
|
|
20
|
+
describe('getShapeAtPoint with inner/outer margins', () => {
|
|
21
|
+
beforeEach(() => {
|
|
22
|
+
// Create a solid filled shape
|
|
23
|
+
editor.createShape({
|
|
24
|
+
id: ids.solidShape,
|
|
25
|
+
type: 'geo',
|
|
26
|
+
x: 100,
|
|
27
|
+
y: 100,
|
|
28
|
+
props: {
|
|
29
|
+
w: 100,
|
|
30
|
+
h: 100,
|
|
31
|
+
fill: 'solid',
|
|
32
|
+
},
|
|
33
|
+
})
|
|
34
|
+
|
|
35
|
+
// Create a hollow shape on top (same position, smaller size)
|
|
36
|
+
editor.createShape({
|
|
37
|
+
id: ids.hollowShape,
|
|
38
|
+
type: 'geo',
|
|
39
|
+
x: 125,
|
|
40
|
+
y: 125,
|
|
41
|
+
props: {
|
|
42
|
+
w: 50,
|
|
43
|
+
h: 50,
|
|
44
|
+
// No fill property - defaults to 'none' (hollow)
|
|
45
|
+
},
|
|
46
|
+
})
|
|
47
|
+
})
|
|
48
|
+
|
|
49
|
+
it('should support inner/outer margin options', () => {
|
|
50
|
+
// Test that the new margin options are accepted
|
|
51
|
+
const point = { x: 150, y: 150 }
|
|
52
|
+
|
|
53
|
+
// Test with array margin [innerMargin, outerMargin]
|
|
54
|
+
const arrayMarginHit = editor.getShapeAtPoint(point, {
|
|
55
|
+
hitInside: true,
|
|
56
|
+
margin: [8, 4],
|
|
57
|
+
})
|
|
58
|
+
expect(arrayMarginHit).toBeDefined()
|
|
59
|
+
|
|
60
|
+
// Test with insideMargin option
|
|
61
|
+
const insideMarginHit = editor.getShapeAtPoint(point, {
|
|
62
|
+
hitInside: true,
|
|
63
|
+
})
|
|
64
|
+
expect(insideMarginHit).toBeDefined()
|
|
65
|
+
|
|
66
|
+
// Test with single number margin (should use same for both)
|
|
67
|
+
const singleMarginHit = editor.getShapeAtPoint(point, {
|
|
68
|
+
hitInside: true,
|
|
69
|
+
margin: 8,
|
|
70
|
+
})
|
|
71
|
+
expect(singleMarginHit).toBeDefined()
|
|
72
|
+
})
|
|
73
|
+
|
|
74
|
+
it('should respect hitInside option for hollow shapes', () => {
|
|
75
|
+
const point = { x: 150, y: 150 }
|
|
76
|
+
|
|
77
|
+
// Without hitInside, should not hit hollow shape
|
|
78
|
+
const noHitInsideHit = editor.getShapeAtPoint(point, {
|
|
79
|
+
margin: [8, 0],
|
|
80
|
+
})
|
|
81
|
+
expect(noHitInsideHit?.id).toBe(ids.solidShape)
|
|
82
|
+
|
|
83
|
+
// With hitInside, should be able to hit hollow shape
|
|
84
|
+
const withHitInsideHit = editor.getShapeAtPoint(point, {
|
|
85
|
+
hitInside: true,
|
|
86
|
+
margin: [8, 0],
|
|
87
|
+
})
|
|
88
|
+
expect(withHitInsideHit).toBeDefined()
|
|
89
|
+
})
|
|
90
|
+
|
|
91
|
+
it('should handle edge cases correctly', () => {
|
|
92
|
+
// Test point exactly on the edge of hollow shape
|
|
93
|
+
const edgePoint = { x: 125, y: 150 }
|
|
94
|
+
|
|
95
|
+
const edgeHit = editor.getShapeAtPoint(edgePoint, {
|
|
96
|
+
hitInside: true,
|
|
97
|
+
margin: [8, 8],
|
|
98
|
+
})
|
|
99
|
+
expect(edgeHit).toBeDefined()
|
|
100
|
+
})
|
|
101
|
+
})
|
|
102
|
+
|
|
103
|
+
describe('Arrow binding with inner/outer margins', () => {
|
|
104
|
+
beforeEach(() => {
|
|
105
|
+
// Create a solid filled shape
|
|
106
|
+
editor.createShape({
|
|
107
|
+
id: ids.solidShape,
|
|
108
|
+
type: 'geo',
|
|
109
|
+
x: 100,
|
|
110
|
+
y: 100,
|
|
111
|
+
props: {
|
|
112
|
+
w: 100,
|
|
113
|
+
h: 100,
|
|
114
|
+
fill: 'solid',
|
|
115
|
+
},
|
|
116
|
+
})
|
|
117
|
+
|
|
118
|
+
// Create a hollow shape on top (same position, smaller size)
|
|
119
|
+
editor.createShape({
|
|
120
|
+
id: ids.hollowShape,
|
|
121
|
+
type: 'geo',
|
|
122
|
+
x: 125,
|
|
123
|
+
y: 125,
|
|
124
|
+
props: {
|
|
125
|
+
w: 50,
|
|
126
|
+
h: 50,
|
|
127
|
+
// No fill property - defaults to 'none' (hollow)
|
|
128
|
+
},
|
|
129
|
+
})
|
|
130
|
+
})
|
|
131
|
+
|
|
132
|
+
it('should create arrow bindings with inner/outer margin support', () => {
|
|
133
|
+
editor.setCurrentTool('arrow')
|
|
134
|
+
|
|
135
|
+
// Start arrow outside both shapes
|
|
136
|
+
editor.pointerDown(50, 150)
|
|
137
|
+
|
|
138
|
+
// Move to center of hollow shape (which overlaps solid shape)
|
|
139
|
+
editor.pointerMove(150, 150)
|
|
140
|
+
editor.pointerUp()
|
|
141
|
+
|
|
142
|
+
const createdArrow = editor
|
|
143
|
+
.getCurrentPageShapes()
|
|
144
|
+
.find((s) => s.type === 'arrow') as TLArrowShape
|
|
145
|
+
expect(createdArrow).toBeDefined()
|
|
146
|
+
|
|
147
|
+
const arrowBindings = getArrowBindings(editor, createdArrow)
|
|
148
|
+
expect(arrowBindings.end).toBeDefined()
|
|
149
|
+
// The binding should be to one of the shapes
|
|
150
|
+
expect([ids.solidShape, ids.hollowShape]).toContain(arrowBindings.end?.toId)
|
|
151
|
+
})
|
|
152
|
+
|
|
153
|
+
it('should bind to solid shape when no hollow shape is present', () => {
|
|
154
|
+
// Remove the hollow shape
|
|
155
|
+
editor.deleteShape(ids.hollowShape)
|
|
156
|
+
|
|
157
|
+
editor.setCurrentTool('arrow')
|
|
158
|
+
|
|
159
|
+
// Start arrow outside shape
|
|
160
|
+
editor.pointerDown(50, 150)
|
|
161
|
+
|
|
162
|
+
// Move to center of solid shape
|
|
163
|
+
editor.pointerMove(150, 150)
|
|
164
|
+
editor.pointerUp()
|
|
165
|
+
|
|
166
|
+
const createdArrow = editor
|
|
167
|
+
.getCurrentPageShapes()
|
|
168
|
+
.find((s) => s.type === 'arrow') as TLArrowShape
|
|
169
|
+
expect(createdArrow).toBeDefined()
|
|
170
|
+
|
|
171
|
+
const arrowBindings = getArrowBindings(editor, createdArrow)
|
|
172
|
+
expect(arrowBindings.end).toBeDefined()
|
|
173
|
+
expect(arrowBindings.end?.toId).toBe(ids.solidShape)
|
|
174
|
+
})
|
|
175
|
+
})
|
|
176
|
+
|
|
177
|
+
describe('Complex overlapping scenarios', () => {
|
|
178
|
+
it('should handle multiple overlapping shapes correctly', () => {
|
|
179
|
+
// Create multiple shapes with different fill states
|
|
180
|
+
editor.createShape({
|
|
181
|
+
id: ids.solidShape,
|
|
182
|
+
type: 'geo',
|
|
183
|
+
x: 100,
|
|
184
|
+
y: 100,
|
|
185
|
+
props: {
|
|
186
|
+
w: 100,
|
|
187
|
+
h: 100,
|
|
188
|
+
fill: 'solid',
|
|
189
|
+
},
|
|
190
|
+
})
|
|
191
|
+
|
|
192
|
+
editor.createShape({
|
|
193
|
+
id: ids.hollowShape,
|
|
194
|
+
type: 'geo',
|
|
195
|
+
x: 125,
|
|
196
|
+
y: 125,
|
|
197
|
+
props: {
|
|
198
|
+
w: 50,
|
|
199
|
+
h: 50,
|
|
200
|
+
// No fill property - defaults to 'none' (hollow)
|
|
201
|
+
},
|
|
202
|
+
})
|
|
203
|
+
|
|
204
|
+
// Create another hollow shape
|
|
205
|
+
const hollowShape2 = createShapeId('hollowShape2')
|
|
206
|
+
editor.createShape({
|
|
207
|
+
id: hollowShape2,
|
|
208
|
+
type: 'geo',
|
|
209
|
+
x: 140,
|
|
210
|
+
y: 140,
|
|
211
|
+
props: {
|
|
212
|
+
w: 30,
|
|
213
|
+
h: 30,
|
|
214
|
+
// No fill property - defaults to 'none' (hollow)
|
|
215
|
+
},
|
|
216
|
+
})
|
|
217
|
+
|
|
218
|
+
// Test point in the innermost hollow shape
|
|
219
|
+
const point = { x: 155, y: 155 }
|
|
220
|
+
|
|
221
|
+
const hit = editor.getShapeAtPoint(point, {
|
|
222
|
+
hitInside: true,
|
|
223
|
+
margin: [8, 8],
|
|
224
|
+
})
|
|
225
|
+
expect(hit).toBeDefined()
|
|
226
|
+
// Should hit one of the shapes
|
|
227
|
+
expect([ids.solidShape, ids.hollowShape, hollowShape2]).toContain(hit?.id)
|
|
228
|
+
})
|
|
229
|
+
|
|
230
|
+
it('should handle shapes with different geometries', () => {
|
|
231
|
+
// Create a solid rectangle
|
|
232
|
+
editor.createShape({
|
|
233
|
+
id: ids.solidShape,
|
|
234
|
+
type: 'geo',
|
|
235
|
+
x: 100,
|
|
236
|
+
y: 100,
|
|
237
|
+
props: {
|
|
238
|
+
w: 100,
|
|
239
|
+
h: 100,
|
|
240
|
+
fill: 'solid',
|
|
241
|
+
},
|
|
242
|
+
})
|
|
243
|
+
|
|
244
|
+
// Create a hollow circle on top
|
|
245
|
+
editor.createShape({
|
|
246
|
+
id: ids.hollowShape,
|
|
247
|
+
type: 'geo',
|
|
248
|
+
x: 125,
|
|
249
|
+
y: 125,
|
|
250
|
+
props: {
|
|
251
|
+
w: 50,
|
|
252
|
+
h: 50,
|
|
253
|
+
geo: 'ellipse',
|
|
254
|
+
// No fill property - defaults to 'none' (hollow)
|
|
255
|
+
},
|
|
256
|
+
})
|
|
257
|
+
|
|
258
|
+
// Test point inside the circle
|
|
259
|
+
const point = { x: 150, y: 150 }
|
|
260
|
+
|
|
261
|
+
const hit = editor.getShapeAtPoint(point, {
|
|
262
|
+
hitInside: true,
|
|
263
|
+
margin: [8, 8],
|
|
264
|
+
})
|
|
265
|
+
expect(hit).toBeDefined()
|
|
266
|
+
// Should hit one of the shapes
|
|
267
|
+
expect([ids.solidShape, ids.hollowShape]).toContain(hit?.id)
|
|
268
|
+
})
|
|
269
|
+
})
|
|
270
|
+
|
|
271
|
+
describe('Regression tests for the original bug', () => {
|
|
272
|
+
it('should support binding to hollow shapes when overlapping solid shapes', () => {
|
|
273
|
+
// This test verifies that the infrastructure exists for the bug fix
|
|
274
|
+
// Create a solid shape
|
|
275
|
+
editor.createShape({
|
|
276
|
+
id: ids.solidShape,
|
|
277
|
+
type: 'geo',
|
|
278
|
+
x: 100,
|
|
279
|
+
y: 100,
|
|
280
|
+
props: {
|
|
281
|
+
w: 100,
|
|
282
|
+
h: 100,
|
|
283
|
+
fill: 'solid',
|
|
284
|
+
},
|
|
285
|
+
})
|
|
286
|
+
|
|
287
|
+
// Create a hollow shape on top
|
|
288
|
+
editor.createShape({
|
|
289
|
+
id: ids.hollowShape,
|
|
290
|
+
type: 'geo',
|
|
291
|
+
x: 125,
|
|
292
|
+
y: 125,
|
|
293
|
+
props: {
|
|
294
|
+
w: 50,
|
|
295
|
+
h: 50,
|
|
296
|
+
// No fill property - defaults to 'none' (hollow)
|
|
297
|
+
},
|
|
298
|
+
})
|
|
299
|
+
|
|
300
|
+
// Test that we can detect both shapes
|
|
301
|
+
const point = { x: 150, y: 150 }
|
|
302
|
+
|
|
303
|
+
// Should be able to hit the solid shape without hitInside
|
|
304
|
+
const solidHit = editor.getShapeAtPoint(point)
|
|
305
|
+
expect(solidHit?.id).toBe(ids.solidShape)
|
|
306
|
+
|
|
307
|
+
// Should be able to hit the hollow shape with hitInside and inner margin
|
|
308
|
+
const hollowHit = editor.getShapeAtPoint(point, {
|
|
309
|
+
hitInside: true,
|
|
310
|
+
margin: [8, 0],
|
|
311
|
+
})
|
|
312
|
+
expect(hollowHit).toBeDefined()
|
|
313
|
+
})
|
|
314
|
+
})
|
|
315
|
+
})
|