@tldraw/editor 3.16.0-internal.a478398270c6 → 3.16.0-internal.f8b97f0c414f
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist-cjs/index.d.ts +350 -142
- package/dist-cjs/index.js +13 -6
- package/dist-cjs/index.js.map +2 -2
- package/dist-cjs/lib/TldrawEditor.js +10 -8
- package/dist-cjs/lib/TldrawEditor.js.map +2 -2
- package/dist-cjs/lib/components/MenuClickCapture.js +0 -5
- package/dist-cjs/lib/components/MenuClickCapture.js.map +2 -2
- package/dist-cjs/lib/components/SVGContainer.js +1 -1
- package/dist-cjs/lib/components/SVGContainer.js.map +2 -2
- package/dist-cjs/lib/components/Shape.js +11 -36
- package/dist-cjs/lib/components/Shape.js.map +2 -2
- package/dist-cjs/lib/components/default-components/DefaultBrush.js +1 -1
- package/dist-cjs/lib/components/default-components/DefaultBrush.js.map +2 -2
- package/dist-cjs/lib/components/default-components/DefaultCanvas.js +15 -24
- package/dist-cjs/lib/components/default-components/DefaultCanvas.js.map +2 -2
- package/dist-cjs/lib/components/default-components/DefaultCollaboratorHint.js +2 -2
- package/dist-cjs/lib/components/default-components/DefaultCollaboratorHint.js.map +2 -2
- package/dist-cjs/lib/components/default-components/DefaultCursor.js +1 -1
- package/dist-cjs/lib/components/default-components/DefaultCursor.js.map +2 -2
- package/dist-cjs/lib/components/default-components/DefaultErrorFallback.js +1 -1
- package/dist-cjs/lib/components/default-components/DefaultErrorFallback.js.map +2 -2
- package/dist-cjs/lib/components/default-components/DefaultGrid.js +1 -1
- package/dist-cjs/lib/components/default-components/DefaultGrid.js.map +2 -2
- package/dist-cjs/lib/components/default-components/DefaultHandles.js +1 -1
- package/dist-cjs/lib/components/default-components/DefaultHandles.js.map +2 -2
- package/dist-cjs/lib/components/default-components/DefaultScribble.js +1 -1
- package/dist-cjs/lib/components/default-components/DefaultScribble.js.map +2 -2
- package/dist-cjs/lib/components/default-components/DefaultShapeIndicator.js +9 -1
- package/dist-cjs/lib/components/default-components/DefaultShapeIndicator.js.map +2 -2
- package/dist-cjs/lib/components/default-components/DefaultShapeWrapper.js +53 -0
- package/dist-cjs/lib/components/default-components/DefaultShapeWrapper.js.map +7 -0
- package/dist-cjs/lib/components/default-components/DefaultSnapIndictor.js +1 -1
- package/dist-cjs/lib/components/default-components/DefaultSnapIndictor.js.map +2 -2
- package/dist-cjs/lib/components/default-components/DefaultSpinner.js +27 -15
- package/dist-cjs/lib/components/default-components/DefaultSpinner.js.map +3 -3
- package/dist-cjs/lib/config/TLUserPreferences.js +15 -3
- package/dist-cjs/lib/config/TLUserPreferences.js.map +2 -2
- package/dist-cjs/lib/editor/Editor.js +174 -180
- package/dist-cjs/lib/editor/Editor.js.map +2 -2
- package/dist-cjs/lib/editor/derivations/notVisibleShapes.js +4 -0
- package/dist-cjs/lib/editor/derivations/notVisibleShapes.js.map +2 -2
- package/dist-cjs/lib/editor/derivations/parentsToChildren.js.map +2 -2
- package/dist-cjs/lib/editor/managers/UserPreferencesManager/UserPreferencesManager.js +14 -4
- package/dist-cjs/lib/editor/managers/UserPreferencesManager/UserPreferencesManager.js.map +2 -2
- package/dist-cjs/lib/editor/shapes/BaseBoxShapeUtil.js.map +1 -1
- package/dist-cjs/lib/editor/shapes/ShapeUtil.js +23 -0
- package/dist-cjs/lib/editor/shapes/ShapeUtil.js.map +2 -2
- package/dist-cjs/lib/editor/tools/BaseBoxShapeTool/BaseBoxShapeTool.js.map +2 -2
- package/dist-cjs/lib/editor/tools/StateNode.js +20 -1
- package/dist-cjs/lib/editor/tools/StateNode.js.map +2 -2
- package/dist-cjs/lib/editor/types/misc-types.js.map +1 -1
- package/dist-cjs/lib/exports/getSvgJsx.js +35 -16
- package/dist-cjs/lib/exports/getSvgJsx.js.map +2 -2
- package/dist-cjs/lib/hooks/useCanvasEvents.js +44 -35
- package/dist-cjs/lib/hooks/useCanvasEvents.js.map +2 -2
- package/dist-cjs/lib/hooks/useDocumentEvents.js +5 -5
- package/dist-cjs/lib/hooks/useDocumentEvents.js.map +2 -2
- package/dist-cjs/lib/hooks/useEditor.js +1 -4
- package/dist-cjs/lib/hooks/useEditor.js.map +2 -2
- package/dist-cjs/lib/hooks/useEditorComponents.js +2 -0
- package/dist-cjs/lib/hooks/useEditorComponents.js.map +2 -2
- package/dist-cjs/lib/hooks/useFixSafariDoubleTapZoomPencilEvents.js +1 -2
- package/dist-cjs/lib/hooks/useFixSafariDoubleTapZoomPencilEvents.js.map +2 -2
- package/dist-cjs/lib/hooks/useGestureEvents.js +1 -1
- package/dist-cjs/lib/hooks/useGestureEvents.js.map +2 -2
- package/dist-cjs/lib/hooks/useHandleEvents.js +3 -3
- package/dist-cjs/lib/hooks/useHandleEvents.js.map +2 -2
- package/dist-cjs/lib/hooks/usePassThroughMouseOverEvents.js +4 -1
- package/dist-cjs/lib/hooks/usePassThroughMouseOverEvents.js.map +2 -2
- package/dist-cjs/lib/hooks/usePassThroughWheelEvents.js +4 -1
- package/dist-cjs/lib/hooks/usePassThroughWheelEvents.js.map +2 -2
- package/dist-cjs/lib/hooks/useSelectionEvents.js +4 -4
- package/dist-cjs/lib/hooks/useSelectionEvents.js.map +2 -2
- package/dist-cjs/lib/{utils/nearestMultiple.js → hooks/useStateAttribute.js} +15 -14
- package/dist-cjs/lib/hooks/useStateAttribute.js.map +7 -0
- package/dist-cjs/lib/license/LicenseManager.js +140 -53
- package/dist-cjs/lib/license/LicenseManager.js.map +2 -2
- package/dist-cjs/lib/license/LicenseProvider.js +39 -1
- package/dist-cjs/lib/license/LicenseProvider.js.map +2 -2
- package/dist-cjs/lib/license/Watermark.js +75 -13
- package/dist-cjs/lib/license/Watermark.js.map +3 -3
- package/dist-cjs/lib/license/useLicenseManagerState.js.map +2 -2
- package/dist-cjs/lib/options.js +7 -0
- package/dist-cjs/lib/options.js.map +2 -2
- package/dist-cjs/lib/primitives/Box.js +3 -0
- package/dist-cjs/lib/primitives/Box.js.map +2 -2
- package/dist-cjs/lib/primitives/Vec.js +0 -4
- package/dist-cjs/lib/primitives/Vec.js.map +2 -2
- package/dist-cjs/lib/primitives/geometry/Arc2d.js +1 -1
- package/dist-cjs/lib/primitives/geometry/Arc2d.js.map +2 -2
- package/dist-cjs/lib/primitives/geometry/Circle2d.js +1 -1
- package/dist-cjs/lib/primitives/geometry/Circle2d.js.map +2 -2
- package/dist-cjs/lib/primitives/geometry/CubicBezier2d.js +3 -1
- package/dist-cjs/lib/primitives/geometry/CubicBezier2d.js.map +2 -2
- package/dist-cjs/lib/primitives/geometry/Ellipse2d.js +1 -1
- package/dist-cjs/lib/primitives/geometry/Ellipse2d.js.map +2 -2
- package/dist-cjs/lib/primitives/geometry/Geometry2d.js +50 -20
- package/dist-cjs/lib/primitives/geometry/Geometry2d.js.map +2 -2
- package/dist-cjs/lib/primitives/geometry/Group2d.js +8 -1
- package/dist-cjs/lib/primitives/geometry/Group2d.js.map +2 -2
- package/dist-cjs/lib/primitives/geometry/geometry-constants.js +2 -2
- package/dist-cjs/lib/primitives/geometry/geometry-constants.js.map +2 -2
- package/dist-cjs/lib/primitives/intersect.js +4 -4
- package/dist-cjs/lib/primitives/intersect.js.map +2 -2
- package/dist-cjs/lib/primitives/utils.js +4 -0
- package/dist-cjs/lib/primitives/utils.js.map +2 -2
- package/dist-cjs/lib/utils/EditorAtom.js +45 -0
- package/dist-cjs/lib/utils/EditorAtom.js.map +7 -0
- package/dist-cjs/lib/utils/dom.js +12 -1
- package/dist-cjs/lib/utils/dom.js.map +2 -2
- package/dist-cjs/lib/utils/getPointerInfo.js +2 -2
- package/dist-cjs/lib/utils/getPointerInfo.js.map +2 -2
- package/dist-cjs/lib/utils/reparenting.js +2 -35
- package/dist-cjs/lib/utils/reparenting.js.map +3 -3
- package/dist-cjs/lib/utils/sync/TLLocalSyncClient.js +0 -1
- package/dist-cjs/lib/utils/sync/TLLocalSyncClient.js.map +2 -2
- package/dist-cjs/version.js +3 -3
- package/dist-cjs/version.js.map +1 -1
- package/dist-esm/index.d.mts +350 -142
- package/dist-esm/index.mjs +24 -8
- package/dist-esm/index.mjs.map +2 -2
- package/dist-esm/lib/TldrawEditor.mjs +11 -9
- package/dist-esm/lib/TldrawEditor.mjs.map +2 -2
- package/dist-esm/lib/components/MenuClickCapture.mjs +0 -5
- package/dist-esm/lib/components/MenuClickCapture.mjs.map +2 -2
- package/dist-esm/lib/components/SVGContainer.mjs +1 -1
- package/dist-esm/lib/components/SVGContainer.mjs.map +2 -2
- package/dist-esm/lib/components/Shape.mjs +11 -36
- package/dist-esm/lib/components/Shape.mjs.map +2 -2
- package/dist-esm/lib/components/default-components/DefaultBrush.mjs +1 -1
- package/dist-esm/lib/components/default-components/DefaultBrush.mjs.map +2 -2
- package/dist-esm/lib/components/default-components/DefaultCanvas.mjs +16 -25
- package/dist-esm/lib/components/default-components/DefaultCanvas.mjs.map +2 -2
- package/dist-esm/lib/components/default-components/DefaultCollaboratorHint.mjs +2 -2
- package/dist-esm/lib/components/default-components/DefaultCollaboratorHint.mjs.map +2 -2
- package/dist-esm/lib/components/default-components/DefaultCursor.mjs +1 -1
- package/dist-esm/lib/components/default-components/DefaultCursor.mjs.map +2 -2
- package/dist-esm/lib/components/default-components/DefaultErrorFallback.mjs +1 -1
- package/dist-esm/lib/components/default-components/DefaultErrorFallback.mjs.map +2 -2
- package/dist-esm/lib/components/default-components/DefaultGrid.mjs +1 -1
- package/dist-esm/lib/components/default-components/DefaultGrid.mjs.map +2 -2
- package/dist-esm/lib/components/default-components/DefaultHandles.mjs +1 -1
- package/dist-esm/lib/components/default-components/DefaultHandles.mjs.map +2 -2
- package/dist-esm/lib/components/default-components/DefaultScribble.mjs +1 -1
- package/dist-esm/lib/components/default-components/DefaultScribble.mjs.map +2 -2
- package/dist-esm/lib/components/default-components/DefaultShapeIndicator.mjs +9 -1
- package/dist-esm/lib/components/default-components/DefaultShapeIndicator.mjs.map +2 -2
- package/dist-esm/lib/components/default-components/DefaultShapeWrapper.mjs +23 -0
- package/dist-esm/lib/components/default-components/DefaultShapeWrapper.mjs.map +7 -0
- package/dist-esm/lib/components/default-components/DefaultSnapIndictor.mjs +1 -1
- package/dist-esm/lib/components/default-components/DefaultSnapIndictor.mjs.map +2 -2
- package/dist-esm/lib/components/default-components/DefaultSpinner.mjs +17 -15
- package/dist-esm/lib/components/default-components/DefaultSpinner.mjs.map +2 -2
- package/dist-esm/lib/config/TLUserPreferences.mjs +15 -3
- package/dist-esm/lib/config/TLUserPreferences.mjs.map +2 -2
- package/dist-esm/lib/editor/Editor.mjs +174 -180
- package/dist-esm/lib/editor/Editor.mjs.map +2 -2
- package/dist-esm/lib/editor/derivations/notVisibleShapes.mjs +4 -0
- package/dist-esm/lib/editor/derivations/notVisibleShapes.mjs.map +2 -2
- package/dist-esm/lib/editor/derivations/parentsToChildren.mjs.map +2 -2
- package/dist-esm/lib/editor/managers/UserPreferencesManager/UserPreferencesManager.mjs +14 -4
- package/dist-esm/lib/editor/managers/UserPreferencesManager/UserPreferencesManager.mjs.map +2 -2
- package/dist-esm/lib/editor/shapes/BaseBoxShapeUtil.mjs.map +1 -1
- package/dist-esm/lib/editor/shapes/ShapeUtil.mjs +23 -0
- package/dist-esm/lib/editor/shapes/ShapeUtil.mjs.map +2 -2
- package/dist-esm/lib/editor/tools/BaseBoxShapeTool/BaseBoxShapeTool.mjs.map +2 -2
- package/dist-esm/lib/editor/tools/StateNode.mjs +20 -1
- package/dist-esm/lib/editor/tools/StateNode.mjs.map +2 -2
- package/dist-esm/lib/exports/getSvgJsx.mjs +36 -16
- package/dist-esm/lib/exports/getSvgJsx.mjs.map +2 -2
- package/dist-esm/lib/hooks/useCanvasEvents.mjs +47 -37
- package/dist-esm/lib/hooks/useCanvasEvents.mjs.map +2 -2
- package/dist-esm/lib/hooks/useDocumentEvents.mjs +11 -6
- package/dist-esm/lib/hooks/useDocumentEvents.mjs.map +2 -2
- package/dist-esm/lib/hooks/useEditor.mjs +1 -4
- package/dist-esm/lib/hooks/useEditor.mjs.map +2 -2
- package/dist-esm/lib/hooks/useEditorComponents.mjs +4 -0
- package/dist-esm/lib/hooks/useEditorComponents.mjs.map +2 -2
- package/dist-esm/lib/hooks/useFixSafariDoubleTapZoomPencilEvents.mjs +2 -3
- package/dist-esm/lib/hooks/useFixSafariDoubleTapZoomPencilEvents.mjs.map +2 -2
- package/dist-esm/lib/hooks/useGestureEvents.mjs +2 -2
- package/dist-esm/lib/hooks/useGestureEvents.mjs.map +2 -2
- package/dist-esm/lib/hooks/useHandleEvents.mjs +9 -4
- package/dist-esm/lib/hooks/useHandleEvents.mjs.map +2 -2
- package/dist-esm/lib/hooks/usePassThroughMouseOverEvents.mjs +4 -1
- package/dist-esm/lib/hooks/usePassThroughMouseOverEvents.mjs.map +2 -2
- package/dist-esm/lib/hooks/usePassThroughWheelEvents.mjs +4 -1
- package/dist-esm/lib/hooks/usePassThroughWheelEvents.mjs.map +2 -2
- package/dist-esm/lib/hooks/useSelectionEvents.mjs +6 -5
- package/dist-esm/lib/hooks/useSelectionEvents.mjs.map +2 -2
- package/dist-esm/lib/hooks/useStateAttribute.mjs +15 -0
- package/dist-esm/lib/hooks/useStateAttribute.mjs.map +7 -0
- package/dist-esm/lib/license/LicenseManager.mjs +141 -54
- package/dist-esm/lib/license/LicenseManager.mjs.map +2 -2
- package/dist-esm/lib/license/LicenseProvider.mjs +39 -2
- package/dist-esm/lib/license/LicenseProvider.mjs.map +2 -2
- package/dist-esm/lib/license/Watermark.mjs +76 -14
- package/dist-esm/lib/license/Watermark.mjs.map +3 -3
- package/dist-esm/lib/license/useLicenseManagerState.mjs.map +2 -2
- package/dist-esm/lib/options.mjs +7 -0
- package/dist-esm/lib/options.mjs.map +2 -2
- package/dist-esm/lib/primitives/Box.mjs +4 -1
- package/dist-esm/lib/primitives/Box.mjs.map +2 -2
- package/dist-esm/lib/primitives/Vec.mjs +0 -4
- package/dist-esm/lib/primitives/Vec.mjs.map +2 -2
- package/dist-esm/lib/primitives/geometry/Arc2d.mjs +2 -2
- package/dist-esm/lib/primitives/geometry/Arc2d.mjs.map +2 -2
- package/dist-esm/lib/primitives/geometry/Circle2d.mjs +2 -2
- package/dist-esm/lib/primitives/geometry/Circle2d.mjs.map +2 -2
- package/dist-esm/lib/primitives/geometry/CubicBezier2d.mjs +3 -1
- package/dist-esm/lib/primitives/geometry/CubicBezier2d.mjs.map +2 -2
- package/dist-esm/lib/primitives/geometry/Ellipse2d.mjs +2 -2
- package/dist-esm/lib/primitives/geometry/Ellipse2d.mjs.map +2 -2
- package/dist-esm/lib/primitives/geometry/Geometry2d.mjs +53 -21
- package/dist-esm/lib/primitives/geometry/Geometry2d.mjs.map +2 -2
- package/dist-esm/lib/primitives/geometry/Group2d.mjs +8 -1
- package/dist-esm/lib/primitives/geometry/Group2d.mjs.map +2 -2
- package/dist-esm/lib/primitives/geometry/geometry-constants.mjs +2 -2
- package/dist-esm/lib/primitives/geometry/geometry-constants.mjs.map +2 -2
- package/dist-esm/lib/primitives/intersect.mjs +5 -5
- package/dist-esm/lib/primitives/intersect.mjs.map +2 -2
- package/dist-esm/lib/primitives/utils.mjs +4 -0
- package/dist-esm/lib/primitives/utils.mjs.map +2 -2
- package/dist-esm/lib/utils/EditorAtom.mjs +25 -0
- package/dist-esm/lib/utils/EditorAtom.mjs.map +7 -0
- package/dist-esm/lib/utils/dom.mjs +12 -1
- package/dist-esm/lib/utils/dom.mjs.map +2 -2
- package/dist-esm/lib/utils/getPointerInfo.mjs +2 -2
- package/dist-esm/lib/utils/getPointerInfo.mjs.map +2 -2
- package/dist-esm/lib/utils/reparenting.mjs +3 -40
- package/dist-esm/lib/utils/reparenting.mjs.map +2 -2
- package/dist-esm/lib/utils/sync/TLLocalSyncClient.mjs +0 -1
- package/dist-esm/lib/utils/sync/TLLocalSyncClient.mjs.map +2 -2
- package/dist-esm/version.mjs +3 -3
- package/dist-esm/version.mjs.map +1 -1
- package/editor.css +327 -315
- package/package.json +16 -38
- package/src/index.ts +19 -10
- package/src/lib/TldrawEditor.tsx +16 -21
- package/src/lib/components/MenuClickCapture.tsx +0 -8
- package/src/lib/components/SVGContainer.tsx +1 -1
- package/src/lib/components/Shape.tsx +12 -33
- package/src/lib/components/default-components/DefaultBrush.tsx +1 -1
- package/src/lib/components/default-components/DefaultCanvas.tsx +13 -24
- package/src/lib/components/default-components/DefaultCollaboratorHint.tsx +2 -2
- package/src/lib/components/default-components/DefaultCursor.tsx +1 -1
- package/src/lib/components/default-components/DefaultErrorFallback.tsx +1 -1
- package/src/lib/components/default-components/DefaultGrid.tsx +1 -1
- package/src/lib/components/default-components/DefaultHandles.tsx +5 -1
- package/src/lib/components/default-components/DefaultScribble.tsx +1 -1
- package/src/lib/components/default-components/DefaultShapeIndicator.tsx +6 -2
- package/src/lib/components/default-components/DefaultShapeWrapper.tsx +35 -0
- package/src/lib/components/default-components/DefaultSnapIndictor.tsx +1 -1
- package/src/lib/components/default-components/DefaultSpinner.tsx +12 -12
- package/src/lib/config/TLUserPreferences.ts +15 -1
- package/src/lib/editor/Editor.test.ts +512 -8
- package/src/lib/editor/Editor.ts +252 -267
- package/src/lib/editor/derivations/notVisibleShapes.ts +6 -0
- package/src/lib/editor/derivations/parentsToChildren.ts +1 -1
- package/src/lib/editor/managers/ClickManager/ClickManager.test.ts +15 -14
- package/src/lib/editor/managers/EdgeScrollManager/EdgeScrollManager.test.ts +16 -15
- package/src/lib/editor/managers/FocusManager/FocusManager.test.ts +49 -48
- package/src/lib/editor/managers/FontManager/FontManager.test.ts +38 -27
- package/src/lib/editor/managers/HistoryManager/HistoryManager.test.ts +7 -6
- package/src/lib/editor/managers/ScribbleManager/ScribbleManager.test.ts +12 -11
- package/src/lib/editor/managers/SnapManager/SnapManager.test.ts +57 -50
- package/src/lib/editor/managers/TextManager/TextManager.test.ts +51 -26
- package/src/lib/editor/managers/TickManager/TickManager.test.ts +14 -13
- package/src/lib/editor/managers/UserPreferencesManager/UserPreferencesManager.test.ts +55 -26
- package/src/lib/editor/managers/UserPreferencesManager/UserPreferencesManager.ts +14 -1
- package/src/lib/editor/shapes/BaseBoxShapeUtil.tsx +2 -2
- package/src/lib/editor/shapes/ShapeUtil.ts +108 -8
- package/src/lib/editor/tools/BaseBoxShapeTool/BaseBoxShapeTool.ts +2 -1
- package/src/lib/editor/tools/StateNode.test.ts +285 -0
- package/src/lib/editor/tools/StateNode.ts +27 -1
- package/src/lib/editor/types/misc-types.ts +73 -7
- package/src/lib/exports/getSvgJsx.test.ts +874 -0
- package/src/lib/exports/getSvgJsx.tsx +78 -21
- package/src/lib/hooks/useCanvasEvents.ts +60 -47
- package/src/lib/hooks/useDocumentEvents.ts +11 -6
- package/src/lib/hooks/useEditor.tsx +6 -5
- package/src/lib/hooks/useEditorComponents.tsx +8 -2
- package/src/lib/hooks/useFixSafariDoubleTapZoomPencilEvents.ts +2 -2
- package/src/lib/hooks/useGestureEvents.ts +2 -2
- package/src/lib/hooks/useHandleEvents.ts +9 -4
- package/src/lib/hooks/usePassThroughMouseOverEvents.ts +4 -1
- package/src/lib/hooks/usePassThroughWheelEvents.ts +6 -1
- package/src/lib/hooks/useSelectionEvents.ts +6 -5
- package/src/lib/hooks/useStateAttribute.ts +15 -0
- package/src/lib/license/LicenseManager.test.ts +724 -383
- package/src/lib/license/LicenseManager.ts +201 -58
- package/src/lib/license/LicenseProvider.tsx +74 -2
- package/src/lib/license/Watermark.test.tsx +2 -1
- package/src/lib/license/Watermark.tsx +81 -14
- package/src/lib/license/useLicenseManagerState.ts +2 -2
- package/src/lib/options.ts +8 -0
- package/src/lib/primitives/Box.test.ts +126 -0
- package/src/lib/primitives/Box.ts +10 -1
- package/src/lib/primitives/Vec.ts +0 -5
- package/src/lib/primitives/geometry/Arc2d.ts +2 -2
- package/src/lib/primitives/geometry/Circle2d.ts +2 -2
- package/src/lib/primitives/geometry/CubicBezier2d.ts +4 -1
- package/src/lib/primitives/geometry/Ellipse2d.ts +2 -2
- package/src/lib/primitives/geometry/Geometry2d.test.ts +420 -0
- package/src/lib/primitives/geometry/Geometry2d.ts +78 -21
- package/src/lib/primitives/geometry/Group2d.ts +10 -1
- package/src/lib/primitives/geometry/geometry-constants.ts +2 -1
- package/src/lib/primitives/intersect.test.ts +946 -0
- package/src/lib/primitives/intersect.ts +12 -5
- package/src/lib/primitives/utils.ts +11 -0
- package/src/lib/test/InFrontOfTheCanvas.test.tsx +187 -0
- package/src/lib/utils/EditorAtom.ts +37 -0
- package/src/lib/utils/dom.test.ts +94 -0
- package/src/lib/utils/dom.ts +38 -1
- package/src/lib/utils/getPointerInfo.ts +2 -1
- package/src/lib/utils/reparenting.ts +3 -69
- package/src/lib/utils/sync/LocalIndexedDb.test.ts +2 -1
- package/src/lib/utils/sync/TLLocalSyncClient.test.ts +15 -15
- package/src/lib/utils/sync/TLLocalSyncClient.ts +0 -1
- package/src/version.ts +3 -3
- package/dist-cjs/lib/utils/nearestMultiple.js.map +0 -7
- package/dist-esm/lib/utils/nearestMultiple.mjs +0 -14
- package/dist-esm/lib/utils/nearestMultiple.mjs.map +0 -7
- package/src/lib/test/currentToolIdMask.test.ts +0 -49
- package/src/lib/utils/nearestMultiple.ts +0 -13
|
@@ -1,19 +1,19 @@
|
|
|
1
|
+
import classNames from 'classnames'
|
|
2
|
+
|
|
1
3
|
/** @public @react */
|
|
2
|
-
export function DefaultSpinner() {
|
|
4
|
+
export function DefaultSpinner(props: React.SVGProps<SVGSVGElement>) {
|
|
3
5
|
return (
|
|
4
|
-
<svg
|
|
6
|
+
<svg
|
|
7
|
+
width={16}
|
|
8
|
+
height={16}
|
|
9
|
+
viewBox="0 0 16 16"
|
|
10
|
+
aria-hidden="false"
|
|
11
|
+
{...props}
|
|
12
|
+
className={classNames('tl-spinner', props.className)}
|
|
13
|
+
>
|
|
5
14
|
<g strokeWidth={2} fill="none" fillRule="evenodd">
|
|
6
15
|
<circle strokeOpacity={0.25} cx={8} cy={8} r={7} stroke="currentColor" />
|
|
7
|
-
<path strokeLinecap="round" d="M15 8c0-4.5-4.5-7-7-7" stroke="currentColor"
|
|
8
|
-
<animateTransform
|
|
9
|
-
attributeName="transform"
|
|
10
|
-
type="rotate"
|
|
11
|
-
from="0 8 8"
|
|
12
|
-
to="360 8 8"
|
|
13
|
-
dur="1s"
|
|
14
|
-
repeatCount="indefinite"
|
|
15
|
-
/>
|
|
16
|
-
</path>
|
|
16
|
+
<path strokeLinecap="round" d="M15 8c0-4.5-4.5-7-7-7" stroke="currentColor" />
|
|
17
17
|
</g>
|
|
18
18
|
</svg>
|
|
19
19
|
)
|
|
@@ -17,12 +17,14 @@ export interface TLUserPreferences {
|
|
|
17
17
|
// N.B. These are duplicated in TLdrawAppUser.
|
|
18
18
|
locale?: string | null
|
|
19
19
|
animationSpeed?: number | null
|
|
20
|
+
areKeyboardShortcutsEnabled?: boolean | null
|
|
20
21
|
edgeScrollSpeed?: number | null
|
|
21
22
|
colorScheme?: 'light' | 'dark' | 'system'
|
|
22
23
|
isSnapMode?: boolean | null
|
|
23
24
|
isWrapMode?: boolean | null
|
|
24
25
|
isDynamicSizeMode?: boolean | null
|
|
25
26
|
isPasteAtCursorMode?: boolean | null
|
|
27
|
+
showUiLabels?: boolean | null
|
|
26
28
|
}
|
|
27
29
|
|
|
28
30
|
interface UserDataSnapshot {
|
|
@@ -44,12 +46,14 @@ export const userTypeValidator: T.Validator<TLUserPreferences> = T.object<TLUser
|
|
|
44
46
|
// N.B. These are duplicated in TLdrawAppUser.
|
|
45
47
|
locale: T.string.nullable().optional(),
|
|
46
48
|
animationSpeed: T.number.nullable().optional(),
|
|
49
|
+
areKeyboardShortcutsEnabled: T.boolean.nullable().optional(),
|
|
47
50
|
edgeScrollSpeed: T.number.nullable().optional(),
|
|
48
51
|
colorScheme: T.literalEnum('light', 'dark', 'system').optional(),
|
|
49
52
|
isSnapMode: T.boolean.nullable().optional(),
|
|
50
53
|
isWrapMode: T.boolean.nullable().optional(),
|
|
51
54
|
isDynamicSizeMode: T.boolean.nullable().optional(),
|
|
52
55
|
isPasteAtCursorMode: T.boolean.nullable().optional(),
|
|
56
|
+
showUiLabels: T.boolean.nullable().optional(),
|
|
53
57
|
})
|
|
54
58
|
|
|
55
59
|
const Versions = {
|
|
@@ -61,6 +65,8 @@ const Versions = {
|
|
|
61
65
|
AddDynamicSizeMode: 6,
|
|
62
66
|
AllowSystemColorScheme: 7,
|
|
63
67
|
AddPasteAtCursor: 8,
|
|
68
|
+
AddKeyboardShortcuts: 9,
|
|
69
|
+
AddShowUiLabels: 10,
|
|
64
70
|
} as const
|
|
65
71
|
|
|
66
72
|
const CURRENT_VERSION = Math.max(...Object.values(Versions))
|
|
@@ -96,6 +102,12 @@ function migrateSnapshot(data: { version: number; user: any }) {
|
|
|
96
102
|
if (data.version < Versions.AddPasteAtCursor) {
|
|
97
103
|
data.user.isPasteAtCursorMode = false
|
|
98
104
|
}
|
|
105
|
+
if (data.version < Versions.AddKeyboardShortcuts) {
|
|
106
|
+
data.user.areKeyboardShortcutsEnabled = true
|
|
107
|
+
}
|
|
108
|
+
if (data.version < Versions.AddShowUiLabels) {
|
|
109
|
+
data.user.showUiLabels = false
|
|
110
|
+
}
|
|
99
111
|
|
|
100
112
|
// finally
|
|
101
113
|
data.version = CURRENT_VERSION
|
|
@@ -123,7 +135,7 @@ function getRandomColor() {
|
|
|
123
135
|
|
|
124
136
|
/** @internal */
|
|
125
137
|
export function userPrefersReducedMotion() {
|
|
126
|
-
if (typeof window !== 'undefined' &&
|
|
138
|
+
if (typeof window !== 'undefined' && window.matchMedia) {
|
|
127
139
|
return window.matchMedia?.('(prefers-reduced-motion: reduce)')?.matches ?? false
|
|
128
140
|
}
|
|
129
141
|
|
|
@@ -139,10 +151,12 @@ export const defaultUserPreferences = Object.freeze({
|
|
|
139
151
|
// N.B. These are duplicated in TLdrawAppUser.
|
|
140
152
|
edgeScrollSpeed: 1,
|
|
141
153
|
animationSpeed: userPrefersReducedMotion() ? 0 : 1,
|
|
154
|
+
areKeyboardShortcutsEnabled: true,
|
|
142
155
|
isSnapMode: false,
|
|
143
156
|
isWrapMode: false,
|
|
144
157
|
isDynamicSizeMode: false,
|
|
145
158
|
isPasteAtCursorMode: false,
|
|
159
|
+
showUiLabels: false,
|
|
146
160
|
colorScheme: 'light',
|
|
147
161
|
}) satisfies Readonly<Omit<TLUserPreferences, 'id'>>
|
|
148
162
|
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { vi } from 'vitest'
|
|
1
2
|
import {
|
|
2
3
|
Box,
|
|
3
4
|
Geometry2d,
|
|
@@ -11,6 +12,12 @@ import {
|
|
|
11
12
|
} from '../..'
|
|
12
13
|
import { Editor } from './Editor'
|
|
13
14
|
|
|
15
|
+
declare module '@tldraw/tlschema' {
|
|
16
|
+
export interface GlobalShapePropsMap {
|
|
17
|
+
'my-custom-shape': ICustomShape
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
14
21
|
type ICustomShape = TLBaseShape<
|
|
15
22
|
'my-custom-shape',
|
|
16
23
|
{
|
|
@@ -59,8 +66,8 @@ beforeEach(() => {
|
|
|
59
66
|
getContainer: () => document.body,
|
|
60
67
|
})
|
|
61
68
|
editor.setCameraOptions({ isLocked: true })
|
|
62
|
-
editor.setCamera =
|
|
63
|
-
editor.user.getAnimationSpeed =
|
|
69
|
+
editor.setCamera = vi.fn()
|
|
70
|
+
editor.user.getAnimationSpeed = vi.fn()
|
|
64
71
|
})
|
|
65
72
|
|
|
66
73
|
describe('centerOnPoint', () => {
|
|
@@ -94,13 +101,13 @@ describe('updateShape', () => {
|
|
|
94
101
|
|
|
95
102
|
describe('zoomToFit', () => {
|
|
96
103
|
it('no-op when isLocked is set', () => {
|
|
97
|
-
editor.getCurrentPageShapeIds =
|
|
104
|
+
editor.getCurrentPageShapeIds = vi.fn(() => new Set([createShapeId('box1')]))
|
|
98
105
|
editor.zoomToFit()
|
|
99
106
|
expect(editor.setCamera).not.toHaveBeenCalled()
|
|
100
107
|
})
|
|
101
108
|
|
|
102
109
|
it('sets camera when isLocked is set and force flag is set', () => {
|
|
103
|
-
editor.getCurrentPageShapeIds =
|
|
110
|
+
editor.getCurrentPageShapeIds = vi.fn(() => new Set([createShapeId('box1')]))
|
|
104
111
|
editor.zoomToFit({ force: true })
|
|
105
112
|
expect(editor.setCamera).toHaveBeenCalled()
|
|
106
113
|
})
|
|
@@ -144,13 +151,13 @@ describe('zoomOut', () => {
|
|
|
144
151
|
|
|
145
152
|
describe('zoomToSelection', () => {
|
|
146
153
|
it('no-op when isLocked is set', () => {
|
|
147
|
-
editor.getSelectionPageBounds =
|
|
154
|
+
editor.getSelectionPageBounds = vi.fn(() => Box.From({ x: 0, y: 0, w: 100, h: 100 }))
|
|
148
155
|
editor.zoomToSelection()
|
|
149
156
|
expect(editor.setCamera).not.toHaveBeenCalled()
|
|
150
157
|
})
|
|
151
158
|
|
|
152
159
|
it('sets camera when isLocked is set and force flag is set', () => {
|
|
153
|
-
editor.getSelectionPageBounds =
|
|
160
|
+
editor.getSelectionPageBounds = vi.fn(() => Box.From({ x: 0, y: 0, w: 100, h: 100 }))
|
|
154
161
|
editor.zoomToSelection({ force: true })
|
|
155
162
|
expect(editor.setCamera).toHaveBeenCalled()
|
|
156
163
|
})
|
|
@@ -286,7 +293,7 @@ describe('getShapesAtPoint', () => {
|
|
|
286
293
|
|
|
287
294
|
it('filters out hidden shapes', () => {
|
|
288
295
|
// Create a spy to mock isShapeHidden
|
|
289
|
-
const isShapeHiddenSpy =
|
|
296
|
+
const isShapeHiddenSpy = vi.spyOn(editor, 'isShapeHidden')
|
|
290
297
|
isShapeHiddenSpy.mockImplementation((shape) => {
|
|
291
298
|
return typeof shape === 'string' ? shape === ids.shape3 : shape.id === ids.shape3
|
|
292
299
|
})
|
|
@@ -352,7 +359,7 @@ describe('getShapesAtPoint', () => {
|
|
|
352
359
|
|
|
353
360
|
it('returns empty array when all shapes are hidden', () => {
|
|
354
361
|
// Mock all shapes as hidden
|
|
355
|
-
const isShapeHiddenSpy =
|
|
362
|
+
const isShapeHiddenSpy = vi.spyOn(editor, 'isShapeHidden')
|
|
356
363
|
isShapeHiddenSpy.mockReturnValue(true)
|
|
357
364
|
|
|
358
365
|
const shapes = editor.getShapesAtPoint({ x: 50, y: 50 })
|
|
@@ -425,3 +432,500 @@ describe('getShapesAtPoint', () => {
|
|
|
425
432
|
expect(hollowShapesWithHitInside[0].id).toBe(ids.hollowShape)
|
|
426
433
|
})
|
|
427
434
|
})
|
|
435
|
+
|
|
436
|
+
describe('selectAll', () => {
|
|
437
|
+
const selectAllIds = {
|
|
438
|
+
pageShape1: createShapeId('pageShape1'),
|
|
439
|
+
pageShape2: createShapeId('pageShape2'),
|
|
440
|
+
pageShape3: createShapeId('pageShape3'),
|
|
441
|
+
container1: createShapeId('container1'),
|
|
442
|
+
containerChild1: createShapeId('containerChild1'),
|
|
443
|
+
containerChild2: createShapeId('containerChild2'),
|
|
444
|
+
containerChild3: createShapeId('containerChild3'),
|
|
445
|
+
containerGrandchild1: createShapeId('containerGrandchild1'),
|
|
446
|
+
container2: createShapeId('container2'),
|
|
447
|
+
container2Child1: createShapeId('container2Child1'),
|
|
448
|
+
container2Child2: createShapeId('container2Child2'),
|
|
449
|
+
container2Grandchild1: createShapeId('container2Grandchild1'),
|
|
450
|
+
lockedShape: createShapeId('lockedShape'),
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
beforeEach(() => {
|
|
454
|
+
// Clear any existing shapes
|
|
455
|
+
editor.selectAll().deleteShapes(editor.getSelectedShapeIds())
|
|
456
|
+
|
|
457
|
+
// Create shapes directly on the page (no parentId means they're children of the page)
|
|
458
|
+
editor.createShapes([
|
|
459
|
+
{
|
|
460
|
+
id: selectAllIds.pageShape1,
|
|
461
|
+
type: 'my-custom-shape',
|
|
462
|
+
x: 100,
|
|
463
|
+
y: 100,
|
|
464
|
+
props: { w: 100, h: 100 },
|
|
465
|
+
},
|
|
466
|
+
{
|
|
467
|
+
id: selectAllIds.pageShape2,
|
|
468
|
+
type: 'my-custom-shape',
|
|
469
|
+
x: 300,
|
|
470
|
+
y: 100,
|
|
471
|
+
props: { w: 100, h: 100 },
|
|
472
|
+
},
|
|
473
|
+
{
|
|
474
|
+
id: selectAllIds.pageShape3,
|
|
475
|
+
type: 'my-custom-shape',
|
|
476
|
+
x: 500,
|
|
477
|
+
y: 100,
|
|
478
|
+
props: { w: 100, h: 100 },
|
|
479
|
+
},
|
|
480
|
+
{
|
|
481
|
+
id: selectAllIds.lockedShape,
|
|
482
|
+
type: 'my-custom-shape',
|
|
483
|
+
x: 700,
|
|
484
|
+
y: 100,
|
|
485
|
+
props: { w: 100, h: 100 },
|
|
486
|
+
isLocked: true,
|
|
487
|
+
},
|
|
488
|
+
])
|
|
489
|
+
|
|
490
|
+
// Create a container shape (simulating a frame or group)
|
|
491
|
+
editor.createShape({
|
|
492
|
+
id: selectAllIds.container1,
|
|
493
|
+
type: 'my-custom-shape',
|
|
494
|
+
x: 100,
|
|
495
|
+
y: 300,
|
|
496
|
+
props: { w: 400, h: 200 },
|
|
497
|
+
})
|
|
498
|
+
|
|
499
|
+
// Create children inside the container (parentId set to container1)
|
|
500
|
+
editor.createShapes([
|
|
501
|
+
{
|
|
502
|
+
id: selectAllIds.containerChild1,
|
|
503
|
+
type: 'my-custom-shape',
|
|
504
|
+
parentId: selectAllIds.container1,
|
|
505
|
+
x: 120,
|
|
506
|
+
y: 320,
|
|
507
|
+
props: { w: 50, h: 50 },
|
|
508
|
+
},
|
|
509
|
+
{
|
|
510
|
+
id: selectAllIds.containerChild2,
|
|
511
|
+
type: 'my-custom-shape',
|
|
512
|
+
parentId: selectAllIds.container1,
|
|
513
|
+
x: 200,
|
|
514
|
+
y: 320,
|
|
515
|
+
props: { w: 50, h: 50 },
|
|
516
|
+
},
|
|
517
|
+
{
|
|
518
|
+
id: selectAllIds.containerChild3,
|
|
519
|
+
type: 'my-custom-shape',
|
|
520
|
+
parentId: selectAllIds.container1,
|
|
521
|
+
x: 280,
|
|
522
|
+
y: 320,
|
|
523
|
+
props: { w: 50, h: 50 },
|
|
524
|
+
},
|
|
525
|
+
])
|
|
526
|
+
|
|
527
|
+
// Create a grandchild inside one of the container children
|
|
528
|
+
editor.createShape({
|
|
529
|
+
id: selectAllIds.containerGrandchild1,
|
|
530
|
+
type: 'my-custom-shape',
|
|
531
|
+
parentId: selectAllIds.containerChild3,
|
|
532
|
+
x: 290,
|
|
533
|
+
y: 330,
|
|
534
|
+
props: { w: 30, h: 30 },
|
|
535
|
+
})
|
|
536
|
+
|
|
537
|
+
// Create a second container (simulating a group)
|
|
538
|
+
editor.createShape({
|
|
539
|
+
id: selectAllIds.container2,
|
|
540
|
+
type: 'my-custom-shape',
|
|
541
|
+
x: 600,
|
|
542
|
+
y: 300,
|
|
543
|
+
props: { w: 200, h: 200 },
|
|
544
|
+
})
|
|
545
|
+
|
|
546
|
+
// Create children inside the second container
|
|
547
|
+
editor.createShapes([
|
|
548
|
+
{
|
|
549
|
+
id: selectAllIds.container2Child1,
|
|
550
|
+
type: 'my-custom-shape',
|
|
551
|
+
parentId: selectAllIds.container2,
|
|
552
|
+
x: 620,
|
|
553
|
+
y: 320,
|
|
554
|
+
props: { w: 50, h: 50 },
|
|
555
|
+
},
|
|
556
|
+
{
|
|
557
|
+
id: selectAllIds.container2Child2,
|
|
558
|
+
type: 'my-custom-shape',
|
|
559
|
+
parentId: selectAllIds.container2,
|
|
560
|
+
x: 680,
|
|
561
|
+
y: 320,
|
|
562
|
+
props: { w: 50, h: 50 },
|
|
563
|
+
},
|
|
564
|
+
])
|
|
565
|
+
|
|
566
|
+
// Create a grandchild in the second container
|
|
567
|
+
editor.createShape({
|
|
568
|
+
id: selectAllIds.container2Grandchild1,
|
|
569
|
+
type: 'my-custom-shape',
|
|
570
|
+
parentId: selectAllIds.container2Child1,
|
|
571
|
+
x: 630,
|
|
572
|
+
y: 330,
|
|
573
|
+
props: { w: 30, h: 30 },
|
|
574
|
+
})
|
|
575
|
+
|
|
576
|
+
// Clear selection
|
|
577
|
+
editor.selectNone()
|
|
578
|
+
})
|
|
579
|
+
|
|
580
|
+
it('when no shapes are selected, selects all page-level shapes (excluding locked ones)', () => {
|
|
581
|
+
// Initially no shapes selected
|
|
582
|
+
expect(editor.getSelectedShapeIds()).toEqual([])
|
|
583
|
+
|
|
584
|
+
// Call selectAll
|
|
585
|
+
editor.selectAll()
|
|
586
|
+
|
|
587
|
+
// Should select all page-level shapes (excluding locked ones)
|
|
588
|
+
const selectedIds = editor.getSelectedShapeIds()
|
|
589
|
+
expect(Array.from(selectedIds).sort()).toEqual(
|
|
590
|
+
[
|
|
591
|
+
selectAllIds.pageShape1,
|
|
592
|
+
selectAllIds.pageShape2,
|
|
593
|
+
selectAllIds.pageShape3,
|
|
594
|
+
selectAllIds.container1,
|
|
595
|
+
selectAllIds.container2,
|
|
596
|
+
].sort()
|
|
597
|
+
)
|
|
598
|
+
|
|
599
|
+
// Should NOT include locked shape or children/grandchildren
|
|
600
|
+
expect(selectedIds).not.toContain(selectAllIds.lockedShape)
|
|
601
|
+
expect(selectedIds).not.toContain(selectAllIds.containerChild1)
|
|
602
|
+
expect(selectedIds).not.toContain(selectAllIds.containerChild2)
|
|
603
|
+
expect(selectedIds).not.toContain(selectAllIds.containerChild3)
|
|
604
|
+
expect(selectedIds).not.toContain(selectAllIds.containerGrandchild1)
|
|
605
|
+
expect(selectedIds).not.toContain(selectAllIds.container2Child1)
|
|
606
|
+
expect(selectedIds).not.toContain(selectAllIds.container2Child2)
|
|
607
|
+
expect(selectedIds).not.toContain(selectAllIds.container2Grandchild1)
|
|
608
|
+
})
|
|
609
|
+
|
|
610
|
+
it('when shapes are selected only on the page, all children of the page should be selected (but not their descendants)', () => {
|
|
611
|
+
// Select some page-level shapes
|
|
612
|
+
editor.select(selectAllIds.pageShape1, selectAllIds.pageShape2)
|
|
613
|
+
|
|
614
|
+
// Call selectAll
|
|
615
|
+
editor.selectAll()
|
|
616
|
+
|
|
617
|
+
// Should select all page-level shapes (excluding locked ones), but not descendants
|
|
618
|
+
const selectedIds = editor.getSelectedShapeIds()
|
|
619
|
+
expect(Array.from(selectedIds).sort()).toEqual(
|
|
620
|
+
[
|
|
621
|
+
selectAllIds.pageShape1,
|
|
622
|
+
selectAllIds.pageShape2,
|
|
623
|
+
selectAllIds.pageShape3,
|
|
624
|
+
selectAllIds.container1,
|
|
625
|
+
selectAllIds.container2,
|
|
626
|
+
].sort()
|
|
627
|
+
)
|
|
628
|
+
|
|
629
|
+
// Should NOT include children or grandchildren or locked shapes
|
|
630
|
+
expect(selectedIds).not.toContain(selectAllIds.containerChild1)
|
|
631
|
+
expect(selectedIds).not.toContain(selectAllIds.containerChild2)
|
|
632
|
+
expect(selectedIds).not.toContain(selectAllIds.containerChild3)
|
|
633
|
+
expect(selectedIds).not.toContain(selectAllIds.containerGrandchild1)
|
|
634
|
+
expect(selectedIds).not.toContain(selectAllIds.container2Child1)
|
|
635
|
+
expect(selectedIds).not.toContain(selectAllIds.container2Child2)
|
|
636
|
+
expect(selectedIds).not.toContain(selectAllIds.container2Grandchild1)
|
|
637
|
+
expect(selectedIds).not.toContain(selectAllIds.lockedShape)
|
|
638
|
+
})
|
|
639
|
+
|
|
640
|
+
it('when shapes are selected within a container, only children of the container should be selected (not their descendants)', () => {
|
|
641
|
+
// Select some container children
|
|
642
|
+
editor.select(selectAllIds.containerChild1, selectAllIds.containerChild2)
|
|
643
|
+
|
|
644
|
+
// Call selectAll
|
|
645
|
+
editor.selectAll()
|
|
646
|
+
|
|
647
|
+
// Should select all container children (but not their descendants)
|
|
648
|
+
const selectedIds = editor.getSelectedShapeIds()
|
|
649
|
+
expect(Array.from(selectedIds).sort()).toEqual(
|
|
650
|
+
[
|
|
651
|
+
selectAllIds.containerChild1,
|
|
652
|
+
selectAllIds.containerChild2,
|
|
653
|
+
selectAllIds.containerChild3,
|
|
654
|
+
].sort()
|
|
655
|
+
)
|
|
656
|
+
|
|
657
|
+
// Should NOT include page-level shapes or grandchildren
|
|
658
|
+
expect(selectedIds).not.toContain(selectAllIds.pageShape1)
|
|
659
|
+
expect(selectedIds).not.toContain(selectAllIds.pageShape2)
|
|
660
|
+
expect(selectedIds).not.toContain(selectAllIds.pageShape3)
|
|
661
|
+
expect(selectedIds).not.toContain(selectAllIds.container1)
|
|
662
|
+
expect(selectedIds).not.toContain(selectAllIds.container2)
|
|
663
|
+
expect(selectedIds).not.toContain(selectAllIds.containerGrandchild1)
|
|
664
|
+
expect(selectedIds).not.toContain(selectAllIds.container2Child1)
|
|
665
|
+
expect(selectedIds).not.toContain(selectAllIds.container2Child2)
|
|
666
|
+
expect(selectedIds).not.toContain(selectAllIds.container2Grandchild1)
|
|
667
|
+
})
|
|
668
|
+
|
|
669
|
+
it('when shapes are selected within a second container, only children of that container should be selected', () => {
|
|
670
|
+
// Select some second container children
|
|
671
|
+
editor.select(selectAllIds.container2Child1)
|
|
672
|
+
|
|
673
|
+
// Call selectAll
|
|
674
|
+
editor.selectAll()
|
|
675
|
+
|
|
676
|
+
// Should select all second container children (but not their descendants)
|
|
677
|
+
const selectedIds = editor.getSelectedShapeIds()
|
|
678
|
+
expect(Array.from(selectedIds).sort()).toEqual(
|
|
679
|
+
[selectAllIds.container2Child1, selectAllIds.container2Child2].sort()
|
|
680
|
+
)
|
|
681
|
+
|
|
682
|
+
// Should NOT include page-level shapes or other container's children or grandchildren
|
|
683
|
+
expect(selectedIds).not.toContain(selectAllIds.pageShape1)
|
|
684
|
+
expect(selectedIds).not.toContain(selectAllIds.pageShape2)
|
|
685
|
+
expect(selectedIds).not.toContain(selectAllIds.pageShape3)
|
|
686
|
+
expect(selectedIds).not.toContain(selectAllIds.container1)
|
|
687
|
+
expect(selectedIds).not.toContain(selectAllIds.container2)
|
|
688
|
+
expect(selectedIds).not.toContain(selectAllIds.containerChild1)
|
|
689
|
+
expect(selectedIds).not.toContain(selectAllIds.containerChild2)
|
|
690
|
+
expect(selectedIds).not.toContain(selectAllIds.containerChild3)
|
|
691
|
+
expect(selectedIds).not.toContain(selectAllIds.containerGrandchild1)
|
|
692
|
+
expect(selectedIds).not.toContain(selectAllIds.container2Grandchild1)
|
|
693
|
+
})
|
|
694
|
+
|
|
695
|
+
it('when shapes are selected that belong to different parents, no change/history entry should be made', () => {
|
|
696
|
+
// Select shapes from different parents (page and container)
|
|
697
|
+
editor.select(selectAllIds.pageShape1, selectAllIds.containerChild1)
|
|
698
|
+
|
|
699
|
+
const initialSelectedIds = editor.getSelectedShapeIds()
|
|
700
|
+
|
|
701
|
+
// Spy on setSelectedShapes to verify it's not called
|
|
702
|
+
const setSelectedShapesSpy = vi.spyOn(editor, 'setSelectedShapes')
|
|
703
|
+
|
|
704
|
+
// Call selectAll
|
|
705
|
+
editor.selectAll()
|
|
706
|
+
|
|
707
|
+
// Selection should remain unchanged
|
|
708
|
+
expect(editor.getSelectedShapeIds()).toEqual(initialSelectedIds)
|
|
709
|
+
|
|
710
|
+
// setSelectedShapes should not have been called (the method returns early)
|
|
711
|
+
expect(setSelectedShapesSpy).not.toHaveBeenCalled()
|
|
712
|
+
|
|
713
|
+
setSelectedShapesSpy.mockRestore()
|
|
714
|
+
})
|
|
715
|
+
|
|
716
|
+
it('when shapes are selected that belong to different containers, no change/history entry should be made', () => {
|
|
717
|
+
// Select shapes from different containers
|
|
718
|
+
editor.select(selectAllIds.containerChild1, selectAllIds.container2Child1)
|
|
719
|
+
|
|
720
|
+
const initialSelectedIds = editor.getSelectedShapeIds()
|
|
721
|
+
|
|
722
|
+
// Spy on setSelectedShapes to verify it's not called
|
|
723
|
+
const setSelectedShapesSpy = vi.spyOn(editor, 'setSelectedShapes')
|
|
724
|
+
|
|
725
|
+
// Call selectAll
|
|
726
|
+
editor.selectAll()
|
|
727
|
+
|
|
728
|
+
// Selection should remain unchanged
|
|
729
|
+
expect(editor.getSelectedShapeIds()).toEqual(initialSelectedIds)
|
|
730
|
+
|
|
731
|
+
// setSelectedShapes should not have been called
|
|
732
|
+
expect(setSelectedShapesSpy).not.toHaveBeenCalled()
|
|
733
|
+
|
|
734
|
+
setSelectedShapesSpy.mockRestore()
|
|
735
|
+
})
|
|
736
|
+
|
|
737
|
+
it('should not select locked shapes', () => {
|
|
738
|
+
// Select a page-level shape
|
|
739
|
+
editor.select(selectAllIds.pageShape1)
|
|
740
|
+
|
|
741
|
+
// Call selectAll
|
|
742
|
+
editor.selectAll()
|
|
743
|
+
|
|
744
|
+
// Should select all page-level shapes except locked ones
|
|
745
|
+
const selectedIds = editor.getSelectedShapeIds()
|
|
746
|
+
expect(selectedIds).not.toContain(selectAllIds.lockedShape)
|
|
747
|
+
expect(selectedIds).toContain(selectAllIds.pageShape1)
|
|
748
|
+
expect(selectedIds).toContain(selectAllIds.pageShape2)
|
|
749
|
+
expect(selectedIds).toContain(selectAllIds.pageShape3)
|
|
750
|
+
expect(selectedIds).toContain(selectAllIds.container1)
|
|
751
|
+
expect(selectedIds).toContain(selectAllIds.container2)
|
|
752
|
+
})
|
|
753
|
+
|
|
754
|
+
it('should handle empty container by selecting all siblings at the same level', () => {
|
|
755
|
+
// Create an empty container
|
|
756
|
+
const emptyContainerId = createShapeId('emptyContainer')
|
|
757
|
+
editor.createShape({
|
|
758
|
+
id: emptyContainerId,
|
|
759
|
+
type: 'my-custom-shape',
|
|
760
|
+
x: 800,
|
|
761
|
+
y: 400,
|
|
762
|
+
props: { w: 100, h: 100 },
|
|
763
|
+
})
|
|
764
|
+
|
|
765
|
+
// Clear selection first
|
|
766
|
+
editor.selectNone()
|
|
767
|
+
|
|
768
|
+
// Select the empty container
|
|
769
|
+
editor.select(emptyContainerId)
|
|
770
|
+
|
|
771
|
+
// Call selectAll - since the empty container has no children, it should select all siblings (page-level shapes)
|
|
772
|
+
editor.selectAll()
|
|
773
|
+
|
|
774
|
+
// Should select all page-level shapes (including the empty container itself)
|
|
775
|
+
const selectedIds = editor.getSelectedShapeIds()
|
|
776
|
+
expect(Array.from(selectedIds).sort()).toEqual(
|
|
777
|
+
[
|
|
778
|
+
selectAllIds.pageShape1,
|
|
779
|
+
selectAllIds.pageShape2,
|
|
780
|
+
selectAllIds.pageShape3,
|
|
781
|
+
selectAllIds.container1,
|
|
782
|
+
selectAllIds.container2,
|
|
783
|
+
emptyContainerId,
|
|
784
|
+
].sort()
|
|
785
|
+
)
|
|
786
|
+
|
|
787
|
+
// Should NOT include locked shapes or children/grandchildren
|
|
788
|
+
expect(selectedIds).not.toContain(selectAllIds.lockedShape)
|
|
789
|
+
expect(selectedIds).not.toContain(selectAllIds.containerChild1)
|
|
790
|
+
expect(selectedIds).not.toContain(selectAllIds.containerChild2)
|
|
791
|
+
expect(selectedIds).not.toContain(selectAllIds.containerChild3)
|
|
792
|
+
expect(selectedIds).not.toContain(selectAllIds.containerGrandchild1)
|
|
793
|
+
expect(selectedIds).not.toContain(selectAllIds.container2Child1)
|
|
794
|
+
expect(selectedIds).not.toContain(selectAllIds.container2Child2)
|
|
795
|
+
expect(selectedIds).not.toContain(selectAllIds.container2Grandchild1)
|
|
796
|
+
})
|
|
797
|
+
|
|
798
|
+
it('should work correctly when selecting all shapes of same parent type', () => {
|
|
799
|
+
// Select all container children
|
|
800
|
+
editor.select(
|
|
801
|
+
selectAllIds.containerChild1,
|
|
802
|
+
selectAllIds.containerChild2,
|
|
803
|
+
selectAllIds.containerChild3
|
|
804
|
+
)
|
|
805
|
+
|
|
806
|
+
// Call selectAll - should maintain the same selection since all children are already selected
|
|
807
|
+
editor.selectAll()
|
|
808
|
+
|
|
809
|
+
// Should still have all container children selected
|
|
810
|
+
const selectedIds = editor.getSelectedShapeIds()
|
|
811
|
+
expect(Array.from(selectedIds).sort()).toEqual(
|
|
812
|
+
[
|
|
813
|
+
selectAllIds.containerChild1,
|
|
814
|
+
selectAllIds.containerChild2,
|
|
815
|
+
selectAllIds.containerChild3,
|
|
816
|
+
].sort()
|
|
817
|
+
)
|
|
818
|
+
})
|
|
819
|
+
|
|
820
|
+
it('should handle mixed selection levels gracefully by doing nothing', () => {
|
|
821
|
+
// Select a mix: page shape (parent=page), container (parent=page), and container child (parent=container1)
|
|
822
|
+
// These all have different parent IDs so selectAll should do nothing
|
|
823
|
+
editor.select(selectAllIds.pageShape1, selectAllIds.containerChild1)
|
|
824
|
+
|
|
825
|
+
const initialSelectedIds = Array.from(editor.getSelectedShapeIds())
|
|
826
|
+
|
|
827
|
+
// Spy on setSelectedShapes to verify it's not called
|
|
828
|
+
const setSelectedShapesSpy = vi.spyOn(editor, 'setSelectedShapes')
|
|
829
|
+
|
|
830
|
+
// Call selectAll
|
|
831
|
+
editor.selectAll()
|
|
832
|
+
|
|
833
|
+
// Selection should remain unchanged since shapes have different parents
|
|
834
|
+
expect(Array.from(editor.getSelectedShapeIds())).toEqual(initialSelectedIds)
|
|
835
|
+
|
|
836
|
+
// setSelectedShapes should not have been called
|
|
837
|
+
expect(setSelectedShapesSpy).not.toHaveBeenCalled()
|
|
838
|
+
|
|
839
|
+
setSelectedShapesSpy.mockRestore()
|
|
840
|
+
})
|
|
841
|
+
})
|
|
842
|
+
|
|
843
|
+
describe('putExternalContent', () => {
|
|
844
|
+
let mockHandler: any
|
|
845
|
+
|
|
846
|
+
beforeEach(() => {
|
|
847
|
+
mockHandler = vi.fn()
|
|
848
|
+
editor.registerExternalContentHandler('text', mockHandler)
|
|
849
|
+
})
|
|
850
|
+
|
|
851
|
+
it('calls external content handler when not readonly', async () => {
|
|
852
|
+
vi.spyOn(editor, 'getIsReadonly').mockReturnValue(false)
|
|
853
|
+
|
|
854
|
+
const info = { type: 'text' as const, text: 'test-data' }
|
|
855
|
+
await editor.putExternalContent(info)
|
|
856
|
+
|
|
857
|
+
expect(mockHandler).toHaveBeenCalledWith(info)
|
|
858
|
+
})
|
|
859
|
+
|
|
860
|
+
it('does not call external content handler when readonly', async () => {
|
|
861
|
+
vi.spyOn(editor, 'getIsReadonly').mockReturnValue(true)
|
|
862
|
+
|
|
863
|
+
const info = { type: 'text' as const, text: 'test-data' }
|
|
864
|
+
await editor.putExternalContent(info)
|
|
865
|
+
|
|
866
|
+
expect(mockHandler).not.toHaveBeenCalled()
|
|
867
|
+
})
|
|
868
|
+
|
|
869
|
+
it('calls external content handler when readonly but force is true', async () => {
|
|
870
|
+
vi.spyOn(editor, 'getIsReadonly').mockReturnValue(true)
|
|
871
|
+
|
|
872
|
+
const info = { type: 'text' as const, text: 'test-data' }
|
|
873
|
+
await editor.putExternalContent(info, { force: true })
|
|
874
|
+
|
|
875
|
+
expect(mockHandler).toHaveBeenCalledWith(info)
|
|
876
|
+
})
|
|
877
|
+
|
|
878
|
+
it('calls external content handler when force is false and not readonly', async () => {
|
|
879
|
+
vi.spyOn(editor, 'getIsReadonly').mockReturnValue(false)
|
|
880
|
+
|
|
881
|
+
const info = { type: 'text' as const, text: 'test-data' }
|
|
882
|
+
await editor.putExternalContent(info, { force: false })
|
|
883
|
+
|
|
884
|
+
expect(mockHandler).toHaveBeenCalledWith(info)
|
|
885
|
+
})
|
|
886
|
+
})
|
|
887
|
+
|
|
888
|
+
describe('replaceExternalContent', () => {
|
|
889
|
+
let mockHandler: any
|
|
890
|
+
|
|
891
|
+
beforeEach(() => {
|
|
892
|
+
mockHandler = vi.fn()
|
|
893
|
+
editor.registerExternalContentHandler('text', mockHandler)
|
|
894
|
+
})
|
|
895
|
+
|
|
896
|
+
it('calls external content handler when not readonly', async () => {
|
|
897
|
+
vi.spyOn(editor, 'getIsReadonly').mockReturnValue(false)
|
|
898
|
+
|
|
899
|
+
const info = { type: 'text' as const, text: 'test-data' }
|
|
900
|
+
await editor.replaceExternalContent(info)
|
|
901
|
+
|
|
902
|
+
expect(mockHandler).toHaveBeenCalledWith(info)
|
|
903
|
+
})
|
|
904
|
+
|
|
905
|
+
it('does not call external content handler when readonly', async () => {
|
|
906
|
+
vi.spyOn(editor, 'getIsReadonly').mockReturnValue(true)
|
|
907
|
+
|
|
908
|
+
const info = { type: 'text' as const, text: 'test-data' }
|
|
909
|
+
await editor.replaceExternalContent(info)
|
|
910
|
+
|
|
911
|
+
expect(mockHandler).not.toHaveBeenCalled()
|
|
912
|
+
})
|
|
913
|
+
|
|
914
|
+
it('calls external content handler when readonly but force is true', async () => {
|
|
915
|
+
vi.spyOn(editor, 'getIsReadonly').mockReturnValue(true)
|
|
916
|
+
|
|
917
|
+
const info = { type: 'text' as const, text: 'test-data' }
|
|
918
|
+
await editor.replaceExternalContent(info, { force: true })
|
|
919
|
+
|
|
920
|
+
expect(mockHandler).toHaveBeenCalledWith(info)
|
|
921
|
+
})
|
|
922
|
+
|
|
923
|
+
it('calls external content handler when force is false and not readonly', async () => {
|
|
924
|
+
vi.spyOn(editor, 'getIsReadonly').mockReturnValue(false)
|
|
925
|
+
|
|
926
|
+
const info = { type: 'text' as const, text: 'test-data' }
|
|
927
|
+
await editor.replaceExternalContent(info, { force: false })
|
|
928
|
+
|
|
929
|
+
expect(mockHandler).toHaveBeenCalledWith(info)
|
|
930
|
+
})
|
|
931
|
+
})
|