tldraw 4.1.0-canary.9c36de6e611c → 4.1.0-canary.a152954244d2
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 +46 -12
- package/dist-cjs/index.js +6 -1
- package/dist-cjs/index.js.map +2 -2
- package/dist-cjs/lib/canvas/TldrawCropHandles.js +1 -1
- package/dist-cjs/lib/canvas/TldrawScribble.js +1 -1
- package/dist-cjs/lib/canvas/TldrawSelectionForeground.js +1 -1
- package/dist-cjs/lib/defaultEmbedDefinitions.js +25 -30
- package/dist-cjs/lib/defaultEmbedDefinitions.js.map +2 -2
- package/dist-cjs/lib/defaultExternalContentHandlers.js +10 -33
- package/dist-cjs/lib/defaultExternalContentHandlers.js.map +2 -2
- package/dist-cjs/lib/shapes/arrow/ArrowShapeUtil.js +3 -0
- package/dist-cjs/lib/shapes/arrow/ArrowShapeUtil.js.map +2 -2
- package/dist-cjs/lib/shapes/arrow/curved-arrow.js +8 -2
- package/dist-cjs/lib/shapes/arrow/curved-arrow.js.map +2 -2
- package/dist-cjs/lib/shapes/arrow/straight-arrow.js +4 -1
- package/dist-cjs/lib/shapes/arrow/straight-arrow.js.map +2 -2
- package/dist-cjs/lib/shapes/bookmark/BookmarkShapeUtil.js +44 -102
- package/dist-cjs/lib/shapes/bookmark/BookmarkShapeUtil.js.map +2 -2
- package/dist-cjs/lib/shapes/bookmark/bookmarks.js +138 -0
- package/dist-cjs/lib/shapes/bookmark/bookmarks.js.map +7 -0
- package/dist-cjs/lib/shapes/embed/EmbedShapeUtil.js +25 -3
- package/dist-cjs/lib/shapes/embed/EmbedShapeUtil.js.map +2 -2
- package/dist-cjs/lib/shapes/frame/FrameShapeUtil.js +1 -1
- package/dist-cjs/lib/shapes/image/ImageShapeUtil.js +1 -1
- package/dist-cjs/lib/shapes/line/LineShapeUtil.js +3 -0
- package/dist-cjs/lib/shapes/line/LineShapeUtil.js.map +2 -2
- package/dist-cjs/lib/shapes/shared/HyperlinkButton.js +1 -1
- package/dist-cjs/lib/shapes/shared/PlainTextLabel.js +1 -1
- package/dist-cjs/lib/shapes/shared/RichTextLabel.js +2 -2
- package/dist-cjs/lib/shapes/shared/RichTextLabel.js.map +2 -2
- package/dist-cjs/lib/shapes/shared/ShapeFill.js +1 -1
- package/dist-cjs/lib/shapes/text/PlainTextArea.js +1 -1
- package/dist-cjs/lib/shapes/text/RichTextArea.js +1 -1
- package/dist-cjs/lib/shapes/video/VideoShapeUtil.js +1 -1
- package/dist-cjs/lib/tools/SelectTool/childStates/Crop/children/Cropping.js +20 -4
- package/dist-cjs/lib/tools/SelectTool/childStates/Crop/children/Cropping.js.map +2 -2
- package/dist-cjs/lib/tools/SelectTool/childStates/Crop/children/Idle.js +1 -1
- package/dist-cjs/lib/tools/SelectTool/childStates/Crop/children/Idle.js.map +2 -2
- package/dist-cjs/lib/tools/SelectTool/childStates/Crop/children/PointingCropHandle.js +23 -11
- package/dist-cjs/lib/tools/SelectTool/childStates/Crop/children/PointingCropHandle.js.map +2 -2
- package/dist-cjs/lib/tools/SelectTool/childStates/DraggingHandle.js +27 -6
- package/dist-cjs/lib/tools/SelectTool/childStates/DraggingHandle.js.map +2 -2
- package/dist-cjs/lib/tools/SelectTool/childStates/Idle.js +1 -1
- package/dist-cjs/lib/tools/SelectTool/childStates/Idle.js.map +2 -2
- package/dist-cjs/lib/tools/SelectTool/childStates/PointingArrowLabel.js +21 -9
- package/dist-cjs/lib/tools/SelectTool/childStates/PointingArrowLabel.js.map +2 -2
- package/dist-cjs/lib/tools/SelectTool/childStates/PointingResizeHandle.js +24 -8
- package/dist-cjs/lib/tools/SelectTool/childStates/PointingResizeHandle.js.map +2 -2
- package/dist-cjs/lib/tools/SelectTool/childStates/PointingRotateHandle.js +21 -9
- package/dist-cjs/lib/tools/SelectTool/childStates/PointingRotateHandle.js.map +2 -2
- package/dist-cjs/lib/tools/SelectTool/childStates/Resizing.js +23 -8
- package/dist-cjs/lib/tools/SelectTool/childStates/Resizing.js.map +2 -2
- package/dist-cjs/lib/tools/SelectTool/childStates/Rotating.js +21 -9
- package/dist-cjs/lib/tools/SelectTool/childStates/Rotating.js.map +2 -2
- package/dist-cjs/lib/tools/SelectTool/childStates/Translating.js +26 -11
- package/dist-cjs/lib/tools/SelectTool/childStates/Translating.js.map +2 -2
- package/dist-cjs/lib/ui/TldrawUi.js +2 -2
- package/dist-cjs/lib/ui/components/DebugMenu/DefaultDebugMenuContent.js +3 -3
- package/dist-cjs/lib/ui/components/DebugMenu/DefaultDebugMenuContent.js.map +1 -1
- package/dist-cjs/lib/ui/components/KeyboardShortcutsDialog/DefaultKeyboardShortcutsDialog.js +1 -1
- package/dist-cjs/lib/ui/components/KeyboardShortcutsDialog/DefaultKeyboardShortcutsDialogContent.js +1 -1
- package/dist-cjs/lib/ui/components/KeyboardShortcutsDialog/DefaultKeyboardShortcutsDialogContent.js.map +1 -1
- package/dist-cjs/lib/ui/components/Minimap/DefaultMinimap.js +1 -1
- package/dist-cjs/lib/ui/components/Minimap/MinimapManager.js +5 -0
- package/dist-cjs/lib/ui/components/Minimap/MinimapManager.js.map +2 -2
- package/dist-cjs/lib/ui/components/OfflineIndicator/OfflineIndicator.js +1 -1
- package/dist-cjs/lib/ui/components/SharePanel/PeopleMenu.js +6 -2
- package/dist-cjs/lib/ui/components/SharePanel/PeopleMenu.js.map +2 -2
- package/dist-cjs/lib/ui/components/SharePanel/UserPresenceColorPicker.js +1 -1
- package/dist-cjs/lib/ui/components/StylePanel/DefaultStylePanel.js +1 -1
- package/dist-cjs/lib/ui/components/StylePanel/DefaultStylePanelContent.js +64 -56
- package/dist-cjs/lib/ui/components/StylePanel/DefaultStylePanelContent.js.map +2 -2
- package/dist-cjs/lib/ui/components/StylePanel/StylePanelButtonPicker.js +54 -47
- package/dist-cjs/lib/ui/components/StylePanel/StylePanelButtonPicker.js.map +3 -3
- package/dist-cjs/lib/ui/components/StylePanel/StylePanelDoubleDropdownPicker.js +63 -56
- package/dist-cjs/lib/ui/components/StylePanel/StylePanelDoubleDropdownPicker.js.map +2 -2
- package/dist-cjs/lib/ui/components/StylePanel/StylePanelDropdownPicker.js +13 -6
- package/dist-cjs/lib/ui/components/StylePanel/StylePanelDropdownPicker.js.map +2 -2
- package/dist-cjs/lib/ui/components/Toolbar/DefaultToolbar.js +1 -1
- package/dist-cjs/lib/ui/components/Toolbar/OverflowingToolbar.js +1 -1
- package/dist-cjs/lib/ui/components/Toolbar/ToggleToolLockedButton.js +1 -1
- package/dist-cjs/lib/ui/components/menu-items.js +2 -2
- package/dist-cjs/lib/ui/components/menu-items.js.map +1 -1
- package/dist-cjs/lib/ui/components/primitives/Button/TldrawUiButton.js +2 -2
- package/dist-cjs/lib/ui/components/primitives/TldrawUiContextualToolbar.js +1 -1
- package/dist-cjs/lib/ui/components/primitives/TldrawUiDialog.js +1 -1
- package/dist-cjs/lib/ui/components/primitives/TldrawUiDropdownMenu.js +1 -1
- package/dist-cjs/lib/ui/components/primitives/TldrawUiIcon.js +1 -1
- package/dist-cjs/lib/ui/components/primitives/TldrawUiInput.js +2 -2
- package/dist-cjs/lib/ui/components/primitives/TldrawUiPopover.js +2 -2
- package/dist-cjs/lib/ui/components/primitives/TldrawUiSlider.js +1 -1
- package/dist-cjs/lib/ui/components/primitives/TldrawUiToolbar.js +2 -2
- package/dist-cjs/lib/ui/components/primitives/TldrawUiTooltip.js +1 -1
- package/dist-cjs/lib/ui/components/primitives/layout.js +1 -1
- package/dist-cjs/lib/ui/components/primitives/menus/TldrawUiMenuGroup.js +1 -1
- package/dist-cjs/lib/ui/context/actions.js +44 -30
- package/dist-cjs/lib/ui/context/actions.js.map +2 -2
- package/dist-cjs/lib/ui/context/breakpoints.js +1 -1
- package/dist-cjs/lib/ui/context/events.js +1 -1
- package/dist-cjs/lib/ui/hooks/useClipboardEvents.js +1 -1
- package/dist-cjs/lib/ui/hooks/useEditorEvents.js +1 -1
- package/dist-cjs/lib/ui/hooks/useEditorEvents.js.map +1 -1
- package/dist-cjs/lib/ui/hooks/useKeyboardShortcuts.js +1 -1
- package/dist-cjs/lib/ui/hooks/useLocalStorageState.js +1 -1
- package/dist-cjs/lib/ui/hooks/useTools.js +1 -1
- package/dist-cjs/lib/ui/hooks/useTranslation/defaultTranslation.js +4 -4
- package/dist-cjs/lib/ui/hooks/useTranslation/defaultTranslation.js.map +1 -1
- package/dist-cjs/lib/ui/hooks/useTranslation/useTranslation.js +1 -1
- package/dist-cjs/lib/ui/version.js +3 -3
- package/dist-cjs/lib/ui/version.js.map +1 -1
- package/dist-cjs/lib/utils/text/richText.js +4 -4
- package/dist-esm/index.d.mts +46 -12
- package/dist-esm/index.mjs +12 -4
- package/dist-esm/index.mjs.map +2 -2
- package/dist-esm/lib/defaultEmbedDefinitions.mjs +25 -30
- package/dist-esm/lib/defaultEmbedDefinitions.mjs.map +2 -2
- package/dist-esm/lib/defaultExternalContentHandlers.mjs +10 -33
- package/dist-esm/lib/defaultExternalContentHandlers.mjs.map +2 -2
- package/dist-esm/lib/shapes/arrow/ArrowShapeUtil.mjs +3 -0
- package/dist-esm/lib/shapes/arrow/ArrowShapeUtil.mjs.map +2 -2
- package/dist-esm/lib/shapes/arrow/curved-arrow.mjs +8 -2
- package/dist-esm/lib/shapes/arrow/curved-arrow.mjs.map +2 -2
- package/dist-esm/lib/shapes/arrow/straight-arrow.mjs +4 -1
- package/dist-esm/lib/shapes/arrow/straight-arrow.mjs.map +2 -2
- package/dist-esm/lib/shapes/bookmark/BookmarkShapeUtil.mjs +46 -101
- package/dist-esm/lib/shapes/bookmark/BookmarkShapeUtil.mjs.map +2 -2
- package/dist-esm/lib/shapes/bookmark/bookmarks.mjs +124 -0
- package/dist-esm/lib/shapes/bookmark/bookmarks.mjs.map +7 -0
- package/dist-esm/lib/shapes/embed/EmbedShapeUtil.mjs +26 -3
- package/dist-esm/lib/shapes/embed/EmbedShapeUtil.mjs.map +2 -2
- package/dist-esm/lib/shapes/line/LineShapeUtil.mjs +3 -0
- package/dist-esm/lib/shapes/line/LineShapeUtil.mjs.map +2 -2
- package/dist-esm/lib/shapes/shared/RichTextLabel.mjs +1 -1
- package/dist-esm/lib/shapes/shared/RichTextLabel.mjs.map +2 -2
- package/dist-esm/lib/tools/SelectTool/childStates/Crop/children/Cropping.mjs +20 -4
- package/dist-esm/lib/tools/SelectTool/childStates/Crop/children/Cropping.mjs.map +2 -2
- package/dist-esm/lib/tools/SelectTool/childStates/Crop/children/Idle.mjs +1 -1
- package/dist-esm/lib/tools/SelectTool/childStates/Crop/children/Idle.mjs.map +2 -2
- package/dist-esm/lib/tools/SelectTool/childStates/Crop/children/PointingCropHandle.mjs +23 -11
- package/dist-esm/lib/tools/SelectTool/childStates/Crop/children/PointingCropHandle.mjs.map +2 -2
- package/dist-esm/lib/tools/SelectTool/childStates/DraggingHandle.mjs +29 -7
- package/dist-esm/lib/tools/SelectTool/childStates/DraggingHandle.mjs.map +2 -2
- package/dist-esm/lib/tools/SelectTool/childStates/Idle.mjs +1 -1
- package/dist-esm/lib/tools/SelectTool/childStates/Idle.mjs.map +2 -2
- package/dist-esm/lib/tools/SelectTool/childStates/PointingArrowLabel.mjs +21 -9
- package/dist-esm/lib/tools/SelectTool/childStates/PointingArrowLabel.mjs.map +2 -2
- package/dist-esm/lib/tools/SelectTool/childStates/PointingResizeHandle.mjs +24 -8
- package/dist-esm/lib/tools/SelectTool/childStates/PointingResizeHandle.mjs.map +2 -2
- package/dist-esm/lib/tools/SelectTool/childStates/PointingRotateHandle.mjs +21 -9
- package/dist-esm/lib/tools/SelectTool/childStates/PointingRotateHandle.mjs.map +2 -2
- package/dist-esm/lib/tools/SelectTool/childStates/Resizing.mjs +23 -8
- package/dist-esm/lib/tools/SelectTool/childStates/Resizing.mjs.map +2 -2
- package/dist-esm/lib/tools/SelectTool/childStates/Rotating.mjs +21 -9
- package/dist-esm/lib/tools/SelectTool/childStates/Rotating.mjs.map +2 -2
- package/dist-esm/lib/tools/SelectTool/childStates/Translating.mjs +26 -11
- package/dist-esm/lib/tools/SelectTool/childStates/Translating.mjs.map +2 -2
- package/dist-esm/lib/ui/components/DebugMenu/DefaultDebugMenuContent.mjs +2 -2
- package/dist-esm/lib/ui/components/DebugMenu/DefaultDebugMenuContent.mjs.map +1 -1
- package/dist-esm/lib/ui/components/KeyboardShortcutsDialog/DefaultKeyboardShortcutsDialogContent.mjs +1 -1
- package/dist-esm/lib/ui/components/KeyboardShortcutsDialog/DefaultKeyboardShortcutsDialogContent.mjs.map +1 -1
- package/dist-esm/lib/ui/components/Minimap/MinimapManager.mjs +5 -0
- package/dist-esm/lib/ui/components/Minimap/MinimapManager.mjs.map +2 -2
- package/dist-esm/lib/ui/components/SharePanel/PeopleMenu.mjs +6 -2
- package/dist-esm/lib/ui/components/SharePanel/PeopleMenu.mjs.map +2 -2
- package/dist-esm/lib/ui/components/StylePanel/DefaultStylePanelContent.mjs +68 -57
- package/dist-esm/lib/ui/components/StylePanel/DefaultStylePanelContent.mjs.map +2 -2
- package/dist-esm/lib/ui/components/StylePanel/StylePanelButtonPicker.mjs +54 -47
- package/dist-esm/lib/ui/components/StylePanel/StylePanelButtonPicker.mjs.map +3 -3
- package/dist-esm/lib/ui/components/StylePanel/StylePanelDoubleDropdownPicker.mjs +63 -56
- package/dist-esm/lib/ui/components/StylePanel/StylePanelDoubleDropdownPicker.mjs.map +2 -2
- package/dist-esm/lib/ui/components/StylePanel/StylePanelDropdownPicker.mjs +12 -5
- package/dist-esm/lib/ui/components/StylePanel/StylePanelDropdownPicker.mjs.map +2 -2
- package/dist-esm/lib/ui/components/menu-items.mjs +2 -2
- package/dist-esm/lib/ui/components/menu-items.mjs.map +1 -1
- package/dist-esm/lib/ui/context/actions.mjs +43 -29
- package/dist-esm/lib/ui/context/actions.mjs.map +2 -2
- package/dist-esm/lib/ui/hooks/useEditorEvents.mjs +1 -1
- package/dist-esm/lib/ui/hooks/useEditorEvents.mjs.map +1 -1
- package/dist-esm/lib/ui/hooks/useTranslation/defaultTranslation.mjs +4 -4
- package/dist-esm/lib/ui/hooks/useTranslation/defaultTranslation.mjs.map +1 -1
- package/dist-esm/lib/ui/version.mjs +3 -3
- package/dist-esm/lib/ui/version.mjs.map +1 -1
- package/package.json +11 -11
- package/src/index.ts +4 -0
- package/src/lib/defaultEmbedDefinitions.ts +20 -24
- package/src/lib/defaultExternalContentHandlers.ts +12 -37
- package/src/lib/shapes/arrow/ArrowShapeUtil.test.ts +211 -1
- package/src/lib/shapes/arrow/ArrowShapeUtil.tsx +3 -0
- package/src/lib/shapes/arrow/curved-arrow.ts +8 -2
- package/src/lib/shapes/arrow/straight-arrow.ts +5 -1
- package/src/lib/shapes/bookmark/BookmarkShapeUtil.tsx +51 -135
- package/src/lib/shapes/bookmark/bookmarks.ts +170 -0
- package/src/lib/shapes/embed/EmbedShapeUtil.tsx +28 -2
- package/src/lib/shapes/line/LineShapeUtil.tsx +3 -0
- package/src/lib/shapes/shared/RichTextLabel.tsx +1 -1
- package/src/lib/shapes/text/TextShapeTool.test.ts +74 -0
- package/src/lib/tools/SelectTool/childStates/Crop/children/Cropping.ts +23 -6
- package/src/lib/tools/SelectTool/childStates/Crop/children/Idle.ts +1 -1
- package/src/lib/tools/SelectTool/childStates/Crop/children/PointingCropHandle.ts +24 -12
- package/src/lib/tools/SelectTool/childStates/DraggingHandle.tsx +34 -11
- package/src/lib/tools/SelectTool/childStates/Idle.ts +1 -1
- package/src/lib/tools/SelectTool/childStates/PointingArrowLabel.ts +23 -11
- package/src/lib/tools/SelectTool/childStates/PointingResizeHandle.ts +26 -9
- package/src/lib/tools/SelectTool/childStates/PointingRotateHandle.ts +23 -10
- package/src/lib/tools/SelectTool/childStates/Resizing.ts +24 -9
- package/src/lib/tools/SelectTool/childStates/Rotating.ts +27 -11
- package/src/lib/tools/SelectTool/childStates/Translating.ts +28 -12
- package/src/lib/ui/components/DebugMenu/DefaultDebugMenuContent.tsx +2 -2
- package/src/lib/ui/components/KeyboardShortcutsDialog/DefaultKeyboardShortcutsDialogContent.tsx +1 -1
- package/src/lib/ui/components/Minimap/MinimapManager.ts +6 -0
- package/src/lib/ui/components/SharePanel/PeopleMenu.tsx +6 -2
- package/src/lib/ui/components/StylePanel/DefaultStylePanelContent.tsx +60 -49
- package/src/lib/ui/components/StylePanel/StylePanelButtonPicker.tsx +70 -53
- package/src/lib/ui/components/StylePanel/StylePanelDoubleDropdownPicker.tsx +105 -90
- package/src/lib/ui/components/StylePanel/StylePanelDropdownPicker.tsx +72 -51
- package/src/lib/ui/components/menu-items.tsx +2 -2
- package/src/lib/ui/context/actions.tsx +49 -31
- package/src/lib/ui/hooks/useEditorEvents.ts +1 -1
- package/src/lib/ui/hooks/useTranslation/defaultTranslation.ts +4 -4
- package/src/lib/ui/version.ts +3 -3
- package/src/lib/utils/embeds/embeds.test.ts +16 -34
- package/src/test/SelectTool.test.ts +251 -0
- package/src/test/bookmark-shapes.test.ts +129 -7
- package/src/test/customSnapping.test.tsx +55 -11
- package/tldraw.css +7 -2
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { HALF_PI, TLArrowShape, TLShapeId, createShapeId, toRichText } from '@tldraw/editor'
|
|
2
2
|
import { vi } from 'vitest'
|
|
3
3
|
import { TestEditor } from '../../../test/TestEditor'
|
|
4
|
-
import { createOrUpdateArrowBinding, getArrowBindings } from './shared'
|
|
4
|
+
import { createOrUpdateArrowBinding, getArrowBindings, getArrowInfo } from './shared'
|
|
5
5
|
|
|
6
6
|
let editor: TestEditor
|
|
7
7
|
|
|
@@ -620,3 +620,213 @@ describe('Arrow export bounds', () => {
|
|
|
620
620
|
expect(arrow.props.richText).not.toBeNull()
|
|
621
621
|
})
|
|
622
622
|
})
|
|
623
|
+
|
|
624
|
+
describe('Arrow terminal positioning bug fix', () => {
|
|
625
|
+
const data = {
|
|
626
|
+
document: {
|
|
627
|
+
store: {
|
|
628
|
+
'shape:1Hm61DGAsY0uqEO-kt75l': {
|
|
629
|
+
x: 637.2890625,
|
|
630
|
+
y: 383.9296875,
|
|
631
|
+
rotation: 0,
|
|
632
|
+
isLocked: false,
|
|
633
|
+
opacity: 1,
|
|
634
|
+
meta: {},
|
|
635
|
+
id: 'shape:1Hm61DGAsY0uqEO-kt75l',
|
|
636
|
+
type: 'geo',
|
|
637
|
+
props: {
|
|
638
|
+
w: 230.66796875,
|
|
639
|
+
h: 114.796875,
|
|
640
|
+
geo: 'rectangle',
|
|
641
|
+
dash: 'draw',
|
|
642
|
+
growY: 0,
|
|
643
|
+
url: '',
|
|
644
|
+
scale: 1,
|
|
645
|
+
color: 'black',
|
|
646
|
+
labelColor: 'black',
|
|
647
|
+
fill: 'none',
|
|
648
|
+
size: 'm',
|
|
649
|
+
font: 'draw',
|
|
650
|
+
align: 'middle',
|
|
651
|
+
verticalAlign: 'middle',
|
|
652
|
+
richText: {
|
|
653
|
+
type: 'doc',
|
|
654
|
+
content: [
|
|
655
|
+
{
|
|
656
|
+
type: 'paragraph',
|
|
657
|
+
},
|
|
658
|
+
],
|
|
659
|
+
},
|
|
660
|
+
},
|
|
661
|
+
parentId: 'page:page',
|
|
662
|
+
index: 'a1',
|
|
663
|
+
typeName: 'shape',
|
|
664
|
+
},
|
|
665
|
+
'binding:nekxhMCGaoEJO98DEqWgo': {
|
|
666
|
+
meta: {},
|
|
667
|
+
id: 'binding:nekxhMCGaoEJO98DEqWgo',
|
|
668
|
+
type: 'arrow',
|
|
669
|
+
fromId: 'shape:j0HKQihjBXqMqgVhfRhDS',
|
|
670
|
+
toId: 'shape:1Hm61DGAsY0uqEO-kt75l',
|
|
671
|
+
props: {
|
|
672
|
+
isPrecise: true,
|
|
673
|
+
isExact: false,
|
|
674
|
+
normalizedAnchor: {
|
|
675
|
+
x: 0.13182672605036325,
|
|
676
|
+
y: 0.8036953858717844,
|
|
677
|
+
},
|
|
678
|
+
snap: 'none',
|
|
679
|
+
terminal: 'start',
|
|
680
|
+
},
|
|
681
|
+
typeName: 'binding',
|
|
682
|
+
},
|
|
683
|
+
'binding:kWamalL_QSq_kFPZDgp7Z': {
|
|
684
|
+
meta: {},
|
|
685
|
+
id: 'binding:kWamalL_QSq_kFPZDgp7Z',
|
|
686
|
+
type: 'arrow',
|
|
687
|
+
fromId: 'shape:j0HKQihjBXqMqgVhfRhDS',
|
|
688
|
+
toId: 'shape:1Hm61DGAsY0uqEO-kt75l',
|
|
689
|
+
props: {
|
|
690
|
+
isPrecise: true,
|
|
691
|
+
isExact: false,
|
|
692
|
+
normalizedAnchor: {
|
|
693
|
+
x: 0.7138744475114731,
|
|
694
|
+
y: 0.45797604464407243,
|
|
695
|
+
},
|
|
696
|
+
snap: 'none',
|
|
697
|
+
terminal: 'end',
|
|
698
|
+
},
|
|
699
|
+
typeName: 'binding',
|
|
700
|
+
},
|
|
701
|
+
'shape:j0HKQihjBXqMqgVhfRhDS': {
|
|
702
|
+
x: 665.296875,
|
|
703
|
+
y: 477.59765625,
|
|
704
|
+
rotation: 0,
|
|
705
|
+
isLocked: false,
|
|
706
|
+
opacity: 1,
|
|
707
|
+
meta: {},
|
|
708
|
+
id: 'shape:j0HKQihjBXqMqgVhfRhDS',
|
|
709
|
+
type: 'arrow',
|
|
710
|
+
props: {
|
|
711
|
+
kind: 'arc',
|
|
712
|
+
elbowMidPoint: 0.5,
|
|
713
|
+
dash: 'draw',
|
|
714
|
+
size: 'm',
|
|
715
|
+
fill: 'none',
|
|
716
|
+
color: 'black',
|
|
717
|
+
labelColor: 'black',
|
|
718
|
+
bend: 0,
|
|
719
|
+
start: {
|
|
720
|
+
x: 0,
|
|
721
|
+
y: 0,
|
|
722
|
+
},
|
|
723
|
+
end: {
|
|
724
|
+
x: 2,
|
|
725
|
+
y: 0,
|
|
726
|
+
},
|
|
727
|
+
arrowheadStart: 'none',
|
|
728
|
+
arrowheadEnd: 'arrow',
|
|
729
|
+
richText: {
|
|
730
|
+
type: 'doc',
|
|
731
|
+
content: [
|
|
732
|
+
{
|
|
733
|
+
type: 'paragraph',
|
|
734
|
+
},
|
|
735
|
+
],
|
|
736
|
+
},
|
|
737
|
+
labelPosition: 0.5,
|
|
738
|
+
font: 'draw',
|
|
739
|
+
scale: 1,
|
|
740
|
+
},
|
|
741
|
+
parentId: 'page:page',
|
|
742
|
+
index: 'a2lbpzZG',
|
|
743
|
+
typeName: 'shape',
|
|
744
|
+
},
|
|
745
|
+
'page:page': {
|
|
746
|
+
meta: {},
|
|
747
|
+
id: 'page:page',
|
|
748
|
+
name: 'Page 1',
|
|
749
|
+
index: 'a1',
|
|
750
|
+
typeName: 'page',
|
|
751
|
+
},
|
|
752
|
+
'document:document': {
|
|
753
|
+
gridSize: 10,
|
|
754
|
+
name: '',
|
|
755
|
+
meta: {},
|
|
756
|
+
id: 'document:document',
|
|
757
|
+
typeName: 'document',
|
|
758
|
+
},
|
|
759
|
+
},
|
|
760
|
+
schema: {
|
|
761
|
+
schemaVersion: 2,
|
|
762
|
+
sequences: {
|
|
763
|
+
'com.tldraw.store': 5,
|
|
764
|
+
'com.tldraw.asset': 1,
|
|
765
|
+
'com.tldraw.camera': 1,
|
|
766
|
+
'com.tldraw.document': 2,
|
|
767
|
+
'com.tldraw.instance': 25,
|
|
768
|
+
'com.tldraw.instance_page_state': 5,
|
|
769
|
+
'com.tldraw.page': 1,
|
|
770
|
+
'com.tldraw.instance_presence': 6,
|
|
771
|
+
'com.tldraw.pointer': 1,
|
|
772
|
+
'com.tldraw.shape': 4,
|
|
773
|
+
'com.tldraw.asset.bookmark': 2,
|
|
774
|
+
'com.tldraw.asset.image': 5,
|
|
775
|
+
'com.tldraw.asset.video': 5,
|
|
776
|
+
'com.tldraw.shape.group': 0,
|
|
777
|
+
'com.tldraw.shape.text': 3,
|
|
778
|
+
'com.tldraw.shape.bookmark': 2,
|
|
779
|
+
'com.tldraw.shape.draw': 2,
|
|
780
|
+
'com.tldraw.shape.geo': 10,
|
|
781
|
+
'com.tldraw.shape.note': 9,
|
|
782
|
+
'com.tldraw.shape.line': 5,
|
|
783
|
+
'com.tldraw.shape.frame': 1,
|
|
784
|
+
'com.tldraw.shape.arrow': 7,
|
|
785
|
+
'com.tldraw.shape.highlight': 1,
|
|
786
|
+
'com.tldraw.shape.embed': 4,
|
|
787
|
+
'com.tldraw.shape.image': 5,
|
|
788
|
+
'com.tldraw.shape.video': 4,
|
|
789
|
+
'com.tldraw.binding.arrow': 1,
|
|
790
|
+
},
|
|
791
|
+
},
|
|
792
|
+
},
|
|
793
|
+
session: {
|
|
794
|
+
version: 0,
|
|
795
|
+
currentPageId: 'page:page',
|
|
796
|
+
exportBackground: true,
|
|
797
|
+
isFocusMode: false,
|
|
798
|
+
isDebugMode: false,
|
|
799
|
+
isToolLocked: false,
|
|
800
|
+
isGridMode: false,
|
|
801
|
+
pageStates: [
|
|
802
|
+
{
|
|
803
|
+
pageId: 'page:page',
|
|
804
|
+
camera: {
|
|
805
|
+
x: 0,
|
|
806
|
+
y: 0,
|
|
807
|
+
z: 1,
|
|
808
|
+
},
|
|
809
|
+
selectedShapeIds: ['shape:j0HKQihjBXqMqgVhfRhDS'],
|
|
810
|
+
focusedGroupId: null,
|
|
811
|
+
},
|
|
812
|
+
],
|
|
813
|
+
},
|
|
814
|
+
}
|
|
815
|
+
|
|
816
|
+
it('should position straight arrow terminals on shape boundary, not text label boundary', () => {
|
|
817
|
+
// Create a geo shape with text label
|
|
818
|
+
editor.loadSnapshot(data as any)
|
|
819
|
+
|
|
820
|
+
const arrow = editor.getShape('shape:j0HKQihjBXqMqgVhfRhDS' as TLShapeId) as TLArrowShape
|
|
821
|
+
expect(arrow).toBeDefined()
|
|
822
|
+
|
|
823
|
+
expect(getArrowBindings(editor, arrow)).toMatchObject({
|
|
824
|
+
end: { props: { isPrecise: true } },
|
|
825
|
+
start: { props: { isPrecise: true } },
|
|
826
|
+
})
|
|
827
|
+
|
|
828
|
+
const info = getArrowInfo(editor, arrow)
|
|
829
|
+
expect(info?.start.handle).toEqual(info?.start.point)
|
|
830
|
+
expect(info?.end.handle).toEqual(info?.end.point)
|
|
831
|
+
})
|
|
832
|
+
})
|
|
@@ -144,6 +144,9 @@ export class ArrowShapeUtil extends ShapeUtil<TLArrowShape> {
|
|
|
144
144
|
override hideSelectionBoundsFg() {
|
|
145
145
|
return true
|
|
146
146
|
}
|
|
147
|
+
override hideInMinimap() {
|
|
148
|
+
return true
|
|
149
|
+
}
|
|
147
150
|
|
|
148
151
|
override canBeLaidOut(shape: TLArrowShape, info: TLShapeUtilCanBeLaidOutOpts) {
|
|
149
152
|
if (info.type === 'flip') {
|
|
@@ -154,7 +154,10 @@ export function getCurvedArrowInfo(
|
|
|
154
154
|
}
|
|
155
155
|
if (!point) {
|
|
156
156
|
if (isClosed) {
|
|
157
|
-
const nearestPoint = startShapeInfo.geometry.nearestPoint(startInStartShapeLocalSpace
|
|
157
|
+
const nearestPoint = startShapeInfo.geometry.nearestPoint(startInStartShapeLocalSpace, {
|
|
158
|
+
includeInternal: false,
|
|
159
|
+
includeLabels: false,
|
|
160
|
+
})
|
|
158
161
|
if (Vec.DistMin(nearestPoint, startInStartShapeLocalSpace, 1)) {
|
|
159
162
|
point = nearestPoint
|
|
160
163
|
}
|
|
@@ -233,7 +236,10 @@ export function getCurvedArrowInfo(
|
|
|
233
236
|
}
|
|
234
237
|
if (!point) {
|
|
235
238
|
if (isClosed) {
|
|
236
|
-
const nearestPoint = endShapeInfo.geometry.nearestPoint(endInEndShapeLocalSpace
|
|
239
|
+
const nearestPoint = endShapeInfo.geometry.nearestPoint(endInEndShapeLocalSpace, {
|
|
240
|
+
includeInternal: false,
|
|
241
|
+
includeLabels: false,
|
|
242
|
+
})
|
|
237
243
|
if (Vec.DistMin(nearestPoint, endInEndShapeLocalSpace, 1)) {
|
|
238
244
|
point = nearestPoint
|
|
239
245
|
}
|
|
@@ -248,7 +248,11 @@ function updateArrowheadPointWithBoundShape(
|
|
|
248
248
|
if (targetInt === undefined) {
|
|
249
249
|
// No intersection? The arrowhead point will be at the arrow terminal.
|
|
250
250
|
// if we _almost_ hit the target, just put the arrowhead at the target.
|
|
251
|
-
targetInt = targetShapeInfo.geometry.nearestPoint(targetTo
|
|
251
|
+
targetInt = targetShapeInfo.geometry.nearestPoint(targetTo, {
|
|
252
|
+
includeLabels: false,
|
|
253
|
+
includeInternal: false,
|
|
254
|
+
})
|
|
255
|
+
|
|
252
256
|
if (!Vec.DistMin(targetInt, targetTo, 1)) {
|
|
253
257
|
return
|
|
254
258
|
}
|
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
import {
|
|
2
|
-
AssetRecordType,
|
|
3
2
|
BaseBoxShapeUtil,
|
|
4
|
-
Editor,
|
|
5
3
|
HTMLContainer,
|
|
6
4
|
T,
|
|
7
5
|
TLAssetId,
|
|
@@ -10,8 +8,6 @@ import {
|
|
|
10
8
|
TLBookmarkShapeProps,
|
|
11
9
|
bookmarkShapeMigrations,
|
|
12
10
|
bookmarkShapeProps,
|
|
13
|
-
debounce,
|
|
14
|
-
getHashForString,
|
|
15
11
|
lerp,
|
|
16
12
|
tlenv,
|
|
17
13
|
toDomPrecision,
|
|
@@ -24,11 +20,13 @@ import { convertCommonTitleHTMLEntities } from '../../utils/text/text'
|
|
|
24
20
|
import { HyperlinkButton } from '../shared/HyperlinkButton'
|
|
25
21
|
import { LINK_ICON } from '../shared/icons-editor'
|
|
26
22
|
import { getRotatedBoxShadow } from '../shared/rotated-box-shadow'
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
23
|
+
import {
|
|
24
|
+
BOOKMARK_HEIGHT,
|
|
25
|
+
BOOKMARK_WIDTH,
|
|
26
|
+
getHumanReadableAddress,
|
|
27
|
+
setBookmarkHeight,
|
|
28
|
+
updateBookmarkAssetOnUrlChange,
|
|
29
|
+
} from './bookmarks'
|
|
32
30
|
|
|
33
31
|
/** @public */
|
|
34
32
|
export class BookmarkShapeUtil extends BaseBoxShapeUtil<TLBookmarkShape> {
|
|
@@ -71,22 +69,18 @@ export class BookmarkShapeUtil extends BaseBoxShapeUtil<TLBookmarkShape> {
|
|
|
71
69
|
}
|
|
72
70
|
|
|
73
71
|
override component(shape: TLBookmarkShape) {
|
|
74
|
-
|
|
72
|
+
const { assetId, url, h } = shape.props
|
|
73
|
+
const rotation = this.editor.getShapePageTransform(shape)!.rotation()
|
|
74
|
+
|
|
75
|
+
return <BookmarkShapeComponent assetId={assetId} url={url} h={h} rotation={rotation} />
|
|
75
76
|
}
|
|
76
77
|
|
|
77
78
|
override indicator(shape: TLBookmarkShape) {
|
|
78
|
-
return
|
|
79
|
-
<rect
|
|
80
|
-
width={toDomPrecision(shape.props.w)}
|
|
81
|
-
height={toDomPrecision(shape.props.h)}
|
|
82
|
-
rx="6"
|
|
83
|
-
ry="6"
|
|
84
|
-
/>
|
|
85
|
-
)
|
|
79
|
+
return <BookmarkIndicatorComponent w={shape.props.w} h={shape.props.h} />
|
|
86
80
|
}
|
|
87
81
|
|
|
88
82
|
override onBeforeCreate(next: TLBookmarkShape) {
|
|
89
|
-
return
|
|
83
|
+
return setBookmarkHeight(this.editor, next)
|
|
90
84
|
}
|
|
91
85
|
|
|
92
86
|
override onBeforeUpdate(prev: TLBookmarkShape, shape: TLBookmarkShape) {
|
|
@@ -99,7 +93,7 @@ export class BookmarkShapeUtil extends BaseBoxShapeUtil<TLBookmarkShape> {
|
|
|
99
93
|
}
|
|
100
94
|
|
|
101
95
|
if (prev.props.assetId !== shape.props.assetId) {
|
|
102
|
-
return
|
|
96
|
+
return setBookmarkHeight(this.editor, shape)
|
|
103
97
|
}
|
|
104
98
|
}
|
|
105
99
|
override getInterpolatedProps(
|
|
@@ -115,18 +109,30 @@ export class BookmarkShapeUtil extends BaseBoxShapeUtil<TLBookmarkShape> {
|
|
|
115
109
|
}
|
|
116
110
|
}
|
|
117
111
|
|
|
118
|
-
function
|
|
112
|
+
export function BookmarkIndicatorComponent({ w, h }: { w: number; h: number }) {
|
|
113
|
+
return <rect width={toDomPrecision(w)} height={toDomPrecision(h)} rx="6" ry="6" />
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
export function BookmarkShapeComponent({
|
|
117
|
+
assetId,
|
|
118
|
+
rotation,
|
|
119
|
+
url,
|
|
120
|
+
h,
|
|
121
|
+
showImageContainer = true,
|
|
122
|
+
}: {
|
|
123
|
+
assetId: TLAssetId | null
|
|
124
|
+
rotation: number
|
|
125
|
+
h: number
|
|
126
|
+
url: string
|
|
127
|
+
showImageContainer?: boolean
|
|
128
|
+
}) {
|
|
119
129
|
const editor = useEditor()
|
|
120
130
|
|
|
121
|
-
const asset = (
|
|
122
|
-
shape.props.assetId ? editor.getAsset(shape.props.assetId) : null
|
|
123
|
-
) as TLBookmarkAsset
|
|
131
|
+
const asset = assetId ? (editor.getAsset(assetId) as TLBookmarkAsset) : null
|
|
124
132
|
|
|
125
133
|
const isSafariExport = !!useSvgExportContext() && tlenv.isSafari
|
|
126
134
|
|
|
127
|
-
const
|
|
128
|
-
|
|
129
|
-
const address = getHumanReadableAddress(shape)
|
|
135
|
+
const address = getHumanReadableAddress(url)
|
|
130
136
|
|
|
131
137
|
const [isFaviconValid, setIsFaviconValid] = useState(true)
|
|
132
138
|
const onFaviconError = () => setIsFaviconValid(false)
|
|
@@ -146,11 +152,11 @@ function BookmarkShapeComponent({ shape }: { shape: TLBookmarkShape }) {
|
|
|
146
152
|
isSafariExport && 'tl-bookmark__container--safariExport'
|
|
147
153
|
)}
|
|
148
154
|
style={{
|
|
149
|
-
boxShadow: isSafariExport ? undefined : getRotatedBoxShadow(
|
|
150
|
-
maxHeight:
|
|
155
|
+
boxShadow: isSafariExport ? undefined : getRotatedBoxShadow(rotation),
|
|
156
|
+
maxHeight: h,
|
|
151
157
|
}}
|
|
152
158
|
>
|
|
153
|
-
{(!asset || asset.props.image) && (
|
|
159
|
+
{showImageContainer && (!asset || asset.props.image) && (
|
|
154
160
|
<div className="tl-bookmark__image_container">
|
|
155
161
|
{asset ? (
|
|
156
162
|
<img
|
|
@@ -163,21 +169,31 @@ function BookmarkShapeComponent({ shape }: { shape: TLBookmarkShape }) {
|
|
|
163
169
|
) : (
|
|
164
170
|
<div className="tl-bookmark__placeholder" />
|
|
165
171
|
)}
|
|
166
|
-
{asset?.props.image && <HyperlinkButton url={
|
|
172
|
+
{asset?.props.image && <HyperlinkButton url={url} />}
|
|
167
173
|
</div>
|
|
168
174
|
)}
|
|
169
175
|
<div className="tl-bookmark__copy_container">
|
|
170
176
|
{asset?.props.title ? (
|
|
171
|
-
<
|
|
172
|
-
|
|
173
|
-
|
|
177
|
+
<a
|
|
178
|
+
className="tl-bookmark__link"
|
|
179
|
+
href={url || ''}
|
|
180
|
+
target="_blank"
|
|
181
|
+
rel="noopener noreferrer"
|
|
182
|
+
draggable={false}
|
|
183
|
+
onPointerDown={markAsHandledOnShiftKey}
|
|
184
|
+
onPointerUp={markAsHandledOnShiftKey}
|
|
185
|
+
>
|
|
186
|
+
<h2 className="tl-bookmark__heading">
|
|
187
|
+
{convertCommonTitleHTMLEntities(asset.props.title)}
|
|
188
|
+
</h2>
|
|
189
|
+
</a>
|
|
174
190
|
) : null}
|
|
175
191
|
{asset?.props.description && asset?.props.image ? (
|
|
176
192
|
<p className="tl-bookmark__description">{asset.props.description}</p>
|
|
177
193
|
) : null}
|
|
178
194
|
<a
|
|
179
195
|
className="tl-bookmark__link"
|
|
180
|
-
href={
|
|
196
|
+
href={url || ''}
|
|
181
197
|
target="_blank"
|
|
182
198
|
rel="noopener noreferrer"
|
|
183
199
|
draggable={false}
|
|
@@ -208,103 +224,3 @@ function BookmarkShapeComponent({ shape }: { shape: TLBookmarkShape }) {
|
|
|
208
224
|
</HTMLContainer>
|
|
209
225
|
)
|
|
210
226
|
}
|
|
211
|
-
|
|
212
|
-
function getBookmarkSize(editor: Editor, shape: TLBookmarkShape) {
|
|
213
|
-
const asset = (
|
|
214
|
-
shape.props.assetId ? editor.getAsset(shape.props.assetId) : null
|
|
215
|
-
) as TLBookmarkAsset
|
|
216
|
-
|
|
217
|
-
let h = BOOKMARK_HEIGHT
|
|
218
|
-
|
|
219
|
-
if (asset) {
|
|
220
|
-
if (!asset.props.image) {
|
|
221
|
-
if (!asset.props.title) {
|
|
222
|
-
h = BOOKMARK_JUST_URL_HEIGHT
|
|
223
|
-
} else {
|
|
224
|
-
h = SHORT_BOOKMARK_HEIGHT
|
|
225
|
-
}
|
|
226
|
-
}
|
|
227
|
-
}
|
|
228
|
-
|
|
229
|
-
return {
|
|
230
|
-
...shape,
|
|
231
|
-
props: {
|
|
232
|
-
...shape.props,
|
|
233
|
-
h,
|
|
234
|
-
},
|
|
235
|
-
}
|
|
236
|
-
}
|
|
237
|
-
|
|
238
|
-
/** @internal */
|
|
239
|
-
export const getHumanReadableAddress = (shape: TLBookmarkShape) => {
|
|
240
|
-
try {
|
|
241
|
-
const url = new URL(shape.props.url)
|
|
242
|
-
// we want the hostname without any www
|
|
243
|
-
return url.hostname.replace(/^www\./, '')
|
|
244
|
-
} catch {
|
|
245
|
-
return shape.props.url
|
|
246
|
-
}
|
|
247
|
-
}
|
|
248
|
-
|
|
249
|
-
function updateBookmarkAssetOnUrlChange(editor: Editor, shape: TLBookmarkShape) {
|
|
250
|
-
const { url } = shape.props
|
|
251
|
-
|
|
252
|
-
// Derive the asset id from the URL
|
|
253
|
-
const assetId: TLAssetId = AssetRecordType.createId(getHashForString(url))
|
|
254
|
-
|
|
255
|
-
if (editor.getAsset(assetId)) {
|
|
256
|
-
// Existing asset for this URL?
|
|
257
|
-
if (shape.props.assetId !== assetId) {
|
|
258
|
-
editor.updateShapes<TLBookmarkShape>([
|
|
259
|
-
{
|
|
260
|
-
id: shape.id,
|
|
261
|
-
type: shape.type,
|
|
262
|
-
props: { assetId },
|
|
263
|
-
},
|
|
264
|
-
])
|
|
265
|
-
}
|
|
266
|
-
} else {
|
|
267
|
-
// No asset for this URL?
|
|
268
|
-
|
|
269
|
-
// First, clear out the existing asset reference
|
|
270
|
-
editor.updateShapes<TLBookmarkShape>([
|
|
271
|
-
{
|
|
272
|
-
id: shape.id,
|
|
273
|
-
type: shape.type,
|
|
274
|
-
props: { assetId: null },
|
|
275
|
-
},
|
|
276
|
-
])
|
|
277
|
-
|
|
278
|
-
// Then try to asyncronously create a new one
|
|
279
|
-
createBookmarkAssetOnUrlChange(editor, shape)
|
|
280
|
-
}
|
|
281
|
-
}
|
|
282
|
-
|
|
283
|
-
const createBookmarkAssetOnUrlChange = debounce(async (editor: Editor, shape: TLBookmarkShape) => {
|
|
284
|
-
if (editor.isDisposed) return
|
|
285
|
-
|
|
286
|
-
const { url } = shape.props
|
|
287
|
-
|
|
288
|
-
// Create the asset using the external content manager's createAssetFromUrl method.
|
|
289
|
-
// This may be overwritten by the user (for example, we overwrite it on tldraw.com)
|
|
290
|
-
const asset = await editor.getAssetForExternalContent({ type: 'url', url })
|
|
291
|
-
|
|
292
|
-
if (!asset) {
|
|
293
|
-
// No asset? Just leave the bookmark as a null assetId.
|
|
294
|
-
return
|
|
295
|
-
}
|
|
296
|
-
|
|
297
|
-
editor.run(() => {
|
|
298
|
-
// Create the new asset
|
|
299
|
-
editor.createAssets([asset])
|
|
300
|
-
|
|
301
|
-
// And update the shape
|
|
302
|
-
editor.updateShapes<TLBookmarkShape>([
|
|
303
|
-
{
|
|
304
|
-
id: shape.id,
|
|
305
|
-
type: shape.type,
|
|
306
|
-
props: { assetId: asset.id },
|
|
307
|
-
},
|
|
308
|
-
])
|
|
309
|
-
})
|
|
310
|
-
}, 500)
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
import {
|
|
2
|
+
AssetRecordType,
|
|
3
|
+
Editor,
|
|
4
|
+
Result,
|
|
5
|
+
TLAssetId,
|
|
6
|
+
TLBookmarkAsset,
|
|
7
|
+
TLBookmarkShape,
|
|
8
|
+
TLShapePartial,
|
|
9
|
+
createShapeId,
|
|
10
|
+
debounce,
|
|
11
|
+
getHashForString,
|
|
12
|
+
} from '@tldraw/editor'
|
|
13
|
+
|
|
14
|
+
export const BOOKMARK_WIDTH = 300
|
|
15
|
+
export const BOOKMARK_HEIGHT = 320
|
|
16
|
+
export const BOOKMARK_JUST_URL_HEIGHT = 46
|
|
17
|
+
const SHORT_BOOKMARK_HEIGHT = 101
|
|
18
|
+
|
|
19
|
+
export function getBookmarkHeight(editor: Editor, assetId?: TLAssetId | null) {
|
|
20
|
+
const asset = (assetId ? editor.getAsset(assetId) : null) as TLBookmarkAsset | null
|
|
21
|
+
|
|
22
|
+
if (asset) {
|
|
23
|
+
if (!asset.props.image) {
|
|
24
|
+
if (!asset.props.title) {
|
|
25
|
+
return BOOKMARK_JUST_URL_HEIGHT
|
|
26
|
+
} else {
|
|
27
|
+
return SHORT_BOOKMARK_HEIGHT
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
return BOOKMARK_HEIGHT
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export function setBookmarkHeight(editor: Editor, shape: TLBookmarkShape) {
|
|
36
|
+
return {
|
|
37
|
+
...shape,
|
|
38
|
+
props: { ...shape.props, h: getBookmarkHeight(editor, shape.props.assetId) },
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/** @internal */
|
|
43
|
+
export const getHumanReadableAddress = (url: string) => {
|
|
44
|
+
try {
|
|
45
|
+
const objUrl = new URL(url)
|
|
46
|
+
// we want the hostname without any www
|
|
47
|
+
return objUrl.hostname.replace(/^www\./, '')
|
|
48
|
+
} catch {
|
|
49
|
+
return url
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export function updateBookmarkAssetOnUrlChange(editor: Editor, shape: TLBookmarkShape) {
|
|
54
|
+
const { url } = shape.props
|
|
55
|
+
|
|
56
|
+
// Derive the asset id from the URL
|
|
57
|
+
const assetId: TLAssetId = AssetRecordType.createId(getHashForString(url))
|
|
58
|
+
|
|
59
|
+
if (editor.getAsset(assetId)) {
|
|
60
|
+
// Existing asset for this URL?
|
|
61
|
+
if (shape.props.assetId !== assetId) {
|
|
62
|
+
editor.updateShapes<TLBookmarkShape>([
|
|
63
|
+
{
|
|
64
|
+
id: shape.id,
|
|
65
|
+
type: shape.type,
|
|
66
|
+
props: { assetId },
|
|
67
|
+
},
|
|
68
|
+
])
|
|
69
|
+
}
|
|
70
|
+
} else {
|
|
71
|
+
// No asset for this URL?
|
|
72
|
+
|
|
73
|
+
// First, clear out the existing asset reference
|
|
74
|
+
editor.updateShapes<TLBookmarkShape>([
|
|
75
|
+
{
|
|
76
|
+
id: shape.id,
|
|
77
|
+
type: shape.type,
|
|
78
|
+
props: { assetId: null },
|
|
79
|
+
},
|
|
80
|
+
])
|
|
81
|
+
|
|
82
|
+
// Then try to asyncronously create a new one
|
|
83
|
+
createBookmarkAssetOnUrlChange(editor, shape)
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
const createBookmarkAssetOnUrlChange = debounce(async (editor: Editor, shape: TLBookmarkShape) => {
|
|
88
|
+
if (editor.isDisposed) return
|
|
89
|
+
|
|
90
|
+
const { url } = shape.props
|
|
91
|
+
|
|
92
|
+
// Create the asset using the external content manager's createAssetFromUrl method.
|
|
93
|
+
// This may be overwritten by the user (for example, we overwrite it on tldraw.com)
|
|
94
|
+
const asset = await editor.getAssetForExternalContent({ type: 'url', url })
|
|
95
|
+
|
|
96
|
+
if (!asset) {
|
|
97
|
+
// No asset? Just leave the bookmark as a null assetId.
|
|
98
|
+
return
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
editor.run(() => {
|
|
102
|
+
// Create the new asset
|
|
103
|
+
editor.createAssets([asset])
|
|
104
|
+
|
|
105
|
+
// And update the shape
|
|
106
|
+
editor.updateShapes<TLBookmarkShape>([
|
|
107
|
+
{
|
|
108
|
+
id: shape.id,
|
|
109
|
+
type: shape.type,
|
|
110
|
+
props: { assetId: asset.id },
|
|
111
|
+
},
|
|
112
|
+
])
|
|
113
|
+
})
|
|
114
|
+
}, 500)
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Creates a bookmark shape from a URL with unfurled metadata.
|
|
118
|
+
*
|
|
119
|
+
* @returns A Result containing the created bookmark shape or an error
|
|
120
|
+
* @public
|
|
121
|
+
*/
|
|
122
|
+
|
|
123
|
+
export async function createBookmarkFromUrl(
|
|
124
|
+
editor: Editor,
|
|
125
|
+
{
|
|
126
|
+
url,
|
|
127
|
+
center = editor.getViewportPageBounds().center,
|
|
128
|
+
}: {
|
|
129
|
+
url: string
|
|
130
|
+
center?: { x: number; y: number }
|
|
131
|
+
}
|
|
132
|
+
): Promise<Result<TLBookmarkShape, string>> {
|
|
133
|
+
try {
|
|
134
|
+
// Create the bookmark asset with unfurled metadata
|
|
135
|
+
const asset = await editor.getAssetForExternalContent({ type: 'url', url })
|
|
136
|
+
|
|
137
|
+
// Create the bookmark shape
|
|
138
|
+
const shapeId = createShapeId()
|
|
139
|
+
const shapePartial: TLShapePartial<TLBookmarkShape> = {
|
|
140
|
+
id: shapeId,
|
|
141
|
+
type: 'bookmark',
|
|
142
|
+
x: center.x - BOOKMARK_WIDTH / 2,
|
|
143
|
+
y: center.y - BOOKMARK_HEIGHT / 2,
|
|
144
|
+
rotation: 0,
|
|
145
|
+
opacity: 1,
|
|
146
|
+
props: {
|
|
147
|
+
url,
|
|
148
|
+
assetId: asset?.id || null,
|
|
149
|
+
w: BOOKMARK_WIDTH,
|
|
150
|
+
h: getBookmarkHeight(editor, asset?.id),
|
|
151
|
+
},
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
editor.run(() => {
|
|
155
|
+
// Create the asset if we have one
|
|
156
|
+
if (asset) {
|
|
157
|
+
editor.createAssets([asset])
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
// Create the shape
|
|
161
|
+
editor.createShapes([shapePartial])
|
|
162
|
+
})
|
|
163
|
+
|
|
164
|
+
// Get the created shape
|
|
165
|
+
const createdShape = editor.getShape(shapeId) as TLBookmarkShape
|
|
166
|
+
return Result.ok(createdShape)
|
|
167
|
+
} catch (error) {
|
|
168
|
+
return Result.err(error instanceof Error ? error.message : 'Failed to create bookmark')
|
|
169
|
+
}
|
|
170
|
+
}
|