tldraw 3.16.0-canary.f55016ece635 → 3.16.0-canary.f5bf2b535ea7
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 +227 -110
- package/dist-cjs/index.js +29 -14
- package/dist-cjs/index.js.map +2 -2
- package/dist-cjs/lib/defaultExternalContentHandlers.js +10 -0
- package/dist-cjs/lib/defaultExternalContentHandlers.js.map +2 -2
- package/dist-cjs/lib/shapes/arrow/arrow-types.js.map +1 -1
- package/dist-cjs/lib/shapes/arrow/arrowLabel.js +6 -0
- package/dist-cjs/lib/shapes/arrow/arrowLabel.js.map +3 -3
- package/dist-cjs/lib/shapes/arrow/arrowTargetState.js +3 -2
- package/dist-cjs/lib/shapes/arrow/arrowTargetState.js.map +2 -2
- package/dist-cjs/lib/shapes/arrow/toolStates/Pointing.js +1 -1
- package/dist-cjs/lib/shapes/arrow/toolStates/Pointing.js.map +2 -2
- package/dist-cjs/lib/shapes/bookmark/BookmarkShapeUtil.js +4 -4
- package/dist-cjs/lib/shapes/bookmark/BookmarkShapeUtil.js.map +2 -2
- package/dist-cjs/lib/shapes/frame/FrameShapeUtil.js +8 -1
- package/dist-cjs/lib/shapes/frame/FrameShapeUtil.js.map +2 -2
- package/dist-cjs/lib/shapes/frame/components/FrameLabelInput.js +8 -2
- package/dist-cjs/lib/shapes/frame/components/FrameLabelInput.js.map +2 -2
- package/dist-cjs/lib/shapes/geo/GeoShapeUtil.js +1 -0
- package/dist-cjs/lib/shapes/geo/GeoShapeUtil.js.map +2 -2
- package/dist-cjs/lib/shapes/image/ImageShapeUtil.js +3 -0
- package/dist-cjs/lib/shapes/image/ImageShapeUtil.js.map +2 -2
- package/dist-cjs/lib/shapes/note/NoteShapeUtil.js +2 -1
- package/dist-cjs/lib/shapes/note/NoteShapeUtil.js.map +2 -2
- package/dist-cjs/lib/shapes/shared/HyperlinkButton.js +4 -4
- package/dist-cjs/lib/shapes/shared/HyperlinkButton.js.map +2 -2
- package/dist-cjs/lib/shapes/shared/PlainTextLabel.js +1 -3
- package/dist-cjs/lib/shapes/shared/PlainTextLabel.js.map +2 -2
- package/dist-cjs/lib/shapes/shared/useEditablePlainText.js +3 -5
- package/dist-cjs/lib/shapes/shared/useEditablePlainText.js.map +2 -2
- package/dist-cjs/lib/shapes/shared/useImageOrVideoAsset.js +0 -2
- package/dist-cjs/lib/shapes/shared/useImageOrVideoAsset.js.map +2 -2
- package/dist-cjs/lib/shapes/text/PlainTextArea.js +3 -2
- package/dist-cjs/lib/shapes/text/PlainTextArea.js.map +2 -2
- package/dist-cjs/lib/shapes/text/RichTextArea.js +3 -3
- package/dist-cjs/lib/shapes/text/RichTextArea.js.map +2 -2
- package/dist-cjs/lib/tools/SelectTool/childStates/DraggingHandle.js +3 -1
- package/dist-cjs/lib/tools/SelectTool/childStates/DraggingHandle.js.map +2 -2
- package/dist-cjs/lib/ui/components/A11y.js +1 -1
- package/dist-cjs/lib/ui/components/A11y.js.map +2 -2
- package/dist-cjs/lib/ui/components/KeyboardShortcutsDialog/DefaultKeyboardShortcutsDialogContent.js +5 -5
- package/dist-cjs/lib/ui/components/KeyboardShortcutsDialog/DefaultKeyboardShortcutsDialogContent.js.map +1 -1
- package/dist-cjs/lib/ui/components/LanguageMenu.js +1 -0
- package/dist-cjs/lib/ui/components/LanguageMenu.js.map +2 -2
- package/dist-cjs/lib/ui/components/Minimap/DefaultMinimap.js +2 -1
- package/dist-cjs/lib/ui/components/Minimap/DefaultMinimap.js.map +2 -2
- package/dist-cjs/lib/ui/components/PageMenu/DefaultPageMenu.js +1 -1
- package/dist-cjs/lib/ui/components/PageMenu/DefaultPageMenu.js.map +2 -2
- package/dist-cjs/lib/ui/components/StylePanel/DefaultStylePanel.js +9 -4
- package/dist-cjs/lib/ui/components/StylePanel/DefaultStylePanel.js.map +2 -2
- package/dist-cjs/lib/ui/components/StylePanel/DefaultStylePanelContent.js +255 -316
- package/dist-cjs/lib/ui/components/StylePanel/DefaultStylePanelContent.js.map +2 -2
- package/dist-cjs/lib/ui/components/StylePanel/StylePanelButtonPicker.js +147 -0
- package/dist-cjs/lib/ui/components/StylePanel/StylePanelButtonPicker.js.map +7 -0
- package/dist-cjs/lib/ui/components/StylePanel/StylePanelContext.js +68 -0
- package/dist-cjs/lib/ui/components/StylePanel/StylePanelContext.js.map +7 -0
- package/dist-cjs/lib/ui/components/StylePanel/{DoubleDropdownPicker.js → StylePanelDoubleDropdownPicker.js} +23 -22
- package/dist-cjs/lib/ui/components/StylePanel/StylePanelDoubleDropdownPicker.js.map +7 -0
- package/dist-cjs/lib/ui/components/StylePanel/{DropdownPicker.js → StylePanelDropdownPicker.js} +23 -20
- package/dist-cjs/lib/ui/components/StylePanel/StylePanelDropdownPicker.js.map +7 -0
- package/dist-cjs/lib/ui/components/StylePanel/StylePanelSubheading.js +28 -0
- package/dist-cjs/lib/ui/components/StylePanel/StylePanelSubheading.js.map +7 -0
- package/dist-cjs/lib/ui/components/Toolbar/AltTextEditor.js +2 -0
- package/dist-cjs/lib/ui/components/Toolbar/AltTextEditor.js.map +2 -2
- package/dist-cjs/lib/ui/components/Toolbar/DefaultImageToolbarContent.js +38 -9
- package/dist-cjs/lib/ui/components/Toolbar/DefaultImageToolbarContent.js.map +2 -2
- package/dist-cjs/lib/ui/components/Toolbar/DefaultVideoToolbarContent.js +15 -3
- package/dist-cjs/lib/ui/components/Toolbar/DefaultVideoToolbarContent.js.map +2 -2
- package/dist-cjs/lib/ui/components/Toolbar/LinkEditor.js +2 -1
- package/dist-cjs/lib/ui/components/Toolbar/LinkEditor.js.map +2 -2
- package/dist-cjs/lib/ui/components/Toolbar/OverflowingToolbar.js +1 -1
- package/dist-cjs/lib/ui/components/Toolbar/OverflowingToolbar.js.map +2 -2
- package/dist-cjs/lib/ui/components/Toolbar/ToggleToolLockedButton.js +6 -2
- package/dist-cjs/lib/ui/components/Toolbar/ToggleToolLockedButton.js.map +2 -2
- package/dist-cjs/lib/ui/components/primitives/TldrawUiContextualToolbar.js +11 -2
- package/dist-cjs/lib/ui/components/primitives/TldrawUiContextualToolbar.js.map +2 -2
- package/dist-cjs/lib/ui/components/primitives/TldrawUiInput.js +5 -3
- package/dist-cjs/lib/ui/components/primitives/TldrawUiInput.js.map +2 -2
- package/dist-cjs/lib/ui/components/primitives/TldrawUiSlider.js +18 -5
- package/dist-cjs/lib/ui/components/primitives/TldrawUiSlider.js.map +2 -2
- package/dist-cjs/lib/ui/components/primitives/TldrawUiToolbar.js +3 -0
- package/dist-cjs/lib/ui/components/primitives/TldrawUiToolbar.js.map +2 -2
- package/dist-cjs/lib/ui/components/primitives/TldrawUiTooltip.js +47 -3
- package/dist-cjs/lib/ui/components/primitives/TldrawUiTooltip.js.map +2 -2
- package/dist-cjs/lib/ui/components/primitives/menus/TldrawUiMenuCheckboxItem.js +3 -0
- package/dist-cjs/lib/ui/components/primitives/menus/TldrawUiMenuCheckboxItem.js.map +2 -2
- package/dist-cjs/lib/ui/components/primitives/menus/TldrawUiMenuItem.js +8 -8
- package/dist-cjs/lib/ui/components/primitives/menus/TldrawUiMenuItem.js.map +2 -2
- package/dist-cjs/lib/ui/context/actions.js +13 -8
- package/dist-cjs/lib/ui/context/actions.js.map +2 -2
- package/dist-cjs/lib/ui/context/events.js.map +1 -1
- package/dist-cjs/lib/ui/hooks/useClipboardEvents.js +1 -1
- package/dist-cjs/lib/ui/hooks/useClipboardEvents.js.map +2 -2
- package/dist-cjs/lib/ui/hooks/useExportAs.js +3 -2
- package/dist-cjs/lib/ui/hooks/useExportAs.js.map +2 -2
- package/dist-cjs/lib/ui/hooks/useTools.js +1 -1
- 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 +6 -2
- package/dist-cjs/lib/ui/hooks/useTranslation/defaultTranslation.js.map +2 -2
- package/dist-cjs/lib/ui/kbd-utils.js +9 -3
- package/dist-cjs/lib/ui/kbd-utils.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/export/copyAs.js +1 -2
- package/dist-cjs/lib/utils/export/copyAs.js.map +2 -2
- package/dist-cjs/lib/utils/export/export.js +0 -20
- package/dist-cjs/lib/utils/export/export.js.map +2 -2
- package/dist-cjs/lib/utils/export/exportAs.js +1 -2
- package/dist-cjs/lib/utils/export/exportAs.js.map +2 -2
- package/dist-esm/index.d.mts +227 -110
- package/dist-esm/index.mjs +59 -28
- package/dist-esm/index.mjs.map +2 -2
- package/dist-esm/lib/defaultExternalContentHandlers.mjs +10 -0
- package/dist-esm/lib/defaultExternalContentHandlers.mjs.map +2 -2
- package/dist-esm/lib/shapes/arrow/arrowLabel.mjs +6 -0
- package/dist-esm/lib/shapes/arrow/arrowLabel.mjs.map +3 -3
- package/dist-esm/lib/shapes/arrow/arrowTargetState.mjs +3 -2
- package/dist-esm/lib/shapes/arrow/arrowTargetState.mjs.map +2 -2
- package/dist-esm/lib/shapes/arrow/toolStates/Pointing.mjs +1 -1
- package/dist-esm/lib/shapes/arrow/toolStates/Pointing.mjs.map +2 -2
- package/dist-esm/lib/shapes/bookmark/BookmarkShapeUtil.mjs +4 -5
- package/dist-esm/lib/shapes/bookmark/BookmarkShapeUtil.mjs.map +2 -2
- package/dist-esm/lib/shapes/frame/FrameShapeUtil.mjs +8 -1
- package/dist-esm/lib/shapes/frame/FrameShapeUtil.mjs.map +2 -2
- package/dist-esm/lib/shapes/frame/components/FrameLabelInput.mjs +9 -3
- package/dist-esm/lib/shapes/frame/components/FrameLabelInput.mjs.map +2 -2
- package/dist-esm/lib/shapes/geo/GeoShapeUtil.mjs +1 -0
- package/dist-esm/lib/shapes/geo/GeoShapeUtil.mjs.map +2 -2
- package/dist-esm/lib/shapes/image/ImageShapeUtil.mjs +3 -0
- package/dist-esm/lib/shapes/image/ImageShapeUtil.mjs.map +2 -2
- package/dist-esm/lib/shapes/note/NoteShapeUtil.mjs +2 -1
- package/dist-esm/lib/shapes/note/NoteShapeUtil.mjs.map +2 -2
- package/dist-esm/lib/shapes/shared/HyperlinkButton.mjs +5 -5
- package/dist-esm/lib/shapes/shared/HyperlinkButton.mjs.map +2 -2
- package/dist-esm/lib/shapes/shared/PlainTextLabel.mjs +1 -3
- package/dist-esm/lib/shapes/shared/PlainTextLabel.mjs.map +2 -2
- package/dist-esm/lib/shapes/shared/useEditablePlainText.mjs +3 -6
- package/dist-esm/lib/shapes/shared/useEditablePlainText.mjs.map +2 -2
- package/dist-esm/lib/shapes/shared/useImageOrVideoAsset.mjs +0 -2
- package/dist-esm/lib/shapes/shared/useImageOrVideoAsset.mjs.map +2 -2
- package/dist-esm/lib/shapes/text/PlainTextArea.mjs +4 -3
- package/dist-esm/lib/shapes/text/PlainTextArea.mjs.map +2 -2
- package/dist-esm/lib/shapes/text/RichTextArea.mjs +3 -4
- package/dist-esm/lib/shapes/text/RichTextArea.mjs.map +2 -2
- package/dist-esm/lib/tools/SelectTool/childStates/DraggingHandle.mjs +3 -1
- package/dist-esm/lib/tools/SelectTool/childStates/DraggingHandle.mjs.map +2 -2
- package/dist-esm/lib/ui/components/A11y.mjs +1 -2
- package/dist-esm/lib/ui/components/A11y.mjs.map +2 -2
- package/dist-esm/lib/ui/components/KeyboardShortcutsDialog/DefaultKeyboardShortcutsDialogContent.mjs +5 -5
- package/dist-esm/lib/ui/components/KeyboardShortcutsDialog/DefaultKeyboardShortcutsDialogContent.mjs.map +1 -1
- package/dist-esm/lib/ui/components/LanguageMenu.mjs +1 -0
- package/dist-esm/lib/ui/components/LanguageMenu.mjs.map +2 -2
- package/dist-esm/lib/ui/components/Minimap/DefaultMinimap.mjs +2 -1
- package/dist-esm/lib/ui/components/Minimap/DefaultMinimap.mjs.map +2 -2
- package/dist-esm/lib/ui/components/PageMenu/DefaultPageMenu.mjs +1 -2
- package/dist-esm/lib/ui/components/PageMenu/DefaultPageMenu.mjs.map +2 -2
- package/dist-esm/lib/ui/components/StylePanel/DefaultStylePanel.mjs +14 -5
- package/dist-esm/lib/ui/components/StylePanel/DefaultStylePanel.mjs.map +2 -2
- package/dist-esm/lib/ui/components/StylePanel/DefaultStylePanelContent.mjs +257 -320
- package/dist-esm/lib/ui/components/StylePanel/DefaultStylePanelContent.mjs.map +2 -2
- package/dist-esm/lib/ui/components/StylePanel/StylePanelButtonPicker.mjs +135 -0
- package/dist-esm/lib/ui/components/StylePanel/StylePanelButtonPicker.mjs.map +7 -0
- package/dist-esm/lib/ui/components/StylePanel/StylePanelContext.mjs +48 -0
- package/dist-esm/lib/ui/components/StylePanel/StylePanelContext.mjs.map +7 -0
- package/dist-esm/lib/ui/components/StylePanel/{DoubleDropdownPicker.mjs → StylePanelDoubleDropdownPicker.mjs} +20 -19
- package/dist-esm/lib/ui/components/StylePanel/StylePanelDoubleDropdownPicker.mjs.map +7 -0
- package/dist-esm/lib/ui/components/StylePanel/{DropdownPicker.mjs → StylePanelDropdownPicker.mjs} +20 -17
- package/dist-esm/lib/ui/components/StylePanel/StylePanelDropdownPicker.mjs.map +7 -0
- package/dist-esm/lib/ui/components/StylePanel/StylePanelSubheading.mjs +8 -0
- package/dist-esm/lib/ui/components/StylePanel/StylePanelSubheading.mjs.map +7 -0
- package/dist-esm/lib/ui/components/Toolbar/AltTextEditor.mjs +2 -0
- package/dist-esm/lib/ui/components/Toolbar/AltTextEditor.mjs.map +2 -2
- package/dist-esm/lib/ui/components/Toolbar/DefaultImageToolbarContent.mjs +38 -9
- package/dist-esm/lib/ui/components/Toolbar/DefaultImageToolbarContent.mjs.map +2 -2
- package/dist-esm/lib/ui/components/Toolbar/DefaultVideoToolbarContent.mjs +15 -3
- package/dist-esm/lib/ui/components/Toolbar/DefaultVideoToolbarContent.mjs.map +2 -2
- package/dist-esm/lib/ui/components/Toolbar/LinkEditor.mjs +2 -1
- package/dist-esm/lib/ui/components/Toolbar/LinkEditor.mjs.map +2 -2
- package/dist-esm/lib/ui/components/Toolbar/OverflowingToolbar.mjs +1 -1
- package/dist-esm/lib/ui/components/Toolbar/OverflowingToolbar.mjs.map +2 -2
- package/dist-esm/lib/ui/components/Toolbar/ToggleToolLockedButton.mjs +6 -2
- package/dist-esm/lib/ui/components/Toolbar/ToggleToolLockedButton.mjs.map +2 -2
- package/dist-esm/lib/ui/components/primitives/TldrawUiContextualToolbar.mjs +11 -3
- package/dist-esm/lib/ui/components/primitives/TldrawUiContextualToolbar.mjs.map +2 -2
- package/dist-esm/lib/ui/components/primitives/TldrawUiInput.mjs +6 -4
- package/dist-esm/lib/ui/components/primitives/TldrawUiInput.mjs.map +2 -2
- package/dist-esm/lib/ui/components/primitives/TldrawUiSlider.mjs +18 -5
- package/dist-esm/lib/ui/components/primitives/TldrawUiSlider.mjs.map +2 -2
- package/dist-esm/lib/ui/components/primitives/TldrawUiToolbar.mjs +3 -0
- package/dist-esm/lib/ui/components/primitives/TldrawUiToolbar.mjs.map +2 -2
- package/dist-esm/lib/ui/components/primitives/TldrawUiTooltip.mjs +48 -3
- package/dist-esm/lib/ui/components/primitives/TldrawUiTooltip.mjs.map +2 -2
- package/dist-esm/lib/ui/components/primitives/menus/TldrawUiMenuCheckboxItem.mjs +3 -0
- package/dist-esm/lib/ui/components/primitives/menus/TldrawUiMenuCheckboxItem.mjs.map +2 -2
- package/dist-esm/lib/ui/components/primitives/menus/TldrawUiMenuItem.mjs +8 -8
- package/dist-esm/lib/ui/components/primitives/menus/TldrawUiMenuItem.mjs.map +2 -2
- package/dist-esm/lib/ui/context/actions.mjs +13 -8
- package/dist-esm/lib/ui/context/actions.mjs.map +2 -2
- package/dist-esm/lib/ui/context/events.mjs.map +1 -1
- package/dist-esm/lib/ui/hooks/useClipboardEvents.mjs +1 -2
- package/dist-esm/lib/ui/hooks/useClipboardEvents.mjs.map +2 -2
- package/dist-esm/lib/ui/hooks/useExportAs.mjs +3 -2
- package/dist-esm/lib/ui/hooks/useExportAs.mjs.map +2 -2
- package/dist-esm/lib/ui/hooks/useTools.mjs +1 -1
- package/dist-esm/lib/ui/hooks/useTools.mjs.map +2 -2
- package/dist-esm/lib/ui/hooks/useTranslation/defaultTranslation.mjs +6 -2
- package/dist-esm/lib/ui/hooks/useTranslation/defaultTranslation.mjs.map +2 -2
- package/dist-esm/lib/ui/kbd-utils.mjs +9 -3
- package/dist-esm/lib/ui/kbd-utils.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/export/copyAs.mjs +1 -2
- package/dist-esm/lib/utils/export/copyAs.mjs.map +2 -2
- package/dist-esm/lib/utils/export/export.mjs +0 -20
- package/dist-esm/lib/utils/export/export.mjs.map +2 -2
- package/dist-esm/lib/utils/export/exportAs.mjs +1 -2
- package/dist-esm/lib/utils/export/exportAs.mjs.map +2 -2
- package/package.json +3 -3
- package/src/index.ts +45 -21
- package/src/lib/defaultExternalContentHandlers.ts +14 -0
- package/src/lib/shapes/arrow/ArrowShapeOptions.test.ts +83 -13
- package/src/lib/shapes/arrow/ArrowShapeTool.test.ts +99 -5
- package/src/lib/shapes/arrow/ArrowShapeUtil.test.ts +41 -0
- package/src/lib/shapes/arrow/arrow-types.ts +3 -5
- package/src/lib/shapes/arrow/arrowLabel.ts +8 -0
- package/src/lib/shapes/arrow/arrowTargetState.ts +34 -3
- package/src/lib/shapes/arrow/toolStates/Pointing.tsx +1 -1
- package/src/lib/shapes/bookmark/BookmarkShapeUtil.tsx +4 -5
- package/src/lib/shapes/frame/FrameShapeUtil.tsx +9 -0
- package/src/lib/shapes/frame/components/FrameLabelInput.tsx +10 -3
- package/src/lib/shapes/geo/GeoShapeUtil.tsx +1 -0
- package/src/lib/shapes/image/ImageShapeUtil.tsx +3 -0
- package/src/lib/shapes/note/NoteShapeUtil.tsx +1 -0
- package/src/lib/shapes/shared/HyperlinkButton.tsx +5 -5
- package/src/lib/shapes/shared/PlainTextLabel.tsx +0 -6
- package/src/lib/shapes/shared/useEditablePlainText.ts +3 -10
- package/src/lib/shapes/shared/useImageOrVideoAsset.ts +0 -7
- package/src/lib/shapes/text/PlainTextArea.tsx +4 -3
- package/src/lib/shapes/text/RichTextArea.tsx +3 -4
- package/src/lib/tools/SelectTool/childStates/DraggingHandle.tsx +6 -2
- package/src/lib/ui/components/A11y.tsx +1 -2
- package/src/lib/ui/components/KeyboardShortcutsDialog/DefaultKeyboardShortcutsDialogContent.tsx +5 -5
- package/src/lib/ui/components/LanguageMenu.tsx +1 -0
- package/src/lib/ui/components/Minimap/DefaultMinimap.tsx +2 -1
- package/src/lib/ui/components/PageMenu/DefaultPageMenu.tsx +1 -2
- package/src/lib/ui/components/StylePanel/DefaultStylePanel.tsx +27 -13
- package/src/lib/ui/components/StylePanel/DefaultStylePanelContent.tsx +260 -381
- package/src/lib/ui/components/{primitives/TldrawUiButtonPicker.tsx → StylePanel/StylePanelButtonPicker.tsx} +70 -50
- package/src/lib/ui/components/StylePanel/StylePanelContext.tsx +63 -0
- package/src/lib/ui/components/StylePanel/{DoubleDropdownPicker.tsx → StylePanelDoubleDropdownPicker.tsx} +28 -19
- package/src/lib/ui/components/StylePanel/StylePanelDropdownPicker.tsx +119 -0
- package/src/lib/ui/components/StylePanel/StylePanelSubheading.tsx +9 -0
- package/src/lib/ui/components/Toolbar/AltTextEditor.tsx +2 -0
- package/src/lib/ui/components/Toolbar/DefaultImageToolbarContent.tsx +32 -15
- package/src/lib/ui/components/Toolbar/DefaultVideoToolbarContent.tsx +12 -4
- package/src/lib/ui/components/Toolbar/LinkEditor.tsx +1 -0
- package/src/lib/ui/components/Toolbar/OverflowingToolbar.tsx +1 -1
- package/src/lib/ui/components/Toolbar/ToggleToolLockedButton.tsx +9 -2
- package/src/lib/ui/components/primitives/TldrawUiContextualToolbar.tsx +7 -3
- package/src/lib/ui/components/primitives/TldrawUiInput.tsx +6 -3
- package/src/lib/ui/components/primitives/TldrawUiSlider.tsx +52 -32
- package/src/lib/ui/components/primitives/TldrawUiToolbar.tsx +5 -1
- package/src/lib/ui/components/primitives/TldrawUiTooltip.tsx +67 -13
- package/src/lib/ui/components/primitives/menus/TldrawUiMenuCheckboxItem.tsx +4 -0
- package/src/lib/ui/components/primitives/menus/TldrawUiMenuItem.tsx +9 -9
- package/src/lib/ui/context/actions.tsx +20 -8
- package/src/lib/ui/context/events.tsx +1 -1
- package/src/lib/ui/hooks/useClipboardEvents.ts +1 -2
- package/src/lib/ui/hooks/useExportAs.ts +3 -2
- package/src/lib/ui/hooks/useTools.tsx +1 -1
- package/src/lib/ui/hooks/useTranslation/TLUiTranslationKey.ts +4 -0
- package/src/lib/ui/hooks/useTranslation/defaultTranslation.ts +6 -2
- package/src/lib/ui/kbd-utils.ts +10 -3
- package/src/lib/ui/version.ts +3 -3
- package/src/lib/ui.css +40 -3
- package/src/lib/utils/export/copyAs.ts +1 -24
- package/src/lib/utils/export/export.ts +0 -36
- package/src/lib/utils/export/exportAs.ts +1 -32
- package/src/test/TestEditor.ts +8 -2
- package/src/test/custom-clipping.test.ts +436 -0
- package/src/test/frames.test.ts +15 -0
- package/src/test/getCulledShapes.test.tsx +71 -2
- package/tldraw.css +48 -6
- package/dist-cjs/lib/ui/components/StylePanel/DoubleDropdownPicker.js.map +0 -7
- package/dist-cjs/lib/ui/components/StylePanel/DropdownPicker.js.map +0 -7
- package/dist-cjs/lib/ui/components/primitives/TldrawUiButtonPicker.js +0 -131
- package/dist-cjs/lib/ui/components/primitives/TldrawUiButtonPicker.js.map +0 -7
- package/dist-esm/lib/ui/components/StylePanel/DoubleDropdownPicker.mjs.map +0 -7
- package/dist-esm/lib/ui/components/StylePanel/DropdownPicker.mjs.map +0 -7
- package/dist-esm/lib/ui/components/primitives/TldrawUiButtonPicker.mjs +0 -115
- package/dist-esm/lib/ui/components/primitives/TldrawUiButtonPicker.mjs.map +0 -7
- package/src/lib/ui/components/StylePanel/DropdownPicker.tsx +0 -110
package/src/lib/ui/kbd-utils.ts
CHANGED
|
@@ -31,9 +31,16 @@ export function kbd(str: string) {
|
|
|
31
31
|
)
|
|
32
32
|
.flat()
|
|
33
33
|
.map((sub, index) => {
|
|
34
|
-
if (sub === '
|
|
35
|
-
|
|
36
|
-
|
|
34
|
+
if (sub[0] === '+') return []
|
|
35
|
+
|
|
36
|
+
let modifiedKey
|
|
37
|
+
if (sub === '__CTRL__') {
|
|
38
|
+
modifiedKey = 'Ctrl'
|
|
39
|
+
} else if (sub === '__ALT__') {
|
|
40
|
+
modifiedKey = 'Alt'
|
|
41
|
+
} else {
|
|
42
|
+
modifiedKey = sub[0].toUpperCase() + sub.slice(1)
|
|
43
|
+
}
|
|
37
44
|
return tlenv.isDarwin || !index ? modifiedKey : ['+', modifiedKey]
|
|
38
45
|
})
|
|
39
46
|
.flat()
|
package/src/lib/ui/version.ts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
// This file is automatically generated by internal/scripts/refresh-assets.ts.
|
|
2
2
|
// Do not edit manually. Or do, I'm a comment, not a cop.
|
|
3
3
|
|
|
4
|
-
export const version = '3.16.0-canary.
|
|
4
|
+
export const version = '3.16.0-canary.f5bf2b535ea7'
|
|
5
5
|
export const publishDates = {
|
|
6
6
|
major: '2024-09-13T14:36:29.063Z',
|
|
7
|
-
minor: '2025-
|
|
8
|
-
patch: '2025-
|
|
7
|
+
minor: '2025-09-18T13:10:54.219Z',
|
|
8
|
+
patch: '2025-09-18T13:10:54.219Z',
|
|
9
9
|
}
|
package/src/lib/ui.css
CHANGED
|
@@ -168,7 +168,7 @@
|
|
|
168
168
|
min-height: 40px;
|
|
169
169
|
width: 100%;
|
|
170
170
|
gap: 8px;
|
|
171
|
-
margin: -4px
|
|
171
|
+
margin-top: -4px;
|
|
172
172
|
}
|
|
173
173
|
|
|
174
174
|
.tlui-button__menu::after {
|
|
@@ -494,6 +494,10 @@
|
|
|
494
494
|
-webkit-user-select: auto !important;
|
|
495
495
|
}
|
|
496
496
|
|
|
497
|
+
.tlui-input::placeholder {
|
|
498
|
+
color: var(--tl-color-text-3);
|
|
499
|
+
}
|
|
500
|
+
|
|
497
501
|
.tlui-input__wrapper {
|
|
498
502
|
width: 100%;
|
|
499
503
|
height: 44px;
|
|
@@ -578,6 +582,12 @@
|
|
|
578
582
|
box-shadow: var(--tl-shadow-3);
|
|
579
583
|
}
|
|
580
584
|
|
|
585
|
+
@media (max-height: 600px) {
|
|
586
|
+
.tlui-menu {
|
|
587
|
+
max-height: 70vh;
|
|
588
|
+
}
|
|
589
|
+
}
|
|
590
|
+
|
|
581
591
|
.tlui-menu::-webkit-scrollbar {
|
|
582
592
|
display: none;
|
|
583
593
|
}
|
|
@@ -1007,21 +1017,48 @@
|
|
|
1007
1017
|
flex-direction: column;
|
|
1008
1018
|
}
|
|
1009
1019
|
|
|
1010
|
-
|
|
1020
|
+
/*
|
|
1021
|
+
add a border to the bottom of all but the last section. we have to handle empty sections too, which
|
|
1022
|
+
are hidden and shouldn't be counted
|
|
1023
|
+
*/
|
|
1024
|
+
.tlui-style-panel__section:not(:nth-last-child(-n + 1 of .tlui-style-panel__section:not(:empty))) {
|
|
1011
1025
|
border-bottom: 1px solid var(--tl-color-divider);
|
|
1012
1026
|
}
|
|
1027
|
+
/*
|
|
1028
|
+
if a section ends with a slider and we're adding a border, we need some extra space for visual
|
|
1029
|
+
balance. we need to handle empty sections as above. is this the most complex css selector in all of
|
|
1030
|
+
tldraw? probably.
|
|
1031
|
+
*/
|
|
1032
|
+
.tlui-style-panel__section:has(.tlui-slider__container:last-child):not(
|
|
1033
|
+
:nth-last-child(-n + 1 of .tlui-style-panel__section:not(:empty))
|
|
1034
|
+
) {
|
|
1035
|
+
margin-bottom: 7px;
|
|
1036
|
+
}
|
|
1013
1037
|
|
|
1014
1038
|
.tlui-style-panel__section:empty {
|
|
1015
1039
|
display: none;
|
|
1016
1040
|
}
|
|
1017
1041
|
|
|
1042
|
+
/*
|
|
1043
|
+
* This is used in a couple places, like Align and Vertical Align.
|
|
1044
|
+
* It's because we have a toolbar with a Toggle Group but then an adjacent button
|
|
1045
|
+
* next to it that opens a popup.
|
|
1046
|
+
*/
|
|
1047
|
+
.tlui-style-panel__section .tlui-toolbar:has(.tlui-toolbar) {
|
|
1048
|
+
flex-wrap: wrap;
|
|
1049
|
+
}
|
|
1050
|
+
|
|
1051
|
+
.tlui-style-panel__section .tlui-toolbar:has(.tlui-toolbar) .tlui-style-panel__subheading {
|
|
1052
|
+
margin-left: -2px;
|
|
1053
|
+
}
|
|
1054
|
+
|
|
1018
1055
|
.tlui-style-panel__section__common:not(:only-child) {
|
|
1019
1056
|
margin-bottom: 7px;
|
|
1020
1057
|
border-bottom: 1px solid var(--tl-color-divider);
|
|
1021
1058
|
}
|
|
1022
1059
|
|
|
1023
1060
|
.tlui-style-panel__dropdown-picker:only-child {
|
|
1024
|
-
|
|
1061
|
+
flex: 1;
|
|
1025
1062
|
}
|
|
1026
1063
|
|
|
1027
1064
|
.tlui-style-panel__double-select-picker {
|
|
@@ -31,30 +31,7 @@ export interface CopyAsOptions extends TLImageExportOptions {
|
|
|
31
31
|
*
|
|
32
32
|
* @public
|
|
33
33
|
*/
|
|
34
|
-
export function copyAs(editor: Editor, ids: TLShapeId[], opts: CopyAsOptions): Promise<void>
|
|
35
|
-
/**
|
|
36
|
-
* @deprecated The format parameter is now part of the opts object.
|
|
37
|
-
* @public
|
|
38
|
-
*/
|
|
39
|
-
export function copyAs(
|
|
40
|
-
editor: Editor,
|
|
41
|
-
ids: TLShapeId[],
|
|
42
|
-
format: TLCopyType,
|
|
43
|
-
opts?: TLImageExportOptions & { format?: undefined }
|
|
44
|
-
): Promise<void>
|
|
45
|
-
export function copyAs(
|
|
46
|
-
...args:
|
|
47
|
-
| [editor: Editor, ids: TLShapeId[], opts: TLImageExportOptions & { format: TLCopyType }]
|
|
48
|
-
| [
|
|
49
|
-
editor: Editor,
|
|
50
|
-
ids: TLShapeId[],
|
|
51
|
-
format: TLCopyType,
|
|
52
|
-
opts?: TLImageExportOptions & { format?: undefined },
|
|
53
|
-
]
|
|
54
|
-
) {
|
|
55
|
-
const [editor, ids, opts] =
|
|
56
|
-
typeof args[2] === 'string' ? [args[0], args[1], { ...args[3], format: args[2] }] : args
|
|
57
|
-
|
|
34
|
+
export function copyAs(editor: Editor, ids: TLShapeId[], opts: CopyAsOptions): Promise<void> {
|
|
58
35
|
// Note: it's important that this function itself isn't async and doesn't really use promises -
|
|
59
36
|
// we need to create the relevant `ClipboardItem`s and call navigator.clipboard.write
|
|
60
37
|
// synchronously to make sure safari knows that the user _wants_ to copy See
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
Editor,
|
|
3
3
|
FileHelpers,
|
|
4
|
-
TLExportType,
|
|
5
4
|
TLImageExportOptions,
|
|
6
5
|
TLShapeId,
|
|
7
6
|
exhaustiveSwitchError,
|
|
@@ -35,41 +34,6 @@ export async function exportToString(
|
|
|
35
34
|
}
|
|
36
35
|
}
|
|
37
36
|
|
|
38
|
-
/**
|
|
39
|
-
* Export the given shapes as a blob.
|
|
40
|
-
* @param editor - The editor instance.
|
|
41
|
-
* @param ids - The ids of the shapes to export.
|
|
42
|
-
* @param format - The format to export as.
|
|
43
|
-
* @param opts - Rendering options.
|
|
44
|
-
* @returns A promise that resolves to a blob.
|
|
45
|
-
* @deprecated Use {@link @tldraw/editor#Editor.toImage} instead.
|
|
46
|
-
* @public
|
|
47
|
-
*/
|
|
48
|
-
export async function exportToBlob({
|
|
49
|
-
editor,
|
|
50
|
-
ids,
|
|
51
|
-
format,
|
|
52
|
-
opts = {},
|
|
53
|
-
}: {
|
|
54
|
-
editor: Editor
|
|
55
|
-
ids: TLShapeId[]
|
|
56
|
-
format: TLExportType
|
|
57
|
-
opts?: TLImageExportOptions
|
|
58
|
-
}): Promise<Blob> {
|
|
59
|
-
const idsToUse = ids?.length ? ids : [...editor.getCurrentPageShapeIds()]
|
|
60
|
-
switch (format) {
|
|
61
|
-
case 'jpeg':
|
|
62
|
-
case 'png':
|
|
63
|
-
case 'webp':
|
|
64
|
-
case 'svg': {
|
|
65
|
-
return (await editor.toImage(idsToUse, { ...opts, format })).blob
|
|
66
|
-
}
|
|
67
|
-
default: {
|
|
68
|
-
exhaustiveSwitchError(format)
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
|
|
73
37
|
const clipboardMimeTypesByFormat = {
|
|
74
38
|
jpeg: 'image/jpeg',
|
|
75
39
|
png: 'image/png',
|
|
@@ -28,38 +28,7 @@ export async function exportAs(
|
|
|
28
28
|
editor: Editor,
|
|
29
29
|
ids: TLShapeId[],
|
|
30
30
|
opts: ExportAsOptions
|
|
31
|
-
): Promise<void>
|
|
32
|
-
/**
|
|
33
|
-
* @deprecated The format & name parameters are now part of the opts object.
|
|
34
|
-
* @public
|
|
35
|
-
*/
|
|
36
|
-
export async function exportAs(
|
|
37
|
-
editor: Editor,
|
|
38
|
-
ids: TLShapeId[],
|
|
39
|
-
format?: TLExportType,
|
|
40
|
-
name?: string,
|
|
41
|
-
opts?: TLImageExportOptions
|
|
42
|
-
): Promise<void>
|
|
43
|
-
export async function exportAs(
|
|
44
|
-
...args:
|
|
45
|
-
| [
|
|
46
|
-
editor: Editor,
|
|
47
|
-
ids: TLShapeId[],
|
|
48
|
-
opts: TLImageExportOptions & { format: TLExportType; name?: string },
|
|
49
|
-
]
|
|
50
|
-
| [
|
|
51
|
-
editor: Editor,
|
|
52
|
-
ids: TLShapeId[],
|
|
53
|
-
format?: TLExportType,
|
|
54
|
-
name?: string,
|
|
55
|
-
opts?: TLImageExportOptions,
|
|
56
|
-
]
|
|
57
|
-
) {
|
|
58
|
-
const [editor, ids, opts] =
|
|
59
|
-
typeof args[2] === 'object'
|
|
60
|
-
? args
|
|
61
|
-
: [args[0], args[1], { ...args[4], format: args[2] ?? 'png', name: args[3] }]
|
|
62
|
-
|
|
31
|
+
): Promise<void> {
|
|
63
32
|
// If we don't get name then use a predefined one
|
|
64
33
|
let name = opts.name
|
|
65
34
|
if (!name) {
|
package/src/test/TestEditor.ts
CHANGED
|
@@ -86,8 +86,14 @@ export class TestEditor extends Editor {
|
|
|
86
86
|
elm.tabIndex = 0
|
|
87
87
|
elm.getBoundingClientRect = () => bounds as DOMRect
|
|
88
88
|
|
|
89
|
-
const shapeUtilsWithDefaults = [
|
|
90
|
-
|
|
89
|
+
const shapeUtilsWithDefaults = [
|
|
90
|
+
...defaultShapeUtils.filter((s) => !options.shapeUtils?.some((su) => su.type === s.type)),
|
|
91
|
+
...(options.shapeUtils ?? []),
|
|
92
|
+
]
|
|
93
|
+
const bindingUtilsWithDefaults = [
|
|
94
|
+
...defaultBindingUtils.filter((b) => !options.bindingUtils?.some((bu) => bu.type === b.type)),
|
|
95
|
+
...(options.bindingUtils ?? []),
|
|
96
|
+
]
|
|
91
97
|
|
|
92
98
|
super({
|
|
93
99
|
...options,
|
|
@@ -0,0 +1,436 @@
|
|
|
1
|
+
import {
|
|
2
|
+
atom,
|
|
3
|
+
BaseBoxShapeUtil,
|
|
4
|
+
Circle2d,
|
|
5
|
+
createShapeId,
|
|
6
|
+
Geometry2d,
|
|
7
|
+
RecordProps,
|
|
8
|
+
resizeBox,
|
|
9
|
+
StateNode,
|
|
10
|
+
T,
|
|
11
|
+
TLBaseShape,
|
|
12
|
+
TLEventHandlers,
|
|
13
|
+
TLGeoShape,
|
|
14
|
+
TLResizeInfo,
|
|
15
|
+
TLShape,
|
|
16
|
+
TLTextShape,
|
|
17
|
+
toRichText,
|
|
18
|
+
Vec,
|
|
19
|
+
} from '@tldraw/editor'
|
|
20
|
+
import { TestEditor } from './TestEditor'
|
|
21
|
+
|
|
22
|
+
// Custom Circle Clip Shape Definition
|
|
23
|
+
export type CircleClipShape = TLBaseShape<
|
|
24
|
+
'circle-clip',
|
|
25
|
+
{
|
|
26
|
+
w: number
|
|
27
|
+
h: number
|
|
28
|
+
}
|
|
29
|
+
>
|
|
30
|
+
|
|
31
|
+
export const isClippingEnabled$ = atom('isClippingEnabled', true)
|
|
32
|
+
|
|
33
|
+
export class CircleClipShapeUtil extends BaseBoxShapeUtil<CircleClipShape> {
|
|
34
|
+
static override type = 'circle-clip' as const
|
|
35
|
+
static override props: RecordProps<CircleClipShape> = {
|
|
36
|
+
w: T.number,
|
|
37
|
+
h: T.number,
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
override canBind() {
|
|
41
|
+
return false
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
override canReceiveNewChildrenOfType(shape: TLShape) {
|
|
45
|
+
return !shape.isLocked
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
override getDefaultProps(): CircleClipShape['props'] {
|
|
49
|
+
return {
|
|
50
|
+
w: 200,
|
|
51
|
+
h: 200,
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
override getGeometry(shape: CircleClipShape): Geometry2d {
|
|
56
|
+
const radius = Math.min(shape.props.w, shape.props.h) / 2
|
|
57
|
+
return new Circle2d({
|
|
58
|
+
radius,
|
|
59
|
+
x: shape.props.w / 2 - radius,
|
|
60
|
+
y: shape.props.h / 2 - radius,
|
|
61
|
+
isFilled: true,
|
|
62
|
+
})
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
override getClipPath(shape: CircleClipShape): Vec[] | undefined {
|
|
66
|
+
// Generate a polygon approximation of the circle
|
|
67
|
+
const centerX = shape.props.w / 2
|
|
68
|
+
const centerY = shape.props.h / 2
|
|
69
|
+
const radius = Math.min(shape.props.w, shape.props.h) / 2
|
|
70
|
+
const segments = 48 // More segments = smoother circle
|
|
71
|
+
|
|
72
|
+
const points: Vec[] = []
|
|
73
|
+
for (let i = 0; i < segments; i++) {
|
|
74
|
+
const angle = (i / segments) * Math.PI * 2
|
|
75
|
+
const x = centerX + Math.cos(angle) * radius
|
|
76
|
+
const y = centerY + Math.sin(angle) * radius
|
|
77
|
+
points.push(new Vec(x, y))
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
return points
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
override shouldClipChild(_child: TLShape): boolean {
|
|
84
|
+
// For now, clip all children - we removed the onlyClipText feature for simplicity
|
|
85
|
+
return isClippingEnabled$.get()
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
override component(_shape: CircleClipShape) {
|
|
89
|
+
// For testing purposes, we'll just return null
|
|
90
|
+
// In a real implementation, this would return JSX
|
|
91
|
+
return null as any
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
override indicator(_shape: CircleClipShape) {
|
|
95
|
+
// For testing purposes, we'll just return null
|
|
96
|
+
// In a real implementation, this would return JSX
|
|
97
|
+
return null as any
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
override onResize(shape: CircleClipShape, info: TLResizeInfo<CircleClipShape>) {
|
|
101
|
+
return resizeBox(shape, info)
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
export class CircleClipShapeTool extends StateNode {
|
|
106
|
+
static override id = 'circle-clip'
|
|
107
|
+
|
|
108
|
+
override onEnter(): void {
|
|
109
|
+
this.editor.setCursor({ type: 'cross', rotation: 0 })
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
override onPointerDown(info: Parameters<TLEventHandlers['onPointerDown']>[0]) {
|
|
113
|
+
if (info.target === 'canvas') {
|
|
114
|
+
const { originPagePoint } = this.editor.inputs
|
|
115
|
+
|
|
116
|
+
this.editor.createShape<CircleClipShape>({
|
|
117
|
+
type: 'circle-clip',
|
|
118
|
+
x: originPagePoint.x - 100,
|
|
119
|
+
y: originPagePoint.y - 100,
|
|
120
|
+
props: {
|
|
121
|
+
w: 200,
|
|
122
|
+
h: 200,
|
|
123
|
+
},
|
|
124
|
+
})
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
let editor: TestEditor
|
|
130
|
+
|
|
131
|
+
afterEach(() => {
|
|
132
|
+
editor?.dispose()
|
|
133
|
+
})
|
|
134
|
+
|
|
135
|
+
const ids = {
|
|
136
|
+
circleClip1: createShapeId('circleClip1'),
|
|
137
|
+
circleClip2: createShapeId('circleClip2'),
|
|
138
|
+
text1: createShapeId('text1'),
|
|
139
|
+
geo1: createShapeId('geo1'),
|
|
140
|
+
geo2: createShapeId('geo2'),
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
beforeEach(() => {
|
|
144
|
+
editor = new TestEditor({
|
|
145
|
+
shapeUtils: [CircleClipShapeUtil],
|
|
146
|
+
tools: [CircleClipShapeTool],
|
|
147
|
+
})
|
|
148
|
+
|
|
149
|
+
// Reset clipping state
|
|
150
|
+
isClippingEnabled$.set(true)
|
|
151
|
+
})
|
|
152
|
+
|
|
153
|
+
describe('CircleClipShapeUtil', () => {
|
|
154
|
+
describe('shape creation and properties', () => {
|
|
155
|
+
it('should create a circle clip shape with default properties', () => {
|
|
156
|
+
editor.createShape<CircleClipShape>({
|
|
157
|
+
id: ids.circleClip1,
|
|
158
|
+
type: 'circle-clip',
|
|
159
|
+
x: 100,
|
|
160
|
+
y: 100,
|
|
161
|
+
props: {
|
|
162
|
+
w: 200,
|
|
163
|
+
h: 200,
|
|
164
|
+
},
|
|
165
|
+
})
|
|
166
|
+
|
|
167
|
+
const shape = editor.getShape<CircleClipShape>(ids.circleClip1)
|
|
168
|
+
expect(shape).toBeDefined()
|
|
169
|
+
expect(shape!.type).toBe('circle-clip')
|
|
170
|
+
expect(shape!.props.w).toBe(200)
|
|
171
|
+
expect(shape!.props.h).toBe(200)
|
|
172
|
+
})
|
|
173
|
+
|
|
174
|
+
it('should use default props when not specified', () => {
|
|
175
|
+
editor.createShape<CircleClipShape>({
|
|
176
|
+
id: ids.circleClip1,
|
|
177
|
+
type: 'circle-clip',
|
|
178
|
+
x: 100,
|
|
179
|
+
y: 100,
|
|
180
|
+
props: {},
|
|
181
|
+
})
|
|
182
|
+
|
|
183
|
+
const shape = editor.getShape<CircleClipShape>(ids.circleClip1)
|
|
184
|
+
expect(shape!.props.w).toBe(200) // default from getDefaultProps
|
|
185
|
+
expect(shape!.props.h).toBe(200) // default from getDefaultProps
|
|
186
|
+
})
|
|
187
|
+
})
|
|
188
|
+
|
|
189
|
+
describe('geometry and clipping', () => {
|
|
190
|
+
it('should generate correct circle geometry', () => {
|
|
191
|
+
editor.createShape<CircleClipShape>({
|
|
192
|
+
id: ids.circleClip1,
|
|
193
|
+
type: 'circle-clip',
|
|
194
|
+
x: 100,
|
|
195
|
+
y: 100,
|
|
196
|
+
props: {
|
|
197
|
+
w: 200,
|
|
198
|
+
h: 200,
|
|
199
|
+
},
|
|
200
|
+
})
|
|
201
|
+
|
|
202
|
+
const shape = editor.getShape<CircleClipShape>(ids.circleClip1)
|
|
203
|
+
const util = editor.getShapeUtil<CircleClipShape>('circle-clip')
|
|
204
|
+
const geometry = util.getGeometry(shape!)
|
|
205
|
+
|
|
206
|
+
expect(geometry).toBeDefined()
|
|
207
|
+
expect(geometry.bounds).toBeDefined()
|
|
208
|
+
expect(geometry.bounds.width).toBe(200)
|
|
209
|
+
expect(geometry.bounds.height).toBe(200)
|
|
210
|
+
})
|
|
211
|
+
|
|
212
|
+
it('should generate clip path for circle', () => {
|
|
213
|
+
editor.createShape<CircleClipShape>({
|
|
214
|
+
id: ids.circleClip1,
|
|
215
|
+
type: 'circle-clip',
|
|
216
|
+
x: 100,
|
|
217
|
+
y: 100,
|
|
218
|
+
props: {
|
|
219
|
+
w: 200,
|
|
220
|
+
h: 200,
|
|
221
|
+
},
|
|
222
|
+
})
|
|
223
|
+
|
|
224
|
+
const shape = editor.getShape<CircleClipShape>(ids.circleClip1)
|
|
225
|
+
const util = editor.getShapeUtil<CircleClipShape>('circle-clip')
|
|
226
|
+
const clipPath = util.getClipPath?.(shape!)
|
|
227
|
+
if (!clipPath) throw new Error('Clip path is undefined')
|
|
228
|
+
|
|
229
|
+
expect(clipPath).toBeDefined()
|
|
230
|
+
expect(Array.isArray(clipPath)).toBe(true)
|
|
231
|
+
expect(clipPath.length).toBeGreaterThan(0)
|
|
232
|
+
|
|
233
|
+
// Should be a polygon approximation of a circle
|
|
234
|
+
// Check that points are roughly in a circle pattern
|
|
235
|
+
const centerX = 100 // shape.x
|
|
236
|
+
const centerY = 100 // shape.y
|
|
237
|
+
const radius = 100 // min(w, h) / 2
|
|
238
|
+
|
|
239
|
+
clipPath.forEach((point) => {
|
|
240
|
+
const distance = Math.sqrt(Math.pow(point.x - centerX, 2) + Math.pow(point.y - centerY, 2))
|
|
241
|
+
expect(distance).toBeCloseTo(radius, 0)
|
|
242
|
+
})
|
|
243
|
+
})
|
|
244
|
+
})
|
|
245
|
+
|
|
246
|
+
describe('child clipping behavior', () => {
|
|
247
|
+
it('should clip children when clipping is enabled', () => {
|
|
248
|
+
editor.createShape<CircleClipShape>({
|
|
249
|
+
id: ids.circleClip1,
|
|
250
|
+
type: 'circle-clip',
|
|
251
|
+
x: 100,
|
|
252
|
+
y: 100,
|
|
253
|
+
props: {
|
|
254
|
+
w: 200,
|
|
255
|
+
h: 200,
|
|
256
|
+
},
|
|
257
|
+
})
|
|
258
|
+
|
|
259
|
+
editor.createShape<TLTextShape>({
|
|
260
|
+
id: ids.text1,
|
|
261
|
+
type: 'text',
|
|
262
|
+
x: 0,
|
|
263
|
+
y: 0,
|
|
264
|
+
parentId: ids.circleClip1,
|
|
265
|
+
props: {
|
|
266
|
+
richText: toRichText('Test text'),
|
|
267
|
+
},
|
|
268
|
+
})
|
|
269
|
+
|
|
270
|
+
const util = editor.getShapeUtil<CircleClipShape>('circle-clip')
|
|
271
|
+
const textShape = editor.getShape<TLTextShape>(ids.text1)
|
|
272
|
+
|
|
273
|
+
// Clipping should be enabled by default
|
|
274
|
+
expect(isClippingEnabled$.get()).toBe(true)
|
|
275
|
+
expect(util.shouldClipChild?.(textShape!)).toBe(true)
|
|
276
|
+
expect(editor.getShapeClipPath(ids.text1)).toBeDefined()
|
|
277
|
+
})
|
|
278
|
+
|
|
279
|
+
it('should not clip children when clipping is disabled', () => {
|
|
280
|
+
isClippingEnabled$.set(false)
|
|
281
|
+
|
|
282
|
+
editor.createShape<CircleClipShape>({
|
|
283
|
+
id: ids.circleClip1,
|
|
284
|
+
type: 'circle-clip',
|
|
285
|
+
x: 100,
|
|
286
|
+
y: 100,
|
|
287
|
+
props: {
|
|
288
|
+
w: 200,
|
|
289
|
+
h: 200,
|
|
290
|
+
},
|
|
291
|
+
})
|
|
292
|
+
|
|
293
|
+
editor.createShape<TLTextShape>({
|
|
294
|
+
id: ids.text1,
|
|
295
|
+
type: 'text',
|
|
296
|
+
x: 0,
|
|
297
|
+
y: 0,
|
|
298
|
+
parentId: ids.circleClip1,
|
|
299
|
+
props: {
|
|
300
|
+
richText: toRichText('Test text'),
|
|
301
|
+
},
|
|
302
|
+
})
|
|
303
|
+
|
|
304
|
+
const util = editor.getShapeUtil<CircleClipShape>('circle-clip')
|
|
305
|
+
const textShape = editor.getShape<TLTextShape>(ids.text1)
|
|
306
|
+
|
|
307
|
+
expect(isClippingEnabled$.get()).toBe(false)
|
|
308
|
+
expect(util.shouldClipChild?.(textShape!)).toBe(false)
|
|
309
|
+
expect(editor.getShapeClipPath(ids.text1)).toBeUndefined()
|
|
310
|
+
})
|
|
311
|
+
})
|
|
312
|
+
})
|
|
313
|
+
|
|
314
|
+
describe('Integration tests', () => {
|
|
315
|
+
it('should create and manage circle clip shapes with children', () => {
|
|
316
|
+
// Create circle clip shape
|
|
317
|
+
editor.createShape<CircleClipShape>({
|
|
318
|
+
id: ids.circleClip1,
|
|
319
|
+
type: 'circle-clip',
|
|
320
|
+
x: 100,
|
|
321
|
+
y: 100,
|
|
322
|
+
props: {
|
|
323
|
+
w: 200,
|
|
324
|
+
h: 200,
|
|
325
|
+
},
|
|
326
|
+
})
|
|
327
|
+
|
|
328
|
+
// Add text child
|
|
329
|
+
editor.createShape<TLTextShape>({
|
|
330
|
+
id: ids.text1,
|
|
331
|
+
type: 'text',
|
|
332
|
+
x: 50,
|
|
333
|
+
y: 50,
|
|
334
|
+
parentId: ids.circleClip1,
|
|
335
|
+
props: {
|
|
336
|
+
richText: toRichText('Clipped text'),
|
|
337
|
+
},
|
|
338
|
+
})
|
|
339
|
+
|
|
340
|
+
// Add geo child
|
|
341
|
+
editor.createShape<TLGeoShape>({
|
|
342
|
+
id: ids.geo1,
|
|
343
|
+
type: 'geo',
|
|
344
|
+
x: 150,
|
|
345
|
+
y: 150,
|
|
346
|
+
parentId: ids.circleClip1,
|
|
347
|
+
props: {
|
|
348
|
+
w: 50,
|
|
349
|
+
h: 50,
|
|
350
|
+
},
|
|
351
|
+
})
|
|
352
|
+
|
|
353
|
+
const circleClipShape = editor.getShape<CircleClipShape>(ids.circleClip1)
|
|
354
|
+
const textShape = editor.getShape<TLTextShape>(ids.text1)
|
|
355
|
+
const geoShape = editor.getShape<TLGeoShape>(ids.geo1)
|
|
356
|
+
|
|
357
|
+
expect(circleClipShape).toBeDefined()
|
|
358
|
+
expect(textShape!.parentId).toBe(ids.circleClip1)
|
|
359
|
+
expect(geoShape!.parentId).toBe(ids.circleClip1)
|
|
360
|
+
|
|
361
|
+
// Verify clipping behavior
|
|
362
|
+
const util = editor.getShapeUtil<CircleClipShape>('circle-clip')
|
|
363
|
+
expect(util.shouldClipChild?.(textShape!)).toBe(true)
|
|
364
|
+
expect(util.shouldClipChild?.(geoShape!)).toBe(true)
|
|
365
|
+
expect(editor.getShapeClipPath(ids.text1)).toBeDefined()
|
|
366
|
+
expect(editor.getShapeClipPath(ids.geo1)).toBeDefined()
|
|
367
|
+
|
|
368
|
+
// Test clipping toggle
|
|
369
|
+
isClippingEnabled$.set(false)
|
|
370
|
+
expect(util.shouldClipChild?.(textShape!)).toBe(false)
|
|
371
|
+
expect(util.shouldClipChild?.(geoShape!)).toBe(false)
|
|
372
|
+
expect(editor.getShapeClipPath(ids.text1)).toBeUndefined()
|
|
373
|
+
expect(editor.getShapeClipPath(ids.geo1)).toBeUndefined()
|
|
374
|
+
})
|
|
375
|
+
|
|
376
|
+
it('should handle multiple circle clip shapes independently', () => {
|
|
377
|
+
// Create two circle clip shapes
|
|
378
|
+
editor.createShape<CircleClipShape>({
|
|
379
|
+
id: ids.circleClip1,
|
|
380
|
+
type: 'circle-clip',
|
|
381
|
+
x: 100,
|
|
382
|
+
y: 100,
|
|
383
|
+
props: {
|
|
384
|
+
w: 200,
|
|
385
|
+
h: 200,
|
|
386
|
+
},
|
|
387
|
+
})
|
|
388
|
+
|
|
389
|
+
editor.createShape<CircleClipShape>({
|
|
390
|
+
id: ids.circleClip2,
|
|
391
|
+
type: 'circle-clip',
|
|
392
|
+
x: 400,
|
|
393
|
+
y: 100,
|
|
394
|
+
props: {
|
|
395
|
+
w: 150,
|
|
396
|
+
h: 150,
|
|
397
|
+
},
|
|
398
|
+
})
|
|
399
|
+
|
|
400
|
+
// Add children to both
|
|
401
|
+
editor.createShape<TLTextShape>({
|
|
402
|
+
id: ids.text1,
|
|
403
|
+
type: 'text',
|
|
404
|
+
x: 0,
|
|
405
|
+
y: 0,
|
|
406
|
+
parentId: ids.circleClip1,
|
|
407
|
+
props: {
|
|
408
|
+
richText: toRichText('First clip'),
|
|
409
|
+
},
|
|
410
|
+
})
|
|
411
|
+
|
|
412
|
+
editor.createShape<TLTextShape>({
|
|
413
|
+
id: ids.geo1,
|
|
414
|
+
type: 'text',
|
|
415
|
+
x: 0,
|
|
416
|
+
y: 0,
|
|
417
|
+
parentId: ids.circleClip2,
|
|
418
|
+
props: {
|
|
419
|
+
richText: toRichText('Second clip'),
|
|
420
|
+
},
|
|
421
|
+
})
|
|
422
|
+
|
|
423
|
+
const util = editor.getShapeUtil<CircleClipShape>('circle-clip')
|
|
424
|
+
const text1 = editor.getShape<TLTextShape>(ids.text1)
|
|
425
|
+
const text2 = editor.getShape<TLTextShape>(ids.geo1)
|
|
426
|
+
|
|
427
|
+
// Both should be clipped when enabled
|
|
428
|
+
expect(util.shouldClipChild?.(text1!)).toBe(true)
|
|
429
|
+
expect(util.shouldClipChild?.(text2!)).toBe(true)
|
|
430
|
+
|
|
431
|
+
// Both should not be clipped when disabled
|
|
432
|
+
isClippingEnabled$.set(false)
|
|
433
|
+
expect(util.shouldClipChild?.(text1!)).toBe(false)
|
|
434
|
+
expect(util.shouldClipChild?.(text2!)).toBe(false)
|
|
435
|
+
})
|
|
436
|
+
})
|
package/src/test/frames.test.ts
CHANGED
|
@@ -1680,3 +1680,18 @@ it('drops into the top-most frame, if there is one', () => {
|
|
|
1680
1680
|
|
|
1681
1681
|
expect(editor.getShape(rect)?.parentId).toBe(editor.getCurrentPageId())
|
|
1682
1682
|
})
|
|
1683
|
+
|
|
1684
|
+
it('does not get drop children of nested frame if they are occluded from the outer frame', () => {
|
|
1685
|
+
const frame1Id = dragCreateFrame({ down: [100, 100], move: [300, 300], up: [300, 300] })
|
|
1686
|
+
const frame2Id = dragCreateFrame({ down: [150, 150], move: [290, 290], up: [290, 290] })
|
|
1687
|
+
|
|
1688
|
+
const rect1 = createRect({ pos: [280, 160], size: [10, 30] })
|
|
1689
|
+
|
|
1690
|
+
expect(editor.getShape(rect1)?.parentId).toBe(frame2Id)
|
|
1691
|
+
expect(editor.getShape(frame2Id)?.parentId).toBe(frame1Id)
|
|
1692
|
+
|
|
1693
|
+
editor.select(frame2Id)
|
|
1694
|
+
editor.translateSelection(30, 0)
|
|
1695
|
+
|
|
1696
|
+
expect(editor.getShape(rect1)?.parentId).toBe(frame2Id)
|
|
1697
|
+
})
|