tldraw 4.3.0-next.f4772c19540d → 4.4.0-canary.29afdff6bb04
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/README.md +0 -2
- package/dist-cjs/index.d.ts +311 -242
- package/dist-cjs/index.js +13 -5
- package/dist-cjs/index.js.map +2 -2
- package/dist-cjs/lib/bindings/arrow/ArrowBindingUtil.js.map +2 -2
- package/dist-cjs/lib/canvas/TldrawSelectionForeground.js +2 -2
- package/dist-cjs/lib/canvas/TldrawSelectionForeground.js.map +2 -2
- package/dist-cjs/lib/defaultEmbedDefinitions.js +1 -1
- package/dist-cjs/lib/defaultEmbedDefinitions.js.map +2 -2
- package/dist-cjs/lib/defaultExternalContentHandlers.js +5 -5
- package/dist-cjs/lib/defaultExternalContentHandlers.js.map +2 -2
- package/dist-cjs/lib/defaultSideEffects.js +6 -1
- package/dist-cjs/lib/defaultSideEffects.js.map +2 -2
- package/dist-cjs/lib/shapes/arrow/ArrowShapeUtil.js +14 -13
- package/dist-cjs/lib/shapes/arrow/ArrowShapeUtil.js.map +2 -2
- package/dist-cjs/lib/shapes/arrow/arrow-types.js.map +1 -1
- package/dist-cjs/lib/shapes/arrow/arrowLabel.js +1 -1
- package/dist-cjs/lib/shapes/arrow/arrowLabel.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/elbowArrowSnapLines.js.map +2 -2
- package/dist-cjs/lib/shapes/arrow/elbow/getElbowArrowInfo.js +1 -1
- package/dist-cjs/lib/shapes/arrow/elbow/getElbowArrowInfo.js.map +2 -2
- package/dist-cjs/lib/shapes/arrow/shared.js.map +2 -2
- package/dist-cjs/lib/shapes/arrow/toolStates/Idle.js +4 -10
- package/dist-cjs/lib/shapes/arrow/toolStates/Idle.js.map +2 -2
- package/dist-cjs/lib/shapes/arrow/toolStates/Pointing.js +7 -4
- package/dist-cjs/lib/shapes/arrow/toolStates/Pointing.js.map +2 -2
- package/dist-cjs/lib/shapes/bookmark/BookmarkShapeUtil.js +1 -1
- package/dist-cjs/lib/shapes/bookmark/BookmarkShapeUtil.js.map +2 -2
- package/dist-cjs/lib/shapes/bookmark/bookmarks.js.map +2 -2
- package/dist-cjs/lib/shapes/draw/DrawShapeUtil.js +25 -23
- package/dist-cjs/lib/shapes/draw/DrawShapeUtil.js.map +2 -2
- package/dist-cjs/lib/shapes/draw/getPath.js +20 -11
- package/dist-cjs/lib/shapes/draw/getPath.js.map +2 -2
- package/dist-cjs/lib/shapes/draw/toolStates/Drawing.js +82 -86
- package/dist-cjs/lib/shapes/draw/toolStates/Drawing.js.map +3 -3
- package/dist-cjs/lib/shapes/embed/EmbedShapeUtil.js +6 -0
- package/dist-cjs/lib/shapes/embed/EmbedShapeUtil.js.map +2 -2
- package/dist-cjs/lib/shapes/frame/FrameShapeTool.js.map +1 -1
- package/dist-cjs/lib/shapes/frame/FrameShapeUtil.js +7 -13
- package/dist-cjs/lib/shapes/frame/FrameShapeUtil.js.map +2 -2
- package/dist-cjs/lib/shapes/geo/GeoShapeUtil.js +146 -142
- package/dist-cjs/lib/shapes/geo/GeoShapeUtil.js.map +2 -2
- package/dist-cjs/lib/shapes/geo/toolStates/Idle.js +5 -10
- package/dist-cjs/lib/shapes/geo/toolStates/Idle.js.map +2 -2
- package/dist-cjs/lib/shapes/geo/toolStates/Pointing.js +3 -3
- package/dist-cjs/lib/shapes/geo/toolStates/Pointing.js.map +2 -2
- package/dist-cjs/lib/shapes/highlight/HighlightShapeUtil.js +23 -21
- package/dist-cjs/lib/shapes/highlight/HighlightShapeUtil.js.map +2 -2
- package/dist-cjs/lib/shapes/line/toolStates/Pointing.js +3 -3
- package/dist-cjs/lib/shapes/line/toolStates/Pointing.js.map +2 -2
- package/dist-cjs/lib/shapes/note/NoteShapeUtil.js +6 -11
- package/dist-cjs/lib/shapes/note/NoteShapeUtil.js.map +2 -2
- package/dist-cjs/lib/shapes/note/noteHelpers.js.map +2 -2
- package/dist-cjs/lib/shapes/note/toolStates/Pointing.js +5 -10
- package/dist-cjs/lib/shapes/note/toolStates/Pointing.js.map +2 -2
- package/dist-cjs/lib/shapes/shared/HyperlinkButton.js +3 -2
- package/dist-cjs/lib/shapes/shared/HyperlinkButton.js.map +2 -2
- package/dist-cjs/lib/shapes/shared/PlainTextLabel.js +14 -2
- package/dist-cjs/lib/shapes/shared/PlainTextLabel.js.map +3 -3
- package/dist-cjs/lib/shapes/shared/RichTextLabel.js +12 -4
- package/dist-cjs/lib/shapes/shared/RichTextLabel.js.map +3 -3
- package/dist-cjs/lib/shapes/shared/ShapeFill.js +2 -2
- package/dist-cjs/lib/shapes/shared/ShapeFill.js.map +2 -2
- package/dist-cjs/lib/shapes/shared/crop.js +1 -0
- package/dist-cjs/lib/shapes/shared/crop.js.map +2 -2
- package/dist-cjs/lib/shapes/shared/interpolate-props.js +3 -3
- package/dist-cjs/lib/shapes/shared/interpolate-props.js.map +2 -2
- package/dist-cjs/lib/shapes/shared/useEditablePlainText.js.map +2 -2
- package/dist-cjs/lib/shapes/shared/useEditableRichText.js.map +2 -2
- package/dist-cjs/lib/shapes/shared/{useForceSolid.js → useEfficientZoomThreshold.js} +10 -7
- package/dist-cjs/lib/shapes/shared/useEfficientZoomThreshold.js.map +7 -0
- package/dist-cjs/lib/shapes/shared/useImageOrVideoAsset.js +1 -1
- package/dist-cjs/lib/shapes/shared/useImageOrVideoAsset.js.map +2 -2
- package/dist-cjs/lib/shapes/text/RichTextArea.js +5 -0
- package/dist-cjs/lib/shapes/text/RichTextArea.js.map +2 -2
- package/dist-cjs/lib/shapes/text/TextShapeUtil.js +5 -2
- package/dist-cjs/lib/shapes/text/TextShapeUtil.js.map +2 -2
- package/dist-cjs/lib/shapes/text/toolStates/Idle.js +4 -10
- package/dist-cjs/lib/shapes/text/toolStates/Idle.js.map +2 -2
- package/dist-cjs/lib/shapes/text/toolStates/Pointing.js +7 -5
- package/dist-cjs/lib/shapes/text/toolStates/Pointing.js.map +2 -2
- package/dist-cjs/lib/shapes/video/VideoShapeUtil.js +1 -1
- package/dist-cjs/lib/shapes/video/VideoShapeUtil.js.map +2 -2
- package/dist-cjs/lib/tools/EraserTool/childStates/Erasing.js +12 -6
- package/dist-cjs/lib/tools/EraserTool/childStates/Erasing.js.map +2 -2
- package/dist-cjs/lib/tools/EraserTool/childStates/Pointing.js +2 -4
- package/dist-cjs/lib/tools/EraserTool/childStates/Pointing.js.map +2 -2
- package/dist-cjs/lib/tools/HandTool/HandTool.js +3 -5
- package/dist-cjs/lib/tools/HandTool/HandTool.js.map +2 -2
- package/dist-cjs/lib/tools/HandTool/childStates/Dragging.js +3 -2
- package/dist-cjs/lib/tools/HandTool/childStates/Dragging.js.map +2 -2
- package/dist-cjs/lib/tools/HandTool/childStates/Pointing.js +1 -1
- package/dist-cjs/lib/tools/HandTool/childStates/Pointing.js.map +2 -2
- package/dist-cjs/lib/tools/LaserTool/childStates/Lasering.js +1 -1
- package/dist-cjs/lib/tools/LaserTool/childStates/Lasering.js.map +2 -2
- package/dist-cjs/lib/tools/SelectTool/DragAndDropManager.js +10 -11
- package/dist-cjs/lib/tools/SelectTool/DragAndDropManager.js.map +2 -2
- package/dist-cjs/lib/tools/SelectTool/childStates/Brushing.js +20 -6
- package/dist-cjs/lib/tools/SelectTool/childStates/Brushing.js.map +3 -3
- package/dist-cjs/lib/tools/SelectTool/childStates/Crop/children/Cropping.js +4 -6
- 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/PointingCrop.js +1 -1
- package/dist-cjs/lib/tools/SelectTool/childStates/Crop/children/PointingCrop.js.map +2 -2
- package/dist-cjs/lib/tools/SelectTool/childStates/Crop/children/PointingCropHandle.js +1 -1
- package/dist-cjs/lib/tools/SelectTool/childStates/Crop/children/PointingCropHandle.js.map +2 -2
- package/dist-cjs/lib/tools/SelectTool/childStates/Crop/children/TranslatingCrop.js +2 -1
- package/dist-cjs/lib/tools/SelectTool/childStates/Crop/children/TranslatingCrop.js.map +2 -2
- package/dist-cjs/lib/tools/SelectTool/childStates/Crop/children/crop_helpers.js +1 -1
- package/dist-cjs/lib/tools/SelectTool/childStates/Crop/children/crop_helpers.js.map +2 -2
- package/dist-cjs/lib/tools/SelectTool/childStates/DraggingHandle.js +8 -6
- package/dist-cjs/lib/tools/SelectTool/childStates/DraggingHandle.js.map +2 -2
- package/dist-cjs/lib/tools/SelectTool/childStates/EditingShape.js +38 -11
- package/dist-cjs/lib/tools/SelectTool/childStates/EditingShape.js.map +2 -2
- package/dist-cjs/lib/tools/SelectTool/childStates/Idle.js +42 -50
- package/dist-cjs/lib/tools/SelectTool/childStates/Idle.js.map +2 -2
- package/dist-cjs/lib/tools/SelectTool/childStates/PointingArrowLabel.js +6 -6
- package/dist-cjs/lib/tools/SelectTool/childStates/PointingArrowLabel.js.map +2 -2
- package/dist-cjs/lib/tools/SelectTool/childStates/PointingCanvas.js +1 -1
- package/dist-cjs/lib/tools/SelectTool/childStates/PointingCanvas.js.map +2 -2
- package/dist-cjs/lib/tools/SelectTool/childStates/PointingHandle.js +4 -14
- package/dist-cjs/lib/tools/SelectTool/childStates/PointingHandle.js.map +2 -2
- package/dist-cjs/lib/tools/SelectTool/childStates/PointingResizeHandle.js +1 -1
- package/dist-cjs/lib/tools/SelectTool/childStates/PointingResizeHandle.js.map +2 -2
- package/dist-cjs/lib/tools/SelectTool/childStates/PointingRotateHandle.js +1 -1
- package/dist-cjs/lib/tools/SelectTool/childStates/PointingRotateHandle.js.map +2 -2
- package/dist-cjs/lib/tools/SelectTool/childStates/PointingSelection.js +2 -2
- package/dist-cjs/lib/tools/SelectTool/childStates/PointingSelection.js.map +2 -2
- package/dist-cjs/lib/tools/SelectTool/childStates/PointingShape.js +4 -13
- package/dist-cjs/lib/tools/SelectTool/childStates/PointingShape.js.map +2 -2
- package/dist-cjs/lib/tools/SelectTool/childStates/Resizing.js +5 -6
- package/dist-cjs/lib/tools/SelectTool/childStates/Resizing.js.map +2 -2
- package/dist-cjs/lib/tools/SelectTool/childStates/Rotating.js +2 -3
- package/dist-cjs/lib/tools/SelectTool/childStates/Rotating.js.map +2 -2
- package/dist-cjs/lib/tools/SelectTool/childStates/ScribbleBrushing.js +22 -8
- package/dist-cjs/lib/tools/SelectTool/childStates/ScribbleBrushing.js.map +3 -3
- package/dist-cjs/lib/tools/SelectTool/childStates/Translating.js +13 -11
- package/dist-cjs/lib/tools/SelectTool/childStates/Translating.js.map +2 -2
- package/dist-cjs/lib/tools/SelectTool/selectHelpers.js +15 -4
- package/dist-cjs/lib/tools/SelectTool/selectHelpers.js.map +2 -2
- package/dist-cjs/lib/tools/ZoomTool/ZoomTool.js +1 -1
- package/dist-cjs/lib/tools/ZoomTool/ZoomTool.js.map +2 -2
- package/dist-cjs/lib/tools/ZoomTool/childStates/Pointing.js +3 -3
- package/dist-cjs/lib/tools/ZoomTool/childStates/Pointing.js.map +2 -2
- package/dist-cjs/lib/tools/ZoomTool/childStates/ZoomBrushing.js +5 -6
- package/dist-cjs/lib/tools/ZoomTool/childStates/ZoomBrushing.js.map +2 -2
- package/dist-cjs/lib/tools/selection-logic/getHitShapeOnCanvasPointerDown.js +1 -3
- package/dist-cjs/lib/tools/selection-logic/getHitShapeOnCanvasPointerDown.js.map +2 -2
- package/dist-cjs/lib/tools/selection-logic/selectOnCanvasPointerUp.js +1 -1
- package/dist-cjs/lib/tools/selection-logic/selectOnCanvasPointerUp.js.map +2 -2
- package/dist-cjs/lib/tools/selection-logic/updateHoveredShapeId.js +1 -1
- package/dist-cjs/lib/tools/selection-logic/updateHoveredShapeId.js.map +2 -2
- package/dist-cjs/lib/ui/TldrawUi.js +2 -2
- package/dist-cjs/lib/ui/TldrawUi.js.map +2 -2
- package/dist-cjs/lib/ui/components/ActionsMenu/DefaultActionsMenuContent.js +3 -9
- package/dist-cjs/lib/ui/components/ActionsMenu/DefaultActionsMenuContent.js.map +2 -2
- package/dist-cjs/lib/ui/components/ContextMenu/DefaultContextMenu.js +1 -3
- package/dist-cjs/lib/ui/components/ContextMenu/DefaultContextMenu.js.map +2 -2
- package/dist-cjs/lib/ui/components/CursorChatBubble.js +1 -1
- package/dist-cjs/lib/ui/components/CursorChatBubble.js.map +2 -2
- package/dist-cjs/lib/ui/components/DefaultDebugPanel.js +2 -22
- package/dist-cjs/lib/ui/components/DefaultDebugPanel.js.map +2 -2
- package/dist-cjs/lib/ui/components/EditLinkDialog.js +11 -1
- package/dist-cjs/lib/ui/components/EditLinkDialog.js.map +2 -2
- package/dist-cjs/lib/ui/components/HelperButtons/BackToContent.js +1 -1
- package/dist-cjs/lib/ui/components/HelperButtons/BackToContent.js.map +2 -2
- package/dist-cjs/lib/ui/components/HelperButtons/StopFollowing.js.map +2 -2
- package/dist-cjs/lib/ui/components/Minimap/DefaultMinimap.js +1 -1
- package/dist-cjs/lib/ui/components/Minimap/DefaultMinimap.js.map +2 -2
- package/dist-cjs/lib/ui/components/OfflineIndicator/OfflineIndicator.js +2 -15
- package/dist-cjs/lib/ui/components/OfflineIndicator/OfflineIndicator.js.map +3 -3
- package/dist-cjs/lib/ui/components/PageMenu/PageItemInput.js +3 -1
- package/dist-cjs/lib/ui/components/PageMenu/PageItemInput.js.map +2 -2
- package/dist-cjs/lib/ui/components/SharePanel/PeopleMenu.js +6 -0
- package/dist-cjs/lib/ui/components/SharePanel/PeopleMenu.js.map +2 -2
- package/dist-cjs/lib/ui/components/Toolbar/AltTextEditor.js.map +2 -2
- package/dist-cjs/lib/ui/components/Toolbar/DefaultImageToolbar.js +1 -1
- package/dist-cjs/lib/ui/components/Toolbar/DefaultImageToolbar.js.map +2 -2
- package/dist-cjs/lib/ui/components/Toolbar/DefaultRichTextToolbar.js +1 -1
- package/dist-cjs/lib/ui/components/Toolbar/DefaultRichTextToolbar.js.map +2 -2
- package/dist-cjs/lib/ui/components/TopPanel/CenteredTopPanelContainer.js.map +1 -1
- package/dist-cjs/lib/ui/components/menu-items.js +3 -1
- package/dist-cjs/lib/ui/components/menu-items.js.map +2 -2
- package/dist-cjs/lib/ui/components/primitives/TldrawUiContextualToolbar.js +3 -1
- package/dist-cjs/lib/ui/components/primitives/TldrawUiContextualToolbar.js.map +2 -2
- package/dist-cjs/lib/ui/components/primitives/TldrawUiSlider.js +1 -1
- package/dist-cjs/lib/ui/components/primitives/TldrawUiSlider.js.map +2 -2
- package/dist-cjs/lib/ui/components/primitives/TldrawUiTooltip.js +149 -93
- package/dist-cjs/lib/ui/components/primitives/TldrawUiTooltip.js.map +2 -2
- package/dist-cjs/lib/ui/components/primitives/menus/TldrawUiMenuActionCheckboxItem.js.map +2 -2
- package/dist-cjs/lib/ui/components/primitives/menus/TldrawUiMenuActionItem.js.map +2 -2
- package/dist-cjs/lib/ui/components/primitives/menus/TldrawUiMenuItem.js +14 -7
- package/dist-cjs/lib/ui/components/primitives/menus/TldrawUiMenuItem.js.map +2 -2
- package/dist-cjs/lib/ui/context/actions.js +7 -8
- package/dist-cjs/lib/ui/context/actions.js.map +2 -2
- package/dist-cjs/lib/ui/context/components.js +1 -2
- package/dist-cjs/lib/ui/context/components.js.map +2 -2
- package/dist-cjs/lib/ui/hooks/menu-hooks.js.map +2 -2
- package/dist-cjs/lib/ui/hooks/useClipboardEvents.js +2 -2
- package/dist-cjs/lib/ui/hooks/useClipboardEvents.js.map +2 -2
- package/dist-cjs/lib/ui/hooks/useFlatten.js.map +2 -2
- package/dist-cjs/lib/ui/hooks/useKeyboardShortcuts.js +2 -2
- package/dist-cjs/lib/ui/hooks/useKeyboardShortcuts.js.map +2 -2
- package/dist-cjs/lib/ui/hooks/useTools.js +4 -5
- package/dist-cjs/lib/ui/hooks/useTools.js.map +2 -2
- package/dist-cjs/lib/ui/version.js +3 -3
- package/dist-cjs/lib/ui/version.js.map +1 -1
- package/dist-cjs/lib/utils/excalidraw/putExcalidrawContent.js +16 -6
- package/dist-cjs/lib/utils/excalidraw/putExcalidrawContent.js.map +2 -2
- package/dist-cjs/lib/utils/export/exportAs.js.map +2 -2
- package/dist-cjs/lib/utils/frames/frames.js.map +2 -2
- package/dist-cjs/lib/{tools/selection-logic/getShouldEnterCropModeOnPointerDown.js → utils/test-helpers.js} +21 -8
- package/dist-cjs/lib/utils/test-helpers.js.map +7 -0
- package/dist-cjs/lib/utils/text/richText.js +15 -19
- package/dist-cjs/lib/utils/text/richText.js.map +3 -3
- package/dist-cjs/lib/utils/tldr/buildFromV1Document.js +7 -2
- package/dist-cjs/lib/utils/tldr/buildFromV1Document.js.map +2 -2
- package/dist-esm/index.d.mts +311 -242
- package/dist-esm/index.mjs +14 -5
- package/dist-esm/index.mjs.map +2 -2
- package/dist-esm/lib/bindings/arrow/ArrowBindingUtil.mjs.map +2 -2
- package/dist-esm/lib/canvas/TldrawSelectionForeground.mjs +2 -2
- package/dist-esm/lib/canvas/TldrawSelectionForeground.mjs.map +2 -2
- package/dist-esm/lib/defaultEmbedDefinitions.mjs +1 -1
- package/dist-esm/lib/defaultEmbedDefinitions.mjs.map +2 -2
- package/dist-esm/lib/defaultExternalContentHandlers.mjs +5 -5
- package/dist-esm/lib/defaultExternalContentHandlers.mjs.map +2 -2
- package/dist-esm/lib/defaultSideEffects.mjs +6 -1
- package/dist-esm/lib/defaultSideEffects.mjs.map +2 -2
- package/dist-esm/lib/shapes/arrow/ArrowShapeUtil.mjs +15 -15
- package/dist-esm/lib/shapes/arrow/ArrowShapeUtil.mjs.map +2 -2
- package/dist-esm/lib/shapes/arrow/arrowLabel.mjs +1 -1
- package/dist-esm/lib/shapes/arrow/arrowLabel.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/elbowArrowSnapLines.mjs.map +2 -2
- package/dist-esm/lib/shapes/arrow/elbow/getElbowArrowInfo.mjs +1 -1
- package/dist-esm/lib/shapes/arrow/elbow/getElbowArrowInfo.mjs.map +2 -2
- package/dist-esm/lib/shapes/arrow/shared.mjs.map +2 -2
- package/dist-esm/lib/shapes/arrow/toolStates/Idle.mjs +4 -10
- package/dist-esm/lib/shapes/arrow/toolStates/Idle.mjs.map +2 -2
- package/dist-esm/lib/shapes/arrow/toolStates/Pointing.mjs +7 -4
- package/dist-esm/lib/shapes/arrow/toolStates/Pointing.mjs.map +2 -2
- package/dist-esm/lib/shapes/bookmark/BookmarkShapeUtil.mjs +1 -1
- package/dist-esm/lib/shapes/bookmark/BookmarkShapeUtil.mjs.map +2 -2
- package/dist-esm/lib/shapes/bookmark/bookmarks.mjs.map +2 -2
- package/dist-esm/lib/shapes/draw/DrawShapeUtil.mjs +30 -25
- package/dist-esm/lib/shapes/draw/DrawShapeUtil.mjs.map +2 -2
- package/dist-esm/lib/shapes/draw/getPath.mjs +21 -11
- package/dist-esm/lib/shapes/draw/getPath.mjs.map +2 -2
- package/dist-esm/lib/shapes/draw/toolStates/Drawing.mjs +83 -86
- package/dist-esm/lib/shapes/draw/toolStates/Drawing.mjs.map +3 -3
- package/dist-esm/lib/shapes/embed/EmbedShapeUtil.mjs +6 -0
- package/dist-esm/lib/shapes/embed/EmbedShapeUtil.mjs.map +2 -2
- package/dist-esm/lib/shapes/frame/FrameShapeTool.mjs.map +1 -1
- package/dist-esm/lib/shapes/frame/FrameShapeUtil.mjs +7 -13
- package/dist-esm/lib/shapes/frame/FrameShapeUtil.mjs.map +2 -2
- package/dist-esm/lib/shapes/geo/GeoShapeUtil.mjs +147 -142
- package/dist-esm/lib/shapes/geo/GeoShapeUtil.mjs.map +2 -2
- package/dist-esm/lib/shapes/geo/toolStates/Idle.mjs +5 -10
- package/dist-esm/lib/shapes/geo/toolStates/Idle.mjs.map +2 -2
- package/dist-esm/lib/shapes/geo/toolStates/Pointing.mjs +3 -3
- package/dist-esm/lib/shapes/geo/toolStates/Pointing.mjs.map +2 -2
- package/dist-esm/lib/shapes/highlight/HighlightShapeUtil.mjs +24 -22
- package/dist-esm/lib/shapes/highlight/HighlightShapeUtil.mjs.map +2 -2
- package/dist-esm/lib/shapes/line/toolStates/Pointing.mjs +3 -3
- package/dist-esm/lib/shapes/line/toolStates/Pointing.mjs.map +2 -2
- package/dist-esm/lib/shapes/note/NoteShapeUtil.mjs +7 -12
- package/dist-esm/lib/shapes/note/NoteShapeUtil.mjs.map +2 -2
- package/dist-esm/lib/shapes/note/noteHelpers.mjs.map +2 -2
- package/dist-esm/lib/shapes/note/toolStates/Pointing.mjs +5 -10
- package/dist-esm/lib/shapes/note/toolStates/Pointing.mjs.map +2 -2
- package/dist-esm/lib/shapes/shared/HyperlinkButton.mjs +4 -3
- package/dist-esm/lib/shapes/shared/HyperlinkButton.mjs.map +2 -2
- package/dist-esm/lib/shapes/shared/PlainTextLabel.mjs +14 -2
- package/dist-esm/lib/shapes/shared/PlainTextLabel.mjs.map +2 -2
- package/dist-esm/lib/shapes/shared/RichTextLabel.mjs +12 -4
- package/dist-esm/lib/shapes/shared/RichTextLabel.mjs.map +2 -2
- package/dist-esm/lib/shapes/shared/ShapeFill.mjs +2 -2
- package/dist-esm/lib/shapes/shared/ShapeFill.mjs.map +2 -2
- package/dist-esm/lib/shapes/shared/crop.mjs +1 -0
- package/dist-esm/lib/shapes/shared/crop.mjs.map +2 -2
- package/dist-esm/lib/shapes/shared/interpolate-props.mjs +4 -4
- package/dist-esm/lib/shapes/shared/interpolate-props.mjs.map +2 -2
- package/dist-esm/lib/shapes/shared/useEditablePlainText.mjs.map +2 -2
- package/dist-esm/lib/shapes/shared/useEditableRichText.mjs.map +2 -2
- package/dist-esm/lib/shapes/shared/useEfficientZoomThreshold.mjs +12 -0
- package/dist-esm/lib/shapes/shared/useEfficientZoomThreshold.mjs.map +7 -0
- package/dist-esm/lib/shapes/shared/useImageOrVideoAsset.mjs +1 -1
- package/dist-esm/lib/shapes/shared/useImageOrVideoAsset.mjs.map +2 -2
- package/dist-esm/lib/shapes/text/RichTextArea.mjs +5 -0
- package/dist-esm/lib/shapes/text/RichTextArea.mjs.map +2 -2
- package/dist-esm/lib/shapes/text/TextShapeUtil.mjs +5 -2
- package/dist-esm/lib/shapes/text/TextShapeUtil.mjs.map +2 -2
- package/dist-esm/lib/shapes/text/toolStates/Idle.mjs +4 -10
- package/dist-esm/lib/shapes/text/toolStates/Idle.mjs.map +2 -2
- package/dist-esm/lib/shapes/text/toolStates/Pointing.mjs +7 -5
- package/dist-esm/lib/shapes/text/toolStates/Pointing.mjs.map +2 -2
- package/dist-esm/lib/shapes/video/VideoShapeUtil.mjs +1 -1
- package/dist-esm/lib/shapes/video/VideoShapeUtil.mjs.map +2 -2
- package/dist-esm/lib/tools/EraserTool/childStates/Erasing.mjs +13 -6
- package/dist-esm/lib/tools/EraserTool/childStates/Erasing.mjs.map +2 -2
- package/dist-esm/lib/tools/EraserTool/childStates/Pointing.mjs +3 -8
- package/dist-esm/lib/tools/EraserTool/childStates/Pointing.mjs.map +2 -2
- package/dist-esm/lib/tools/HandTool/HandTool.mjs +3 -5
- package/dist-esm/lib/tools/HandTool/HandTool.mjs.map +2 -2
- package/dist-esm/lib/tools/HandTool/childStates/Dragging.mjs +3 -2
- package/dist-esm/lib/tools/HandTool/childStates/Dragging.mjs.map +2 -2
- package/dist-esm/lib/tools/HandTool/childStates/Pointing.mjs +1 -1
- package/dist-esm/lib/tools/HandTool/childStates/Pointing.mjs.map +2 -2
- package/dist-esm/lib/tools/LaserTool/childStates/Lasering.mjs +1 -1
- package/dist-esm/lib/tools/LaserTool/childStates/Lasering.mjs.map +2 -2
- package/dist-esm/lib/tools/SelectTool/DragAndDropManager.mjs +10 -11
- package/dist-esm/lib/tools/SelectTool/DragAndDropManager.mjs.map +2 -2
- package/dist-esm/lib/tools/SelectTool/childStates/Brushing.mjs +20 -6
- package/dist-esm/lib/tools/SelectTool/childStates/Brushing.mjs.map +3 -3
- package/dist-esm/lib/tools/SelectTool/childStates/Crop/children/Cropping.mjs +4 -6
- 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/PointingCrop.mjs +1 -1
- package/dist-esm/lib/tools/SelectTool/childStates/Crop/children/PointingCrop.mjs.map +2 -2
- package/dist-esm/lib/tools/SelectTool/childStates/Crop/children/PointingCropHandle.mjs +1 -1
- package/dist-esm/lib/tools/SelectTool/childStates/Crop/children/PointingCropHandle.mjs.map +2 -2
- package/dist-esm/lib/tools/SelectTool/childStates/Crop/children/TranslatingCrop.mjs +2 -1
- package/dist-esm/lib/tools/SelectTool/childStates/Crop/children/TranslatingCrop.mjs.map +2 -2
- package/dist-esm/lib/tools/SelectTool/childStates/Crop/children/crop_helpers.mjs +1 -1
- package/dist-esm/lib/tools/SelectTool/childStates/Crop/children/crop_helpers.mjs.map +2 -2
- package/dist-esm/lib/tools/SelectTool/childStates/DraggingHandle.mjs +8 -6
- package/dist-esm/lib/tools/SelectTool/childStates/DraggingHandle.mjs.map +2 -2
- package/dist-esm/lib/tools/SelectTool/childStates/EditingShape.mjs +38 -11
- package/dist-esm/lib/tools/SelectTool/childStates/EditingShape.mjs.map +2 -2
- package/dist-esm/lib/tools/SelectTool/childStates/Idle.mjs +43 -51
- package/dist-esm/lib/tools/SelectTool/childStates/Idle.mjs.map +2 -2
- package/dist-esm/lib/tools/SelectTool/childStates/PointingArrowLabel.mjs +6 -6
- package/dist-esm/lib/tools/SelectTool/childStates/PointingArrowLabel.mjs.map +2 -2
- package/dist-esm/lib/tools/SelectTool/childStates/PointingCanvas.mjs +1 -1
- package/dist-esm/lib/tools/SelectTool/childStates/PointingCanvas.mjs.map +2 -2
- package/dist-esm/lib/tools/SelectTool/childStates/PointingHandle.mjs +6 -19
- package/dist-esm/lib/tools/SelectTool/childStates/PointingHandle.mjs.map +2 -2
- package/dist-esm/lib/tools/SelectTool/childStates/PointingResizeHandle.mjs +1 -1
- package/dist-esm/lib/tools/SelectTool/childStates/PointingResizeHandle.mjs.map +2 -2
- package/dist-esm/lib/tools/SelectTool/childStates/PointingRotateHandle.mjs +1 -1
- package/dist-esm/lib/tools/SelectTool/childStates/PointingRotateHandle.mjs.map +2 -2
- package/dist-esm/lib/tools/SelectTool/childStates/PointingSelection.mjs +2 -2
- package/dist-esm/lib/tools/SelectTool/childStates/PointingSelection.mjs.map +2 -2
- package/dist-esm/lib/tools/SelectTool/childStates/PointingShape.mjs +4 -13
- package/dist-esm/lib/tools/SelectTool/childStates/PointingShape.mjs.map +2 -2
- package/dist-esm/lib/tools/SelectTool/childStates/Resizing.mjs +5 -6
- package/dist-esm/lib/tools/SelectTool/childStates/Resizing.mjs.map +2 -2
- package/dist-esm/lib/tools/SelectTool/childStates/Rotating.mjs +2 -3
- package/dist-esm/lib/tools/SelectTool/childStates/Rotating.mjs.map +2 -2
- package/dist-esm/lib/tools/SelectTool/childStates/ScribbleBrushing.mjs +23 -8
- package/dist-esm/lib/tools/SelectTool/childStates/ScribbleBrushing.mjs.map +3 -3
- package/dist-esm/lib/tools/SelectTool/childStates/Translating.mjs +13 -11
- package/dist-esm/lib/tools/SelectTool/childStates/Translating.mjs.map +2 -2
- package/dist-esm/lib/tools/SelectTool/selectHelpers.mjs +17 -4
- package/dist-esm/lib/tools/SelectTool/selectHelpers.mjs.map +2 -2
- package/dist-esm/lib/tools/ZoomTool/ZoomTool.mjs +1 -1
- package/dist-esm/lib/tools/ZoomTool/ZoomTool.mjs.map +2 -2
- package/dist-esm/lib/tools/ZoomTool/childStates/Pointing.mjs +3 -3
- package/dist-esm/lib/tools/ZoomTool/childStates/Pointing.mjs.map +2 -2
- package/dist-esm/lib/tools/ZoomTool/childStates/ZoomBrushing.mjs +5 -6
- package/dist-esm/lib/tools/ZoomTool/childStates/ZoomBrushing.mjs.map +2 -2
- package/dist-esm/lib/tools/selection-logic/getHitShapeOnCanvasPointerDown.mjs +1 -3
- package/dist-esm/lib/tools/selection-logic/getHitShapeOnCanvasPointerDown.mjs.map +2 -2
- package/dist-esm/lib/tools/selection-logic/selectOnCanvasPointerUp.mjs +1 -1
- package/dist-esm/lib/tools/selection-logic/selectOnCanvasPointerUp.mjs.map +2 -2
- package/dist-esm/lib/tools/selection-logic/updateHoveredShapeId.mjs +1 -1
- package/dist-esm/lib/tools/selection-logic/updateHoveredShapeId.mjs.map +2 -2
- package/dist-esm/lib/ui/TldrawUi.mjs +2 -2
- package/dist-esm/lib/ui/TldrawUi.mjs.map +2 -2
- package/dist-esm/lib/ui/components/ActionsMenu/DefaultActionsMenuContent.mjs +2 -8
- package/dist-esm/lib/ui/components/ActionsMenu/DefaultActionsMenuContent.mjs.map +2 -2
- package/dist-esm/lib/ui/components/ContextMenu/DefaultContextMenu.mjs +1 -3
- package/dist-esm/lib/ui/components/ContextMenu/DefaultContextMenu.mjs.map +2 -2
- package/dist-esm/lib/ui/components/CursorChatBubble.mjs +1 -1
- package/dist-esm/lib/ui/components/CursorChatBubble.mjs.map +2 -2
- package/dist-esm/lib/ui/components/DefaultDebugPanel.mjs +4 -31
- package/dist-esm/lib/ui/components/DefaultDebugPanel.mjs.map +2 -2
- package/dist-esm/lib/ui/components/EditLinkDialog.mjs +11 -1
- package/dist-esm/lib/ui/components/EditLinkDialog.mjs.map +2 -2
- package/dist-esm/lib/ui/components/HelperButtons/BackToContent.mjs +1 -1
- package/dist-esm/lib/ui/components/HelperButtons/BackToContent.mjs.map +2 -2
- package/dist-esm/lib/ui/components/HelperButtons/StopFollowing.mjs.map +2 -2
- package/dist-esm/lib/ui/components/Minimap/DefaultMinimap.mjs +1 -1
- package/dist-esm/lib/ui/components/Minimap/DefaultMinimap.mjs.map +2 -2
- package/dist-esm/lib/ui/components/OfflineIndicator/OfflineIndicator.mjs +3 -6
- package/dist-esm/lib/ui/components/OfflineIndicator/OfflineIndicator.mjs.map +2 -2
- package/dist-esm/lib/ui/components/PageMenu/PageItemInput.mjs +3 -1
- package/dist-esm/lib/ui/components/PageMenu/PageItemInput.mjs.map +2 -2
- package/dist-esm/lib/ui/components/SharePanel/PeopleMenu.mjs +6 -0
- package/dist-esm/lib/ui/components/SharePanel/PeopleMenu.mjs.map +2 -2
- package/dist-esm/lib/ui/components/Toolbar/AltTextEditor.mjs.map +2 -2
- package/dist-esm/lib/ui/components/Toolbar/DefaultImageToolbar.mjs +1 -1
- package/dist-esm/lib/ui/components/Toolbar/DefaultImageToolbar.mjs.map +2 -2
- package/dist-esm/lib/ui/components/Toolbar/DefaultRichTextToolbar.mjs +1 -1
- package/dist-esm/lib/ui/components/Toolbar/DefaultRichTextToolbar.mjs.map +2 -2
- package/dist-esm/lib/ui/components/TopPanel/CenteredTopPanelContainer.mjs.map +1 -1
- package/dist-esm/lib/ui/components/menu-items.mjs +4 -5
- package/dist-esm/lib/ui/components/menu-items.mjs.map +2 -2
- package/dist-esm/lib/ui/components/primitives/TldrawUiContextualToolbar.mjs +3 -1
- package/dist-esm/lib/ui/components/primitives/TldrawUiContextualToolbar.mjs.map +2 -2
- package/dist-esm/lib/ui/components/primitives/TldrawUiSlider.mjs +2 -2
- package/dist-esm/lib/ui/components/primitives/TldrawUiSlider.mjs.map +2 -2
- package/dist-esm/lib/ui/components/primitives/TldrawUiTooltip.mjs +157 -95
- package/dist-esm/lib/ui/components/primitives/TldrawUiTooltip.mjs.map +2 -2
- package/dist-esm/lib/ui/components/primitives/menus/TldrawUiMenuActionCheckboxItem.mjs.map +2 -2
- package/dist-esm/lib/ui/components/primitives/menus/TldrawUiMenuActionItem.mjs.map +2 -2
- package/dist-esm/lib/ui/components/primitives/menus/TldrawUiMenuItem.mjs +15 -8
- package/dist-esm/lib/ui/components/primitives/menus/TldrawUiMenuItem.mjs.map +2 -2
- package/dist-esm/lib/ui/context/actions.mjs +7 -8
- package/dist-esm/lib/ui/context/actions.mjs.map +2 -2
- package/dist-esm/lib/ui/context/components.mjs +1 -2
- package/dist-esm/lib/ui/context/components.mjs.map +2 -2
- package/dist-esm/lib/ui/hooks/menu-hooks.mjs +1 -4
- package/dist-esm/lib/ui/hooks/menu-hooks.mjs.map +2 -2
- package/dist-esm/lib/ui/hooks/useClipboardEvents.mjs +2 -2
- package/dist-esm/lib/ui/hooks/useClipboardEvents.mjs.map +2 -2
- package/dist-esm/lib/ui/hooks/useFlatten.mjs.map +2 -2
- package/dist-esm/lib/ui/hooks/useKeyboardShortcuts.mjs +2 -2
- package/dist-esm/lib/ui/hooks/useKeyboardShortcuts.mjs.map +2 -2
- package/dist-esm/lib/ui/hooks/useTools.mjs +4 -5
- package/dist-esm/lib/ui/hooks/useTools.mjs.map +2 -2
- package/dist-esm/lib/ui/version.mjs +3 -3
- package/dist-esm/lib/ui/version.mjs.map +1 -1
- package/dist-esm/lib/utils/excalidraw/putExcalidrawContent.mjs +17 -6
- package/dist-esm/lib/utils/excalidraw/putExcalidrawContent.mjs.map +2 -2
- package/dist-esm/lib/utils/export/exportAs.mjs +1 -3
- package/dist-esm/lib/utils/export/exportAs.mjs.map +2 -2
- package/dist-esm/lib/utils/frames/frames.mjs.map +2 -2
- package/dist-esm/lib/utils/test-helpers.mjs +21 -0
- package/dist-esm/lib/utils/test-helpers.mjs.map +7 -0
- package/dist-esm/lib/utils/text/richText.mjs +12 -5
- package/dist-esm/lib/utils/text/richText.mjs.map +2 -2
- package/dist-esm/lib/utils/tldr/buildFromV1Document.mjs +8 -2
- package/dist-esm/lib/utils/tldr/buildFromV1Document.mjs.map +2 -2
- package/package.json +18 -16
- package/src/index.ts +6 -2
- package/src/lib/Tldraw.test.tsx +46 -1
- package/src/lib/bindings/arrow/ArrowBindingUtil.ts +1 -1
- package/src/lib/canvas/TldrawSelectionForeground.tsx +6 -11
- package/src/lib/defaultEmbedDefinitions.ts +2 -1
- package/src/lib/defaultExternalContentHandlers.ts +13 -14
- package/src/lib/defaultSideEffects.ts +6 -1
- package/src/lib/shapes/arrow/ArrowShapeOptions.test.ts +40 -133
- package/src/lib/shapes/arrow/ArrowShapeTool.test.ts +8 -8
- package/src/lib/shapes/arrow/ArrowShapeUtil.test.ts +2 -2
- package/src/lib/shapes/arrow/ArrowShapeUtil.tsx +16 -16
- package/src/lib/shapes/arrow/arrow-types.ts +2 -0
- package/src/lib/shapes/arrow/arrowLabel.ts +2 -2
- package/src/lib/shapes/arrow/arrowTargetState.ts +2 -2
- package/src/lib/shapes/arrow/elbow/elbowArrowSnapLines.tsx +3 -3
- package/src/lib/shapes/arrow/elbow/getElbowArrowInfo.test.ts +80 -0
- package/src/lib/shapes/arrow/elbow/getElbowArrowInfo.tsx +1 -1
- package/src/lib/shapes/arrow/shared.ts +4 -4
- package/src/lib/shapes/arrow/toolStates/Idle.tsx +4 -14
- package/src/lib/shapes/arrow/toolStates/Pointing.tsx +8 -5
- package/src/lib/shapes/bookmark/BookmarkShapeUtil.tsx +1 -1
- package/src/lib/shapes/bookmark/bookmarks.ts +3 -3
- package/src/lib/shapes/draw/DrawShapeUtil.test.ts +146 -0
- package/src/lib/shapes/draw/DrawShapeUtil.tsx +33 -27
- package/src/lib/shapes/draw/getPath.ts +31 -10
- package/src/lib/shapes/draw/toolStates/Drawing.ts +100 -90
- package/src/lib/shapes/embed/EmbedShapeUtil.tsx +8 -1
- package/src/lib/shapes/frame/FrameShapeTool.ts +1 -1
- package/src/lib/shapes/frame/FrameShapeUtil.tsx +11 -11
- package/src/lib/shapes/geo/GeoShapeUtil.test.tsx +10 -2
- package/src/lib/shapes/geo/GeoShapeUtil.tsx +228 -176
- package/src/lib/shapes/geo/toolStates/Idle.ts +5 -15
- package/src/lib/shapes/geo/toolStates/Pointing.ts +6 -6
- package/src/lib/shapes/highlight/HighlightShapeUtil.test.ts +146 -0
- package/src/lib/shapes/highlight/HighlightShapeUtil.tsx +27 -24
- package/src/lib/shapes/line/LineShapeTool.test.ts +6 -6
- package/src/lib/shapes/line/LineShapeUtil.test.tsx +5 -5
- package/src/lib/shapes/line/toolStates/Pointing.ts +4 -4
- package/src/lib/shapes/note/NoteShapeTool.test.ts +2 -1
- package/src/lib/shapes/note/NoteShapeUtil.tsx +9 -10
- package/src/lib/shapes/note/noteCloning.test.ts +3 -1
- package/src/lib/shapes/note/noteHelpers.ts +2 -2
- package/src/lib/shapes/note/toolStates/Pointing.ts +5 -10
- package/src/lib/shapes/shared/HyperlinkButton.tsx +4 -3
- package/src/lib/shapes/shared/PlainTextLabel.tsx +12 -2
- package/src/lib/shapes/shared/RichTextLabel.tsx +14 -4
- package/src/lib/shapes/shared/ShapeFill.tsx +2 -2
- package/src/lib/shapes/shared/crop.ts +1 -0
- package/src/lib/shapes/shared/interpolate-props.ts +4 -4
- package/src/lib/shapes/shared/useEditablePlainText.ts +7 -3
- package/src/lib/shapes/shared/useEditableRichText.ts +7 -3
- package/src/lib/shapes/shared/useEfficientZoomThreshold.ts +10 -0
- package/src/lib/shapes/shared/useImageOrVideoAsset.ts +1 -1
- package/src/lib/shapes/text/RichTextArea.tsx +5 -0
- package/src/lib/shapes/text/TextShapeTool.test.ts +4 -4
- package/src/lib/shapes/text/TextShapeUtil.tsx +5 -0
- package/src/lib/shapes/text/toolStates/Idle.ts +4 -14
- package/src/lib/shapes/text/toolStates/Pointing.ts +8 -8
- package/src/lib/shapes/video/VideoShapeUtil.tsx +2 -1
- package/src/lib/tools/EraserTool/childStates/Erasing.ts +21 -11
- package/src/lib/tools/EraserTool/childStates/Pointing.ts +5 -20
- package/src/lib/tools/HandTool/HandTool.ts +3 -5
- package/src/lib/tools/HandTool/childStates/Dragging.ts +3 -2
- package/src/lib/tools/HandTool/childStates/Pointing.ts +1 -1
- package/src/lib/tools/LaserTool/childStates/Lasering.ts +1 -1
- package/src/lib/tools/SelectTool/DragAndDropManager.ts +14 -11
- package/src/lib/tools/SelectTool/childStates/Brushing.ts +30 -14
- package/src/lib/tools/SelectTool/childStates/Crop/children/Cropping.ts +7 -6
- package/src/lib/tools/SelectTool/childStates/Crop/children/Idle.ts +3 -4
- package/src/lib/tools/SelectTool/childStates/Crop/children/PointingCrop.ts +1 -1
- package/src/lib/tools/SelectTool/childStates/Crop/children/PointingCropHandle.ts +1 -1
- package/src/lib/tools/SelectTool/childStates/Crop/children/TranslatingCrop.ts +2 -1
- package/src/lib/tools/SelectTool/childStates/Crop/children/crop_helpers.ts +1 -1
- package/src/lib/tools/SelectTool/childStates/DraggingHandle.tsx +11 -12
- package/src/lib/tools/SelectTool/childStates/EditingShape.ts +57 -16
- package/src/lib/tools/SelectTool/childStates/Idle.ts +64 -81
- package/src/lib/tools/SelectTool/childStates/PointingArrowLabel.ts +7 -8
- package/src/lib/tools/SelectTool/childStates/PointingCanvas.ts +1 -1
- package/src/lib/tools/SelectTool/childStates/PointingHandle.ts +9 -17
- package/src/lib/tools/SelectTool/childStates/PointingResizeHandle.ts +1 -1
- package/src/lib/tools/SelectTool/childStates/PointingRotateHandle.ts +1 -1
- package/src/lib/tools/SelectTool/childStates/PointingSelection.ts +4 -4
- package/src/lib/tools/SelectTool/childStates/PointingShape.ts +4 -14
- package/src/lib/tools/SelectTool/childStates/Resizing.ts +8 -10
- package/src/lib/tools/SelectTool/childStates/Rotating.ts +2 -3
- package/src/lib/tools/SelectTool/childStates/ScribbleBrushing.ts +34 -14
- package/src/lib/tools/SelectTool/childStates/Translating.ts +16 -15
- package/src/lib/tools/SelectTool/selectHelpers.ts +39 -4
- package/src/lib/tools/ZoomTool/ZoomTool.ts +1 -1
- package/src/lib/tools/ZoomTool/childStates/Pointing.ts +3 -3
- package/src/lib/tools/ZoomTool/childStates/ZoomBrushing.ts +5 -6
- package/src/lib/tools/selection-logic/getHitShapeOnCanvasPointerDown.ts +1 -3
- package/src/lib/tools/selection-logic/selectOnCanvasPointerUp.ts +1 -1
- package/src/lib/tools/selection-logic/updateHoveredShapeId.ts +1 -1
- package/src/lib/ui/TldrawUi.tsx +5 -2
- package/src/lib/ui/components/ActionsMenu/DefaultActionsMenuContent.tsx +1 -9
- package/src/lib/ui/components/ContextMenu/DefaultContextMenu.tsx +1 -3
- package/src/lib/ui/components/CursorChatBubble.tsx +2 -2
- package/src/lib/ui/components/DefaultDebugPanel.tsx +4 -43
- package/src/lib/ui/components/EditLinkDialog.tsx +16 -6
- package/src/lib/ui/components/HelperButtons/BackToContent.tsx +1 -1
- package/src/lib/ui/components/HelperButtons/StopFollowing.tsx +2 -2
- package/src/lib/ui/components/Minimap/DefaultMinimap.tsx +1 -1
- package/src/lib/ui/components/OfflineIndicator/OfflineIndicator.tsx +6 -5
- package/src/lib/ui/components/PageMenu/PageItemInput.tsx +3 -1
- package/src/lib/ui/components/SharePanel/PeopleMenu.tsx +8 -0
- package/src/lib/ui/components/Toolbar/AltTextEditor.tsx +2 -2
- package/src/lib/ui/components/Toolbar/DefaultImageToolbar.tsx +1 -1
- package/src/lib/ui/components/Toolbar/DefaultRichTextToolbar.tsx +1 -1
- package/src/lib/ui/components/TopPanel/CenteredTopPanelContainer.tsx +1 -1
- package/src/lib/ui/components/menu-items.tsx +9 -15
- package/src/lib/ui/components/primitives/TldrawUiContextualToolbar.tsx +5 -3
- package/src/lib/ui/components/primitives/TldrawUiSlider.tsx +2 -2
- package/src/lib/ui/components/primitives/TldrawUiTooltip.tsx +208 -113
- package/src/lib/ui/components/primitives/menus/TldrawUiMenuActionCheckboxItem.tsx +1 -1
- package/src/lib/ui/components/primitives/menus/TldrawUiMenuActionItem.tsx +1 -1
- package/src/lib/ui/components/primitives/menus/TldrawUiMenuItem.tsx +7 -4
- package/src/lib/ui/context/actions.tsx +15 -19
- package/src/lib/ui/context/components.tsx +1 -2
- package/src/lib/ui/hooks/menu-hooks.ts +9 -19
- package/src/lib/ui/hooks/useClipboardEvents.ts +2 -2
- package/src/lib/ui/hooks/useFlatten.ts +1 -2
- package/src/lib/ui/hooks/useKeyboardShortcuts.ts +2 -2
- package/src/lib/ui/hooks/useTools.tsx +5 -7
- package/src/lib/ui/version.ts +3 -3
- package/src/lib/ui.css +27 -23
- package/src/lib/utils/excalidraw/__snapshots__/putExcalidrawContent.test.tsx.snap +12 -48
- package/src/lib/utils/excalidraw/putExcalidrawContent.ts +19 -6
- package/src/lib/utils/export/exportAs.ts +2 -9
- package/src/lib/utils/frames/frames.ts +1 -1
- package/src/lib/utils/test-helpers.ts +62 -0
- package/src/lib/utils/text/richText.ts +13 -8
- package/src/lib/utils/tldr/buildFromV1Document.ts +21 -19
- package/src/test/Editor.test.tsx +78 -41
- package/src/test/EraserTool.test.ts +10 -12
- package/src/test/SelectTool.test.ts +11 -19
- package/src/test/TestEditor.ts +49 -51
- package/src/test/TldrawEditor.test.tsx +24 -20
- package/src/test/__snapshots__/drawing.test.ts.snap +3 -1257
- package/src/test/__snapshots__/resizing.test.ts.snap +3 -12
- package/src/test/arrows-megabus.test.tsx +1 -1
- package/src/test/bindings.test.tsx +29 -25
- package/src/test/bindingsIndex.test.tsx +4 -4
- package/src/test/commands/__snapshots__/getSvgString.test.ts.snap +10 -10
- package/src/test/commands/cameraState.test.ts +299 -0
- package/src/test/commands/createShape.test.ts +64 -0
- package/src/test/commands/createShapes.test.ts +15 -1
- package/src/test/commands/getSvgString.test.ts +2 -2
- package/src/test/commands/isShapeOfType.test.ts +44 -0
- package/src/test/commands/putContent.test.ts +80 -1
- package/src/test/commands/setCamera.test.ts +13 -11
- package/src/test/commands/stackShapes.test.ts +34 -8
- package/src/test/commands/updateShape.test.ts +67 -0
- package/src/test/commands/updateShapes.test.ts +21 -5
- package/src/test/commands/zoomToBounds.test.ts +19 -3
- package/src/test/commands/zoomToSelection.test.ts +14 -3
- package/src/test/custom-clipping.test.ts +52 -44
- package/src/test/customSnapping.test.tsx +77 -62
- package/src/test/drawing.test.ts +17 -10
- package/src/test/duplicate.test.ts +1 -1
- package/src/test/flipShapes.test.ts +33 -0
- package/src/test/frames.test.ts +94 -2
- package/src/test/getCulledShapes.test.tsx +11 -3
- package/src/test/getShapeAtPoint.test.ts +2 -2
- package/src/test/groups.test.tsx +7 -4
- package/src/test/modifiers.test.ts +6 -6
- package/src/test/notVisibleShapes.test.ts +698 -0
- package/src/test/resizing.test.ts +16 -22
- package/src/test/selection-omnibus.test.ts +13 -13
- package/src/test/shapeutils.test.ts +1 -1
- package/src/test/spacebarPanning.test.ts +28 -10
- package/src/test/styles2.test.tsx +1 -1
- package/src/test/styles3.test.ts +5 -5
- package/src/test/test-jsx.tsx +72 -57
- package/src/test/text.test.ts +15 -17
- package/src/test/translating.test.ts +6 -8
- package/src/test/ui/BackToContent.test.tsx +111 -0
- package/tldraw.css +41 -35
- package/dist-cjs/lib/shapes/shared/useForceSolid.js.map +0 -7
- package/dist-cjs/lib/tools/selection-logic/getShouldEnterCropModeOnPointerDown.js.map +0 -7
- package/dist-cjs/lib/ui/components/TopPanel/DefaultTopPanel.js +0 -32
- package/dist-cjs/lib/ui/components/TopPanel/DefaultTopPanel.js.map +0 -7
- package/dist-cjs/lib/utils/text/textDirection.js +0 -51
- package/dist-cjs/lib/utils/text/textDirection.js.map +0 -7
- package/dist-esm/lib/shapes/shared/useForceSolid.mjs +0 -9
- package/dist-esm/lib/shapes/shared/useForceSolid.mjs.map +0 -7
- package/dist-esm/lib/tools/selection-logic/getShouldEnterCropModeOnPointerDown.mjs +0 -8
- package/dist-esm/lib/tools/selection-logic/getShouldEnterCropModeOnPointerDown.mjs.map +0 -7
- package/dist-esm/lib/ui/components/TopPanel/DefaultTopPanel.mjs +0 -12
- package/dist-esm/lib/ui/components/TopPanel/DefaultTopPanel.mjs.map +0 -7
- package/dist-esm/lib/utils/text/textDirection.mjs +0 -31
- package/dist-esm/lib/utils/text/textDirection.mjs.map +0 -7
- package/src/lib/shapes/shared/useForceSolid.ts +0 -6
- package/src/lib/tools/selection-logic/getShouldEnterCropModeOnPointerDown.ts +0 -10
- package/src/lib/ui/components/TopPanel/DefaultTopPanel.tsx +0 -10
- package/src/lib/utils/text/textDirection.ts +0 -32
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../src/lib/shapes/shared/useEditableRichText.ts"],
|
|
4
|
-
"sourcesContent": ["import { TLRichText, TLShapeId,
|
|
5
|
-
"mappings": ";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,
|
|
4
|
+
"sourcesContent": ["import { ExtractShapeByProps, TLRichText, TLShapeId, isAccelKey, useEditor } from '@tldraw/editor'\nimport { useCallback, useEffect, useRef } from 'react'\nimport { isEmptyRichText } from '../../utils/text/richText'\nimport { useEditableTextCommon } from './useEditablePlainText'\n\n/** @public */\nexport function useEditableRichText(\n\tshapeId: TLShapeId,\n\ttype: ExtractShapeByProps<{ richText: TLRichText }>['type'],\n\trichText?: TLRichText\n) {\n\tconst commonUseEditableTextHandlers = useEditableTextCommon(shapeId)\n\tconst isEditing = commonUseEditableTextHandlers.isEditing\n\tconst editor = useEditor()\n\tconst rInput = useRef<HTMLDivElement>(null)\n\tconst isEmpty = richText && isEmptyRichText(richText)\n\n\tuseEffect(() => {\n\t\tif (!isEditing) return\n\n\t\t// N.B. In Development mode you need to ensure you're testing this without StrictMode on.\n\t\t// Otherwise it's not gonna work as expected on iOS.\n\t\tconst contentEditable = rInput.current?.querySelector('[contenteditable]')\n\t\tif (contentEditable && document.activeElement !== rInput.current) {\n\t\t\t// This is a crucial difference with useEditablePlainText, that we need to select the\n\t\t\t// child contentEditable <div> not rInput.current directly.\n\t\t\t// Specifically, this is to ensure iOS works. Otherwise, we could just use rInput.current.\n\t\t\t;(contentEditable as HTMLElement).focus()\n\t\t}\n\t}, [editor, isEditing])\n\n\t// When the user presses ctrl / meta enter, complete the editing state.\n\tconst handleKeyDown = useCallback(\n\t\t(e: KeyboardEvent) => {\n\t\t\tif (editor.getEditingShapeId() !== shapeId) return\n\t\t\tif (e.key === 'Enter' && isAccelKey(e)) editor.complete()\n\t\t},\n\t\t[editor, shapeId]\n\t)\n\n\t// When the text changes, update the text value.\n\tconst handleChange = useCallback(\n\t\t({ richText }: { richText: TLRichText }) => {\n\t\t\tif (editor.getEditingShapeId() !== shapeId) return\n\n\t\t\teditor.updateShape({\n\t\t\t\tid: shapeId,\n\t\t\t\ttype,\n\t\t\t\tprops: { richText },\n\t\t\t})\n\t\t},\n\t\t[editor, shapeId, type]\n\t)\n\n\treturn {\n\t\trInput,\n\t\thandleKeyDown,\n\t\thandleChange,\n\t\tisEmpty,\n\t\t...commonUseEditableTextHandlers,\n\t}\n}\n"],
|
|
5
|
+
"mappings": ";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,oBAAkF;AAClF,mBAA+C;AAC/C,sBAAgC;AAChC,kCAAsC;AAG/B,SAAS,oBACf,SACA,MACA,UACC;AACD,QAAM,oCAAgC,mDAAsB,OAAO;AACnE,QAAM,YAAY,8BAA8B;AAChD,QAAM,aAAS,yBAAU;AACzB,QAAM,aAAS,qBAAuB,IAAI;AAC1C,QAAM,UAAU,gBAAY,iCAAgB,QAAQ;AAEpD,8BAAU,MAAM;AACf,QAAI,CAAC,UAAW;AAIhB,UAAM,kBAAkB,OAAO,SAAS,cAAc,mBAAmB;AACzE,QAAI,mBAAmB,SAAS,kBAAkB,OAAO,SAAS;AAIjE;AAAC,MAAC,gBAAgC,MAAM;AAAA,IACzC;AAAA,EACD,GAAG,CAAC,QAAQ,SAAS,CAAC;AAGtB,QAAM,oBAAgB;AAAA,IACrB,CAAC,MAAqB;AACrB,UAAI,OAAO,kBAAkB,MAAM,QAAS;AAC5C,UAAI,EAAE,QAAQ,eAAW,0BAAW,CAAC,EAAG,QAAO,SAAS;AAAA,IACzD;AAAA,IACA,CAAC,QAAQ,OAAO;AAAA,EACjB;AAGA,QAAM,mBAAe;AAAA,IACpB,CAAC,EAAE,UAAAA,UAAS,MAAgC;AAC3C,UAAI,OAAO,kBAAkB,MAAM,QAAS;AAE5C,aAAO,YAAY;AAAA,QAClB,IAAI;AAAA,QACJ;AAAA,QACA,OAAO,EAAE,UAAAA,UAAS;AAAA,MACnB,CAAC;AAAA,IACF;AAAA,IACA,CAAC,QAAQ,SAAS,IAAI;AAAA,EACvB;AAEA,SAAO;AAAA,IACN;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,GAAG;AAAA,EACJ;AACD;",
|
|
6
6
|
"names": ["richText"]
|
|
7
7
|
}
|
|
@@ -16,14 +16,17 @@ var __copyProps = (to, from, except, desc) => {
|
|
|
16
16
|
return to;
|
|
17
17
|
};
|
|
18
18
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
-
var
|
|
20
|
-
__export(
|
|
21
|
-
|
|
19
|
+
var useEfficientZoomThreshold_exports = {};
|
|
20
|
+
__export(useEfficientZoomThreshold_exports, {
|
|
21
|
+
useEfficientZoomThreshold: () => useEfficientZoomThreshold
|
|
22
22
|
});
|
|
23
|
-
module.exports = __toCommonJS(
|
|
23
|
+
module.exports = __toCommonJS(useEfficientZoomThreshold_exports);
|
|
24
24
|
var import_editor = require("@tldraw/editor");
|
|
25
|
-
function
|
|
25
|
+
function useEfficientZoomThreshold(threshold = 0.25) {
|
|
26
26
|
const editor = (0, import_editor.useEditor)();
|
|
27
|
-
return (0, import_editor.useValue)("zoom", () => editor.
|
|
27
|
+
return (0, import_editor.useValue)("efficient zoom threshold", () => editor.getEfficientZoomLevel() < threshold, [
|
|
28
|
+
editor,
|
|
29
|
+
threshold
|
|
30
|
+
]);
|
|
28
31
|
}
|
|
29
|
-
//# sourceMappingURL=
|
|
32
|
+
//# sourceMappingURL=useEfficientZoomThreshold.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../../src/lib/shapes/shared/useEfficientZoomThreshold.ts"],
|
|
4
|
+
"sourcesContent": ["import { useEditor, useValue } from '@tldraw/editor'\n\n/** Returns true when zoomed out far enough that shapes should render in a simplified \"solid\" style. */\nexport function useEfficientZoomThreshold(threshold = 0.25) {\n\tconst editor = useEditor()\n\treturn useValue('efficient zoom threshold', () => editor.getEfficientZoomLevel() < threshold, [\n\t\teditor,\n\t\tthreshold,\n\t])\n}\n"],
|
|
5
|
+
"mappings": ";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,oBAAoC;AAG7B,SAAS,0BAA0B,YAAY,MAAM;AAC3D,QAAM,aAAS,yBAAU;AACzB,aAAO,wBAAS,4BAA4B,MAAM,OAAO,sBAAsB,IAAI,WAAW;AAAA,IAC7F;AAAA,IACA;AAAA,EACD,CAAC;AACF;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -55,7 +55,7 @@ function useImageOrVideoAsset({ shapeId, assetId, width }) {
|
|
|
55
55
|
return;
|
|
56
56
|
}
|
|
57
57
|
}
|
|
58
|
-
const screenScale = exportInfo ? exportInfo.scale * (width / asset.props.w) : editor.
|
|
58
|
+
const screenScale = exportInfo ? exportInfo.scale * (width / asset.props.w) : editor.getEfficientZoomLevel() * (width / asset.props.w);
|
|
59
59
|
function resolve(asset2, url) {
|
|
60
60
|
if (isCancelled) return;
|
|
61
61
|
if (previousUrl.current === url) return;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../src/lib/shapes/shared/useImageOrVideoAsset.ts"],
|
|
4
|
-
"sourcesContent": ["import {\n\tEditor,\n\tSvgExportContext,\n\tTLAssetId,\n\tTLImageAsset,\n\tTLShapeId,\n\tTLVideoAsset,\n\treact,\n\tuseDelaySvgExport,\n\tuseEditor,\n\tuseSvgExportContext,\n} from '@tldraw/editor'\nimport { useEffect, useRef, useState } from 'react'\n\n/**\n * Options for {@link useImageOrVideoAsset}.\n *\n * @public\n */\nexport interface UseImageOrVideoAssetOptions {\n\t/** The asset ID you want a URL for. */\n\tassetId: TLAssetId | null\n\t/**\n\t * The shape the asset is being used for. We won't update the resolved URL while the shape is\n\t * off-screen.\n\t */\n\tshapeId?: TLShapeId\n\t/**\n\t * The width at which the asset will be displayed, in shape-space pixels.\n\t */\n\twidth: number\n}\n\n/**\n * This is a handy helper hook that resolves an asset to an optimized URL for a given shape, or its\n * {@link @tldraw/editor#Editor.createTemporaryAssetPreview | placeholder} if the asset is still\n * uploading. This is used in particular for high-resolution images when you want lower and higher\n * resolution depending on the size of the image on the canvas and the zoom level.\n *\n * For image scaling to work, you need to implement scaled URLs in\n * {@link @tldraw/tlschema#TLAssetStore.resolve}.\n *\n * @public\n */\nexport function useImageOrVideoAsset({ shapeId, assetId, width }: UseImageOrVideoAssetOptions) {\n\tconst editor = useEditor()\n\tconst exportInfo = useSvgExportContext()\n\tconst exportIsReady = useDelaySvgExport()\n\n\t// We use a state to store the result of the asset resolution, and we're going to avoid updating this whenever we can\n\tconst [result, setResult] = useState<{\n\t\tasset: (TLImageAsset | TLVideoAsset) | null\n\t\turl: string | null\n\t}>(() => ({\n\t\tasset: assetId ? (editor.getAsset<TLImageAsset | TLVideoAsset>(assetId) ?? null) : null,\n\t\turl: null as string | null,\n\t}))\n\n\t// A flag for whether we've resolved the asset URL at least once, after which we can debounce\n\tconst didAlreadyResolve = useRef(false)\n\n\t// The last URL that we've seen for the shape\n\tconst previousUrl = useRef<string | null>(null)\n\n\tuseEffect(() => {\n\t\tif (!assetId) return\n\n\t\tlet isCancelled = false\n\t\tlet cancelDebounceFn: (() => void) | undefined\n\n\t\tconst cleanupEffectScheduler = react('update state', () => {\n\t\t\tif (!exportInfo && shapeId && editor.getCulledShapes().has(shapeId)) return\n\n\t\t\t// Get the fresh asset\n\t\t\tconst asset = editor.getAsset<TLImageAsset | TLVideoAsset>(assetId)\n\t\t\tif (!asset) {\n\t\t\t\t// If the asset is deleted, such as when an upload fails, set the URL to null\n\t\t\t\tsetResult((prev) => ({ ...prev, asset: null, url: null }))\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\t// Set initial preview for the shape if it has no source (if it was pasted into a local project as base64)\n\t\t\tif (!asset.props.src) {\n\t\t\t\tconst preview = editor.getTemporaryAssetPreview(asset.id)\n\t\t\t\tif (preview) {\n\t\t\t\t\tif (previousUrl.current !== preview) {\n\t\t\t\t\t\tpreviousUrl.current = preview // just for kicks, let's save the url as the previous URL\n\t\t\t\t\t\tsetResult((prev) => ({ ...prev, isPlaceholder: true, url: preview })) // set the preview as the URL\n\t\t\t\t\t\texportIsReady() // let the SVG export know we're ready for export\n\t\t\t\t\t}\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// aside ...we could bail here if the only thing that has changed is the shape has changed from culled to not culled\n\n\t\t\tconst screenScale = exportInfo\n\t\t\t\t? exportInfo.scale * (width / asset.props.w)\n\t\t\t\t: editor.
|
|
5
|
-
"mappings": ";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,oBAWO;AACP,mBAA4C;AAgCrC,SAAS,qBAAqB,EAAE,SAAS,SAAS,MAAM,GAAgC;AAC9F,QAAM,aAAS,yBAAU;AACzB,QAAM,iBAAa,mCAAoB;AACvC,QAAM,oBAAgB,iCAAkB;AAGxC,QAAM,CAAC,QAAQ,SAAS,QAAI,uBAGzB,OAAO;AAAA,IACT,OAAO,UAAW,OAAO,SAAsC,OAAO,KAAK,OAAQ;AAAA,IACnF,KAAK;AAAA,EACN,EAAE;AAGF,QAAM,wBAAoB,qBAAO,KAAK;AAGtC,QAAM,kBAAc,qBAAsB,IAAI;AAE9C,8BAAU,MAAM;AACf,QAAI,CAAC,QAAS;AAEd,QAAI,cAAc;AAClB,QAAI;AAEJ,UAAM,6BAAyB,qBAAM,gBAAgB,MAAM;AAC1D,UAAI,CAAC,cAAc,WAAW,OAAO,gBAAgB,EAAE,IAAI,OAAO,EAAG;AAGrE,YAAM,QAAQ,OAAO,SAAsC,OAAO;AAClE,UAAI,CAAC,OAAO;AAEX,kBAAU,CAAC,UAAU,EAAE,GAAG,MAAM,OAAO,MAAM,KAAK,KAAK,EAAE;AACzD;AAAA,MACD;AAGA,UAAI,CAAC,MAAM,MAAM,KAAK;AACrB,cAAM,UAAU,OAAO,yBAAyB,MAAM,EAAE;AACxD,YAAI,SAAS;AACZ,cAAI,YAAY,YAAY,SAAS;AACpC,wBAAY,UAAU;AACtB,sBAAU,CAAC,UAAU,EAAE,GAAG,MAAM,eAAe,MAAM,KAAK,QAAQ,EAAE;AACpE,0BAAc;AAAA,UACf;AACA;AAAA,QACD;AAAA,MACD;AAIA,YAAM,cAAc,aACjB,WAAW,SAAS,QAAQ,MAAM,MAAM,KACxC,OAAO,
|
|
4
|
+
"sourcesContent": ["import {\n\tEditor,\n\tSvgExportContext,\n\tTLAssetId,\n\tTLImageAsset,\n\tTLShapeId,\n\tTLVideoAsset,\n\treact,\n\tuseDelaySvgExport,\n\tuseEditor,\n\tuseSvgExportContext,\n} from '@tldraw/editor'\nimport { useEffect, useRef, useState } from 'react'\n\n/**\n * Options for {@link useImageOrVideoAsset}.\n *\n * @public\n */\nexport interface UseImageOrVideoAssetOptions {\n\t/** The asset ID you want a URL for. */\n\tassetId: TLAssetId | null\n\t/**\n\t * The shape the asset is being used for. We won't update the resolved URL while the shape is\n\t * off-screen.\n\t */\n\tshapeId?: TLShapeId\n\t/**\n\t * The width at which the asset will be displayed, in shape-space pixels.\n\t */\n\twidth: number\n}\n\n/**\n * This is a handy helper hook that resolves an asset to an optimized URL for a given shape, or its\n * {@link @tldraw/editor#Editor.createTemporaryAssetPreview | placeholder} if the asset is still\n * uploading. This is used in particular for high-resolution images when you want lower and higher\n * resolution depending on the size of the image on the canvas and the zoom level.\n *\n * For image scaling to work, you need to implement scaled URLs in\n * {@link @tldraw/tlschema#TLAssetStore.resolve}.\n *\n * @public\n */\nexport function useImageOrVideoAsset({ shapeId, assetId, width }: UseImageOrVideoAssetOptions) {\n\tconst editor = useEditor()\n\tconst exportInfo = useSvgExportContext()\n\tconst exportIsReady = useDelaySvgExport()\n\n\t// We use a state to store the result of the asset resolution, and we're going to avoid updating this whenever we can\n\tconst [result, setResult] = useState<{\n\t\tasset: (TLImageAsset | TLVideoAsset) | null\n\t\turl: string | null\n\t}>(() => ({\n\t\tasset: assetId ? (editor.getAsset<TLImageAsset | TLVideoAsset>(assetId) ?? null) : null,\n\t\turl: null as string | null,\n\t}))\n\n\t// A flag for whether we've resolved the asset URL at least once, after which we can debounce\n\tconst didAlreadyResolve = useRef(false)\n\n\t// The last URL that we've seen for the shape\n\tconst previousUrl = useRef<string | null>(null)\n\n\tuseEffect(() => {\n\t\tif (!assetId) return\n\n\t\tlet isCancelled = false\n\t\tlet cancelDebounceFn: (() => void) | undefined\n\n\t\tconst cleanupEffectScheduler = react('update state', () => {\n\t\t\tif (!exportInfo && shapeId && editor.getCulledShapes().has(shapeId)) return\n\n\t\t\t// Get the fresh asset\n\t\t\tconst asset = editor.getAsset<TLImageAsset | TLVideoAsset>(assetId)\n\t\t\tif (!asset) {\n\t\t\t\t// If the asset is deleted, such as when an upload fails, set the URL to null\n\t\t\t\tsetResult((prev) => ({ ...prev, asset: null, url: null }))\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\t// Set initial preview for the shape if it has no source (if it was pasted into a local project as base64)\n\t\t\tif (!asset.props.src) {\n\t\t\t\tconst preview = editor.getTemporaryAssetPreview(asset.id)\n\t\t\t\tif (preview) {\n\t\t\t\t\tif (previousUrl.current !== preview) {\n\t\t\t\t\t\tpreviousUrl.current = preview // just for kicks, let's save the url as the previous URL\n\t\t\t\t\t\tsetResult((prev) => ({ ...prev, isPlaceholder: true, url: preview })) // set the preview as the URL\n\t\t\t\t\t\texportIsReady() // let the SVG export know we're ready for export\n\t\t\t\t\t}\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// aside ...we could bail here if the only thing that has changed is the shape has changed from culled to not culled\n\n\t\t\tconst screenScale = exportInfo\n\t\t\t\t? exportInfo.scale * (width / asset.props.w)\n\t\t\t\t: editor.getEfficientZoomLevel() * (width / asset.props.w)\n\n\t\t\tfunction resolve(asset: TLImageAsset | TLVideoAsset, url: string | null) {\n\t\t\t\tif (isCancelled) return // don't update if the hook has remounted\n\t\t\t\tif (previousUrl.current === url) return // don't update the state if the url is the same\n\t\t\t\tdidAlreadyResolve.current = true // mark that we've resolved our first image\n\t\t\t\tpreviousUrl.current = url // keep the url around to compare with the next one\n\t\t\t\tsetResult({ asset, url })\n\t\t\t\texportIsReady() // let the SVG export know we're ready for export\n\t\t\t}\n\n\t\t\t// If we already resolved the URL, debounce fetching potentially multiple image variations.\n\t\t\tif (didAlreadyResolve.current) {\n\t\t\t\tlet tick = 0\n\n\t\t\t\tconst resolveAssetAfterAWhile = () => {\n\t\t\t\t\ttick++\n\t\t\t\t\tif (tick > 500 / 16) {\n\t\t\t\t\t\t// debounce for 500ms\n\t\t\t\t\t\tresolveAssetUrl(editor, assetId, screenScale, exportInfo, (url) => resolve(asset, url))\n\t\t\t\t\t\tcancelDebounceFn?.()\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tcancelDebounceFn?.()\n\t\t\t\teditor.on('tick', resolveAssetAfterAWhile)\n\t\t\t\tcancelDebounceFn = () => editor.off('tick', resolveAssetAfterAWhile)\n\t\t\t} else {\n\t\t\t\tresolveAssetUrl(editor, assetId, screenScale, exportInfo, (url) => resolve(asset, url))\n\t\t\t}\n\t\t})\n\n\t\treturn () => {\n\t\t\tcleanupEffectScheduler()\n\t\t\tcancelDebounceFn?.()\n\t\t\tisCancelled = true\n\t\t}\n\t}, [editor, assetId, exportInfo, exportIsReady, shapeId, width])\n\n\treturn result\n}\n\nfunction resolveAssetUrl(\n\teditor: Editor,\n\tassetId: TLAssetId,\n\tscreenScale: number,\n\texportInfo: SvgExportContext | null,\n\tcallback: (url: string | null) => void\n) {\n\teditor\n\t\t.resolveAssetUrl(assetId, {\n\t\t\tscreenScale,\n\t\t\tshouldResolveToOriginal: exportInfo ? exportInfo.pixelRatio === null : false,\n\t\t\tdpr: exportInfo?.pixelRatio ?? undefined,\n\t\t})\n\t\t// There's a weird bug with out debounce function that doesn't\n\t\t// make it work right with async functions, so we use a callback\n\t\t// here instead of returning a promise.\n\t\t.then((url) => {\n\t\t\tcallback(url)\n\t\t})\n}\n"],
|
|
5
|
+
"mappings": ";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,oBAWO;AACP,mBAA4C;AAgCrC,SAAS,qBAAqB,EAAE,SAAS,SAAS,MAAM,GAAgC;AAC9F,QAAM,aAAS,yBAAU;AACzB,QAAM,iBAAa,mCAAoB;AACvC,QAAM,oBAAgB,iCAAkB;AAGxC,QAAM,CAAC,QAAQ,SAAS,QAAI,uBAGzB,OAAO;AAAA,IACT,OAAO,UAAW,OAAO,SAAsC,OAAO,KAAK,OAAQ;AAAA,IACnF,KAAK;AAAA,EACN,EAAE;AAGF,QAAM,wBAAoB,qBAAO,KAAK;AAGtC,QAAM,kBAAc,qBAAsB,IAAI;AAE9C,8BAAU,MAAM;AACf,QAAI,CAAC,QAAS;AAEd,QAAI,cAAc;AAClB,QAAI;AAEJ,UAAM,6BAAyB,qBAAM,gBAAgB,MAAM;AAC1D,UAAI,CAAC,cAAc,WAAW,OAAO,gBAAgB,EAAE,IAAI,OAAO,EAAG;AAGrE,YAAM,QAAQ,OAAO,SAAsC,OAAO;AAClE,UAAI,CAAC,OAAO;AAEX,kBAAU,CAAC,UAAU,EAAE,GAAG,MAAM,OAAO,MAAM,KAAK,KAAK,EAAE;AACzD;AAAA,MACD;AAGA,UAAI,CAAC,MAAM,MAAM,KAAK;AACrB,cAAM,UAAU,OAAO,yBAAyB,MAAM,EAAE;AACxD,YAAI,SAAS;AACZ,cAAI,YAAY,YAAY,SAAS;AACpC,wBAAY,UAAU;AACtB,sBAAU,CAAC,UAAU,EAAE,GAAG,MAAM,eAAe,MAAM,KAAK,QAAQ,EAAE;AACpE,0BAAc;AAAA,UACf;AACA;AAAA,QACD;AAAA,MACD;AAIA,YAAM,cAAc,aACjB,WAAW,SAAS,QAAQ,MAAM,MAAM,KACxC,OAAO,sBAAsB,KAAK,QAAQ,MAAM,MAAM;AAEzD,eAAS,QAAQA,QAAoC,KAAoB;AACxE,YAAI,YAAa;AACjB,YAAI,YAAY,YAAY,IAAK;AACjC,0BAAkB,UAAU;AAC5B,oBAAY,UAAU;AACtB,kBAAU,EAAE,OAAAA,QAAO,IAAI,CAAC;AACxB,sBAAc;AAAA,MACf;AAGA,UAAI,kBAAkB,SAAS;AAC9B,YAAI,OAAO;AAEX,cAAM,0BAA0B,MAAM;AACrC;AACA,cAAI,OAAO,MAAM,IAAI;AAEpB,4BAAgB,QAAQ,SAAS,aAAa,YAAY,CAAC,QAAQ,QAAQ,OAAO,GAAG,CAAC;AACtF,+BAAmB;AAAA,UACpB;AAAA,QACD;AAEA,2BAAmB;AACnB,eAAO,GAAG,QAAQ,uBAAuB;AACzC,2BAAmB,MAAM,OAAO,IAAI,QAAQ,uBAAuB;AAAA,MACpE,OAAO;AACN,wBAAgB,QAAQ,SAAS,aAAa,YAAY,CAAC,QAAQ,QAAQ,OAAO,GAAG,CAAC;AAAA,MACvF;AAAA,IACD,CAAC;AAED,WAAO,MAAM;AACZ,6BAAuB;AACvB,yBAAmB;AACnB,oBAAc;AAAA,IACf;AAAA,EACD,GAAG,CAAC,QAAQ,SAAS,YAAY,eAAe,SAAS,KAAK,CAAC;AAE/D,SAAO;AACR;AAEA,SAAS,gBACR,QACA,SACA,aACA,YACA,UACC;AACD,SACE,gBAAgB,SAAS;AAAA,IACzB;AAAA,IACA,yBAAyB,aAAa,WAAW,eAAe,OAAO;AAAA,IACvE,KAAK,YAAY,cAAc;AAAA,EAChC,CAAC,EAIA,KAAK,CAAC,QAAQ;AACd,aAAS,GAAG;AAAA,EACb,CAAC;AACH;",
|
|
6
6
|
"names": ["asset"]
|
|
7
7
|
}
|
|
@@ -141,6 +141,11 @@ const RichTextArea = import_react2.default.forwardRef(function RichTextArea2({
|
|
|
141
141
|
blockSeparator: "\n"
|
|
142
142
|
}
|
|
143
143
|
},
|
|
144
|
+
// N.B. We disable the text direction in the core list here,
|
|
145
|
+
// but we add it back in again in our own extensions list so that
|
|
146
|
+
// people can omit/override it if they want to.
|
|
147
|
+
enableCoreExtensions: { textDirection: false },
|
|
148
|
+
textDirection: "auto",
|
|
144
149
|
...restOfTipTapConfig,
|
|
145
150
|
content: rInitialRichText.current
|
|
146
151
|
});
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../src/lib/shapes/text/RichTextArea.tsx"],
|
|
4
|
-
"sourcesContent": ["import { EditorView } from '@tiptap/pm/view'\nimport {\n\tEditorEvents,\n\tJSONContent,\n\tEditor as TextEditor,\n\ttype Editor as TTEditor,\n} from '@tiptap/react'\nimport {\n\tEditor,\n\tTLRichText,\n\tTLShapeId,\n\tpreventDefault,\n\tuseEditor,\n\tuseEvent,\n\tuseUniqueSafeId,\n} from '@tldraw/editor'\nimport React, { useLayoutEffect, useRef } from 'react'\n\n/** @public */\nexport interface TextAreaProps {\n\tisEditing: boolean\n\ttext?: string\n\tshapeId: TLShapeId\n\trichText?: TLRichText\n\thandleFocus(): void\n\thandleBlur(): void\n\thandleKeyDown(e: KeyboardEvent): void\n\thandleChange(changeInfo: { plaintext?: string; richText?: TLRichText }): void\n\thandleInputPointerDown(e: React.PointerEvent<HTMLElement>): void\n\thandleDoubleClick(e: any): any\n\thandlePaste(e: ClipboardEvent | React.ClipboardEvent<HTMLTextAreaElement>): void\n\thasCustomTabBehavior?: boolean\n}\n\n/**\n * N.B. In Development mode you need to ensure you're testing this without StrictMode on.\n * Otherwise it's not gonna work as expected on iOS.\n * Specifically, it means that the virtual keyboard won't pop open sometimes\n * (iOS starts flipping out when you render multiple times when trying to focus something) .\n */\n\n/**\n * A rich text area that can be used for editing text with rich text formatting.\n * This component uses the TipTap editor under the hood.\n *\n * @public @react\n */\nexport const RichTextArea = React.forwardRef<HTMLDivElement, TextAreaProps>(function RichTextArea(\n\t{\n\t\tshapeId,\n\t\tisEditing,\n\t\trichText,\n\t\thandleFocus,\n\t\thandleChange,\n\t\thandleBlur,\n\t\thandleKeyDown,\n\t\thandleDoubleClick,\n\t\thasCustomTabBehavior,\n\t\thandlePaste,\n\t},\n\tref\n) {\n\tconst editor = useEditor()\n\tconst tipTapId = useUniqueSafeId('tip-tap-editor')\n\tconst tipTapConfig = editor.getTextOptions().tipTapConfig\n\n\tconst rInitialRichText = useRef(richText)\n\tconst rTextEditor = useRef<TTEditor | null>(null)\n\tconst rTextEditorEl = useRef<HTMLDivElement>(null)\n\n\tuseLayoutEffect(() => {\n\t\tif (!rTextEditor.current) {\n\t\t\trInitialRichText.current = richText\n\t\t} else if (rInitialRichText.current !== richText) {\n\t\t\trTextEditor.current.commands.setContent(richText as JSONContent)\n\t\t}\n\t}, [richText])\n\n\t// The order of events is:\n\t// - editor begins editing any shape\n\t// - we set listeners for select all / place caret events\n\t// - if the user is editing this shape, this component is rendered\n\t// - editor emits the select all event / place caret event\n\t// - the text editor is onCreate callback is called\n\tconst rCreateInfo = useRef({\n\t\tselectAll: false,\n\t\tcaretPosition: null as { x: number; y: number } | null,\n\t})\n\n\tuseLayoutEffect(() => {\n\t\tfunction selectAllIfEditing(event: { shapeId: TLShapeId }) {\n\t\t\tif (event.shapeId === editor.getEditingShapeId()) {\n\t\t\t\trCreateInfo.current.selectAll = true\n\t\t\t}\n\t\t}\n\n\t\tfunction placeCaret(event: { shapeId: TLShapeId; point: { x: number; y: number } }) {\n\t\t\tif (event.shapeId === editor.getEditingShapeId()) {\n\t\t\t\trCreateInfo.current.caretPosition = event.point\n\t\t\t}\n\t\t}\n\n\t\teditor.on('select-all-text', selectAllIfEditing)\n\t\teditor.on('place-caret', placeCaret)\n\t\treturn () => {\n\t\t\teditor.off('select-all-text', selectAllIfEditing)\n\t\t\teditor.off('place-caret', placeCaret)\n\t\t}\n\t}, [editor, isEditing])\n\n\tconst onChange = useEvent(handleChange)\n\tconst onKeyDown = useEvent(handleKeyDown)\n\tconst onFocus = useEvent(handleFocus)\n\tconst onBlur = useEvent(handleBlur)\n\tconst onDoubleClick = useEvent(handleDoubleClick)\n\tconst onPaste = useEvent(handlePaste)\n\tuseLayoutEffect(() => {\n\t\tif (!isEditing || !tipTapConfig || !rTextEditorEl.current) return\n\n\t\tconst { editorProps, ...restOfTipTapConfig } = tipTapConfig\n\n\t\t// Because React can double-render in Strict Mode, we need to make sure we're not setting\n\t\t// the text editor twice. This became more much more prevalent in React 19, but also it\n\t\t// started manifesting in some cases in Next 14.2 (which maybe patches React 18.3 in weird\n\t\t// ways). So we used to use EditorProvider but we into weird rendering issues.\n\t\tconst textEditorInstance = new TextEditor({\n\t\t\telement: rTextEditorEl.current,\n\t\t\tautofocus: true,\n\t\t\teditable: isEditing,\n\t\t\tonUpdate: (props: EditorEvents['update']) => {\n\t\t\t\tconst content: TLRichText = props.editor.state.doc.toJSON()\n\t\t\t\trInitialRichText.current = content\n\t\t\t\tonChange({ richText: content })\n\t\t\t},\n\t\t\tonFocus,\n\t\t\tonBlur,\n\t\t\t// onCreate is called after a `setTimeout(0)`\n\t\t\tonCreate: (props) => {\n\t\t\t\t// If we're not still editing the original shape, then don't do anything.\n\t\t\t\tif (editor.getEditingShapeId() !== shapeId) return\n\n\t\t\t\tconst textEditor = props.editor\n\t\t\t\teditor.setRichTextEditor(textEditor)\n\n\t\t\t\tconst { selectAll, caretPosition } = rCreateInfo.current\n\n\t\t\t\tif (selectAll) {\n\t\t\t\t\t// Select all of the text\n\t\t\t\t\ttextEditor.chain().focus().selectAll().run()\n\t\t\t\t} else if (caretPosition) {\n\t\t\t\t\t// Set the initial caret screen position\n\t\t\t\t\tconst pos = textEditor.view.posAtCoords({\n\t\t\t\t\t\tleft: caretPosition.x,\n\t\t\t\t\t\ttop: caretPosition.y,\n\t\t\t\t\t})?.pos\n\n\t\t\t\t\tif (pos) {\n\t\t\t\t\t\t// Focus to that position.\n\t\t\t\t\t\ttextEditor.chain().focus().setTextSelection(pos).run()\n\t\t\t\t\t} else {\n\t\t\t\t\t\t// If no position, default to select all.\n\t\t\t\t\t\ttextEditor.chain().focus().selectAll().run()\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t},\n\t\t\teditorProps: {\n\t\t\t\thandleKeyDown: (view: EditorView, event: KeyboardEvent) => {\n\t\t\t\t\tif (!hasCustomTabBehavior && event.key === 'Tab') {\n\t\t\t\t\t\thandleTab(editor, view, event)\n\t\t\t\t\t}\n\n\t\t\t\t\tonKeyDown(event)\n\t\t\t\t},\n\t\t\t\thandlePaste: (view: EditorView, event: ClipboardEvent) => {\n\t\t\t\t\tonPaste(event)\n\t\t\t\t\tif (event.defaultPrevented) return true\n\t\t\t\t},\n\t\t\t\thandleDoubleClick: (_view, _pos, event) => onDoubleClick(event),\n\t\t\t\t...editorProps,\n\t\t\t},\n\t\t\tcoreExtensionOptions: {\n\t\t\t\tclipboardTextSerializer: {\n\t\t\t\t\tblockSeparator: '\\n',\n\t\t\t\t},\n\t\t\t},\n\t\t\t...restOfTipTapConfig,\n\t\t\tcontent: rInitialRichText.current as JSONContent,\n\t\t})\n\n\t\t// XXX: When creating a brand new shape and double-clicking into it quickly to edit it,\n\t\t// there's some kind of race condition happening where the editor doesn't focus properly.\n\t\tconst timeout = editor.timers.setTimeout(() => {\n\t\t\tif (rCreateInfo.current.caretPosition || rCreateInfo.current.selectAll) {\n\t\t\t\ttextEditorInstance.commands.focus()\n\t\t\t} else {\n\t\t\t\ttextEditorInstance.commands.focus('end')\n\t\t\t}\n\n\t\t\trCreateInfo.current.selectAll = false\n\t\t\trCreateInfo.current.caretPosition = null\n\t\t}, 100)\n\n\t\trTextEditor.current = textEditorInstance\n\n\t\treturn () => {\n\t\t\trTextEditor.current = null\n\t\t\tclearTimeout(timeout)\n\t\t\ttextEditorInstance.destroy()\n\t\t}\n\t}, [\n\t\tisEditing,\n\t\ttipTapConfig,\n\t\tonFocus,\n\t\tonBlur,\n\t\tonDoubleClick,\n\t\tonChange,\n\t\tonPaste,\n\t\tonKeyDown,\n\t\teditor,\n\t\tshapeId,\n\t\thasCustomTabBehavior,\n\t])\n\n\tif (!isEditing || !tipTapConfig) {\n\t\treturn null\n\t}\n\n\treturn (\n\t\t<div\n\t\t\tid={tipTapId}\n\t\t\tref={ref}\n\t\t\ttabIndex={-1}\n\t\t\tdata-testid=\"rich-text-area\"\n\t\t\tclassName=\"tl-rich-text tl-text tl-text-input\"\n\t\t\tonContextMenu={isEditing ? (e) => e.stopPropagation() : undefined}\n\t\t\t// N.B. When PointerStateExtension was introduced, this was moved there.\n\t\t\t// However, that caused selecting over list items to break.\n\t\t\t// The handleDOMEvents in TipTap don't seem to support the pointerDownCapture event.\n\t\t\tonPointerDownCapture={(e) => e.stopPropagation()}\n\t\t\t// This onTouchEnd is important for Android to be able to change selection on text.\n\t\t\tonTouchEnd={(e) => e.stopPropagation()}\n\t\t\t// On FF, there's a behavior where dragging a selection will grab that selection into\n\t\t\t// the drag event. However, once the drag is over, and you select away from the textarea,\n\t\t\t// starting a drag over the textarea will restart a selection drag instead of a shape drag.\n\t\t\t// This prevents that default behavior in FF.\n\t\t\tonDragStart={preventDefault}\n\t\t>\n\t\t\t<div className=\"tl-rich-text\" ref={rTextEditorEl} />\n\t\t</div>\n\t)\n})\n\n// Prevent exiting the editor when hitting Tab.\n// Also, insert a tab character at the front of the line if the shift key isn't pressed,\n// otherwise if shift is pressed, remove a tab character from the front of the line.\nfunction handleTab(editor: Editor, view: EditorView, event: KeyboardEvent) {\n\t// Don't exit the editor.\n\tevent.preventDefault()\n\n\tconst textEditor = editor.getRichTextEditor()\n\tif (textEditor?.isActive('bulletList') || textEditor?.isActive('orderedList')) return\n\n\tconst { state, dispatch } = view\n\tconst { $from, $to } = state.selection\n\tconst isShift = event.shiftKey\n\n\t// Create a new transaction\n\tlet tr = state.tr\n\n\t// Iterate over each line in the selection in reverse so that the positions\n\t// are stable as we modify the document.\n\tlet pos = $to.end()\n\twhile (pos >= $from.start()) {\n\t\tconst line = state.doc.resolve(pos).blockRange()\n\t\tif (!line) break\n\n\t\tconst lineStart = line.start\n\t\tconst lineEnd = line.end\n\t\tconst lineText = state.doc.textBetween(lineStart, lineEnd, '\\n')\n\n\t\t// Check if the current line or any of its parent nodes are part of a list\n\t\tlet isInList = false\n\t\tstate.doc.nodesBetween(lineStart, lineEnd, (node) => {\n\t\t\tif (node.type.name === 'bulletList' || node.type.name === 'orderedList') {\n\t\t\t\tisInList = true\n\t\t\t\treturn false // Stop iteration\n\t\t\t}\n\t\t})\n\n\t\t// TODO: for now skip over lists. Later, we might consider handling them using\n\t\t// sinkListItem and liftListItem from @tiptap/pm/schema-list\n\t\tif (!isInList) {\n\t\t\tif (!isShift) {\n\t\t\t\t// Insert a tab character at the start of the line\n\t\t\t\ttr = tr.insertText('\\t', lineStart + 1)\n\t\t\t} else {\n\t\t\t\t// Remove a tab character from the start of the line\n\t\t\t\tif (lineText.startsWith('\\t')) {\n\t\t\t\t\ttr = tr.delete(lineStart + 1, lineStart + 2)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tpos = lineStart - 1\n\t}\n\n\tconst mappedSelection = state.selection.map(tr.doc, tr.mapping)\n\ttr.setSelection(mappedSelection)\n\n\tif (tr.docChanged) {\n\t\tdispatch(tr)\n\t}\n}\n"],
|
|
5
|
-
"mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;
|
|
4
|
+
"sourcesContent": ["import { EditorView } from '@tiptap/pm/view'\nimport {\n\tEditorEvents,\n\tJSONContent,\n\tEditor as TextEditor,\n\ttype Editor as TTEditor,\n} from '@tiptap/react'\nimport {\n\tEditor,\n\tTLRichText,\n\tTLShapeId,\n\tpreventDefault,\n\tuseEditor,\n\tuseEvent,\n\tuseUniqueSafeId,\n} from '@tldraw/editor'\nimport React, { useLayoutEffect, useRef } from 'react'\n\n/** @public */\nexport interface TextAreaProps {\n\tisEditing: boolean\n\ttext?: string\n\tshapeId: TLShapeId\n\trichText?: TLRichText\n\thandleFocus(): void\n\thandleBlur(): void\n\thandleKeyDown(e: KeyboardEvent): void\n\thandleChange(changeInfo: { plaintext?: string; richText?: TLRichText }): void\n\thandleInputPointerDown(e: React.PointerEvent<HTMLElement>): void\n\thandleDoubleClick(e: any): any\n\thandlePaste(e: ClipboardEvent | React.ClipboardEvent<HTMLTextAreaElement>): void\n\thasCustomTabBehavior?: boolean\n}\n\n/**\n * N.B. In Development mode you need to ensure you're testing this without StrictMode on.\n * Otherwise it's not gonna work as expected on iOS.\n * Specifically, it means that the virtual keyboard won't pop open sometimes\n * (iOS starts flipping out when you render multiple times when trying to focus something) .\n */\n\n/**\n * A rich text area that can be used for editing text with rich text formatting.\n * This component uses the TipTap editor under the hood.\n *\n * @public @react\n */\nexport const RichTextArea = React.forwardRef<HTMLDivElement, TextAreaProps>(function RichTextArea(\n\t{\n\t\tshapeId,\n\t\tisEditing,\n\t\trichText,\n\t\thandleFocus,\n\t\thandleChange,\n\t\thandleBlur,\n\t\thandleKeyDown,\n\t\thandleDoubleClick,\n\t\thasCustomTabBehavior,\n\t\thandlePaste,\n\t},\n\tref\n) {\n\tconst editor = useEditor()\n\tconst tipTapId = useUniqueSafeId('tip-tap-editor')\n\tconst tipTapConfig = editor.getTextOptions().tipTapConfig\n\n\tconst rInitialRichText = useRef(richText)\n\tconst rTextEditor = useRef<TTEditor | null>(null)\n\tconst rTextEditorEl = useRef<HTMLDivElement>(null)\n\n\tuseLayoutEffect(() => {\n\t\tif (!rTextEditor.current) {\n\t\t\trInitialRichText.current = richText\n\t\t} else if (rInitialRichText.current !== richText) {\n\t\t\trTextEditor.current.commands.setContent(richText as JSONContent)\n\t\t}\n\t}, [richText])\n\n\t// The order of events is:\n\t// - editor begins editing any shape\n\t// - we set listeners for select all / place caret events\n\t// - if the user is editing this shape, this component is rendered\n\t// - editor emits the select all event / place caret event\n\t// - the text editor is onCreate callback is called\n\tconst rCreateInfo = useRef({\n\t\tselectAll: false,\n\t\tcaretPosition: null as { x: number; y: number } | null,\n\t})\n\n\tuseLayoutEffect(() => {\n\t\tfunction selectAllIfEditing(event: { shapeId: TLShapeId }) {\n\t\t\tif (event.shapeId === editor.getEditingShapeId()) {\n\t\t\t\trCreateInfo.current.selectAll = true\n\t\t\t}\n\t\t}\n\n\t\tfunction placeCaret(event: { shapeId: TLShapeId; point: { x: number; y: number } }) {\n\t\t\tif (event.shapeId === editor.getEditingShapeId()) {\n\t\t\t\trCreateInfo.current.caretPosition = event.point\n\t\t\t}\n\t\t}\n\n\t\teditor.on('select-all-text', selectAllIfEditing)\n\t\teditor.on('place-caret', placeCaret)\n\t\treturn () => {\n\t\t\teditor.off('select-all-text', selectAllIfEditing)\n\t\t\teditor.off('place-caret', placeCaret)\n\t\t}\n\t}, [editor, isEditing])\n\n\tconst onChange = useEvent(handleChange)\n\tconst onKeyDown = useEvent(handleKeyDown)\n\tconst onFocus = useEvent(handleFocus)\n\tconst onBlur = useEvent(handleBlur)\n\tconst onDoubleClick = useEvent(handleDoubleClick)\n\tconst onPaste = useEvent(handlePaste)\n\tuseLayoutEffect(() => {\n\t\tif (!isEditing || !tipTapConfig || !rTextEditorEl.current) return\n\n\t\tconst { editorProps, ...restOfTipTapConfig } = tipTapConfig\n\n\t\t// Because React can double-render in Strict Mode, we need to make sure we're not setting\n\t\t// the text editor twice. This became more much more prevalent in React 19, but also it\n\t\t// started manifesting in some cases in Next 14.2 (which maybe patches React 18.3 in weird\n\t\t// ways). So we used to use EditorProvider but we into weird rendering issues.\n\t\tconst textEditorInstance = new TextEditor({\n\t\t\telement: rTextEditorEl.current,\n\t\t\tautofocus: true,\n\t\t\teditable: isEditing,\n\t\t\tonUpdate: (props: EditorEvents['update']) => {\n\t\t\t\tconst content: TLRichText = props.editor.state.doc.toJSON()\n\t\t\t\trInitialRichText.current = content\n\t\t\t\tonChange({ richText: content })\n\t\t\t},\n\t\t\tonFocus,\n\t\t\tonBlur,\n\t\t\t// onCreate is called after a `setTimeout(0)`\n\t\t\tonCreate: (props) => {\n\t\t\t\t// If we're not still editing the original shape, then don't do anything.\n\t\t\t\tif (editor.getEditingShapeId() !== shapeId) return\n\n\t\t\t\tconst textEditor = props.editor\n\t\t\t\teditor.setRichTextEditor(textEditor)\n\n\t\t\t\tconst { selectAll, caretPosition } = rCreateInfo.current\n\n\t\t\t\tif (selectAll) {\n\t\t\t\t\t// Select all of the text\n\t\t\t\t\ttextEditor.chain().focus().selectAll().run()\n\t\t\t\t} else if (caretPosition) {\n\t\t\t\t\t// Set the initial caret screen position\n\t\t\t\t\tconst pos = textEditor.view.posAtCoords({\n\t\t\t\t\t\tleft: caretPosition.x,\n\t\t\t\t\t\ttop: caretPosition.y,\n\t\t\t\t\t})?.pos\n\n\t\t\t\t\tif (pos) {\n\t\t\t\t\t\t// Focus to that position.\n\t\t\t\t\t\ttextEditor.chain().focus().setTextSelection(pos).run()\n\t\t\t\t\t} else {\n\t\t\t\t\t\t// If no position, default to select all.\n\t\t\t\t\t\ttextEditor.chain().focus().selectAll().run()\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t},\n\t\t\teditorProps: {\n\t\t\t\thandleKeyDown: (view: EditorView, event: KeyboardEvent) => {\n\t\t\t\t\tif (!hasCustomTabBehavior && event.key === 'Tab') {\n\t\t\t\t\t\thandleTab(editor, view, event)\n\t\t\t\t\t}\n\n\t\t\t\t\tonKeyDown(event)\n\t\t\t\t},\n\t\t\t\thandlePaste: (view: EditorView, event: ClipboardEvent) => {\n\t\t\t\t\tonPaste(event)\n\t\t\t\t\tif (event.defaultPrevented) return true\n\t\t\t\t},\n\t\t\t\thandleDoubleClick: (_view, _pos, event) => onDoubleClick(event),\n\t\t\t\t...editorProps,\n\t\t\t},\n\t\t\tcoreExtensionOptions: {\n\t\t\t\tclipboardTextSerializer: {\n\t\t\t\t\tblockSeparator: '\\n',\n\t\t\t\t},\n\t\t\t},\n\t\t\t// N.B. We disable the text direction in the core list here,\n\t\t\t// but we add it back in again in our own extensions list so that\n\t\t\t// people can omit/override it if they want to.\n\t\t\tenableCoreExtensions: { textDirection: false },\n\t\t\ttextDirection: 'auto',\n\t\t\t...restOfTipTapConfig,\n\t\t\tcontent: rInitialRichText.current as JSONContent,\n\t\t})\n\n\t\t// XXX: When creating a brand new shape and double-clicking into it quickly to edit it,\n\t\t// there's some kind of race condition happening where the editor doesn't focus properly.\n\t\tconst timeout = editor.timers.setTimeout(() => {\n\t\t\tif (rCreateInfo.current.caretPosition || rCreateInfo.current.selectAll) {\n\t\t\t\ttextEditorInstance.commands.focus()\n\t\t\t} else {\n\t\t\t\ttextEditorInstance.commands.focus('end')\n\t\t\t}\n\n\t\t\trCreateInfo.current.selectAll = false\n\t\t\trCreateInfo.current.caretPosition = null\n\t\t}, 100)\n\n\t\trTextEditor.current = textEditorInstance\n\n\t\treturn () => {\n\t\t\trTextEditor.current = null\n\t\t\tclearTimeout(timeout)\n\t\t\ttextEditorInstance.destroy()\n\t\t}\n\t}, [\n\t\tisEditing,\n\t\ttipTapConfig,\n\t\tonFocus,\n\t\tonBlur,\n\t\tonDoubleClick,\n\t\tonChange,\n\t\tonPaste,\n\t\tonKeyDown,\n\t\teditor,\n\t\tshapeId,\n\t\thasCustomTabBehavior,\n\t])\n\n\tif (!isEditing || !tipTapConfig) {\n\t\treturn null\n\t}\n\n\treturn (\n\t\t<div\n\t\t\tid={tipTapId}\n\t\t\tref={ref}\n\t\t\ttabIndex={-1}\n\t\t\tdata-testid=\"rich-text-area\"\n\t\t\tclassName=\"tl-rich-text tl-text tl-text-input\"\n\t\t\tonContextMenu={isEditing ? (e) => e.stopPropagation() : undefined}\n\t\t\t// N.B. When PointerStateExtension was introduced, this was moved there.\n\t\t\t// However, that caused selecting over list items to break.\n\t\t\t// The handleDOMEvents in TipTap don't seem to support the pointerDownCapture event.\n\t\t\tonPointerDownCapture={(e) => e.stopPropagation()}\n\t\t\t// This onTouchEnd is important for Android to be able to change selection on text.\n\t\t\tonTouchEnd={(e) => e.stopPropagation()}\n\t\t\t// On FF, there's a behavior where dragging a selection will grab that selection into\n\t\t\t// the drag event. However, once the drag is over, and you select away from the textarea,\n\t\t\t// starting a drag over the textarea will restart a selection drag instead of a shape drag.\n\t\t\t// This prevents that default behavior in FF.\n\t\t\tonDragStart={preventDefault}\n\t\t>\n\t\t\t<div className=\"tl-rich-text\" ref={rTextEditorEl} />\n\t\t</div>\n\t)\n})\n\n// Prevent exiting the editor when hitting Tab.\n// Also, insert a tab character at the front of the line if the shift key isn't pressed,\n// otherwise if shift is pressed, remove a tab character from the front of the line.\nfunction handleTab(editor: Editor, view: EditorView, event: KeyboardEvent) {\n\t// Don't exit the editor.\n\tevent.preventDefault()\n\n\tconst textEditor = editor.getRichTextEditor()\n\tif (textEditor?.isActive('bulletList') || textEditor?.isActive('orderedList')) return\n\n\tconst { state, dispatch } = view\n\tconst { $from, $to } = state.selection\n\tconst isShift = event.shiftKey\n\n\t// Create a new transaction\n\tlet tr = state.tr\n\n\t// Iterate over each line in the selection in reverse so that the positions\n\t// are stable as we modify the document.\n\tlet pos = $to.end()\n\twhile (pos >= $from.start()) {\n\t\tconst line = state.doc.resolve(pos).blockRange()\n\t\tif (!line) break\n\n\t\tconst lineStart = line.start\n\t\tconst lineEnd = line.end\n\t\tconst lineText = state.doc.textBetween(lineStart, lineEnd, '\\n')\n\n\t\t// Check if the current line or any of its parent nodes are part of a list\n\t\tlet isInList = false\n\t\tstate.doc.nodesBetween(lineStart, lineEnd, (node) => {\n\t\t\tif (node.type.name === 'bulletList' || node.type.name === 'orderedList') {\n\t\t\t\tisInList = true\n\t\t\t\treturn false // Stop iteration\n\t\t\t}\n\t\t})\n\n\t\t// TODO: for now skip over lists. Later, we might consider handling them using\n\t\t// sinkListItem and liftListItem from @tiptap/pm/schema-list\n\t\tif (!isInList) {\n\t\t\tif (!isShift) {\n\t\t\t\t// Insert a tab character at the start of the line\n\t\t\t\ttr = tr.insertText('\\t', lineStart + 1)\n\t\t\t} else {\n\t\t\t\t// Remove a tab character from the start of the line\n\t\t\t\tif (lineText.startsWith('\\t')) {\n\t\t\t\t\ttr = tr.delete(lineStart + 1, lineStart + 2)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tpos = lineStart - 1\n\t}\n\n\tconst mappedSelection = state.selection.map(tr.doc, tr.mapping)\n\ttr.setSelection(mappedSelection)\n\n\tif (tr.docChanged) {\n\t\tdispatch(tr)\n\t}\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": ["import_react", "React", "RichTextArea", "TextEditor"]
|
|
7
7
|
}
|
|
@@ -41,7 +41,8 @@ class TextShapeUtil extends import_editor.ShapeUtil {
|
|
|
41
41
|
static props = import_editor.textShapeProps;
|
|
42
42
|
static migrations = import_editor.textShapeMigrations;
|
|
43
43
|
options = {
|
|
44
|
-
extraArrowHorizontalPadding: 10
|
|
44
|
+
extraArrowHorizontalPadding: 10,
|
|
45
|
+
showTextOutline: true
|
|
45
46
|
};
|
|
46
47
|
getDefaultProps() {
|
|
47
48
|
return {
|
|
@@ -112,6 +113,7 @@ class TextShapeUtil extends import_editor.ShapeUtil {
|
|
|
112
113
|
isSelected,
|
|
113
114
|
textWidth: width,
|
|
114
115
|
textHeight: height,
|
|
116
|
+
showTextOutline: this.options.showTextOutline,
|
|
115
117
|
style: {
|
|
116
118
|
transform: `scale(${scale})`,
|
|
117
119
|
transformOrigin: "top left"
|
|
@@ -143,7 +145,8 @@ class TextShapeUtil extends import_editor.ShapeUtil {
|
|
|
143
145
|
richText: shape.props.richText,
|
|
144
146
|
labelColor: (0, import_editor.getColorValue)(theme, shape.props.color, "solid"),
|
|
145
147
|
bounds: exportBounds,
|
|
146
|
-
padding: 0
|
|
148
|
+
padding: 0,
|
|
149
|
+
showTextOutline: this.options.showTextOutline
|
|
147
150
|
}
|
|
148
151
|
);
|
|
149
152
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../src/lib/shapes/text/TextShapeUtil.tsx"],
|
|
4
|
-
"sourcesContent": ["/* eslint-disable react-hooks/rules-of-hooks */\nimport {\n\tBox,\n\tEditor,\n\tRectangle2d,\n\tShapeUtil,\n\tSvgExportContext,\n\tTLGeometryOpts,\n\tTLResizeInfo,\n\tTLShapeId,\n\tTLTextShape,\n\tVec,\n\tcreateComputedCache,\n\tgetColorValue,\n\tgetDefaultColorTheme,\n\tgetFontsFromRichText,\n\tisEqual,\n\tresizeScaled,\n\ttextShapeMigrations,\n\ttextShapeProps,\n\ttoDomPrecision,\n\ttoRichText,\n\tuseEditor,\n} from '@tldraw/editor'\nimport { useCallback } from 'react'\nimport {\n\trenderHtmlFromRichTextForMeasurement,\n\trenderPlaintextFromRichText,\n} from '../../utils/text/richText'\nimport { RichTextLabel, RichTextSVG } from '../shared/RichTextLabel'\nimport { FONT_FAMILIES, FONT_SIZES, TEXT_PROPS } from '../shared/default-shape-constants'\nimport { useDefaultColorTheme } from '../shared/useDefaultColorTheme'\n\nconst sizeCache = createComputedCache(\n\t'text size',\n\t(editor: Editor, shape: TLTextShape) => {\n\t\teditor.fonts.trackFontsForShape(shape)\n\t\treturn getTextSize(editor, shape.props)\n\t},\n\t{ areRecordsEqual: (a, b) => a.props === b.props }\n)\n/** @public */\nexport interface TextShapeOptions {\n\t/** How much addition padding should be added to the horizontal geometry of the shape when binding to an arrow? */\n\textraArrowHorizontalPadding: number\n}\n\n/** @public */\nexport class TextShapeUtil extends ShapeUtil<TLTextShape> {\n\tstatic override type = 'text' as const\n\tstatic override props = textShapeProps\n\tstatic override migrations = textShapeMigrations\n\n\toverride options: TextShapeOptions = {\n\t\textraArrowHorizontalPadding: 10,\n\t}\n\n\tgetDefaultProps(): TLTextShape['props'] {\n\t\treturn {\n\t\t\tcolor: 'black',\n\t\t\tsize: 'm',\n\t\t\tw: 8,\n\t\t\tfont: 'draw',\n\t\t\ttextAlign: 'start',\n\t\t\tautoSize: true,\n\t\t\tscale: 1,\n\t\t\trichText: toRichText(''),\n\t\t}\n\t}\n\n\tgetMinDimensions(shape: TLTextShape) {\n\t\treturn sizeCache.get(this.editor, shape.id)!\n\t}\n\n\tgetGeometry(shape: TLTextShape, opts: TLGeometryOpts) {\n\t\tconst { scale } = shape.props\n\t\tconst { width, height } = this.getMinDimensions(shape)!\n\t\tconst context = opts?.context ?? 'none'\n\t\treturn new Rectangle2d({\n\t\t\tx:\n\t\t\t\t(context === '@tldraw/arrow-without-arrowhead'\n\t\t\t\t\t? -this.options.extraArrowHorizontalPadding\n\t\t\t\t\t: 0) * scale,\n\t\t\twidth:\n\t\t\t\t(width +\n\t\t\t\t\t(context === '@tldraw/arrow-without-arrowhead'\n\t\t\t\t\t\t? this.options.extraArrowHorizontalPadding * 2\n\t\t\t\t\t\t: 0)) *\n\t\t\t\tscale,\n\t\t\theight: height * scale,\n\t\t\tisFilled: true,\n\t\t\tisLabel: true,\n\t\t})\n\t}\n\n\toverride getFontFaces(shape: TLTextShape) {\n\t\t// no need for an empty rich text check here\n\t\treturn getFontsFromRichText(this.editor, shape.props.richText, {\n\t\t\tfamily: `tldraw_${shape.props.font}`,\n\t\t\tweight: 'normal',\n\t\t\tstyle: 'normal',\n\t\t})\n\t}\n\n\toverride getText(shape: TLTextShape) {\n\t\treturn renderPlaintextFromRichText(this.editor, shape.props.richText)\n\t}\n\n\toverride canEdit() {\n\t\treturn true\n\t}\n\n\toverride isAspectRatioLocked() {\n\t\treturn true\n\t} // WAIT NO THIS IS HARD CODED IN THE RESIZE HANDLER\n\n\tcomponent(shape: TLTextShape) {\n\t\tconst {\n\t\t\tid,\n\t\t\tprops: { font, size, richText, color, scale, textAlign },\n\t\t} = shape\n\n\t\tconst { width, height } = this.getMinDimensions(shape)\n\t\tconst isSelected = shape.id === this.editor.getOnlySelectedShapeId()\n\t\tconst theme = useDefaultColorTheme()\n\t\tconst handleKeyDown = useTextShapeKeydownHandler(id)\n\n\t\treturn (\n\t\t\t<RichTextLabel\n\t\t\t\tshapeId={id}\n\t\t\t\tclassNamePrefix=\"tl-text-shape\"\n\t\t\t\ttype=\"text\"\n\t\t\t\tfont={font}\n\t\t\t\tfontSize={FONT_SIZES[size]}\n\t\t\t\tlineHeight={TEXT_PROPS.lineHeight}\n\t\t\t\talign={textAlign}\n\t\t\t\tverticalAlign=\"middle\"\n\t\t\t\trichText={richText}\n\t\t\t\tlabelColor={getColorValue(theme, color, 'solid')}\n\t\t\t\tisSelected={isSelected}\n\t\t\t\ttextWidth={width}\n\t\t\t\ttextHeight={height}\n\t\t\t\tstyle={{\n\t\t\t\t\ttransform: `scale(${scale})`,\n\t\t\t\t\ttransformOrigin: 'top left',\n\t\t\t\t}}\n\t\t\t\twrap\n\t\t\t\tonKeyDown={handleKeyDown}\n\t\t\t/>\n\t\t)\n\t}\n\n\tindicator(shape: TLTextShape) {\n\t\tconst bounds = this.editor.getShapeGeometry(shape).bounds\n\t\tconst editor = useEditor()\n\t\tif (shape.props.autoSize && editor.getEditingShapeId() === shape.id) return null\n\t\treturn <rect width={toDomPrecision(bounds.width)} height={toDomPrecision(bounds.height)} />\n\t}\n\n\toverride toSvg(shape: TLTextShape, ctx: SvgExportContext) {\n\t\tconst bounds = this.editor.getShapeGeometry(shape).bounds\n\t\tconst width = bounds.width / (shape.props.scale ?? 1)\n\t\tconst height = bounds.height / (shape.props.scale ?? 1)\n\n\t\tconst theme = getDefaultColorTheme(ctx)\n\n\t\tconst exportBounds = new Box(0, 0, width, height)\n\t\treturn (\n\t\t\t<RichTextSVG\n\t\t\t\tfontSize={FONT_SIZES[shape.props.size]}\n\t\t\t\tfont={shape.props.font}\n\t\t\t\talign={shape.props.textAlign}\n\t\t\t\tverticalAlign=\"middle\"\n\t\t\t\trichText={shape.props.richText}\n\t\t\t\tlabelColor={getColorValue(theme, shape.props.color, 'solid')}\n\t\t\t\tbounds={exportBounds}\n\t\t\t\tpadding={0}\n\t\t\t/>\n\t\t)\n\t}\n\n\toverride onResize(shape: TLTextShape, info: TLResizeInfo<TLTextShape>) {\n\t\tconst { newPoint, initialBounds, initialShape, scaleX, handle } = info\n\n\t\tif (info.mode === 'scale_shape' || (handle !== 'right' && handle !== 'left')) {\n\t\t\treturn {\n\t\t\t\tid: shape.id,\n\t\t\t\ttype: shape.type,\n\t\t\t\t...resizeScaled(shape, info),\n\t\t\t}\n\t\t} else {\n\t\t\tconst nextWidth = Math.max(1, Math.abs(initialBounds.width * scaleX))\n\t\t\tconst { x, y } =\n\t\t\t\tscaleX < 0 ? Vec.Sub(newPoint, Vec.FromAngle(shape.rotation).mul(nextWidth)) : newPoint\n\n\t\t\treturn {\n\t\t\t\tid: shape.id,\n\t\t\t\ttype: shape.type,\n\t\t\t\tx,\n\t\t\t\ty,\n\t\t\t\tprops: {\n\t\t\t\t\tw: nextWidth / initialShape.props.scale,\n\t\t\t\t\tautoSize: false,\n\t\t\t\t},\n\t\t\t}\n\t\t}\n\t}\n\n\toverride onEditEnd(shape: TLTextShape) {\n\t\t// todo: find a way to check if the rich text has any nodes that aren't empty spaces\n\t\tconst trimmedText = renderPlaintextFromRichText(this.editor, shape.props.richText).trimEnd()\n\n\t\tif (trimmedText.length === 0) {\n\t\t\tthis.editor.deleteShapes([shape.id])\n\t\t}\n\t}\n\n\toverride onBeforeUpdate(prev: TLTextShape, next: TLTextShape) {\n\t\tif (!next.props.autoSize) return\n\n\t\tconst styleDidChange =\n\t\t\tprev.props.size !== next.props.size ||\n\t\t\tprev.props.textAlign !== next.props.textAlign ||\n\t\t\tprev.props.font !== next.props.font ||\n\t\t\t(prev.props.scale !== 1 && next.props.scale === 1)\n\n\t\tconst textDidChange = !isEqual(prev.props.richText, next.props.richText)\n\n\t\t// Only update position if either changed\n\t\tif (!styleDidChange && !textDidChange) return\n\n\t\t// Might return a cached value for the bounds\n\t\tconst boundsA = this.getMinDimensions(prev)\n\n\t\t// Will always be a fresh call to getTextSize\n\t\tconst boundsB = getTextSize(this.editor, next.props)\n\n\t\tconst wA = boundsA.width * prev.props.scale\n\t\tconst hA = boundsA.height * prev.props.scale\n\t\tconst wB = boundsB.width * next.props.scale\n\t\tconst hB = boundsB.height * next.props.scale\n\n\t\tlet delta: Vec | undefined\n\n\t\tswitch (next.props.textAlign) {\n\t\t\tcase 'middle': {\n\t\t\t\tdelta = new Vec((wB - wA) / 2, textDidChange ? 0 : (hB - hA) / 2)\n\t\t\t\tbreak\n\t\t\t}\n\t\t\tcase 'end': {\n\t\t\t\tdelta = new Vec(wB - wA, textDidChange ? 0 : (hB - hA) / 2)\n\t\t\t\tbreak\n\t\t\t}\n\t\t\tdefault: {\n\t\t\t\tif (textDidChange) break\n\t\t\t\tdelta = new Vec(0, (hB - hA) / 2)\n\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\n\t\tif (delta) {\n\t\t\t// account for shape rotation when writing text:\n\t\t\tdelta.rot(next.rotation)\n\t\t\tconst { x, y } = next\n\t\t\treturn {\n\t\t\t\t...next,\n\t\t\t\tx: x - delta.x,\n\t\t\t\ty: y - delta.y,\n\t\t\t\tprops: { ...next.props, w: wB },\n\t\t\t}\n\t\t} else {\n\t\t\treturn {\n\t\t\t\t...next,\n\t\t\t\tprops: { ...next.props, w: wB },\n\t\t\t}\n\t\t}\n\t}\n\n\t// \ttodo: The edge doubleclicking feels like a mistake more often than\n\t// not, especially on multiline text. Removed June 16 2024\n\n\t// override onDoubleClickEdge = (shape: TLTextShape) => {\n\t// \t// If the shape has a fixed width, set it to autoSize.\n\t// \tif (!shape.props.autoSize) {\n\t// \t\treturn {\n\t// \t\t\tid: shape.id,\n\t// \t\t\ttype: shape.type,\n\t// \t\t\tprops: {\n\t// \t\t\t\tautoSize: true,\n\t// \t\t\t},\n\t// \t\t}\n\t// \t}\n\t// \t// If the shape is scaled, reset the scale to 1.\n\t// \tif (shape.props.scale !== 1) {\n\t// \t\treturn {\n\t// \t\t\tid: shape.id,\n\t// \t\t\ttype: shape.type,\n\t// \t\t\tprops: {\n\t// \t\t\t\tscale: 1,\n\t// \t\t\t},\n\t// \t\t}\n\t// \t}\n\t// }\n}\n\nfunction getTextSize(editor: Editor, props: TLTextShape['props']) {\n\tconst { font, richText, size, w } = props\n\n\tconst minWidth = 16\n\tconst fontSize = FONT_SIZES[size]\n\n\tconst maybeFixedWidth = props.autoSize ? null : Math.max(minWidth, Math.floor(w))\n\n\tconst html = renderHtmlFromRichTextForMeasurement(editor, richText)\n\tconst result = editor.textMeasure.measureHtml(html, {\n\t\t...TEXT_PROPS,\n\t\tfontFamily: FONT_FAMILIES[font],\n\t\tfontSize: fontSize,\n\t\tmaxWidth: maybeFixedWidth,\n\t})\n\n\t// If we're autosizing the measureText will essentially `Math.floor`\n\t// the numbers so `19` rather than `19.3`, this means we must +1 to\n\t// whatever we get to avoid wrapping.\n\treturn {\n\t\twidth: maybeFixedWidth ?? Math.max(minWidth, result.w + 1),\n\t\theight: Math.max(fontSize, result.h),\n\t}\n}\n\nfunction useTextShapeKeydownHandler(id: TLShapeId) {\n\tconst editor = useEditor()\n\n\treturn useCallback(\n\t\t(e: KeyboardEvent) => {\n\t\t\tif (editor.getEditingShapeId() !== id) return\n\n\t\t\tswitch (e.key) {\n\t\t\t\tcase 'Enter': {\n\t\t\t\t\tif (e.ctrlKey || e.metaKey) {\n\t\t\t\t\t\teditor.complete()\n\t\t\t\t\t}\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t[editor, id]\n\t)\n}\n"],
|
|
5
|
-
"mappings": ";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;
|
|
4
|
+
"sourcesContent": ["/* eslint-disable react-hooks/rules-of-hooks */\nimport {\n\tBox,\n\tEditor,\n\tRectangle2d,\n\tShapeUtil,\n\tSvgExportContext,\n\tTLGeometryOpts,\n\tTLResizeInfo,\n\tTLShapeId,\n\tTLTextShape,\n\tVec,\n\tcreateComputedCache,\n\tgetColorValue,\n\tgetDefaultColorTheme,\n\tgetFontsFromRichText,\n\tisEqual,\n\tresizeScaled,\n\ttextShapeMigrations,\n\ttextShapeProps,\n\ttoDomPrecision,\n\ttoRichText,\n\tuseEditor,\n} from '@tldraw/editor'\nimport { useCallback } from 'react'\nimport {\n\trenderHtmlFromRichTextForMeasurement,\n\trenderPlaintextFromRichText,\n} from '../../utils/text/richText'\nimport { RichTextLabel, RichTextSVG } from '../shared/RichTextLabel'\nimport { FONT_FAMILIES, FONT_SIZES, TEXT_PROPS } from '../shared/default-shape-constants'\nimport { useDefaultColorTheme } from '../shared/useDefaultColorTheme'\n\nconst sizeCache = createComputedCache(\n\t'text size',\n\t(editor: Editor, shape: TLTextShape) => {\n\t\teditor.fonts.trackFontsForShape(shape)\n\t\treturn getTextSize(editor, shape.props)\n\t},\n\t{ areRecordsEqual: (a, b) => a.props === b.props }\n)\n/** @public */\nexport interface TextShapeOptions {\n\t/** How much addition padding should be added to the horizontal geometry of the shape when binding to an arrow? */\n\textraArrowHorizontalPadding: number\n\t/** Whether to show the outline of the text shape (using the same color as the canvas). This helps with overlapping shapes. It does not show up on Safari, where text outline is a performance issues. */\n\tshowTextOutline: boolean\n}\n\n/** @public */\nexport class TextShapeUtil extends ShapeUtil<TLTextShape> {\n\tstatic override type = 'text' as const\n\tstatic override props = textShapeProps\n\tstatic override migrations = textShapeMigrations\n\n\toverride options: TextShapeOptions = {\n\t\textraArrowHorizontalPadding: 10,\n\t\tshowTextOutline: true,\n\t}\n\n\tgetDefaultProps(): TLTextShape['props'] {\n\t\treturn {\n\t\t\tcolor: 'black',\n\t\t\tsize: 'm',\n\t\t\tw: 8,\n\t\t\tfont: 'draw',\n\t\t\ttextAlign: 'start',\n\t\t\tautoSize: true,\n\t\t\tscale: 1,\n\t\t\trichText: toRichText(''),\n\t\t}\n\t}\n\n\tgetMinDimensions(shape: TLTextShape) {\n\t\treturn sizeCache.get(this.editor, shape.id)!\n\t}\n\n\tgetGeometry(shape: TLTextShape, opts: TLGeometryOpts) {\n\t\tconst { scale } = shape.props\n\t\tconst { width, height } = this.getMinDimensions(shape)!\n\t\tconst context = opts?.context ?? 'none'\n\t\treturn new Rectangle2d({\n\t\t\tx:\n\t\t\t\t(context === '@tldraw/arrow-without-arrowhead'\n\t\t\t\t\t? -this.options.extraArrowHorizontalPadding\n\t\t\t\t\t: 0) * scale,\n\t\t\twidth:\n\t\t\t\t(width +\n\t\t\t\t\t(context === '@tldraw/arrow-without-arrowhead'\n\t\t\t\t\t\t? this.options.extraArrowHorizontalPadding * 2\n\t\t\t\t\t\t: 0)) *\n\t\t\t\tscale,\n\t\t\theight: height * scale,\n\t\t\tisFilled: true,\n\t\t\tisLabel: true,\n\t\t})\n\t}\n\n\toverride getFontFaces(shape: TLTextShape) {\n\t\t// no need for an empty rich text check here\n\t\treturn getFontsFromRichText(this.editor, shape.props.richText, {\n\t\t\tfamily: `tldraw_${shape.props.font}`,\n\t\t\tweight: 'normal',\n\t\t\tstyle: 'normal',\n\t\t})\n\t}\n\n\toverride getText(shape: TLTextShape) {\n\t\treturn renderPlaintextFromRichText(this.editor, shape.props.richText)\n\t}\n\n\toverride canEdit() {\n\t\treturn true\n\t}\n\n\toverride isAspectRatioLocked() {\n\t\treturn true\n\t} // WAIT NO THIS IS HARD CODED IN THE RESIZE HANDLER\n\n\tcomponent(shape: TLTextShape) {\n\t\tconst {\n\t\t\tid,\n\t\t\tprops: { font, size, richText, color, scale, textAlign },\n\t\t} = shape\n\n\t\tconst { width, height } = this.getMinDimensions(shape)\n\t\tconst isSelected = shape.id === this.editor.getOnlySelectedShapeId()\n\t\tconst theme = useDefaultColorTheme()\n\t\tconst handleKeyDown = useTextShapeKeydownHandler(id)\n\n\t\treturn (\n\t\t\t<RichTextLabel\n\t\t\t\tshapeId={id}\n\t\t\t\tclassNamePrefix=\"tl-text-shape\"\n\t\t\t\ttype=\"text\"\n\t\t\t\tfont={font}\n\t\t\t\tfontSize={FONT_SIZES[size]}\n\t\t\t\tlineHeight={TEXT_PROPS.lineHeight}\n\t\t\t\talign={textAlign}\n\t\t\t\tverticalAlign=\"middle\"\n\t\t\t\trichText={richText}\n\t\t\t\tlabelColor={getColorValue(theme, color, 'solid')}\n\t\t\t\tisSelected={isSelected}\n\t\t\t\ttextWidth={width}\n\t\t\t\ttextHeight={height}\n\t\t\t\tshowTextOutline={this.options.showTextOutline}\n\t\t\t\tstyle={{\n\t\t\t\t\ttransform: `scale(${scale})`,\n\t\t\t\t\ttransformOrigin: 'top left',\n\t\t\t\t}}\n\t\t\t\twrap\n\t\t\t\tonKeyDown={handleKeyDown}\n\t\t\t/>\n\t\t)\n\t}\n\n\tindicator(shape: TLTextShape) {\n\t\tconst bounds = this.editor.getShapeGeometry(shape).bounds\n\t\tconst editor = useEditor()\n\t\tif (shape.props.autoSize && editor.getEditingShapeId() === shape.id) return null\n\t\treturn <rect width={toDomPrecision(bounds.width)} height={toDomPrecision(bounds.height)} />\n\t}\n\n\toverride toSvg(shape: TLTextShape, ctx: SvgExportContext) {\n\t\tconst bounds = this.editor.getShapeGeometry(shape).bounds\n\t\tconst width = bounds.width / (shape.props.scale ?? 1)\n\t\tconst height = bounds.height / (shape.props.scale ?? 1)\n\n\t\tconst theme = getDefaultColorTheme(ctx)\n\n\t\tconst exportBounds = new Box(0, 0, width, height)\n\t\treturn (\n\t\t\t<RichTextSVG\n\t\t\t\tfontSize={FONT_SIZES[shape.props.size]}\n\t\t\t\tfont={shape.props.font}\n\t\t\t\talign={shape.props.textAlign}\n\t\t\t\tverticalAlign=\"middle\"\n\t\t\t\trichText={shape.props.richText}\n\t\t\t\tlabelColor={getColorValue(theme, shape.props.color, 'solid')}\n\t\t\t\tbounds={exportBounds}\n\t\t\t\tpadding={0}\n\t\t\t\tshowTextOutline={this.options.showTextOutline}\n\t\t\t/>\n\t\t)\n\t}\n\n\toverride onResize(shape: TLTextShape, info: TLResizeInfo<TLTextShape>) {\n\t\tconst { newPoint, initialBounds, initialShape, scaleX, handle } = info\n\n\t\tif (info.mode === 'scale_shape' || (handle !== 'right' && handle !== 'left')) {\n\t\t\treturn {\n\t\t\t\tid: shape.id,\n\t\t\t\ttype: shape.type,\n\t\t\t\t...resizeScaled(shape, info),\n\t\t\t}\n\t\t} else {\n\t\t\tconst nextWidth = Math.max(1, Math.abs(initialBounds.width * scaleX))\n\t\t\tconst { x, y } =\n\t\t\t\tscaleX < 0 ? Vec.Sub(newPoint, Vec.FromAngle(shape.rotation).mul(nextWidth)) : newPoint\n\n\t\t\treturn {\n\t\t\t\tid: shape.id,\n\t\t\t\ttype: shape.type,\n\t\t\t\tx,\n\t\t\t\ty,\n\t\t\t\tprops: {\n\t\t\t\t\tw: nextWidth / initialShape.props.scale,\n\t\t\t\t\tautoSize: false,\n\t\t\t\t},\n\t\t\t}\n\t\t}\n\t}\n\n\toverride onEditEnd(shape: TLTextShape) {\n\t\t// todo: find a way to check if the rich text has any nodes that aren't empty spaces\n\t\tconst trimmedText = renderPlaintextFromRichText(this.editor, shape.props.richText).trimEnd()\n\n\t\tif (trimmedText.length === 0) {\n\t\t\tthis.editor.deleteShapes([shape.id])\n\t\t}\n\t}\n\n\toverride onBeforeUpdate(prev: TLTextShape, next: TLTextShape) {\n\t\tif (!next.props.autoSize) return\n\n\t\tconst styleDidChange =\n\t\t\tprev.props.size !== next.props.size ||\n\t\t\tprev.props.textAlign !== next.props.textAlign ||\n\t\t\tprev.props.font !== next.props.font ||\n\t\t\t(prev.props.scale !== 1 && next.props.scale === 1)\n\n\t\tconst textDidChange = !isEqual(prev.props.richText, next.props.richText)\n\n\t\t// Only update position if either changed\n\t\tif (!styleDidChange && !textDidChange) return\n\n\t\t// Might return a cached value for the bounds\n\t\tconst boundsA = this.getMinDimensions(prev)\n\n\t\t// Will always be a fresh call to getTextSize\n\t\tconst boundsB = getTextSize(this.editor, next.props)\n\n\t\tconst wA = boundsA.width * prev.props.scale\n\t\tconst hA = boundsA.height * prev.props.scale\n\t\tconst wB = boundsB.width * next.props.scale\n\t\tconst hB = boundsB.height * next.props.scale\n\n\t\tlet delta: Vec | undefined\n\n\t\tswitch (next.props.textAlign) {\n\t\t\tcase 'middle': {\n\t\t\t\tdelta = new Vec((wB - wA) / 2, textDidChange ? 0 : (hB - hA) / 2)\n\t\t\t\tbreak\n\t\t\t}\n\t\t\tcase 'end': {\n\t\t\t\tdelta = new Vec(wB - wA, textDidChange ? 0 : (hB - hA) / 2)\n\t\t\t\tbreak\n\t\t\t}\n\t\t\tdefault: {\n\t\t\t\tif (textDidChange) break\n\t\t\t\tdelta = new Vec(0, (hB - hA) / 2)\n\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\n\t\tif (delta) {\n\t\t\t// account for shape rotation when writing text:\n\t\t\tdelta.rot(next.rotation)\n\t\t\tconst { x, y } = next\n\t\t\treturn {\n\t\t\t\t...next,\n\t\t\t\tx: x - delta.x,\n\t\t\t\ty: y - delta.y,\n\t\t\t\tprops: { ...next.props, w: wB },\n\t\t\t}\n\t\t} else {\n\t\t\treturn {\n\t\t\t\t...next,\n\t\t\t\tprops: { ...next.props, w: wB },\n\t\t\t}\n\t\t}\n\t}\n\n\t// \ttodo: The edge doubleclicking feels like a mistake more often than\n\t// not, especially on multiline text. Removed June 16 2024\n\n\t// override onDoubleClickEdge = (shape: TLTextShape) => {\n\t// \t// If the shape has a fixed width, set it to autoSize.\n\t// \tif (!shape.props.autoSize) {\n\t// \t\treturn {\n\t// \t\t\tid: shape.id,\n\t// \t\t\ttype: shape.type,\n\t// \t\t\tprops: {\n\t// \t\t\t\tautoSize: true,\n\t// \t\t\t},\n\t// \t\t}\n\t// \t}\n\t// \t// If the shape is scaled, reset the scale to 1.\n\t// \tif (shape.props.scale !== 1) {\n\t// \t\treturn {\n\t// \t\t\tid: shape.id,\n\t// \t\t\ttype: shape.type,\n\t// \t\t\tprops: {\n\t// \t\t\t\tscale: 1,\n\t// \t\t\t},\n\t// \t\t}\n\t// \t}\n\t// }\n}\n\nfunction getTextSize(editor: Editor, props: TLTextShape['props']) {\n\tconst { font, richText, size, w } = props\n\n\tconst minWidth = 16\n\tconst fontSize = FONT_SIZES[size]\n\n\tconst maybeFixedWidth = props.autoSize ? null : Math.max(minWidth, Math.floor(w))\n\n\tconst html = renderHtmlFromRichTextForMeasurement(editor, richText)\n\tconst result = editor.textMeasure.measureHtml(html, {\n\t\t...TEXT_PROPS,\n\t\tfontFamily: FONT_FAMILIES[font],\n\t\tfontSize: fontSize,\n\t\tmaxWidth: maybeFixedWidth,\n\t})\n\n\t// If we're autosizing the measureText will essentially `Math.floor`\n\t// the numbers so `19` rather than `19.3`, this means we must +1 to\n\t// whatever we get to avoid wrapping.\n\treturn {\n\t\twidth: maybeFixedWidth ?? Math.max(minWidth, result.w + 1),\n\t\theight: Math.max(fontSize, result.h),\n\t}\n}\n\nfunction useTextShapeKeydownHandler(id: TLShapeId) {\n\tconst editor = useEditor()\n\n\treturn useCallback(\n\t\t(e: KeyboardEvent) => {\n\t\t\tif (editor.getEditingShapeId() !== id) return\n\n\t\t\tswitch (e.key) {\n\t\t\t\tcase 'Enter': {\n\t\t\t\t\tif (e.ctrlKey || e.metaKey) {\n\t\t\t\t\t\teditor.complete()\n\t\t\t\t\t}\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t[editor, id]\n\t)\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -23,6 +23,7 @@ __export(Idle_exports, {
|
|
|
23
23
|
module.exports = __toCommonJS(Idle_exports);
|
|
24
24
|
var import_editor = require("@tldraw/editor");
|
|
25
25
|
var import_updateHoveredShapeId = require("../../../tools/selection-logic/updateHoveredShapeId");
|
|
26
|
+
var import_selectHelpers = require("../../../tools/SelectTool/selectHelpers");
|
|
26
27
|
class Idle extends import_editor.StateNode {
|
|
27
28
|
static id = "idle";
|
|
28
29
|
onPointerMove(info) {
|
|
@@ -44,17 +45,10 @@ class Idle extends import_editor.StateNode {
|
|
|
44
45
|
}
|
|
45
46
|
onKeyDown(info) {
|
|
46
47
|
if (info.key === "Enter") {
|
|
47
|
-
if (this.editor.getIsReadonly()) return null;
|
|
48
48
|
const onlySelectedShape = this.editor.getOnlySelectedShape();
|
|
49
|
-
if (
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
this.editor.root.getCurrent()?.transition("editing_shape", {
|
|
53
|
-
...info,
|
|
54
|
-
target: "shape",
|
|
55
|
-
shape: onlySelectedShape
|
|
56
|
-
});
|
|
57
|
-
}
|
|
49
|
+
if (!this.editor.canEditShape(onlySelectedShape)) return;
|
|
50
|
+
this.editor.setCurrentTool("select");
|
|
51
|
+
(0, import_selectHelpers.startEditingShapeWithRichText)(this.editor, onlySelectedShape.id, { info });
|
|
58
52
|
}
|
|
59
53
|
}
|
|
60
54
|
onCancel() {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../../src/lib/shapes/text/toolStates/Idle.ts"],
|
|
4
|
-
"sourcesContent": ["import { StateNode, TLKeyboardEventInfo, TLPointerEventInfo } from '@tldraw/editor'\nimport { updateHoveredShapeId } from '../../../tools/selection-logic/updateHoveredShapeId'\n\nexport class Idle extends StateNode {\n\tstatic override id = 'idle'\n\n\toverride onPointerMove(info: TLPointerEventInfo) {\n\t\tswitch (info.target) {\n\t\t\tcase 'shape':\n\t\t\tcase 'canvas': {\n\t\t\t\tupdateHoveredShapeId(this.editor)\n\t\t\t}\n\t\t}\n\t}\n\n\toverride onPointerDown(info: TLPointerEventInfo) {\n\t\tthis.parent.transition('pointing', info)\n\t}\n\n\toverride onEnter() {\n\t\tthis.editor.setCursor({ type: 'cross', rotation: 0 })\n\t}\n\n\toverride onExit() {\n\t\tupdateHoveredShapeId.cancel()\n\t}\n\n\toverride onKeyDown(info: TLKeyboardEventInfo) {\n\t\tif (info.key === 'Enter') {\n\t\t\
|
|
5
|
-
"mappings": ";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,oBAAmE;AACnE,kCAAqC;
|
|
4
|
+
"sourcesContent": ["import { StateNode, TLKeyboardEventInfo, TLPointerEventInfo } from '@tldraw/editor'\nimport { updateHoveredShapeId } from '../../../tools/selection-logic/updateHoveredShapeId'\nimport { startEditingShapeWithRichText } from '../../../tools/SelectTool/selectHelpers'\n\nexport class Idle extends StateNode {\n\tstatic override id = 'idle'\n\n\toverride onPointerMove(info: TLPointerEventInfo) {\n\t\tswitch (info.target) {\n\t\t\tcase 'shape':\n\t\t\tcase 'canvas': {\n\t\t\t\tupdateHoveredShapeId(this.editor)\n\t\t\t}\n\t\t}\n\t}\n\n\toverride onPointerDown(info: TLPointerEventInfo) {\n\t\tthis.parent.transition('pointing', info)\n\t}\n\n\toverride onEnter() {\n\t\tthis.editor.setCursor({ type: 'cross', rotation: 0 })\n\t}\n\n\toverride onExit() {\n\t\tupdateHoveredShapeId.cancel()\n\t}\n\n\toverride onKeyDown(info: TLKeyboardEventInfo) {\n\t\tif (info.key === 'Enter') {\n\t\t\tconst onlySelectedShape = this.editor.getOnlySelectedShape()\n\t\t\tif (!this.editor.canEditShape(onlySelectedShape)) return\n\t\t\tthis.editor.setCurrentTool('select')\n\t\t\tstartEditingShapeWithRichText(this.editor, onlySelectedShape.id, { info })\n\t\t}\n\t}\n\n\toverride onCancel() {\n\t\tthis.editor.setCurrentTool('select')\n\t}\n}\n"],
|
|
5
|
+
"mappings": ";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,oBAAmE;AACnE,kCAAqC;AACrC,2BAA8C;AAEvC,MAAM,aAAa,wBAAU;AAAA,EACnC,OAAgB,KAAK;AAAA,EAEZ,cAAc,MAA0B;AAChD,YAAQ,KAAK,QAAQ;AAAA,MACpB,KAAK;AAAA,MACL,KAAK,UAAU;AACd,8DAAqB,KAAK,MAAM;AAAA,MACjC;AAAA,IACD;AAAA,EACD;AAAA,EAES,cAAc,MAA0B;AAChD,SAAK,OAAO,WAAW,YAAY,IAAI;AAAA,EACxC;AAAA,EAES,UAAU;AAClB,SAAK,OAAO,UAAU,EAAE,MAAM,SAAS,UAAU,EAAE,CAAC;AAAA,EACrD;AAAA,EAES,SAAS;AACjB,qDAAqB,OAAO;AAAA,EAC7B;AAAA,EAES,UAAU,MAA2B;AAC7C,QAAI,KAAK,QAAQ,SAAS;AACzB,YAAM,oBAAoB,KAAK,OAAO,qBAAqB;AAC3D,UAAI,CAAC,KAAK,OAAO,aAAa,iBAAiB,EAAG;AAClD,WAAK,OAAO,eAAe,QAAQ;AACnC,8DAA8B,KAAK,QAAQ,kBAAkB,IAAI,EAAE,KAAK,CAAC;AAAA,IAC1E;AAAA,EACD;AAAA,EAES,WAAW;AACnB,SAAK,OAAO,eAAe,QAAQ;AAAA,EACpC;AACD;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -22,6 +22,7 @@ __export(Pointing_exports, {
|
|
|
22
22
|
});
|
|
23
23
|
module.exports = __toCommonJS(Pointing_exports);
|
|
24
24
|
var import_editor = require("@tldraw/editor");
|
|
25
|
+
var import_selectHelpers = require("../../../tools/SelectTool/selectHelpers");
|
|
25
26
|
class Pointing extends import_editor.StateNode {
|
|
26
27
|
static id = "pointing";
|
|
27
28
|
shape;
|
|
@@ -36,9 +37,10 @@ class Pointing extends import_editor.StateNode {
|
|
|
36
37
|
onPointerMove(info) {
|
|
37
38
|
if (Date.now() - this.enterTime < 150) return;
|
|
38
39
|
const { editor } = this;
|
|
39
|
-
const
|
|
40
|
+
const isPointing = editor.inputs.getIsPointing();
|
|
40
41
|
if (!isPointing) return;
|
|
41
|
-
const
|
|
42
|
+
const originPagePoint = editor.inputs.getOriginPagePoint();
|
|
43
|
+
const currentPagePoint = editor.inputs.getCurrentPagePoint();
|
|
42
44
|
const currentDragDist = Math.abs(originPagePoint.x - currentPagePoint.x);
|
|
43
45
|
const baseMinDragDistForFixedWidth = Math.sqrt(
|
|
44
46
|
editor.getInstanceState().isCoarsePointer ? editor.options.coarseDragDistanceSquared : editor.options.dragDistanceSquared
|
|
@@ -65,7 +67,7 @@ class Pointing extends import_editor.StateNode {
|
|
|
65
67
|
creationCursorOffset: { x: currentDragDist * scale, y: 1 },
|
|
66
68
|
onInteractionEnd: "text",
|
|
67
69
|
onCreate: () => {
|
|
68
|
-
|
|
70
|
+
(0, import_selectHelpers.startEditingShapeWithRichText)(editor, shape.id);
|
|
69
71
|
}
|
|
70
72
|
});
|
|
71
73
|
}
|
|
@@ -85,11 +87,11 @@ class Pointing extends import_editor.StateNode {
|
|
|
85
87
|
complete() {
|
|
86
88
|
this.editor.markHistoryStoppingPoint("creating text shape");
|
|
87
89
|
const id = (0, import_editor.createShapeId)();
|
|
88
|
-
const
|
|
90
|
+
const originPagePoint = this.editor.inputs.getOriginPagePoint();
|
|
89
91
|
const shape = this.createTextShape(id, originPagePoint, true, 20);
|
|
90
92
|
if (!shape) return;
|
|
91
93
|
this.editor.select(id);
|
|
92
|
-
this.editor
|
|
94
|
+
(0, import_selectHelpers.startEditingShapeWithRichText)(this.editor, id);
|
|
93
95
|
}
|
|
94
96
|
cancel() {
|
|
95
97
|
this.parent.transition("idle");
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../../src/lib/shapes/text/toolStates/Pointing.ts"],
|
|
4
|
-
"sourcesContent": ["import {\n\tStateNode,\n\tTLPointerEventInfo,\n\tTLShapeId,\n\tTLTextShape,\n\tVec,\n\tcreateShapeId,\n\tisShapeId,\n\tmaybeSnapToGrid,\n\ttoRichText,\n} from '@tldraw/editor'\n\nexport class Pointing extends StateNode {\n\tstatic override id = 'pointing'\n\n\tshape?: TLTextShape\n\n\tmarkId = ''\n\n\tenterTime = 0\n\toverride onEnter(): void {\n\t\tthis.enterTime = Date.now()\n\t}\n\n\toverride onExit() {\n\t\tthis.editor.setHintingShapes([])\n\t}\n\n\toverride onPointerMove(info: TLPointerEventInfo) {\n\t\t// Create a fixed width shape if the user wants to do that.\n\n\t\t// Don't create a fixed width shape unless the the drag is a little larger,\n\t\t// otherwise you get a vertical column of single characters if you accidentally\n\t\t// drag a bit unintentionally.\n\n\t\t// If the user hasn't been pointing for more than 150ms, don't create a fixed width shape\n\t\tif (Date.now() - this.enterTime < 150) return\n\n\t\tconst { editor } = this\n\t\tconst
|
|
5
|
-
"mappings": ";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,oBAUO;
|
|
4
|
+
"sourcesContent": ["import {\n\tStateNode,\n\tTLPointerEventInfo,\n\tTLShapeId,\n\tTLTextShape,\n\tVec,\n\tcreateShapeId,\n\tisShapeId,\n\tmaybeSnapToGrid,\n\ttoRichText,\n} from '@tldraw/editor'\nimport { startEditingShapeWithRichText } from '../../../tools/SelectTool/selectHelpers'\n\nexport class Pointing extends StateNode {\n\tstatic override id = 'pointing'\n\n\tshape?: TLTextShape\n\n\tmarkId = ''\n\n\tenterTime = 0\n\toverride onEnter(): void {\n\t\tthis.enterTime = Date.now()\n\t}\n\n\toverride onExit() {\n\t\tthis.editor.setHintingShapes([])\n\t}\n\n\toverride onPointerMove(info: TLPointerEventInfo) {\n\t\t// Create a fixed width shape if the user wants to do that.\n\n\t\t// Don't create a fixed width shape unless the the drag is a little larger,\n\t\t// otherwise you get a vertical column of single characters if you accidentally\n\t\t// drag a bit unintentionally.\n\n\t\t// If the user hasn't been pointing for more than 150ms, don't create a fixed width shape\n\t\tif (Date.now() - this.enterTime < 150) return\n\n\t\tconst { editor } = this\n\t\tconst isPointing = editor.inputs.getIsPointing()\n\n\t\tif (!isPointing) return\n\n\t\tconst originPagePoint = editor.inputs.getOriginPagePoint()\n\t\tconst currentPagePoint = editor.inputs.getCurrentPagePoint()\n\n\t\tconst currentDragDist = Math.abs(originPagePoint.x - currentPagePoint.x)\n\n\t\tconst baseMinDragDistForFixedWidth = Math.sqrt(\n\t\t\teditor.getInstanceState().isCoarsePointer\n\t\t\t\t? editor.options.coarseDragDistanceSquared\n\t\t\t\t: editor.options.dragDistanceSquared\n\t\t)\n\n\t\t// Ten times the base drag distance for fixed width\n\t\tconst minSquaredDragDist = (baseMinDragDistForFixedWidth * 6) / editor.getZoomLevel()\n\n\t\tif (currentDragDist > minSquaredDragDist) {\n\t\t\tconst id = createShapeId()\n\t\t\tthis.markId = editor.markHistoryStoppingPoint(`creating_text:${id}`)\n\n\t\t\t// create the initial shape with the width that we've dragged\n\t\t\tconst shape = this.createTextShape(id, originPagePoint, false, currentDragDist)\n\n\t\t\tif (!shape) {\n\t\t\t\tthis.cancel()\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\t// Now save the fresh reference\n\t\t\tthis.shape = editor.getShape(shape)\n\n\t\t\teditor.select(id)\n\n\t\t\tconst scale = this.editor.user.getIsDynamicResizeMode() ? 1 / this.editor.getZoomLevel() : 1\n\n\t\t\teditor.setCurrentTool('select.resizing', {\n\t\t\t\t...info,\n\t\t\t\ttarget: 'selection',\n\t\t\t\thandle: 'right',\n\t\t\t\tisCreating: true,\n\t\t\t\tcreatingMarkId: this.markId,\n\t\t\t\t// Make sure the cursor offset takes into account how far we've already dragged\n\t\t\t\tcreationCursorOffset: { x: currentDragDist * scale, y: 1 },\n\t\t\t\tonInteractionEnd: 'text',\n\t\t\t\tonCreate: () => {\n\t\t\t\t\tstartEditingShapeWithRichText(editor, shape.id)\n\t\t\t\t},\n\t\t\t})\n\t\t}\n\t}\n\n\toverride onPointerUp() {\n\t\tthis.complete()\n\t}\n\n\toverride onComplete() {\n\t\tthis.cancel()\n\t}\n\n\toverride onCancel() {\n\t\tthis.cancel()\n\t}\n\n\toverride onInterrupt() {\n\t\tthis.cancel()\n\t}\n\n\tprivate complete() {\n\t\tthis.editor.markHistoryStoppingPoint('creating text shape')\n\t\tconst id = createShapeId()\n\t\tconst originPagePoint = this.editor.inputs.getOriginPagePoint()\n\t\tconst shape = this.createTextShape(id, originPagePoint, true, 20)\n\t\tif (!shape) return\n\n\t\tthis.editor.select(id)\n\t\tstartEditingShapeWithRichText(this.editor, id)\n\t}\n\n\tprivate cancel() {\n\t\tthis.parent.transition('idle')\n\t\tthis.editor.bailToMark(this.markId)\n\t}\n\n\tprivate createTextShape(id: TLShapeId, point: Vec, autoSize: boolean, width: number) {\n\t\tthis.editor.createShape({\n\t\t\tid,\n\t\t\ttype: 'text',\n\t\t\tx: point.x,\n\t\t\ty: point.y,\n\t\t\tprops: {\n\t\t\t\trichText: toRichText(''),\n\t\t\t\tautoSize,\n\t\t\t\tw: width,\n\t\t\t\tscale: this.editor.user.getIsDynamicResizeMode() ? 1 / this.editor.getZoomLevel() : 1,\n\t\t\t},\n\t\t})\n\n\t\tconst shape = this.editor.getShape<TLTextShape>(id)\n\t\tif (!shape) {\n\t\t\tthis.cancel()\n\t\t\treturn\n\t\t}\n\n\t\tconst bounds = this.editor.getShapePageBounds(shape)!\n\n\t\tconst delta = new Vec()\n\n\t\tif (autoSize) {\n\t\t\tswitch (shape.props.textAlign) {\n\t\t\t\tcase 'start': {\n\t\t\t\t\tdelta.x = 0\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t\tcase 'middle': {\n\t\t\t\t\tdelta.x = -bounds.width / 2\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t\tcase 'end': {\n\t\t\t\t\tdelta.x = -bounds.width\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t}\n\t\t} else {\n\t\t\tdelta.x = 0\n\t\t}\n\n\t\tdelta.y = -bounds.height / 2\n\n\t\tif (isShapeId(shape.parentId)) {\n\t\t\tconst transform = this.editor.getShapeParentTransform(shape)\n\t\t\tdelta.rot(-transform.rotation())\n\t\t}\n\n\t\tconst shapeX = shape.x + delta.x\n\t\tconst shapeY = shape.y + delta.y\n\t\tif (this.editor.getInstanceState().isGridMode) {\n\t\t\tconst topLeft = new Vec(shapeX, shapeY)\n\t\t\tconst gridSnappedPoint = maybeSnapToGrid(topLeft, this.editor)\n\t\t\tconst gridDelta = Vec.Sub(topLeft, gridSnappedPoint)\n\t\t\tthis.editor.updateShape({\n\t\t\t\t...shape,\n\t\t\t\tx: shapeX - gridDelta.x,\n\t\t\t\ty: shapeY - gridDelta.y,\n\t\t\t})\n\t\t} else {\n\t\t\tthis.editor.updateShape({\n\t\t\t\t...shape,\n\t\t\t\tx: shapeX,\n\t\t\t\ty: shapeY,\n\t\t\t})\n\t\t}\n\n\t\treturn shape\n\t}\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -92,7 +92,7 @@ class VideoShapeUtil extends import_editor.BaseBoxShapeUtil {
|
|
|
92
92
|
}
|
|
93
93
|
const VideoShape = (0, import_react.memo)(function VideoShape2({ shape }) {
|
|
94
94
|
const editor = (0, import_editor.useEditor)();
|
|
95
|
-
const showControls = editor.getShapeGeometry(shape).bounds.w * editor.
|
|
95
|
+
const showControls = editor.getShapeGeometry(shape).bounds.w * editor.getEfficientZoomLevel() >= 110;
|
|
96
96
|
const isEditing = (0, import_editor.useIsEditing)(shape.id);
|
|
97
97
|
const prefersReducedMotion = (0, import_usePrefersReducedMotion.usePrefersReducedMotion)();
|
|
98
98
|
const { Spinner } = (0, import_editor.useEditorComponents)();
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../src/lib/shapes/video/VideoShapeUtil.tsx"],
|
|
4
|
-
"sourcesContent": ["import {\n\tBaseBoxShapeUtil,\n\tHTMLContainer,\n\tMediaHelpers,\n\tSvgExportContext,\n\tTLAsset,\n\tTLVideoShape,\n\tWeakCache,\n\ttoDomPrecision,\n\tuseEditor,\n\tuseEditorComponents,\n\tuseIsEditing,\n\tvideoShapeMigrations,\n\tvideoShapeProps,\n} from '@tldraw/editor'\nimport classNames from 'classnames'\nimport { ReactEventHandler, memo, useCallback, useEffect, useRef, useState } from 'react'\nimport { BrokenAssetIcon } from '../shared/BrokenAssetIcon'\nimport { HyperlinkButton } from '../shared/HyperlinkButton'\nimport { useImageOrVideoAsset } from '../shared/useImageOrVideoAsset'\nimport { usePrefersReducedMotion } from '../shared/usePrefersReducedMotion'\n\nconst videoSvgExportCache = new WeakCache<TLAsset, Promise<string | null>>()\n\n/** @public */\nexport interface VideoShapeOptions {\n\t/**\n\t * Should videos play automatically?\n\t */\n\tautoplay: boolean\n}\n\n/** @public */\nexport class VideoShapeUtil extends BaseBoxShapeUtil<TLVideoShape> {\n\tstatic override type = 'video' as const\n\tstatic override props = videoShapeProps\n\tstatic override migrations = videoShapeMigrations\n\n\toverride options: VideoShapeOptions = {\n\t\tautoplay: true,\n\t}\n\n\toverride canEdit() {\n\t\treturn true\n\t}\n\toverride isAspectRatioLocked() {\n\t\treturn true\n\t}\n\n\toverride getDefaultProps(): TLVideoShape['props'] {\n\t\treturn {\n\t\t\tw: 100,\n\t\t\th: 100,\n\t\t\tassetId: null,\n\t\t\tautoplay: this.options.autoplay,\n\t\t\turl: '',\n\t\t\taltText: '',\n\t\t\t// Not used, but once upon a time were used to sync video state between users\n\t\t\ttime: 0,\n\t\t\tplaying: true,\n\t\t}\n\t}\n\n\toverride getAriaDescriptor(shape: TLVideoShape) {\n\t\treturn shape.props.altText\n\t}\n\n\tcomponent(shape: TLVideoShape) {\n\t\treturn <VideoShape shape={shape} />\n\t}\n\n\tindicator(shape: TLVideoShape) {\n\t\treturn <rect width={toDomPrecision(shape.props.w)} height={toDomPrecision(shape.props.h)} />\n\t}\n\n\toverride async toSvg(shape: TLVideoShape, ctx: SvgExportContext) {\n\t\tconst props = shape.props\n\t\tif (!props.assetId) return null\n\n\t\tconst asset = this.editor.getAsset<TLAsset>(props.assetId)\n\t\tif (!asset) return null\n\n\t\tconst src = await videoSvgExportCache.get(asset, async () => {\n\t\t\tconst assetUrl = await ctx.resolveAssetUrl(asset.id, props.w)\n\t\t\tif (!assetUrl) return null\n\t\t\tconst video = await MediaHelpers.loadVideo(assetUrl)\n\t\t\treturn await MediaHelpers.getVideoFrameAsDataUrl(video, 0)\n\t\t})\n\n\t\tif (!src) return null\n\n\t\treturn <image href={src} width={props.w} height={props.h} aria-label={shape.props.altText} />\n\t}\n}\n\nconst VideoShape = memo(function VideoShape({ shape }: { shape: TLVideoShape }) {\n\tconst editor = useEditor()\n\tconst showControls
|
|
5
|
-
"mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAoES;AApET,oBAcO;AACP,wBAAuB;AACvB,mBAAkF;AAClF,6BAAgC;AAChC,6BAAgC;AAChC,kCAAqC;AACrC,qCAAwC;AAExC,MAAM,sBAAsB,IAAI,wBAA2C;AAWpE,MAAM,uBAAuB,+BAA+B;AAAA,EAClE,OAAgB,OAAO;AAAA,EACvB,OAAgB,QAAQ;AAAA,EACxB,OAAgB,aAAa;AAAA,EAEpB,UAA6B;AAAA,IACrC,UAAU;AAAA,EACX;AAAA,EAES,UAAU;AAClB,WAAO;AAAA,EACR;AAAA,EACS,sBAAsB;AAC9B,WAAO;AAAA,EACR;AAAA,EAES,kBAAyC;AACjD,WAAO;AAAA,MACN,GAAG;AAAA,MACH,GAAG;AAAA,MACH,SAAS;AAAA,MACT,UAAU,KAAK,QAAQ;AAAA,MACvB,KAAK;AAAA,MACL,SAAS;AAAA;AAAA,MAET,MAAM;AAAA,MACN,SAAS;AAAA,IACV;AAAA,EACD;AAAA,EAES,kBAAkB,OAAqB;AAC/C,WAAO,MAAM,MAAM;AAAA,EACpB;AAAA,EAEA,UAAU,OAAqB;AAC9B,WAAO,4CAAC,cAAW,OAAc;AAAA,EAClC;AAAA,EAEA,UAAU,OAAqB;AAC9B,WAAO,4CAAC,UAAK,WAAO,8BAAe,MAAM,MAAM,CAAC,GAAG,YAAQ,8BAAe,MAAM,MAAM,CAAC,GAAG;AAAA,EAC3F;AAAA,EAEA,MAAe,MAAM,OAAqB,KAAuB;AAChE,UAAM,QAAQ,MAAM;AACpB,QAAI,CAAC,MAAM,QAAS,QAAO;AAE3B,UAAM,QAAQ,KAAK,OAAO,SAAkB,MAAM,OAAO;AACzD,QAAI,CAAC,MAAO,QAAO;AAEnB,UAAM,MAAM,MAAM,oBAAoB,IAAI,OAAO,YAAY;AAC5D,YAAM,WAAW,MAAM,IAAI,gBAAgB,MAAM,IAAI,MAAM,CAAC;AAC5D,UAAI,CAAC,SAAU,QAAO;AACtB,YAAM,QAAQ,MAAM,2BAAa,UAAU,QAAQ;AACnD,aAAO,MAAM,2BAAa,uBAAuB,OAAO,CAAC;AAAA,IAC1D,CAAC;AAED,QAAI,CAAC,IAAK,QAAO;AAEjB,WAAO,4CAAC,WAAM,MAAM,KAAK,OAAO,MAAM,GAAG,QAAQ,MAAM,GAAG,cAAY,MAAM,MAAM,SAAS;AAAA,EAC5F;AACD;AAEA,MAAM,iBAAa,mBAAK,SAASA,YAAW,EAAE,MAAM,GAA4B;AAC/E,QAAM,aAAS,yBAAU;AACzB,QAAM,
|
|
4
|
+
"sourcesContent": ["import {\n\tBaseBoxShapeUtil,\n\tHTMLContainer,\n\tMediaHelpers,\n\tSvgExportContext,\n\tTLAsset,\n\tTLVideoShape,\n\tWeakCache,\n\ttoDomPrecision,\n\tuseEditor,\n\tuseEditorComponents,\n\tuseIsEditing,\n\tvideoShapeMigrations,\n\tvideoShapeProps,\n} from '@tldraw/editor'\nimport classNames from 'classnames'\nimport { ReactEventHandler, memo, useCallback, useEffect, useRef, useState } from 'react'\nimport { BrokenAssetIcon } from '../shared/BrokenAssetIcon'\nimport { HyperlinkButton } from '../shared/HyperlinkButton'\nimport { useImageOrVideoAsset } from '../shared/useImageOrVideoAsset'\nimport { usePrefersReducedMotion } from '../shared/usePrefersReducedMotion'\n\nconst videoSvgExportCache = new WeakCache<TLAsset, Promise<string | null>>()\n\n/** @public */\nexport interface VideoShapeOptions {\n\t/**\n\t * Should videos play automatically?\n\t */\n\tautoplay: boolean\n}\n\n/** @public */\nexport class VideoShapeUtil extends BaseBoxShapeUtil<TLVideoShape> {\n\tstatic override type = 'video' as const\n\tstatic override props = videoShapeProps\n\tstatic override migrations = videoShapeMigrations\n\n\toverride options: VideoShapeOptions = {\n\t\tautoplay: true,\n\t}\n\n\toverride canEdit() {\n\t\treturn true\n\t}\n\toverride isAspectRatioLocked() {\n\t\treturn true\n\t}\n\n\toverride getDefaultProps(): TLVideoShape['props'] {\n\t\treturn {\n\t\t\tw: 100,\n\t\t\th: 100,\n\t\t\tassetId: null,\n\t\t\tautoplay: this.options.autoplay,\n\t\t\turl: '',\n\t\t\taltText: '',\n\t\t\t// Not used, but once upon a time were used to sync video state between users\n\t\t\ttime: 0,\n\t\t\tplaying: true,\n\t\t}\n\t}\n\n\toverride getAriaDescriptor(shape: TLVideoShape) {\n\t\treturn shape.props.altText\n\t}\n\n\tcomponent(shape: TLVideoShape) {\n\t\treturn <VideoShape shape={shape} />\n\t}\n\n\tindicator(shape: TLVideoShape) {\n\t\treturn <rect width={toDomPrecision(shape.props.w)} height={toDomPrecision(shape.props.h)} />\n\t}\n\n\toverride async toSvg(shape: TLVideoShape, ctx: SvgExportContext) {\n\t\tconst props = shape.props\n\t\tif (!props.assetId) return null\n\n\t\tconst asset = this.editor.getAsset<TLAsset>(props.assetId)\n\t\tif (!asset) return null\n\n\t\tconst src = await videoSvgExportCache.get(asset, async () => {\n\t\t\tconst assetUrl = await ctx.resolveAssetUrl(asset.id, props.w)\n\t\t\tif (!assetUrl) return null\n\t\t\tconst video = await MediaHelpers.loadVideo(assetUrl)\n\t\t\treturn await MediaHelpers.getVideoFrameAsDataUrl(video, 0)\n\t\t})\n\n\t\tif (!src) return null\n\n\t\treturn <image href={src} width={props.w} height={props.h} aria-label={shape.props.altText} />\n\t}\n}\n\nconst VideoShape = memo(function VideoShape({ shape }: { shape: TLVideoShape }) {\n\tconst editor = useEditor()\n\tconst showControls =\n\t\teditor.getShapeGeometry(shape).bounds.w * editor.getEfficientZoomLevel() >= 110\n\tconst isEditing = useIsEditing(shape.id)\n\tconst prefersReducedMotion = usePrefersReducedMotion()\n\tconst { Spinner } = useEditorComponents()\n\n\tconst { asset, url } = useImageOrVideoAsset({\n\t\tshapeId: shape.id,\n\t\tassetId: shape.props.assetId,\n\t\twidth: shape.props.w,\n\t})\n\n\tconst rVideo = useRef<HTMLVideoElement>(null!)\n\n\tconst [isLoaded, setIsLoaded] = useState(false)\n\n\tconst handleLoadedData = useCallback<ReactEventHandler<HTMLVideoElement>>((e) => {\n\t\tconst video = e.currentTarget\n\t\tif (!video) return\n\t\tsetIsLoaded(true)\n\t}, [])\n\n\tconst [isFullscreen, setIsFullscreen] = useState(false)\n\n\tuseEffect(() => {\n\t\tconst fullscreenChange = () => setIsFullscreen(document.fullscreenElement === rVideo.current)\n\t\tdocument.addEventListener('fullscreenchange', fullscreenChange)\n\n\t\treturn () => document.removeEventListener('fullscreenchange', fullscreenChange)\n\t})\n\n\t// Focus the video when editing\n\tuseEffect(() => {\n\t\tconst video = rVideo.current\n\t\tif (!video) return\n\n\t\tif (isEditing) {\n\t\t\tif (document.activeElement !== video) {\n\t\t\t\tvideo.focus()\n\t\t\t}\n\t\t}\n\t}, [isEditing, isLoaded])\n\n\treturn (\n\t\t<>\n\t\t\t<HTMLContainer\n\t\t\t\tid={shape.id}\n\t\t\t\tstyle={{\n\t\t\t\t\tcolor: 'var(--tl-color-text-3)',\n\t\t\t\t\tbackgroundColor: asset ? 'transparent' : 'var(--tl-color-low)',\n\t\t\t\t\tborder: asset ? 'none' : '1px solid var(--tl-color-low-border)',\n\t\t\t\t}}\n\t\t\t>\n\t\t\t\t<div className=\"tl-counter-scaled\">\n\t\t\t\t\t<div className=\"tl-video-container\">\n\t\t\t\t\t\t{!asset ? (\n\t\t\t\t\t\t\t<BrokenAssetIcon />\n\t\t\t\t\t\t) : Spinner && !asset.props.src ? (\n\t\t\t\t\t\t\t<Spinner />\n\t\t\t\t\t\t) : url ? (\n\t\t\t\t\t\t\t<>\n\t\t\t\t\t\t\t\t<video\n\t\t\t\t\t\t\t\t\tkey={url}\n\t\t\t\t\t\t\t\t\tref={rVideo}\n\t\t\t\t\t\t\t\t\tstyle={\n\t\t\t\t\t\t\t\t\t\tisEditing\n\t\t\t\t\t\t\t\t\t\t\t? { pointerEvents: 'all' }\n\t\t\t\t\t\t\t\t\t\t\t: !isLoaded\n\t\t\t\t\t\t\t\t\t\t\t\t? { display: 'none' }\n\t\t\t\t\t\t\t\t\t\t\t\t: undefined\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\tclassName={classNames('tl-video', `tl-video-shape-${shape.id.split(':')[1]}`, {\n\t\t\t\t\t\t\t\t\t\t'tl-video-is-fullscreen': isFullscreen,\n\t\t\t\t\t\t\t\t\t})}\n\t\t\t\t\t\t\t\t\twidth=\"100%\"\n\t\t\t\t\t\t\t\t\theight=\"100%\"\n\t\t\t\t\t\t\t\t\tdraggable={false}\n\t\t\t\t\t\t\t\t\tplaysInline\n\t\t\t\t\t\t\t\t\tautoPlay={shape.props.autoplay && !prefersReducedMotion}\n\t\t\t\t\t\t\t\t\tmuted\n\t\t\t\t\t\t\t\t\tloop\n\t\t\t\t\t\t\t\t\tdisableRemotePlayback\n\t\t\t\t\t\t\t\t\tdisablePictureInPicture\n\t\t\t\t\t\t\t\t\tcontrols={isEditing && showControls}\n\t\t\t\t\t\t\t\t\tonLoadedData={handleLoadedData}\n\t\t\t\t\t\t\t\t\thidden={!isLoaded}\n\t\t\t\t\t\t\t\t\taria-label={shape.props.altText}\n\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t<source src={url} />\n\t\t\t\t\t\t\t\t</video>\n\t\t\t\t\t\t\t\t{!isLoaded && Spinner && <Spinner />}\n\t\t\t\t\t\t\t</>\n\t\t\t\t\t\t) : null}\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\t\t\t</HTMLContainer>\n\t\t\t{'url' in shape.props && shape.props.url && <HyperlinkButton url={shape.props.url} />}\n\t\t</>\n\t)\n})\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": ["VideoShape", "classNames"]
|
|
7
7
|
}
|
|
@@ -37,7 +37,7 @@ class Erasing extends import_editor.StateNode {
|
|
|
37
37
|
this._erasingShapeIds = this.editor.getErasingShapeIds();
|
|
38
38
|
this.markId = this.editor.markHistoryStoppingPoint("erase scribble begin");
|
|
39
39
|
this.info = info;
|
|
40
|
-
const
|
|
40
|
+
const originPagePoint = this.editor.inputs.getOriginPagePoint();
|
|
41
41
|
this.excludedShapeIds = new Set(
|
|
42
42
|
this.editor.getCurrentPageShapes().filter((shape) => {
|
|
43
43
|
if (this.editor.isShapeOrAncestorLocked(shape)) return true;
|
|
@@ -57,7 +57,7 @@ class Erasing extends import_editor.StateNode {
|
|
|
57
57
|
this.update();
|
|
58
58
|
}
|
|
59
59
|
pushPointToScribble() {
|
|
60
|
-
const { x, y } = this.editor.inputs.
|
|
60
|
+
const { x, y } = this.editor.inputs.getCurrentPagePoint();
|
|
61
61
|
this.editor.scribbles.addPoint(this.scribbleId, x, y);
|
|
62
62
|
}
|
|
63
63
|
onExit() {
|
|
@@ -88,13 +88,19 @@ class Erasing extends import_editor.StateNode {
|
|
|
88
88
|
const { editor, excludedShapeIds } = this;
|
|
89
89
|
const erasingShapeIds = editor.getErasingShapeIds();
|
|
90
90
|
const zoomLevel = editor.getZoomLevel();
|
|
91
|
-
const
|
|
92
|
-
const
|
|
93
|
-
inputs: { currentPagePoint, previousPagePoint }
|
|
94
|
-
} = editor;
|
|
91
|
+
const currentPagePoint = editor.inputs.getCurrentPagePoint();
|
|
92
|
+
const previousPagePoint = editor.inputs.getPreviousPagePoint();
|
|
95
93
|
this.pushPointToScribble();
|
|
96
94
|
const erasing = new Set(erasingShapeIds);
|
|
97
95
|
const minDist = this.editor.options.hitTestMargin / zoomLevel;
|
|
96
|
+
const lineBounds = import_editor.Box.FromPoints([previousPagePoint, currentPagePoint]).expandBy(minDist);
|
|
97
|
+
const candidateIds = editor.getShapeIdsInsideBounds(lineBounds);
|
|
98
|
+
if (candidateIds.size === 0) {
|
|
99
|
+
editor.setErasingShapes(Array.from(erasing));
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
102
|
+
const allShapes = editor.getCurrentPageRenderingShapesSorted();
|
|
103
|
+
const currentPageShapes = allShapes.filter((shape) => candidateIds.has(shape.id));
|
|
98
104
|
for (const shape of currentPageShapes) {
|
|
99
105
|
if (editor.isShapeOfType(shape, "group")) continue;
|
|
100
106
|
const pageMask = editor.getShapeMask(shape.id);
|