@tldraw/editor 5.4.0-next.ef99ab47e5ce → 5.5.0-canary.078a77321dc3
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 +119 -49
- package/dist-cjs/index.js +4 -1
- package/dist-cjs/index.js.map +2 -2
- package/dist-cjs/lib/TldrawEditor.js +7 -2
- package/dist-cjs/lib/TldrawEditor.js.map +2 -2
- package/dist-cjs/lib/components/MenuClickCapture.js +6 -8
- package/dist-cjs/lib/components/MenuClickCapture.js.map +2 -2
- package/dist-cjs/lib/components/default-components/CanvasOverlays.js +1 -3
- package/dist-cjs/lib/components/default-components/CanvasOverlays.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 +1 -1
- package/dist-cjs/lib/config/TLUserPreferences.js +6 -2
- package/dist-cjs/lib/config/TLUserPreferences.js.map +2 -2
- package/dist-cjs/lib/config/createTLStore.js +1 -1
- package/dist-cjs/lib/config/createTLStore.js.map +2 -2
- package/dist-cjs/lib/constants.js +2 -0
- package/dist-cjs/lib/constants.js.map +2 -2
- package/dist-cjs/lib/editor/Editor.js +190 -300
- package/dist-cjs/lib/editor/Editor.js.map +2 -2
- package/dist-cjs/lib/editor/derivations/shapeIdsInCurrentPage.js +7 -3
- package/dist-cjs/lib/editor/derivations/shapeIdsInCurrentPage.js.map +2 -2
- package/dist-cjs/lib/editor/managers/ClickManager/ClickManager.js +3 -3
- package/dist-cjs/lib/editor/managers/ClickManager/ClickManager.js.map +2 -2
- package/dist-cjs/lib/editor/managers/CollaboratorsManager/CollaboratorsManager.js +8 -8
- package/dist-cjs/lib/editor/managers/CollaboratorsManager/CollaboratorsManager.js.map +2 -2
- package/dist-cjs/lib/editor/managers/EdgeScrollManager/EdgeScrollManager.js +10 -6
- package/dist-cjs/lib/editor/managers/EdgeScrollManager/EdgeScrollManager.js.map +2 -2
- package/dist-cjs/lib/editor/managers/FontManager/FontManager.js +2 -1
- package/dist-cjs/lib/editor/managers/FontManager/FontManager.js.map +2 -2
- package/dist-cjs/lib/editor/managers/InputsManager/InputsManager.js +79 -6
- package/dist-cjs/lib/editor/managers/InputsManager/InputsManager.js.map +2 -2
- package/dist-cjs/lib/editor/managers/ScribbleManager/ScribbleManager.js +7 -0
- package/dist-cjs/lib/editor/managers/ScribbleManager/ScribbleManager.js.map +2 -2
- package/dist-cjs/lib/editor/managers/SnapManager/BoundsSnaps.js +0 -2
- package/dist-cjs/lib/editor/managers/SnapManager/BoundsSnaps.js.map +2 -2
- package/dist-cjs/lib/editor/managers/TextManager/TextManager.js +1 -2
- package/dist-cjs/lib/editor/managers/TextManager/TextManager.js.map +2 -2
- package/dist-cjs/lib/editor/managers/ThemeManager/ThemeManager.js +7 -3
- package/dist-cjs/lib/editor/managers/ThemeManager/ThemeManager.js.map +2 -2
- package/dist-cjs/lib/editor/managers/TickManager/TickManager.js +6 -3
- package/dist-cjs/lib/editor/managers/TickManager/TickManager.js.map +2 -2
- package/dist-cjs/lib/editor/managers/UserPreferencesManager/UserPreferencesManager.js +12 -1
- package/dist-cjs/lib/editor/managers/UserPreferencesManager/UserPreferencesManager.js.map +2 -2
- package/dist-cjs/lib/editor/overlays/OverlayManager.js +3 -7
- package/dist-cjs/lib/editor/overlays/OverlayManager.js.map +2 -2
- package/dist-cjs/lib/editor/overlays/strokeShapeIndicators.js +0 -1
- package/dist-cjs/lib/editor/overlays/strokeShapeIndicators.js.map +2 -2
- package/dist-cjs/lib/editor/shapes/BaseFrameLikeShapeUtil.js +3 -1
- package/dist-cjs/lib/editor/shapes/BaseFrameLikeShapeUtil.js.map +2 -2
- package/dist-cjs/lib/editor/shapes/shared/getPerfectDashProps.js +1 -0
- package/dist-cjs/lib/editor/shapes/shared/getPerfectDashProps.js.map +2 -2
- package/dist-cjs/lib/editor/tools/BaseBoxShapeTool/children/Pointing.js +1 -1
- package/dist-cjs/lib/editor/tools/BaseBoxShapeTool/children/Pointing.js.map +2 -2
- package/dist-cjs/lib/editor/tools/StateNode.js +3 -2
- package/dist-cjs/lib/editor/tools/StateNode.js.map +2 -2
- package/dist-cjs/lib/globals/editors.js +57 -0
- package/dist-cjs/lib/globals/editors.js.map +7 -0
- package/dist-cjs/lib/globals/menus.js +9 -4
- package/dist-cjs/lib/globals/menus.js.map +2 -2
- package/dist-cjs/lib/hooks/useCanvasEvents.js +2 -0
- package/dist-cjs/lib/hooks/useCanvasEvents.js.map +2 -2
- package/dist-cjs/lib/hooks/useDocumentEvents.js +39 -9
- package/dist-cjs/lib/hooks/useDocumentEvents.js.map +2 -2
- package/dist-cjs/lib/hooks/useLocalStore.js +10 -6
- package/dist-cjs/lib/hooks/useLocalStore.js.map +2 -2
- package/dist-cjs/lib/hooks/usePassThroughWheelEvents.js +1 -0
- package/dist-cjs/lib/hooks/usePassThroughWheelEvents.js.map +2 -2
- package/dist-cjs/lib/hooks/usePeerIds.js +2 -1
- package/dist-cjs/lib/hooks/usePeerIds.js.map +2 -2
- package/dist-cjs/lib/license/LicenseManager.js +9 -2
- package/dist-cjs/lib/license/LicenseManager.js.map +2 -2
- package/dist-cjs/lib/license/LicenseProvider.js +16 -3
- package/dist-cjs/lib/license/LicenseProvider.js.map +2 -2
- package/dist-cjs/lib/license/useLicenseManagerState.js +5 -4
- package/dist-cjs/lib/license/useLicenseManagerState.js.map +2 -2
- package/dist-cjs/lib/primitives/Box.js +2 -1
- package/dist-cjs/lib/primitives/Box.js.map +2 -2
- package/dist-cjs/lib/primitives/Vec.js +4 -3
- 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/CubicBezier2d.js +1 -1
- package/dist-cjs/lib/primitives/geometry/CubicBezier2d.js.map +2 -2
- package/dist-cjs/lib/primitives/geometry/Edge2d.js +1 -1
- package/dist-cjs/lib/primitives/geometry/Edge2d.js.map +2 -2
- package/dist-cjs/lib/primitives/geometry/Group2d.js +71 -26
- package/dist-cjs/lib/primitives/geometry/Group2d.js.map +2 -2
- package/dist-cjs/lib/primitives/geometry/Point2d.js +1 -1
- package/dist-cjs/lib/primitives/geometry/Point2d.js.map +2 -2
- package/dist-cjs/lib/primitives/utils.js +2 -1
- package/dist-cjs/lib/primitives/utils.js.map +2 -2
- package/dist-cjs/lib/utils/deepLinks.js +5 -1
- package/dist-cjs/lib/utils/deepLinks.js.map +2 -2
- package/dist-cjs/lib/utils/normalizeWheel.js +2 -0
- package/dist-cjs/lib/utils/normalizeWheel.js.map +2 -2
- package/dist-cjs/lib/utils/rotation.js +1 -9
- package/dist-cjs/lib/utils/rotation.js.map +2 -2
- package/dist-cjs/lib/utils/sync/TLLocalSyncClient.js +17 -0
- package/dist-cjs/lib/utils/sync/TLLocalSyncClient.js.map +2 -2
- package/dist-cjs/lib/utils/sync/alerts.js +1 -1
- package/dist-cjs/lib/utils/sync/alerts.js.map +1 -1
- package/dist-cjs/version.js +3 -3
- package/dist-cjs/version.js.map +1 -1
- package/dist-esm/index.d.mts +119 -49
- package/dist-esm/index.mjs +9 -2
- package/dist-esm/index.mjs.map +2 -2
- package/dist-esm/lib/TldrawEditor.mjs +9 -4
- package/dist-esm/lib/TldrawEditor.mjs.map +2 -2
- package/dist-esm/lib/components/MenuClickCapture.mjs +6 -8
- package/dist-esm/lib/components/MenuClickCapture.mjs.map +2 -2
- package/dist-esm/lib/components/default-components/CanvasOverlays.mjs +1 -3
- package/dist-esm/lib/components/default-components/CanvasOverlays.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 +1 -1
- package/dist-esm/lib/config/TLUserPreferences.mjs +6 -2
- package/dist-esm/lib/config/TLUserPreferences.mjs.map +2 -2
- package/dist-esm/lib/config/createTLStore.mjs +1 -1
- package/dist-esm/lib/config/createTLStore.mjs.map +2 -2
- package/dist-esm/lib/constants.mjs +2 -0
- package/dist-esm/lib/constants.mjs.map +2 -2
- package/dist-esm/lib/editor/Editor.mjs +199 -301
- package/dist-esm/lib/editor/Editor.mjs.map +2 -2
- package/dist-esm/lib/editor/derivations/shapeIdsInCurrentPage.mjs +7 -3
- package/dist-esm/lib/editor/derivations/shapeIdsInCurrentPage.mjs.map +2 -2
- package/dist-esm/lib/editor/managers/ClickManager/ClickManager.mjs +3 -3
- package/dist-esm/lib/editor/managers/ClickManager/ClickManager.mjs.map +2 -2
- package/dist-esm/lib/editor/managers/CollaboratorsManager/CollaboratorsManager.mjs +9 -9
- package/dist-esm/lib/editor/managers/CollaboratorsManager/CollaboratorsManager.mjs.map +2 -2
- package/dist-esm/lib/editor/managers/EdgeScrollManager/EdgeScrollManager.mjs +10 -6
- package/dist-esm/lib/editor/managers/EdgeScrollManager/EdgeScrollManager.mjs.map +2 -2
- package/dist-esm/lib/editor/managers/FontManager/FontManager.mjs +2 -1
- package/dist-esm/lib/editor/managers/FontManager/FontManager.mjs.map +2 -2
- package/dist-esm/lib/editor/managers/InputsManager/InputsManager.mjs +81 -8
- package/dist-esm/lib/editor/managers/InputsManager/InputsManager.mjs.map +2 -2
- package/dist-esm/lib/editor/managers/ScribbleManager/ScribbleManager.mjs +7 -0
- package/dist-esm/lib/editor/managers/ScribbleManager/ScribbleManager.mjs.map +2 -2
- package/dist-esm/lib/editor/managers/SnapManager/BoundsSnaps.mjs +0 -2
- package/dist-esm/lib/editor/managers/SnapManager/BoundsSnaps.mjs.map +2 -2
- package/dist-esm/lib/editor/managers/TextManager/TextManager.mjs +2 -3
- package/dist-esm/lib/editor/managers/TextManager/TextManager.mjs.map +2 -2
- package/dist-esm/lib/editor/managers/ThemeManager/ThemeManager.mjs +7 -3
- package/dist-esm/lib/editor/managers/ThemeManager/ThemeManager.mjs.map +2 -2
- package/dist-esm/lib/editor/managers/TickManager/TickManager.mjs +6 -3
- package/dist-esm/lib/editor/managers/TickManager/TickManager.mjs.map +2 -2
- package/dist-esm/lib/editor/managers/UserPreferencesManager/UserPreferencesManager.mjs +12 -1
- package/dist-esm/lib/editor/managers/UserPreferencesManager/UserPreferencesManager.mjs.map +2 -2
- package/dist-esm/lib/editor/overlays/OverlayManager.mjs +3 -7
- package/dist-esm/lib/editor/overlays/OverlayManager.mjs.map +2 -2
- package/dist-esm/lib/editor/overlays/strokeShapeIndicators.mjs +0 -1
- package/dist-esm/lib/editor/overlays/strokeShapeIndicators.mjs.map +2 -2
- package/dist-esm/lib/editor/shapes/BaseFrameLikeShapeUtil.mjs +3 -1
- package/dist-esm/lib/editor/shapes/BaseFrameLikeShapeUtil.mjs.map +2 -2
- package/dist-esm/lib/editor/shapes/shared/getPerfectDashProps.mjs +1 -0
- package/dist-esm/lib/editor/shapes/shared/getPerfectDashProps.mjs.map +2 -2
- package/dist-esm/lib/editor/tools/BaseBoxShapeTool/children/Pointing.mjs +1 -1
- package/dist-esm/lib/editor/tools/BaseBoxShapeTool/children/Pointing.mjs.map +2 -2
- package/dist-esm/lib/editor/tools/StateNode.mjs +3 -2
- package/dist-esm/lib/editor/tools/StateNode.mjs.map +2 -2
- package/dist-esm/lib/globals/editors.mjs +37 -0
- package/dist-esm/lib/globals/editors.mjs.map +7 -0
- package/dist-esm/lib/globals/menus.mjs +9 -4
- package/dist-esm/lib/globals/menus.mjs.map +2 -2
- package/dist-esm/lib/hooks/useCanvasEvents.mjs +2 -0
- package/dist-esm/lib/hooks/useCanvasEvents.mjs.map +2 -2
- package/dist-esm/lib/hooks/useDocumentEvents.mjs +40 -10
- package/dist-esm/lib/hooks/useDocumentEvents.mjs.map +2 -2
- package/dist-esm/lib/hooks/useLocalStore.mjs +11 -7
- package/dist-esm/lib/hooks/useLocalStore.mjs.map +2 -2
- package/dist-esm/lib/hooks/usePassThroughWheelEvents.mjs +1 -0
- package/dist-esm/lib/hooks/usePassThroughWheelEvents.mjs.map +2 -2
- package/dist-esm/lib/hooks/usePeerIds.mjs +2 -1
- package/dist-esm/lib/hooks/usePeerIds.mjs.map +2 -2
- package/dist-esm/lib/license/LicenseManager.mjs +9 -2
- package/dist-esm/lib/license/LicenseManager.mjs.map +2 -2
- package/dist-esm/lib/license/LicenseProvider.mjs +16 -3
- package/dist-esm/lib/license/LicenseProvider.mjs.map +2 -2
- package/dist-esm/lib/license/useLicenseManagerState.mjs +5 -4
- package/dist-esm/lib/license/useLicenseManagerState.mjs.map +2 -2
- package/dist-esm/lib/primitives/Box.mjs +2 -1
- package/dist-esm/lib/primitives/Box.mjs.map +2 -2
- package/dist-esm/lib/primitives/Vec.mjs +4 -3
- package/dist-esm/lib/primitives/Vec.mjs.map +2 -2
- package/dist-esm/lib/primitives/geometry/Arc2d.mjs +1 -1
- package/dist-esm/lib/primitives/geometry/Arc2d.mjs.map +2 -2
- package/dist-esm/lib/primitives/geometry/CubicBezier2d.mjs +1 -1
- package/dist-esm/lib/primitives/geometry/CubicBezier2d.mjs.map +2 -2
- package/dist-esm/lib/primitives/geometry/Edge2d.mjs +1 -1
- package/dist-esm/lib/primitives/geometry/Edge2d.mjs.map +2 -2
- package/dist-esm/lib/primitives/geometry/Group2d.mjs +75 -27
- package/dist-esm/lib/primitives/geometry/Group2d.mjs.map +2 -2
- package/dist-esm/lib/primitives/geometry/Point2d.mjs +1 -1
- package/dist-esm/lib/primitives/geometry/Point2d.mjs.map +2 -2
- package/dist-esm/lib/primitives/utils.mjs +2 -1
- package/dist-esm/lib/primitives/utils.mjs.map +2 -2
- package/dist-esm/lib/utils/deepLinks.mjs +5 -1
- package/dist-esm/lib/utils/deepLinks.mjs.map +2 -2
- package/dist-esm/lib/utils/normalizeWheel.mjs +2 -0
- package/dist-esm/lib/utils/normalizeWheel.mjs.map +2 -2
- package/dist-esm/lib/utils/rotation.mjs +1 -9
- package/dist-esm/lib/utils/rotation.mjs.map +2 -2
- package/dist-esm/lib/utils/sync/TLLocalSyncClient.mjs +17 -0
- package/dist-esm/lib/utils/sync/TLLocalSyncClient.mjs.map +2 -2
- package/dist-esm/lib/utils/sync/alerts.mjs +1 -1
- package/dist-esm/lib/utils/sync/alerts.mjs.map +1 -1
- package/dist-esm/version.mjs +3 -3
- package/dist-esm/version.mjs.map +1 -1
- package/package.json +13 -7
- package/src/index.ts +6 -1
- package/src/lib/TldrawEditor.tsx +16 -4
- package/src/lib/components/MenuClickCapture.tsx +19 -18
- package/src/lib/components/default-components/CanvasOverlays.tsx +1 -3
- package/src/lib/components/default-components/DefaultErrorFallback.tsx +1 -1
- package/src/lib/config/TLUserPreferences.test.ts +18 -1
- package/src/lib/config/TLUserPreferences.ts +8 -4
- package/src/lib/config/createTLStore.ts +1 -1
- package/src/lib/constants.ts +8 -0
- package/src/lib/editor/Editor.ts +285 -392
- package/src/lib/editor/derivations/shapeIdsInCurrentPage.ts +7 -3
- package/src/lib/editor/managers/ClickManager/ClickManager.ts +5 -3
- package/src/lib/editor/managers/CollaboratorsManager/CollaboratorsManager.ts +9 -9
- package/src/lib/editor/managers/EdgeScrollManager/EdgeScrollManager.test.ts +35 -4
- package/src/lib/editor/managers/EdgeScrollManager/EdgeScrollManager.ts +13 -7
- package/src/lib/editor/managers/FontManager/FontManager.ts +2 -1
- package/src/lib/editor/managers/InputsManager/InputsManager.test.ts +199 -3
- package/src/lib/editor/managers/InputsManager/InputsManager.ts +128 -9
- package/src/lib/editor/managers/ScribbleManager/ScribbleManager.ts +12 -1
- package/src/lib/editor/managers/SnapManager/BoundsSnaps.ts +0 -2
- package/src/lib/editor/managers/TextManager/TextManager.ts +2 -6
- package/src/lib/editor/managers/ThemeManager/ThemeManager.test.ts +53 -0
- package/src/lib/editor/managers/ThemeManager/ThemeManager.ts +9 -3
- package/src/lib/editor/managers/TickManager/TickManager.test.ts +13 -0
- package/src/lib/editor/managers/TickManager/TickManager.ts +9 -3
- package/src/lib/editor/managers/UserPreferencesManager/UserPreferencesManager.test.ts +64 -3
- package/src/lib/editor/managers/UserPreferencesManager/UserPreferencesManager.ts +18 -1
- package/src/lib/editor/overlays/OverlayManager.ts +3 -7
- package/src/lib/editor/overlays/strokeShapeIndicators.ts +0 -1
- package/src/lib/editor/shapes/BaseFrameLikeShapeUtil.tsx +5 -1
- package/src/lib/editor/shapes/shared/getPerfectDashProps.ts +3 -0
- package/src/lib/editor/tools/BaseBoxShapeTool/children/Pointing.ts +4 -1
- package/src/lib/editor/tools/StateNode.test.ts +64 -0
- package/src/lib/editor/tools/StateNode.ts +12 -2
- package/src/lib/globals/editors.test.ts +76 -0
- package/src/lib/globals/editors.ts +53 -0
- package/src/lib/globals/menus.test.ts +33 -0
- package/src/lib/globals/menus.ts +11 -4
- package/src/lib/hooks/useCanvasEvents.ts +2 -0
- package/src/lib/hooks/useDocumentEvents.ts +60 -16
- package/src/lib/hooks/useLocalStore.ts +16 -8
- package/src/lib/hooks/usePassThroughWheelEvents.ts +3 -0
- package/src/lib/hooks/usePeerIds.ts +2 -1
- package/src/lib/license/LicenseManager.test.ts +160 -0
- package/src/lib/license/LicenseManager.ts +13 -4
- package/src/lib/license/LicenseProvider.tsx +24 -3
- package/src/lib/license/useLicenseManagerState.ts +8 -6
- package/src/lib/primitives/Box.test.ts +9 -1
- package/src/lib/primitives/Box.ts +3 -1
- package/src/lib/primitives/Vec.test.ts +10 -0
- package/src/lib/primitives/Vec.ts +4 -4
- package/src/lib/primitives/geometry/Arc2d.test.ts +235 -3
- package/src/lib/primitives/geometry/Arc2d.ts +1 -1
- package/src/lib/primitives/geometry/CubicBezier2d.test.ts +186 -3
- package/src/lib/primitives/geometry/CubicBezier2d.ts +1 -1
- package/src/lib/primitives/geometry/CubicSpline2d.test.ts +160 -3
- package/src/lib/primitives/geometry/Edge2d.test.ts +21 -3
- package/src/lib/primitives/geometry/Edge2d.ts +1 -1
- package/src/lib/primitives/geometry/Ellipse2d.test.ts +243 -3
- package/src/lib/primitives/geometry/Geometry2d.test.ts +553 -1
- package/src/lib/primitives/geometry/Group2d.test.ts +419 -3
- package/src/lib/primitives/geometry/Group2d.ts +81 -34
- package/src/lib/primitives/geometry/Point2d.test.ts +14 -3
- package/src/lib/primitives/geometry/Point2d.ts +1 -1
- package/src/lib/primitives/geometry/Polyline2d.test.ts +207 -0
- package/src/lib/primitives/geometry/Stadium2d.test.ts +191 -3
- package/src/lib/primitives/utils.test.ts +10 -1
- package/src/lib/primitives/utils.ts +4 -1
- package/src/lib/test/useCanvasEvents.test.tsx +74 -0
- package/src/lib/test/useDocumentEvents.test.tsx +107 -0
- package/src/lib/test/usePassThroughWheelEvents.test.tsx +25 -2
- package/src/lib/utils/deepLinks.test.ts +15 -0
- package/src/lib/utils/deepLinks.ts +7 -1
- package/src/lib/utils/normalizeWheel.test.ts +39 -0
- package/src/lib/utils/normalizeWheel.ts +6 -0
- package/src/lib/utils/rotation.ts +4 -12
- package/src/lib/utils/sync/TLLocalSyncClient.test.ts +56 -0
- package/src/lib/utils/sync/TLLocalSyncClient.ts +24 -0
- package/src/lib/utils/sync/alerts.ts +1 -1
- package/src/version.ts +3 -3
- package/src/lib/primitives/geometry/Polyline.test.ts +0 -5
package/src/lib/editor/Editor.ts
CHANGED
|
@@ -72,6 +72,7 @@ import {
|
|
|
72
72
|
Result,
|
|
73
73
|
ZERO_INDEX_KEY,
|
|
74
74
|
annotateError,
|
|
75
|
+
areArraysShallowEqual,
|
|
75
76
|
assert,
|
|
76
77
|
assertExists,
|
|
77
78
|
bind,
|
|
@@ -109,6 +110,7 @@ import {
|
|
|
109
110
|
import {
|
|
110
111
|
DEFAULT_ANIMATION_OPTIONS,
|
|
111
112
|
DEFAULT_CAMERA_OPTIONS,
|
|
113
|
+
FRAME_MS_60HZ,
|
|
112
114
|
INTERNAL_POINTER_IDS,
|
|
113
115
|
LEFT_MOUSE_BUTTON,
|
|
114
116
|
MIDDLE_MOUSE_BUTTON,
|
|
@@ -118,8 +120,10 @@ import {
|
|
|
118
120
|
import { getOwnerWindow } from '../exports/domUtils'
|
|
119
121
|
import { exportToSvg } from '../exports/exportToSvg'
|
|
120
122
|
import { getSvgAsImageWithOptions, trimSvgToContent } from '../exports/getSvgAsImage'
|
|
123
|
+
import { registerMountedEditor, unregisterMountedEditor } from '../globals/editors'
|
|
121
124
|
import { tlmenus } from '../globals/menus'
|
|
122
125
|
import { tltime } from '../globals/time'
|
|
126
|
+
import { LicenseManager } from '../license/LicenseManager'
|
|
123
127
|
import { TldrawOptions, defaultTldrawOptions } from '../options'
|
|
124
128
|
import { Box, BoxLike } from '../primitives/Box'
|
|
125
129
|
import { EASINGS } from '../primitives/easings'
|
|
@@ -127,7 +131,13 @@ import { Geometry2d } from '../primitives/geometry/Geometry2d'
|
|
|
127
131
|
import { Group2d } from '../primitives/geometry/Group2d'
|
|
128
132
|
import { intersectPolygonPolygon } from '../primitives/intersect'
|
|
129
133
|
import { Mat, MatLike } from '../primitives/Mat'
|
|
130
|
-
import {
|
|
134
|
+
import {
|
|
135
|
+
HALF_PI,
|
|
136
|
+
approximately,
|
|
137
|
+
areAnglesCompatible,
|
|
138
|
+
clamp,
|
|
139
|
+
pointInPolygon,
|
|
140
|
+
} from '../primitives/utils'
|
|
131
141
|
import { Vec, VecLike } from '../primitives/Vec'
|
|
132
142
|
import { areShapesContentEqual } from '../utils/areShapesContentEqual'
|
|
133
143
|
import { dataUrlToFile } from '../utils/assets'
|
|
@@ -185,6 +195,7 @@ import { TLHistoryBatchOptions } from './types/history-types'
|
|
|
185
195
|
import {
|
|
186
196
|
OptionalKeys,
|
|
187
197
|
RequiredKeys,
|
|
198
|
+
TLCameraConstraints,
|
|
188
199
|
TLCameraMoveOptions,
|
|
189
200
|
TLCameraOptions,
|
|
190
201
|
TLGetShapeAtPointOptions,
|
|
@@ -543,6 +554,14 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
543
554
|
nextPageState.editingShapeId = null
|
|
544
555
|
}
|
|
545
556
|
|
|
557
|
+
if (
|
|
558
|
+
prevPageState.croppingShapeId &&
|
|
559
|
+
shapesNoLongerInPage.has(prevPageState.croppingShapeId)
|
|
560
|
+
) {
|
|
561
|
+
if (!nextPageState) nextPageState = { ...prevPageState }
|
|
562
|
+
nextPageState.croppingShapeId = null
|
|
563
|
+
}
|
|
564
|
+
|
|
546
565
|
const hintingShapeIds = prevPageState.hintingShapeIds.filter(
|
|
547
566
|
(id) => !shapesNoLongerInPage.has(id)
|
|
548
567
|
)
|
|
@@ -828,10 +847,11 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
828
847
|
|
|
829
848
|
if (prev?.selectedShapeIds !== next?.selectedShapeIds) {
|
|
830
849
|
// ensure that descendants and ancestors are not selected at the same time
|
|
850
|
+
const selectedShapeIds = new Set(next.selectedShapeIds)
|
|
831
851
|
const filtered = next.selectedShapeIds.filter((id) => {
|
|
832
852
|
let parentId = this.getShape(id)?.parentId
|
|
833
853
|
while (isShapeId(parentId)) {
|
|
834
|
-
if (
|
|
854
|
+
if (selectedShapeIds.has(parentId)) {
|
|
835
855
|
return false
|
|
836
856
|
}
|
|
837
857
|
parentId = this.getShape(parentId)?.parentId
|
|
@@ -918,10 +938,12 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
918
938
|
|
|
919
939
|
this.on('mount', () => {
|
|
920
940
|
this._isMounted.set(true)
|
|
941
|
+
registerMountedEditor(this)
|
|
921
942
|
})
|
|
922
943
|
|
|
923
944
|
this.on('unmount', () => {
|
|
924
945
|
this._isMounted.set(false)
|
|
946
|
+
unregisterMountedEditor(this)
|
|
925
947
|
})
|
|
926
948
|
|
|
927
949
|
this.timers.requestAnimationFrame(() => {
|
|
@@ -934,7 +956,11 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
934
956
|
const mode = this.store.props.collaboration.mode
|
|
935
957
|
this.disposables.add(
|
|
936
958
|
react('update collaboration mode', () => {
|
|
937
|
-
|
|
959
|
+
const isReadonly = mode.get() === 'readonly'
|
|
960
|
+
// only track `mode`, and keep the sync out of the user's undo history
|
|
961
|
+
unsafe__withoutCapture(() =>
|
|
962
|
+
this._updateInstanceState({ isReadonly }, { history: 'ignore' })
|
|
963
|
+
)
|
|
938
964
|
})
|
|
939
965
|
)
|
|
940
966
|
}
|
|
@@ -973,6 +999,15 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
973
999
|
|
|
974
1000
|
readonly options: TldrawOptions
|
|
975
1001
|
|
|
1002
|
+
/**
|
|
1003
|
+
* The license manager whose feature flags apply to this editor. Assigned by `<TldrawEditor />`
|
|
1004
|
+
* when it creates the editor, so that UI rendered outside the editor tree can resolve the
|
|
1005
|
+
* editor's license through the editor instance. Undefined for editors created directly.
|
|
1006
|
+
*
|
|
1007
|
+
* @internal
|
|
1008
|
+
*/
|
|
1009
|
+
licenseManager?: LicenseManager
|
|
1010
|
+
|
|
976
1011
|
readonly contextId = uniqueId()
|
|
977
1012
|
|
|
978
1013
|
/**
|
|
@@ -1209,6 +1244,9 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
1209
1244
|
if (this._isMounted.get()) {
|
|
1210
1245
|
this.emit('unmount')
|
|
1211
1246
|
}
|
|
1247
|
+
// The unmount listener above normally handles this; unregister again in case a listener
|
|
1248
|
+
// threw or was removed, so a disposed editor never lingers in `tleditors`.
|
|
1249
|
+
unregisterMountedEditor(this)
|
|
1212
1250
|
|
|
1213
1251
|
// Stop any in-progress camera animations and following before
|
|
1214
1252
|
// running disposables, so their cleanup listeners fire first
|
|
@@ -1374,7 +1412,8 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
1374
1412
|
getShapeUtil(arg: string | { type: string }) {
|
|
1375
1413
|
const type = typeof arg === 'string' ? arg : arg.type
|
|
1376
1414
|
const shapeUtil = getOwnProperty(this.shapeUtils, type)
|
|
1377
|
-
|
|
1415
|
+
// hot path: avoid building the message (and the assert wrapper) on every successful call
|
|
1416
|
+
if (!shapeUtil) throw new Error(`No shape util found for type "${type}"`)
|
|
1378
1417
|
return shapeUtil
|
|
1379
1418
|
}
|
|
1380
1419
|
|
|
@@ -2265,8 +2304,11 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
2265
2304
|
const shapeIds = readingOrderShapes.map((shape) => shape.id)
|
|
2266
2305
|
|
|
2267
2306
|
const currentIndex = currentShapeId ? shapeIds.indexOf(currentShapeId) : -1
|
|
2307
|
+
// With no current index, stepping from -1 makes 'prev' land one shape
|
|
2308
|
+
// before the last; seed it from 0 so it wraps to the last shape. See #10559.
|
|
2309
|
+
const startIndex = currentIndex === -1 && direction === 'prev' ? 0 : currentIndex
|
|
2268
2310
|
const adjacentIndex =
|
|
2269
|
-
(
|
|
2311
|
+
(startIndex + (direction === 'next' ? 1 : -1) + shapeIds.length) % shapeIds.length
|
|
2270
2312
|
adjacentShapeId = shapeIds[adjacentIndex]
|
|
2271
2313
|
} else {
|
|
2272
2314
|
if (!currentShapeId) return
|
|
@@ -2451,9 +2493,9 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
2451
2493
|
const selectedShapes = this.getSelectedShapes()
|
|
2452
2494
|
if (!selectedShapes.length) return
|
|
2453
2495
|
const selectedShape = selectedShapes[0]
|
|
2454
|
-
const children =
|
|
2455
|
-
.map((id) => this.getShape(id))
|
|
2456
|
-
|
|
2496
|
+
const children = compact(
|
|
2497
|
+
this.getSortedChildIdsForParent(selectedShape.id).map((id) => this.getShape(id))
|
|
2498
|
+
)
|
|
2457
2499
|
const sortedChildren = this._getShapesInReadingOrder(children)
|
|
2458
2500
|
if (sortedChildren.length === 0) return
|
|
2459
2501
|
this._selectShapesAndZoom([sortedChildren[0].id])
|
|
@@ -2554,20 +2596,14 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
2554
2596
|
* @internal
|
|
2555
2597
|
*/
|
|
2556
2598
|
getShapesSharedRotation(shapeIds: TLShapeId[]) {
|
|
2557
|
-
let foundFirst = false // annoying but we can't use an i===0 check because we need to skip over undefineds
|
|
2558
2599
|
let rotation = 0
|
|
2559
2600
|
for (let i = 0, n = shapeIds.length; i < n; i++) {
|
|
2560
|
-
const
|
|
2561
|
-
if (
|
|
2562
|
-
|
|
2563
|
-
|
|
2564
|
-
|
|
2565
|
-
|
|
2566
|
-
}
|
|
2567
|
-
} else {
|
|
2568
|
-
// First rotation found
|
|
2569
|
-
foundFirst = true
|
|
2570
|
-
rotation = pageTransform.rotation()
|
|
2601
|
+
const pageRotation = this.getShapePageTransform(shapeIds[i]).rotation()
|
|
2602
|
+
if (i === 0) {
|
|
2603
|
+
rotation = pageRotation
|
|
2604
|
+
} else if (pageRotation !== rotation) {
|
|
2605
|
+
// There are at least 2 different rotations, so the common rotation is zero
|
|
2606
|
+
return 0
|
|
2571
2607
|
}
|
|
2572
2608
|
}
|
|
2573
2609
|
|
|
@@ -2599,7 +2635,7 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
2599
2635
|
|
|
2600
2636
|
if (shapeIds.length === 1) {
|
|
2601
2637
|
const bounds = this.getShapeGeometry(shapeIds[0]).bounds.clone()
|
|
2602
|
-
const pageTransform = this.getShapePageTransform(shapeIds[0])
|
|
2638
|
+
const pageTransform = this.getShapePageTransform(shapeIds[0])
|
|
2603
2639
|
bounds.point = pageTransform.applyToPoint(bounds.point)
|
|
2604
2640
|
return bounds
|
|
2605
2641
|
}
|
|
@@ -2607,11 +2643,9 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
2607
2643
|
// need to 'un-rotate' all the outlines of the existing nodes so we can fit them inside a box
|
|
2608
2644
|
const boxFromRotatedVertices = Box.FromPoints(
|
|
2609
2645
|
shapeIds
|
|
2610
|
-
.flatMap((id) =>
|
|
2611
|
-
|
|
2612
|
-
|
|
2613
|
-
return pageTransform.applyToPoints(this.getShapeGeometry(id).bounds.corners)
|
|
2614
|
-
})
|
|
2646
|
+
.flatMap((id) =>
|
|
2647
|
+
this.getShapePageTransform(id).applyToPoints(this.getShapeGeometry(id).bounds.corners)
|
|
2648
|
+
)
|
|
2615
2649
|
.map((p) => p.rot(-selectionRotation))
|
|
2616
2650
|
)
|
|
2617
2651
|
// now position box so that it's top-left corner is in the right place
|
|
@@ -2638,9 +2672,16 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
2638
2672
|
@computed getSelectionRotatedScreenBounds(): Box | undefined {
|
|
2639
2673
|
const bounds = this.getSelectionRotatedPageBounds()
|
|
2640
2674
|
if (!bounds) return undefined
|
|
2641
|
-
|
|
2642
|
-
|
|
2643
|
-
|
|
2675
|
+
// Don't use pageToScreen here: it reads the screen bounds without capturing them, so this
|
|
2676
|
+
// computed would never invalidate when the container moves
|
|
2677
|
+
const screenBounds = this.getViewportScreenBounds()
|
|
2678
|
+
const { x: cx, y: cy, z: zoom } = this.getCamera()
|
|
2679
|
+
return new Box(
|
|
2680
|
+
(bounds.x + cx) * zoom + screenBounds.x,
|
|
2681
|
+
(bounds.y + cy) * zoom + screenBounds.y,
|
|
2682
|
+
bounds.width * zoom,
|
|
2683
|
+
bounds.height * zoom
|
|
2684
|
+
)
|
|
2644
2685
|
}
|
|
2645
2686
|
|
|
2646
2687
|
// Focus Group
|
|
@@ -2780,57 +2821,38 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
2780
2821
|
setEditingShape(shape: TLShapeId | TLShape | null): this {
|
|
2781
2822
|
const id = typeof shape === 'string' ? shape : (shape?.id ?? null)
|
|
2782
2823
|
|
|
2783
|
-
|
|
2784
|
-
|
|
2824
|
+
// id was provided but the next editing shape was not editable or didn't exist, so do nothing
|
|
2825
|
+
if (id && !this.canEditShape(id)) return this
|
|
2826
|
+
|
|
2827
|
+
this.run(() => {
|
|
2828
|
+
// Clean up the previous editing shape. This runs outside the history-ignored batch below,
|
|
2829
|
+
// otherwise document changes made by onEditEnd (e.g. deleting an empty text shape) are
|
|
2830
|
+
// never recorded and leave a phantom undo entry whose redo resurrects the shape.
|
|
2831
|
+
const prevEditingShapeId = this.getEditingShapeId()
|
|
2832
|
+
if (prevEditingShapeId) {
|
|
2833
|
+
const prevEditingShape = this.getShape(prevEditingShapeId)
|
|
2834
|
+
if (prevEditingShape) {
|
|
2835
|
+
this.getShapeUtil(prevEditingShape).onEditEnd?.(prevEditingShape)
|
|
2836
|
+
}
|
|
2837
|
+
}
|
|
2838
|
+
|
|
2785
2839
|
this.run(
|
|
2786
2840
|
() => {
|
|
2787
|
-
// Clean up the previous editing shape
|
|
2788
|
-
const prevEditingShapeId = this.getEditingShapeId()
|
|
2789
|
-
if (prevEditingShapeId) {
|
|
2790
|
-
const prevEditingShape = this.getShape(prevEditingShapeId)
|
|
2791
|
-
if (prevEditingShape) {
|
|
2792
|
-
this.getShapeUtil(prevEditingShape).onEditEnd?.(prevEditingShape)
|
|
2793
|
-
}
|
|
2794
|
-
}
|
|
2795
|
-
|
|
2796
2841
|
// Clean up the editing shape state and rich text editor
|
|
2797
2842
|
this._updateCurrentPageState({ editingShapeId: null })
|
|
2798
2843
|
this._currentRichTextEditor.set(null)
|
|
2799
|
-
},
|
|
2800
|
-
{ history: 'ignore' }
|
|
2801
|
-
)
|
|
2802
2844
|
|
|
2803
|
-
|
|
2804
|
-
}
|
|
2845
|
+
if (!id) return
|
|
2805
2846
|
|
|
2806
|
-
|
|
2807
|
-
|
|
2847
|
+
this.select(id)
|
|
2848
|
+
this._updateCurrentPageState({ editingShapeId: id })
|
|
2808
2849
|
|
|
2809
|
-
|
|
2810
|
-
|
|
2811
|
-
|
|
2812
|
-
|
|
2813
|
-
|
|
2814
|
-
|
|
2815
|
-
const prevEditingShape = this.getShape(prevEditingShapeId)
|
|
2816
|
-
if (prevEditingShape) {
|
|
2817
|
-
this.getShapeUtil(prevEditingShape).onEditEnd?.(prevEditingShape)
|
|
2818
|
-
}
|
|
2819
|
-
}
|
|
2820
|
-
|
|
2821
|
-
// Clean up the editing shape state and rich text editor
|
|
2822
|
-
this._updateCurrentPageState({ editingShapeId: null })
|
|
2823
|
-
this._currentRichTextEditor.set(null)
|
|
2824
|
-
|
|
2825
|
-
// Set the new editing shape
|
|
2826
|
-
this.select(id)
|
|
2827
|
-
this._updateCurrentPageState({ editingShapeId: id })
|
|
2828
|
-
|
|
2829
|
-
const nextEditingShape = this.getShape(id)! // shape should be there because canEditShape checked it. Possible small chance that onEditEnd deleted it?
|
|
2830
|
-
this.getShapeUtil(nextEditingShape).onEditStart?.(nextEditingShape)
|
|
2831
|
-
},
|
|
2832
|
-
{ history: 'ignore' }
|
|
2833
|
-
)
|
|
2850
|
+
const nextEditingShape = this.getShape(id)! // shape should be there because canEditShape checked it. Possible small chance that onEditEnd deleted it?
|
|
2851
|
+
this.getShapeUtil(nextEditingShape).onEditStart?.(nextEditingShape)
|
|
2852
|
+
},
|
|
2853
|
+
{ history: 'ignore' }
|
|
2854
|
+
)
|
|
2855
|
+
})
|
|
2834
2856
|
|
|
2835
2857
|
return this
|
|
2836
2858
|
}
|
|
@@ -2995,26 +3017,17 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
2995
3017
|
* @public
|
|
2996
3018
|
*/
|
|
2997
3019
|
setErasingShapes(shapes: TLShapeId[] | TLShape[]): this {
|
|
3020
|
+
// copy before sorting: the caller may pass a store-owned (frozen) array
|
|
2998
3021
|
const ids =
|
|
2999
3022
|
typeof shapes[0] === 'string'
|
|
3000
|
-
? (shapes as TLShapeId[])
|
|
3023
|
+
? (shapes as TLShapeId[]).slice()
|
|
3001
3024
|
: (shapes as TLShape[]).map((shape) => shape.id)
|
|
3002
3025
|
ids.sort() // sort the incoming ids
|
|
3003
3026
|
const erasingShapeIds = this.getErasingShapeIds()
|
|
3004
3027
|
this.run(
|
|
3005
3028
|
() => {
|
|
3006
|
-
|
|
3007
|
-
|
|
3008
|
-
// presuming the current ids are also sorted, check each item to see if it's the same;
|
|
3009
|
-
// if we find any unequal, then we know the new ids are different.
|
|
3010
|
-
for (let i = 0; i < ids.length; i++) {
|
|
3011
|
-
if (ids[i] !== erasingShapeIds[i]) {
|
|
3012
|
-
this._updateCurrentPageState({ erasingShapeIds: ids })
|
|
3013
|
-
break
|
|
3014
|
-
}
|
|
3015
|
-
}
|
|
3016
|
-
} else {
|
|
3017
|
-
// if the ids are a different length, then we know they're different.
|
|
3029
|
+
// the current ids are also sorted, so a shallow comparison tells us whether they changed
|
|
3030
|
+
if (!areArraysShallowEqual(ids, erasingShapeIds)) {
|
|
3018
3031
|
this._updateCurrentPageState({ erasingShapeIds: ids })
|
|
3019
3032
|
}
|
|
3020
3033
|
},
|
|
@@ -3051,6 +3064,7 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
3051
3064
|
if (!_shape) return false
|
|
3052
3065
|
const util = this.getShapeUtil(_shape)
|
|
3053
3066
|
if (!util.canCrop(_shape)) return false
|
|
3067
|
+
if (this.getIsReadonly()) return false
|
|
3054
3068
|
if (this.isShapeOrAncestorLocked(_shape)) return false
|
|
3055
3069
|
return true
|
|
3056
3070
|
}
|
|
@@ -3130,11 +3144,13 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
3130
3144
|
const collaborators = this.getCollaborators()
|
|
3131
3145
|
let leaderPresence = null as null | TLInstancePresence
|
|
3132
3146
|
while (targetUserId && !visited.includes(targetUserId)) {
|
|
3133
|
-
|
|
3134
|
-
|
|
3135
|
-
|
|
3136
|
-
|
|
3137
|
-
|
|
3147
|
+
const nextPresence = collaborators.find((c) => c.userId === targetUserId)
|
|
3148
|
+
// Stop at the last resolvable presence, otherwise a leader whose own leader has left
|
|
3149
|
+
// (or whose presence hasn't arrived yet) can't be followed at all
|
|
3150
|
+
if (!nextPresence) break
|
|
3151
|
+
leaderPresence = nextPresence
|
|
3152
|
+
targetUserId = nextPresence.followingUserId ?? null
|
|
3153
|
+
visited.push(nextPresence.userId)
|
|
3138
3154
|
}
|
|
3139
3155
|
return leaderPresence
|
|
3140
3156
|
}
|
|
@@ -3252,44 +3268,7 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
3252
3268
|
*
|
|
3253
3269
|
* @public */
|
|
3254
3270
|
getInitialZoom() {
|
|
3255
|
-
|
|
3256
|
-
// If no camera constraints are provided, the default zoom is 100%
|
|
3257
|
-
if (!cameraOptions.constraints) return 1
|
|
3258
|
-
|
|
3259
|
-
// When defaultZoom is default, the default zoom is 100%
|
|
3260
|
-
if (cameraOptions.constraints.initialZoom === 'default') return 1
|
|
3261
|
-
|
|
3262
|
-
const { zx, zy } = getCameraFitXFitY(this, cameraOptions)
|
|
3263
|
-
|
|
3264
|
-
switch (cameraOptions.constraints.initialZoom) {
|
|
3265
|
-
case 'fit-min': {
|
|
3266
|
-
return Math.max(zx, zy)
|
|
3267
|
-
}
|
|
3268
|
-
case 'fit-max': {
|
|
3269
|
-
return Math.min(zx, zy)
|
|
3270
|
-
}
|
|
3271
|
-
case 'fit-x': {
|
|
3272
|
-
return zx
|
|
3273
|
-
}
|
|
3274
|
-
case 'fit-y': {
|
|
3275
|
-
return zy
|
|
3276
|
-
}
|
|
3277
|
-
case 'fit-min-100': {
|
|
3278
|
-
return Math.min(1, Math.max(zx, zy))
|
|
3279
|
-
}
|
|
3280
|
-
case 'fit-max-100': {
|
|
3281
|
-
return Math.min(1, Math.min(zx, zy))
|
|
3282
|
-
}
|
|
3283
|
-
case 'fit-x-100': {
|
|
3284
|
-
return Math.min(1, zx)
|
|
3285
|
-
}
|
|
3286
|
-
case 'fit-y-100': {
|
|
3287
|
-
return Math.min(1, zy)
|
|
3288
|
-
}
|
|
3289
|
-
default: {
|
|
3290
|
-
throw exhaustiveSwitchError(cameraOptions.constraints.initialZoom)
|
|
3291
|
-
}
|
|
3292
|
-
}
|
|
3271
|
+
return this._getFitZoom(this.getCameraOptions().constraints?.initialZoom ?? 'default')
|
|
3293
3272
|
}
|
|
3294
3273
|
|
|
3295
3274
|
/**
|
|
@@ -3302,16 +3281,16 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
3302
3281
|
*
|
|
3303
3282
|
* @public */
|
|
3304
3283
|
getBaseZoom() {
|
|
3305
|
-
|
|
3306
|
-
|
|
3307
|
-
if (!cameraOptions.constraints) return 1
|
|
3284
|
+
return this._getFitZoom(this.getCameraOptions().constraints?.baseZoom ?? 'default')
|
|
3285
|
+
}
|
|
3308
3286
|
|
|
3309
|
-
|
|
3310
|
-
|
|
3287
|
+
private _getFitZoom(fit: TLCameraConstraints['initialZoom']): number {
|
|
3288
|
+
const cameraOptions = this.getCameraOptions()
|
|
3289
|
+
if (!cameraOptions.constraints || fit === 'default') return 1
|
|
3311
3290
|
|
|
3312
3291
|
const { zx, zy } = getCameraFitXFitY(this, cameraOptions)
|
|
3313
3292
|
|
|
3314
|
-
switch (
|
|
3293
|
+
switch (fit) {
|
|
3315
3294
|
case 'fit-min': {
|
|
3316
3295
|
return Math.max(zx, zy)
|
|
3317
3296
|
}
|
|
@@ -3337,7 +3316,7 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
3337
3316
|
return Math.min(1, zy)
|
|
3338
3317
|
}
|
|
3339
3318
|
default: {
|
|
3340
|
-
throw exhaustiveSwitchError(
|
|
3319
|
+
throw exhaustiveSwitchError(fit)
|
|
3341
3320
|
}
|
|
3342
3321
|
}
|
|
3343
3322
|
}
|
|
@@ -3374,7 +3353,9 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
3374
3353
|
...this._cameraOptions.__unsafe__getWithoutCapture(),
|
|
3375
3354
|
...opts,
|
|
3376
3355
|
})
|
|
3377
|
-
|
|
3356
|
+
// `undefined < 1` is false, so an explicit `zoomSteps: undefined` would otherwise get through
|
|
3357
|
+
// and make every later camera move throw
|
|
3358
|
+
if (!next.zoomSteps || next.zoomSteps.length < 1) next.zoomSteps = [1]
|
|
3378
3359
|
this._cameraOptions.set(next)
|
|
3379
3360
|
this.setCamera(this.getCamera())
|
|
3380
3361
|
return this
|
|
@@ -3420,8 +3401,8 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
3420
3401
|
const { constraints } = cameraOptions
|
|
3421
3402
|
|
|
3422
3403
|
// Clamp padding to half the viewport size on either dimension
|
|
3423
|
-
const
|
|
3424
|
-
const
|
|
3404
|
+
const px = Math.min(constraints.padding.x, vsb.w / 2)
|
|
3405
|
+
const py = Math.min(constraints.padding.y, vsb.h / 2)
|
|
3425
3406
|
|
|
3426
3407
|
// Expand the bounds by the padding
|
|
3427
3408
|
const bounds = Box.From(cameraOptions.constraints.bounds)
|
|
@@ -3490,14 +3471,14 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
3490
3471
|
}
|
|
3491
3472
|
case 'inside': {
|
|
3492
3473
|
// When below fit zoom, constrain the camera so that the bounds stay completely within the viewport
|
|
3493
|
-
if (z < zx) x = clamp(x, minX, (vsb.w - px) / z - bounds.w)
|
|
3474
|
+
if (z < zx) x = clamp(x, minX, (vsb.w - px) / z - bounds.w - bounds.x)
|
|
3494
3475
|
// When above fit zoom, keep the bounds within padding distance of the viewport edge
|
|
3495
3476
|
else x = clamp(x, minX + freeW, minX)
|
|
3496
3477
|
break
|
|
3497
3478
|
}
|
|
3498
3479
|
case 'outside': {
|
|
3499
3480
|
// Constrain the camera so that the bounds never leaves the viewport
|
|
3500
|
-
x = clamp(x, px / z - bounds.w, (vsb.w - px) / z)
|
|
3481
|
+
x = clamp(x, px / z - bounds.w - bounds.x, (vsb.w - px) / z - bounds.x)
|
|
3501
3482
|
break
|
|
3502
3483
|
}
|
|
3503
3484
|
case 'free': {
|
|
@@ -3522,12 +3503,12 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
3522
3503
|
break
|
|
3523
3504
|
}
|
|
3524
3505
|
case 'inside': {
|
|
3525
|
-
if (z < zy) y = clamp(y, minY, (vsb.h - py) / z - bounds.h)
|
|
3506
|
+
if (z < zy) y = clamp(y, minY, (vsb.h - py) / z - bounds.h - bounds.y)
|
|
3526
3507
|
else y = clamp(y, minY + freeH, minY)
|
|
3527
3508
|
break
|
|
3528
3509
|
}
|
|
3529
3510
|
case 'outside': {
|
|
3530
|
-
y = clamp(y, py / z - bounds.h, (vsb.h - py) / z)
|
|
3511
|
+
y = clamp(y, py / z - bounds.h - bounds.y, (vsb.h - py) / z - bounds.y)
|
|
3531
3512
|
break
|
|
3532
3513
|
}
|
|
3533
3514
|
case 'free': {
|
|
@@ -3614,6 +3595,18 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
3614
3595
|
const { isLocked } = this._cameraOptions.__unsafe__getWithoutCapture()
|
|
3615
3596
|
if (isLocked && !opts?.force) return this
|
|
3616
3597
|
|
|
3598
|
+
const _point = Vec.Cast(point)
|
|
3599
|
+
|
|
3600
|
+
// Reject non-finite values before anything else, so the call is a no-op rather than a
|
|
3601
|
+
// partial one. An animated move writes the camera from a 'tick' listener, and a listener
|
|
3602
|
+
// that throws stops TickManager scheduling the next frame, which kills every frame-driven
|
|
3603
|
+
// behavior for the rest of the session instead of surfacing the error to the caller.
|
|
3604
|
+
if (!Number.isFinite(_point.x) || !Number.isFinite(_point.y) || !Number.isFinite(_point.z)) {
|
|
3605
|
+
throw Error(
|
|
3606
|
+
`Editor.setCamera: expected finite values, got (${_point.x}, ${_point.y}, ${_point.z}).`
|
|
3607
|
+
)
|
|
3608
|
+
}
|
|
3609
|
+
|
|
3617
3610
|
// Stop any camera animations
|
|
3618
3611
|
this.stopCameraAnimation()
|
|
3619
3612
|
|
|
@@ -3622,12 +3615,6 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
3622
3615
|
this.stopFollowingUser()
|
|
3623
3616
|
}
|
|
3624
3617
|
|
|
3625
|
-
const _point = Vec.Cast(point)
|
|
3626
|
-
|
|
3627
|
-
if (!Number.isFinite(_point.x)) _point.x = 0
|
|
3628
|
-
if (!Number.isFinite(_point.y)) _point.y = 0
|
|
3629
|
-
if (_point.z === undefined || !Number.isFinite(_point.z)) point.z = this.getZoomLevel()
|
|
3630
|
-
|
|
3631
3618
|
const camera = this.getConstrainedCamera(_point, opts)
|
|
3632
3619
|
|
|
3633
3620
|
if (opts?.animation) {
|
|
@@ -3982,9 +3969,9 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
3982
3969
|
const remaining = duration - elapsed
|
|
3983
3970
|
const t = easing(1 - remaining / duration)
|
|
3984
3971
|
|
|
3985
|
-
const left = start.minX
|
|
3986
|
-
const top = start.minY
|
|
3987
|
-
const right = start.maxX
|
|
3972
|
+
const left = lerp(start.minX, end.minX, t)
|
|
3973
|
+
const top = lerp(start.minY, end.minY, t)
|
|
3974
|
+
const right = lerp(start.maxX, end.maxX, t)
|
|
3988
3975
|
|
|
3989
3976
|
this._setCamera(new Vec(-left, -top, this.getViewportScreenBounds().width / (right - left)), {
|
|
3990
3977
|
force: true,
|
|
@@ -4099,17 +4086,18 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
4099
4086
|
let newCy = cy + dy
|
|
4100
4087
|
let newCz = cz
|
|
4101
4088
|
|
|
4102
|
-
// animate zoom if z direction is passed in
|
|
4089
|
+
// animate zoom if z direction is passed in. Use an exponential factor so that a single
|
|
4090
|
+
// long frame can't drive the zoom to zero or negative (which would give NaN coordinates)
|
|
4103
4091
|
if (dirZ !== 0) {
|
|
4104
|
-
newCz = cz * (
|
|
4092
|
+
newCz = cz * Math.exp(dirZ * currentSpeed * elapsed)
|
|
4105
4093
|
// Adjust x/y to keep the viewport center fixed while zooming
|
|
4106
4094
|
const center = this.getViewportScreenCenter()
|
|
4107
4095
|
newCx += center.x / newCz - center.x / cz
|
|
4108
4096
|
newCy += center.y / newCz - center.y / cz
|
|
4109
4097
|
}
|
|
4110
4098
|
|
|
4111
|
-
// Apply friction
|
|
4112
|
-
currentSpeed *= 1 - friction
|
|
4099
|
+
// Apply friction per unit of elapsed time, not per tick, or a 120 Hz display decays twice as fast
|
|
4100
|
+
currentSpeed *= (1 - friction) ** (elapsed / FRAME_MS_60HZ)
|
|
4113
4101
|
if (currentSpeed < speedThreshold) {
|
|
4114
4102
|
cancel()
|
|
4115
4103
|
} else {
|
|
@@ -4686,7 +4674,7 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
4686
4674
|
let nextIndex = this.options.maxShapesPerPage * 2
|
|
4687
4675
|
let nextBackgroundIndex = this.options.maxShapesPerPage
|
|
4688
4676
|
|
|
4689
|
-
const erasingShapeIds = this.getErasingShapeIds()
|
|
4677
|
+
const erasingShapeIds = new Set(this.getErasingShapeIds())
|
|
4690
4678
|
|
|
4691
4679
|
const addShapeById = (id: TLShapeId, opacity: number, isAncestorErasing: boolean) => {
|
|
4692
4680
|
const shape = this.getShape(id)
|
|
@@ -4694,7 +4682,7 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
4694
4682
|
|
|
4695
4683
|
if (this.isShapeHidden(shape)) {
|
|
4696
4684
|
// process children just in case they are overriding the hidden state
|
|
4697
|
-
const isErasing = isAncestorErasing || erasingShapeIds.
|
|
4685
|
+
const isErasing = isAncestorErasing || erasingShapeIds.has(id)
|
|
4698
4686
|
for (const childId of this.getSortedChildIdsForParent(id)) {
|
|
4699
4687
|
addShapeById(childId, opacity, isErasing)
|
|
4700
4688
|
}
|
|
@@ -4706,7 +4694,7 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
4706
4694
|
const util = this.getShapeUtil(shape)
|
|
4707
4695
|
|
|
4708
4696
|
if (useEditorState) {
|
|
4709
|
-
isShapeErasing = !isAncestorErasing && erasingShapeIds.
|
|
4697
|
+
isShapeErasing = !isAncestorErasing && erasingShapeIds.has(id)
|
|
4710
4698
|
if (isShapeErasing) {
|
|
4711
4699
|
opacity *= 0.32
|
|
4712
4700
|
}
|
|
@@ -5465,11 +5453,8 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
5465
5453
|
/** @internal */
|
|
5466
5454
|
@computed private _getShapePageBoundsCache(): ComputedCache<Box, TLShape> {
|
|
5467
5455
|
return this.store.createComputedCache<Box, TLShape>('pageBoundsCache', (shape) => {
|
|
5468
|
-
const pageTransform = this.getShapePageTransform(shape)
|
|
5469
|
-
if (!pageTransform) return undefined
|
|
5470
|
-
|
|
5471
5456
|
return Box.FromPoints(
|
|
5472
|
-
|
|
5457
|
+
this.getShapePageTransform(shape).applyToPoints(this.getShapeGeometry(shape).boundsVertices)
|
|
5473
5458
|
)
|
|
5474
5459
|
})
|
|
5475
5460
|
}
|
|
@@ -5608,7 +5593,13 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
5608
5593
|
if (pageMask) {
|
|
5609
5594
|
if (pageMask.length === 0) return undefined
|
|
5610
5595
|
const { corners } = pageBounds
|
|
5611
|
-
|
|
5596
|
+
// the mask may have fewer than four points (e.g. a triangular clip path)
|
|
5597
|
+
if (
|
|
5598
|
+
pageMask.length === corners.length &&
|
|
5599
|
+
corners.every((p, i) => Vec.Equals(p, pageMask[i]))
|
|
5600
|
+
) {
|
|
5601
|
+
return pageBounds.clone()
|
|
5602
|
+
}
|
|
5612
5603
|
const intersection = intersectPolygonPolygon(pageMask, corners)
|
|
5613
5604
|
if (!intersection) return
|
|
5614
5605
|
return Box.FromPoints(intersection)
|
|
@@ -5862,6 +5853,7 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
5862
5853
|
const margin = this.options.hitTestMargin / this.getZoomLevel()
|
|
5863
5854
|
const sortedShapes = this.getCurrentPageShapesSorted()
|
|
5864
5855
|
|
|
5856
|
+
// iterate from the top (highest z-index) to find the top-most matching shape
|
|
5865
5857
|
for (let i = sortedShapes.length - 1; i >= 0; i--) {
|
|
5866
5858
|
const shape = sortedShapes[i]
|
|
5867
5859
|
if (shape.type === 'group') continue
|
|
@@ -5911,44 +5903,43 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
5911
5903
|
const searchMargin = Math.max(innerMargin, outerMargin, this.getHitTestMargin())
|
|
5912
5904
|
const candidateIds = this._spatialIndex.getShapeIdsAtPoint(point, searchMargin)
|
|
5913
5905
|
|
|
5914
|
-
const shapesToCheck =
|
|
5915
|
-
|
|
5916
|
-
|
|
5917
|
-
: this.getCurrentPageShapesSorted()
|
|
5918
|
-
).filter((shape) => {
|
|
5919
|
-
// Frame-like shapes have labels positioned above the shape (outside bounds), so always include them
|
|
5920
|
-
if (!candidateIds.has(shape.id) && !this.isShapeFrameLike(shape)) return false
|
|
5906
|
+
const shapesToCheck = opts.renderingOnly
|
|
5907
|
+
? this.getCurrentPageRenderingShapesSorted()
|
|
5908
|
+
: this.getCurrentPageShapesSorted()
|
|
5921
5909
|
|
|
5910
|
+
for (let i = shapesToCheck.length - 1; i >= 0; i--) {
|
|
5911
|
+
const shape = shapesToCheck[i]
|
|
5912
|
+
// Frame-like shapes have labels positioned above the shape (outside bounds), so always include them
|
|
5913
|
+
if (!candidateIds.has(shape.id) && !this.isShapeFrameLike(shape)) continue
|
|
5922
5914
|
if (
|
|
5923
5915
|
(shape.isLocked && !hitLocked) ||
|
|
5924
5916
|
this.isShapeHidden(shape) ||
|
|
5925
5917
|
this.isShapeOfType(shape, 'group')
|
|
5926
|
-
)
|
|
5927
|
-
|
|
5918
|
+
) {
|
|
5919
|
+
continue
|
|
5920
|
+
}
|
|
5928
5921
|
const pageMask = this.getShapeMask(shape)
|
|
5929
|
-
if (pageMask && !pointInPolygon(point, pageMask))
|
|
5930
|
-
if (filter && !filter(shape))
|
|
5931
|
-
return true
|
|
5932
|
-
})
|
|
5922
|
+
if (pageMask && !pointInPolygon(point, pageMask)) continue
|
|
5923
|
+
if (filter && !filter(shape)) continue
|
|
5933
5924
|
|
|
5934
|
-
for (let i = shapesToCheck.length - 1; i >= 0; i--) {
|
|
5935
|
-
const shape = shapesToCheck[i]
|
|
5936
5925
|
const geometry = this.getShapeGeometry(shape)
|
|
5937
5926
|
const isGroup = geometry instanceof Group2d
|
|
5938
5927
|
|
|
5939
5928
|
const pointInShapeSpace = this.getPointInShapeSpace(shape, point)
|
|
5940
5929
|
|
|
5941
|
-
// Check labels first
|
|
5930
|
+
// Check labels first. Only group geometries can carry a label child; a frame-like
|
|
5931
|
+
// shape util may still return a plain geometry.
|
|
5942
5932
|
const shapeUtil = this.getShapeUtil(shape)
|
|
5943
5933
|
const isShapeFrameLike = this.isShapeFrameLike(shape)
|
|
5944
5934
|
if (
|
|
5945
|
-
|
|
5946
|
-
(
|
|
5947
|
-
this.isShapeOfType(shape, '
|
|
5948
|
-
|
|
5949
|
-
|
|
5935
|
+
isGroup &&
|
|
5936
|
+
(isShapeFrameLike ||
|
|
5937
|
+
((this.isShapeOfType(shape, 'note') ||
|
|
5938
|
+
this.isShapeOfType(shape, 'arrow') ||
|
|
5939
|
+
(this.isShapeOfType(shape, 'geo') && shape.props.fill === 'none')) &&
|
|
5940
|
+
shapeUtil.getText(shape)?.trim()))
|
|
5950
5941
|
) {
|
|
5951
|
-
for (const childGeometry of
|
|
5942
|
+
for (const childGeometry of geometry.children) {
|
|
5952
5943
|
if (childGeometry.isLabel && childGeometry.isPointInBounds(pointInShapeSpace)) {
|
|
5953
5944
|
return shape
|
|
5954
5945
|
}
|
|
@@ -6114,15 +6105,18 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
6114
6105
|
const margin = opts.margin ?? 0
|
|
6115
6106
|
const candidateIds = this._spatialIndex.getShapeIdsAtPoint(point, margin)
|
|
6116
6107
|
|
|
6117
|
-
// Get all page shapes in z-index order and filter to candidates that pass isPointInShape
|
|
6118
|
-
// Frame-like shapes are always checked because their labels can be outside their bounds
|
|
6119
|
-
|
|
6120
|
-
|
|
6121
|
-
|
|
6122
|
-
|
|
6123
|
-
|
|
6124
|
-
|
|
6125
|
-
.
|
|
6108
|
+
// Get all page shapes in z-index order and filter to candidates that pass isPointInShape.
|
|
6109
|
+
// Frame-like shapes are always checked because their labels can be outside their bounds.
|
|
6110
|
+
// Iterate backwards so the result is pre-sorted in reverse z-index order (top-most first).
|
|
6111
|
+
const sorted = this.getCurrentPageShapesSorted()
|
|
6112
|
+
const result: TLShape[] = []
|
|
6113
|
+
for (let i = sorted.length - 1; i >= 0; i--) {
|
|
6114
|
+
const shape = sorted[i]
|
|
6115
|
+
if (this.isShapeHidden(shape)) continue
|
|
6116
|
+
if (!candidateIds.has(shape.id) && !this.isShapeFrameLike(shape)) continue
|
|
6117
|
+
if (this.isPointInShape(shape, point, opts)) result.push(shape)
|
|
6118
|
+
}
|
|
6119
|
+
return result
|
|
6126
6120
|
}
|
|
6127
6121
|
|
|
6128
6122
|
/**
|
|
@@ -6222,9 +6216,7 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
6222
6216
|
if (!freshShape) return new Vec(0, 0)
|
|
6223
6217
|
if (isPageId(freshShape.parentId)) return Vec.From(point)
|
|
6224
6218
|
|
|
6225
|
-
|
|
6226
|
-
if (!parentTransform) return Vec.From(point)
|
|
6227
|
-
return parentTransform.clone().invert().applyToPoint(point)
|
|
6219
|
+
return this.getShapePageTransform(freshShape.parentId).clone().invert().applyToPoint(point)
|
|
6228
6220
|
}
|
|
6229
6221
|
|
|
6230
6222
|
/**
|
|
@@ -6391,26 +6383,7 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
6391
6383
|
* @public
|
|
6392
6384
|
*/
|
|
6393
6385
|
isShapeInPage(shape: TLShape | TLShapeId, pageId = this.getCurrentPageId()): boolean {
|
|
6394
|
-
|
|
6395
|
-
const shapeToCheck = this.getShape(id)
|
|
6396
|
-
if (!shapeToCheck) return false
|
|
6397
|
-
|
|
6398
|
-
let shapeIsInPage = false
|
|
6399
|
-
|
|
6400
|
-
if (shapeToCheck.parentId === pageId) {
|
|
6401
|
-
shapeIsInPage = true
|
|
6402
|
-
} else {
|
|
6403
|
-
let parent = this.getShape(shapeToCheck.parentId)
|
|
6404
|
-
isInPageSearch: while (parent) {
|
|
6405
|
-
if (parent.parentId === pageId) {
|
|
6406
|
-
shapeIsInPage = true
|
|
6407
|
-
break isInPageSearch
|
|
6408
|
-
}
|
|
6409
|
-
parent = this.getShape(parent.parentId)
|
|
6410
|
-
}
|
|
6411
|
-
}
|
|
6412
|
-
|
|
6413
|
-
return shapeIsInPage
|
|
6386
|
+
return this.getAncestorPageId(shape) === pageId
|
|
6414
6387
|
}
|
|
6415
6388
|
|
|
6416
6389
|
/**
|
|
@@ -6521,13 +6494,8 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
6521
6494
|
for (let i = 0; i < shapesToReparent.length; i++) {
|
|
6522
6495
|
const shape = shapesToReparent[i]
|
|
6523
6496
|
|
|
6524
|
-
const pageTransform = this.getShapePageTransform(shape)
|
|
6525
|
-
|
|
6526
|
-
|
|
6527
|
-
const pagePoint = pageTransform.point()
|
|
6528
|
-
if (!pagePoint) continue
|
|
6529
|
-
|
|
6530
|
-
const newPoint = invertedParentTransform.applyToPoint(pagePoint)
|
|
6497
|
+
const pageTransform = this.getShapePageTransform(shape)
|
|
6498
|
+
const newPoint = invertedParentTransform.applyToPoint(pageTransform.point())
|
|
6531
6499
|
const newRotation = pageTransform.rotation() - parentPageRotation
|
|
6532
6500
|
|
|
6533
6501
|
if (shape.id === parentId) {
|
|
@@ -6628,7 +6596,7 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
6628
6596
|
*/
|
|
6629
6597
|
getShapeAndDescendantIds(ids: TLShapeId[]): Set<TLShapeId> {
|
|
6630
6598
|
const shapeIds = new Set<TLShapeId>()
|
|
6631
|
-
for (const shape of ids.map((id) => this.getShape(id)
|
|
6599
|
+
for (const shape of compact(ids.map((id) => this.getShape(id))).sort(sortByIndex)) {
|
|
6632
6600
|
shapeIds.add(shape.id)
|
|
6633
6601
|
this.visitDescendants(shape, (descendantId) => {
|
|
6634
6602
|
shapeIds.add(descendantId)
|
|
@@ -6652,6 +6620,9 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
6652
6620
|
const draggingShapes = compact(droppingShapes.map((s) => this.getShape(s))).filter(
|
|
6653
6621
|
(s) => !s.isLocked && !this.isShapeHidden(s)
|
|
6654
6622
|
)
|
|
6623
|
+
// Descendants of the dragged shapes can't be the target, otherwise dragging a frame out of
|
|
6624
|
+
// its parent reports a nested child as the target and the parent never sees the drag leave
|
|
6625
|
+
const excludedIds = this.getShapeAndDescendantIds(draggingShapes.map((s) => s.id))
|
|
6655
6626
|
|
|
6656
6627
|
const maybeDraggingOverShapes = this.getShapesAtPoint(point, {
|
|
6657
6628
|
hitInside: true,
|
|
@@ -6661,7 +6632,7 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
6661
6632
|
!droppingShapes.includes(s) &&
|
|
6662
6633
|
!s.isLocked &&
|
|
6663
6634
|
!this.isShapeHidden(s) &&
|
|
6664
|
-
!
|
|
6635
|
+
!excludedIds.has(s.id)
|
|
6665
6636
|
)
|
|
6666
6637
|
|
|
6667
6638
|
for (const maybeDraggingOverShape of maybeDraggingOverShapes) {
|
|
@@ -7035,8 +7006,7 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
7035
7006
|
for (const id of ids) {
|
|
7036
7007
|
const shape = this.getShape(id)!
|
|
7037
7008
|
const localDelta = Vec.From(offset)
|
|
7038
|
-
|
|
7039
|
-
if (parentTransform) localDelta.rot(-parentTransform.rotation())
|
|
7009
|
+
localDelta.rot(-this.getShapeParentTransform(shape).rotation())
|
|
7040
7010
|
|
|
7041
7011
|
changes.push(this.getChangesToTranslateShape(shape, localDelta.add(shape)))
|
|
7042
7012
|
}
|
|
@@ -7106,7 +7076,13 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
7106
7076
|
let ox = 0
|
|
7107
7077
|
let oy = 0
|
|
7108
7078
|
|
|
7109
|
-
|
|
7079
|
+
// Only offset the roots of the duplicated tree: a descendant that was also passed in
|
|
7080
|
+
// follows its duplicated parent, so offsetting it too would move it twice
|
|
7081
|
+
if (
|
|
7082
|
+
offset &&
|
|
7083
|
+
initialIds.has(originalId) &&
|
|
7084
|
+
!shapeIdSet.has(originalShape.parentId as TLShapeId)
|
|
7085
|
+
) {
|
|
7110
7086
|
const parentTransform = this.getShapeParentTransform(originalShape)
|
|
7111
7087
|
const vec = new Vec(offset.x, offset.y).rot(-parentTransform!.rotation())
|
|
7112
7088
|
ox = vec.x
|
|
@@ -7272,27 +7248,17 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
7272
7248
|
|
|
7273
7249
|
if (this.getIsReadonly() || ids.length === 0) return this
|
|
7274
7250
|
|
|
7275
|
-
let allLocked = true
|
|
7276
|
-
allUnlocked = true
|
|
7251
|
+
let allLocked = true
|
|
7277
7252
|
const shapesToToggle: TLShape[] = []
|
|
7278
7253
|
for (const id of ids) {
|
|
7279
7254
|
const shape = this.getShape(id)
|
|
7280
7255
|
if (shape) {
|
|
7281
7256
|
shapesToToggle.push(shape)
|
|
7282
|
-
if (shape.isLocked)
|
|
7283
|
-
allUnlocked = false
|
|
7284
|
-
} else {
|
|
7285
|
-
allLocked = false
|
|
7286
|
-
}
|
|
7257
|
+
if (!shape.isLocked) allLocked = false
|
|
7287
7258
|
}
|
|
7288
7259
|
}
|
|
7289
7260
|
this.run(() => {
|
|
7290
|
-
if (
|
|
7291
|
-
this.updateShapes(
|
|
7292
|
-
shapesToToggle.map((shape) => ({ id: shape.id, type: shape.type, isLocked: true }))
|
|
7293
|
-
)
|
|
7294
|
-
this.setSelectedShapes([])
|
|
7295
|
-
} else if (allLocked) {
|
|
7261
|
+
if (allLocked) {
|
|
7296
7262
|
this.updateShapes(
|
|
7297
7263
|
shapesToToggle.map((shape) => ({ id: shape.id, type: shape.type, isLocked: false }))
|
|
7298
7264
|
)
|
|
@@ -7300,6 +7266,7 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
7300
7266
|
this.updateShapes(
|
|
7301
7267
|
shapesToToggle.map((shape) => ({ id: shape.id, type: shape.type, isLocked: true }))
|
|
7302
7268
|
)
|
|
7269
|
+
this.setSelectedShapes([])
|
|
7303
7270
|
}
|
|
7304
7271
|
})
|
|
7305
7272
|
|
|
@@ -7439,9 +7406,12 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
7439
7406
|
|
|
7440
7407
|
// optionally filter to axis-aligned shapes (rotation is a multiple of 90 degrees)
|
|
7441
7408
|
if (opts?.filterAxisAligned) {
|
|
7442
|
-
freshShapes = freshShapes.filter(
|
|
7443
|
-
|
|
7444
|
-
|
|
7409
|
+
freshShapes = freshShapes.filter((s) => {
|
|
7410
|
+
// Page rotations come back through atan2 on a composed matrix, so a 270° shape can land
|
|
7411
|
+
// a hair either side of the multiple; an exact === 0 check drops it
|
|
7412
|
+
const remainder = Math.abs(this.getShapePageTransform(s).rotation() % HALF_PI)
|
|
7413
|
+
return Math.min(remainder, HALF_PI - remainder) < 1e-9
|
|
7414
|
+
})
|
|
7445
7415
|
}
|
|
7446
7416
|
|
|
7447
7417
|
const clusters: { shapes: TLShape[]; pageBounds: Box }[] = []
|
|
@@ -7662,13 +7632,14 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
7662
7632
|
|
|
7663
7633
|
let shapeGap: number = 0
|
|
7664
7634
|
|
|
7635
|
+
// Stack in spatial order rather than input (z) order, otherwise shapes swap places
|
|
7636
|
+
shapeClustersToStack.sort((a, b) => a.pageBounds[min] - b.pageBounds[min])
|
|
7637
|
+
|
|
7665
7638
|
if (_gap === 0) {
|
|
7666
7639
|
// note: this is not used in the current tldraw.com; there we use a specified stack
|
|
7667
7640
|
|
|
7668
7641
|
const gaps: Record<number, number> = {}
|
|
7669
7642
|
|
|
7670
|
-
shapeClustersToStack.sort((a, b) => a.pageBounds[min] - b.pageBounds[min])
|
|
7671
|
-
|
|
7672
7643
|
// Collect all of the gaps between shapes. We want to find
|
|
7673
7644
|
// patterns (equal gaps between shapes) and use the most common
|
|
7674
7645
|
// one as the gap for all of the shapes.
|
|
@@ -7721,8 +7692,7 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
7721
7692
|
// todo: ensure that the parent isn't being aligned together with its children
|
|
7722
7693
|
const parent = this.getShapeParent(shape)
|
|
7723
7694
|
if (parent) {
|
|
7724
|
-
|
|
7725
|
-
if (parentTransform) shapeDelta.rot(-parentTransform.rotation())
|
|
7695
|
+
shapeDelta.rot(-this.getShapePageTransform(parent).rotation())
|
|
7726
7696
|
}
|
|
7727
7697
|
|
|
7728
7698
|
shapeDelta.add(shape) // add the shape's x and y to the delta
|
|
@@ -7772,10 +7742,11 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
7772
7742
|
|
|
7773
7743
|
const maxWidth = commonBounds.width
|
|
7774
7744
|
|
|
7775
|
-
// sort the shape clusters by width and then height, descending
|
|
7745
|
+
// sort the shape clusters by width and then height, descending: potpack fills each row's
|
|
7746
|
+
// right-hand space with the shapes that follow, so they must be no taller than the row
|
|
7776
7747
|
shapeClustersToPack
|
|
7777
|
-
.sort((a, b) =>
|
|
7778
|
-
.sort((a, b) =>
|
|
7748
|
+
.sort((a, b) => b.pageBounds.width - a.pageBounds.width)
|
|
7749
|
+
.sort((a, b) => b.pageBounds.height - a.pageBounds.height)
|
|
7779
7750
|
|
|
7780
7751
|
// Start with is (sort of) the square of the area
|
|
7781
7752
|
const startWidth = Math.max(Math.ceil(Math.sqrt(area / 0.95)), maxWidth)
|
|
@@ -7845,8 +7816,7 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
7845
7816
|
|
|
7846
7817
|
const parent = this.getShapeParent(shape)
|
|
7847
7818
|
if (parent) {
|
|
7848
|
-
|
|
7849
|
-
if (parentTransform) shapeDelta.rot(-parentTransform.rotation())
|
|
7819
|
+
shapeDelta.rot(-this.getShapeParentTransform(shape).rotation())
|
|
7850
7820
|
}
|
|
7851
7821
|
|
|
7852
7822
|
shapeDelta.add(shape)
|
|
@@ -7936,8 +7906,7 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
7936
7906
|
// todo: ensure that the parent isn't being aligned together with its children
|
|
7937
7907
|
const parent = this.getShapeParent(shape)
|
|
7938
7908
|
if (parent) {
|
|
7939
|
-
|
|
7940
|
-
if (parentTransform) shapeDelta.rot(-parentTransform.rotation())
|
|
7909
|
+
shapeDelta.rot(-this.getShapePageTransform(parent).rotation())
|
|
7941
7910
|
}
|
|
7942
7911
|
|
|
7943
7912
|
shapeDelta.add(shape) // add the shape's x and y to the delta
|
|
@@ -8037,8 +8006,7 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
8037
8006
|
// todo: ensure that the parent isn't being aligned together with its children
|
|
8038
8007
|
const parent = this.getShapeParent(shape)
|
|
8039
8008
|
if (parent) {
|
|
8040
|
-
|
|
8041
|
-
if (parentTransform) shapeDelta.rot(-parentTransform.rotation())
|
|
8009
|
+
shapeDelta.rot(-this.getShapePageTransform(parent).rotation())
|
|
8042
8010
|
}
|
|
8043
8011
|
|
|
8044
8012
|
shapeDelta.add(shape) // add the shape's x and y to the delta
|
|
@@ -8106,8 +8074,7 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
8106
8074
|
for (const shape of shapes) {
|
|
8107
8075
|
// First translate
|
|
8108
8076
|
const shapeLocalOffset = localOffset.clone()
|
|
8109
|
-
|
|
8110
|
-
if (parentTransform) shapeLocalOffset.rot(-parentTransform.rotation())
|
|
8077
|
+
shapeLocalOffset.rot(-this.getShapeParentTransform(shape).rotation())
|
|
8111
8078
|
shapeLocalOffset.add(shape)
|
|
8112
8079
|
const changes = this.getChangesToTranslateShape(shape, shapeLocalOffset)
|
|
8113
8080
|
this.updateShape(changes)
|
|
@@ -8178,8 +8145,7 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
8178
8145
|
for (const shape of shapes) {
|
|
8179
8146
|
// First translate
|
|
8180
8147
|
const shapeLocalOffset = localOffset.clone()
|
|
8181
|
-
|
|
8182
|
-
if (parentTransform) shapeLocalOffset.rot(-parentTransform.rotation())
|
|
8148
|
+
shapeLocalOffset.rot(-this.getShapeParentTransform(shape).rotation())
|
|
8183
8149
|
shapeLocalOffset.add(shape)
|
|
8184
8150
|
const changes = this.getChangesToTranslateShape(shape, shapeLocalOffset)
|
|
8185
8151
|
this.updateShape(changes)
|
|
@@ -8243,12 +8209,9 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
8243
8209
|
const pageTransform = opts.initialPageTransform
|
|
8244
8210
|
? Mat.Cast(opts.initialPageTransform)
|
|
8245
8211
|
: this.getShapePageTransform(id)
|
|
8246
|
-
if (!pageTransform) return null
|
|
8247
8212
|
|
|
8248
8213
|
const pageRotation = pageTransform.rotation()
|
|
8249
8214
|
|
|
8250
|
-
if (pageRotation == null) return null
|
|
8251
|
-
|
|
8252
8215
|
const scaleAxisRotation = opts.scaleAxisRotation ?? pageRotation
|
|
8253
8216
|
|
|
8254
8217
|
const initialBounds = opts.initialBounds ?? this.getShapeGeometry(id).bounds
|
|
@@ -8485,7 +8448,7 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
8485
8448
|
)
|
|
8486
8449
|
|
|
8487
8450
|
// now calculate how far away the shape is from where it needs to be
|
|
8488
|
-
const pageTransform = this.getShapePageTransform(id)
|
|
8451
|
+
const pageTransform = this.getShapePageTransform(id)
|
|
8489
8452
|
// We need to use the local bounds center transformed to page space, not the axis-aligned
|
|
8490
8453
|
// page bounds center. This is because the page bounds are axis-aligned and their center
|
|
8491
8454
|
// changes when the rotation changes, but we want to use the same reference point as
|
|
@@ -8493,7 +8456,6 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
8493
8456
|
const currentLocalBounds = this.getShapeGeometry(id).bounds
|
|
8494
8457
|
const currentPageCenter = Mat.applyToPoint(pageTransform, currentLocalBounds.center)
|
|
8495
8458
|
const shapePageTransformOrigin = pageTransform.point()
|
|
8496
|
-
if (!currentPageCenter || !shapePageTransformOrigin) return this
|
|
8497
8459
|
const pageDelta = Vec.Sub(postScaleShapePageCenter, currentPageCenter)
|
|
8498
8460
|
|
|
8499
8461
|
// and finally figure out what the shape's new position should be
|
|
@@ -8881,10 +8843,10 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
8881
8843
|
|
|
8882
8844
|
updates.push({
|
|
8883
8845
|
...end,
|
|
8884
|
-
x: start.x
|
|
8885
|
-
y: start.y
|
|
8886
|
-
opacity: start.opacity
|
|
8887
|
-
rotation: start.rotation
|
|
8846
|
+
x: lerp(start.x, end.x, t),
|
|
8847
|
+
y: lerp(start.y, end.y, t),
|
|
8848
|
+
opacity: lerp(start.opacity, end.opacity, t),
|
|
8849
|
+
rotation: lerp(start.rotation, end.rotation, t),
|
|
8888
8850
|
props: this.getShapeUtil(end).getInterpolatedProps?.(start, end, t) ?? end.props,
|
|
8889
8851
|
})
|
|
8890
8852
|
}
|
|
@@ -9100,7 +9062,7 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
9100
9062
|
* @public
|
|
9101
9063
|
*/
|
|
9102
9064
|
updateShapes<T extends TLShape>(partials: (TLShapePartial<T> | null | undefined)[]) {
|
|
9103
|
-
const compactedPartials: TLShapePartial<T>[] =
|
|
9065
|
+
const compactedPartials: TLShapePartial<T>[] = []
|
|
9104
9066
|
|
|
9105
9067
|
for (let i = 0, n = partials.length; i < n; i++) {
|
|
9106
9068
|
const partial = partials[i]
|
|
@@ -9246,26 +9208,25 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
9246
9208
|
/* --------------------- Styles --------------------- */
|
|
9247
9209
|
|
|
9248
9210
|
/**
|
|
9249
|
-
*
|
|
9211
|
+
* Groups have no styles of their own: a style read or write on a selection applies to the
|
|
9212
|
+
* non-group shapes beneath each group, however deeply nested. Returns those shapes.
|
|
9250
9213
|
*
|
|
9251
9214
|
* @internal
|
|
9252
9215
|
*/
|
|
9253
|
-
private
|
|
9254
|
-
|
|
9255
|
-
|
|
9256
|
-
|
|
9257
|
-
|
|
9258
|
-
|
|
9259
|
-
|
|
9260
|
-
|
|
9261
|
-
|
|
9262
|
-
|
|
9263
|
-
}
|
|
9264
|
-
} else {
|
|
9265
|
-
for (const [style, propKey] of this.styleProps[shape.type]) {
|
|
9266
|
-
sharedStyleMap.applyValue(style, getOwnProperty(shape.props, propKey))
|
|
9216
|
+
private _getStyleableShapes(shapes: TLShape[]): TLShape[] {
|
|
9217
|
+
const result: TLShape[] = []
|
|
9218
|
+
const visit = (shape: TLShape) => {
|
|
9219
|
+
if (this.isShapeOfType(shape, 'group')) {
|
|
9220
|
+
for (const childId of this.getSortedChildIdsForParent(shape.id)) {
|
|
9221
|
+
const child = this.getShape(childId)
|
|
9222
|
+
if (child) visit(child)
|
|
9223
|
+
}
|
|
9224
|
+
} else {
|
|
9225
|
+
result.push(shape)
|
|
9267
9226
|
}
|
|
9268
9227
|
}
|
|
9228
|
+
for (const shape of shapes) visit(shape)
|
|
9229
|
+
return result
|
|
9269
9230
|
}
|
|
9270
9231
|
|
|
9271
9232
|
/**
|
|
@@ -9275,11 +9236,11 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
9275
9236
|
*/
|
|
9276
9237
|
@computed
|
|
9277
9238
|
private _getSelectionSharedStyles(): ReadonlySharedStyleMap {
|
|
9278
|
-
const selectedShapes = this.getSelectedShapes()
|
|
9279
|
-
|
|
9280
9239
|
const sharedStyles = new SharedStyleMap()
|
|
9281
|
-
for (const
|
|
9282
|
-
|
|
9240
|
+
for (const shape of this._getStyleableShapes(this.getSelectedShapes())) {
|
|
9241
|
+
for (const [style, propKey] of this.styleProps[shape.type]) {
|
|
9242
|
+
sharedStyles.applyValue(style, getOwnProperty(shape.props, propKey))
|
|
9243
|
+
}
|
|
9283
9244
|
}
|
|
9284
9245
|
|
|
9285
9246
|
return sharedStyles
|
|
@@ -9337,18 +9298,8 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
9337
9298
|
if (!currentTool) return styles
|
|
9338
9299
|
|
|
9339
9300
|
if (currentTool.shapeType) {
|
|
9340
|
-
|
|
9341
|
-
|
|
9342
|
-
!(this.getShapeUtil('frame')!.options as any).showColors
|
|
9343
|
-
) {
|
|
9344
|
-
for (const style of this.styleProps[currentTool.shapeType].keys()) {
|
|
9345
|
-
if (style.id === 'tldraw:color') continue
|
|
9346
|
-
styles.applyValue(style, this.getStyleForNextShape(style))
|
|
9347
|
-
}
|
|
9348
|
-
} else {
|
|
9349
|
-
for (const style of this.styleProps[currentTool.shapeType].keys()) {
|
|
9350
|
-
styles.applyValue(style, this.getStyleForNextShape(style))
|
|
9351
|
-
}
|
|
9301
|
+
for (const style of this.styleProps[currentTool.shapeType].keys()) {
|
|
9302
|
+
styles.applyValue(style, this.getStyleForNextShape(style))
|
|
9352
9303
|
}
|
|
9353
9304
|
}
|
|
9354
9305
|
|
|
@@ -9364,27 +9315,8 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
9364
9315
|
*/
|
|
9365
9316
|
@computed getSharedOpacity(): SharedStyle<number> {
|
|
9366
9317
|
if (this.isIn('select') && this.getSelectedShapeIds().length > 0) {
|
|
9367
|
-
const shapesToCheck: TLShape[] = []
|
|
9368
|
-
const addShape = (shapeId: TLShapeId) => {
|
|
9369
|
-
const shape = this.getShape(shapeId)
|
|
9370
|
-
if (!shape) return
|
|
9371
|
-
// For groups, ignore the opacity of the group shape and instead include
|
|
9372
|
-
// the opacity of the group's children. These are the shapes that would have
|
|
9373
|
-
// their opacity changed if the user called `setOpacity` on the current selection.
|
|
9374
|
-
if (this.isShapeOfType(shape, 'group')) {
|
|
9375
|
-
for (const childId of this.getSortedChildIdsForParent(shape.id)) {
|
|
9376
|
-
addShape(childId)
|
|
9377
|
-
}
|
|
9378
|
-
} else {
|
|
9379
|
-
shapesToCheck.push(shape)
|
|
9380
|
-
}
|
|
9381
|
-
}
|
|
9382
|
-
for (const shapeId of this.getSelectedShapeIds()) {
|
|
9383
|
-
addShape(shapeId)
|
|
9384
|
-
}
|
|
9385
|
-
|
|
9386
9318
|
let opacity: number | null = null
|
|
9387
|
-
for (const shape of
|
|
9319
|
+
for (const shape of this._getStyleableShapes(this.getSelectedShapes())) {
|
|
9388
9320
|
if (opacity === null) {
|
|
9389
9321
|
opacity = shape.opacity
|
|
9390
9322
|
} else if (opacity !== shape.opacity) {
|
|
@@ -9427,33 +9359,12 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
9427
9359
|
const selectedShapes = this.getSelectedShapes()
|
|
9428
9360
|
|
|
9429
9361
|
if (selectedShapes.length > 0) {
|
|
9430
|
-
const shapesToUpdate: TLShape[] = []
|
|
9431
|
-
|
|
9432
|
-
// We can have many deep levels of grouped shape
|
|
9433
|
-
// Making a recursive function to look through all the levels
|
|
9434
|
-
const addShapeById = (shape: TLShape) => {
|
|
9435
|
-
if (this.isShapeOfType(shape, 'group')) {
|
|
9436
|
-
const childIds = this.getSortedChildIdsForParent(shape)
|
|
9437
|
-
for (const childId of childIds) {
|
|
9438
|
-
addShapeById(this.getShape(childId)!)
|
|
9439
|
-
}
|
|
9440
|
-
} else {
|
|
9441
|
-
shapesToUpdate.push(shape)
|
|
9442
|
-
}
|
|
9443
|
-
}
|
|
9444
|
-
|
|
9445
|
-
for (const id of selectedShapes) {
|
|
9446
|
-
addShapeById(id)
|
|
9447
|
-
}
|
|
9448
|
-
|
|
9449
9362
|
this.updateShapes(
|
|
9450
|
-
|
|
9451
|
-
|
|
9452
|
-
|
|
9453
|
-
|
|
9454
|
-
|
|
9455
|
-
}
|
|
9456
|
-
})
|
|
9363
|
+
this._getStyleableShapes(selectedShapes).map((shape) => ({
|
|
9364
|
+
id: shape.id,
|
|
9365
|
+
type: shape.type,
|
|
9366
|
+
opacity,
|
|
9367
|
+
}))
|
|
9457
9368
|
)
|
|
9458
9369
|
}
|
|
9459
9370
|
|
|
@@ -9507,43 +9418,19 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
9507
9418
|
const selectedShapes = this.getSelectedShapes()
|
|
9508
9419
|
|
|
9509
9420
|
if (selectedShapes.length > 0) {
|
|
9510
|
-
const updates:
|
|
9511
|
-
|
|
9512
|
-
|
|
9513
|
-
|
|
9514
|
-
|
|
9515
|
-
|
|
9516
|
-
|
|
9517
|
-
|
|
9518
|
-
|
|
9519
|
-
if (this.isShapeOfType(shape, 'group')) {
|
|
9520
|
-
const childIds = this.getSortedChildIdsForParent(shape.id)
|
|
9521
|
-
for (const childId of childIds) {
|
|
9522
|
-
addShapeById(this.getShape(childId)!)
|
|
9523
|
-
}
|
|
9524
|
-
} else {
|
|
9525
|
-
const util = this.getShapeUtil(shape)
|
|
9526
|
-
const stylePropKey = this.styleProps[shape.type].get(style)
|
|
9527
|
-
if (stylePropKey) {
|
|
9528
|
-
const shapePartial: TLShapePartial = {
|
|
9529
|
-
id: shape.id,
|
|
9530
|
-
type: shape.type,
|
|
9531
|
-
props: { [stylePropKey]: value },
|
|
9532
|
-
}
|
|
9533
|
-
updates.push({
|
|
9534
|
-
util,
|
|
9535
|
-
originalShape: shape,
|
|
9536
|
-
updatePartial: shapePartial,
|
|
9537
|
-
})
|
|
9538
|
-
}
|
|
9421
|
+
const updates: TLShapePartial[] = []
|
|
9422
|
+
for (const shape of this._getStyleableShapes(selectedShapes)) {
|
|
9423
|
+
const stylePropKey = this.styleProps[shape.type].get(style)
|
|
9424
|
+
if (stylePropKey) {
|
|
9425
|
+
updates.push({
|
|
9426
|
+
id: shape.id,
|
|
9427
|
+
type: shape.type,
|
|
9428
|
+
props: { [stylePropKey]: value },
|
|
9429
|
+
})
|
|
9539
9430
|
}
|
|
9540
9431
|
}
|
|
9541
9432
|
|
|
9542
|
-
|
|
9543
|
-
addShapeById(shape)
|
|
9544
|
-
}
|
|
9545
|
-
|
|
9546
|
-
this.updateShapes(updates.map(({ updatePartial }) => updatePartial))
|
|
9433
|
+
this.updateShapes(updates)
|
|
9547
9434
|
}
|
|
9548
9435
|
|
|
9549
9436
|
return this
|
|
@@ -10075,7 +9962,6 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
10075
9962
|
if (shapeIdMap.has(newShape.parentId)) {
|
|
10076
9963
|
newShape.parentId = shapeIdMap.get(oldShape.parentId)!
|
|
10077
9964
|
} else {
|
|
10078
|
-
rootShapeIds.push(newShape.id)
|
|
10079
9965
|
// newShape.parentId = pasteParentId
|
|
10080
9966
|
newShape.index = index
|
|
10081
9967
|
index = getIndexAbove(index)
|
|
@@ -10118,10 +10004,14 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
10118
10004
|
(asset.type === 'video' && asset.props.src?.startsWith('data:video'))
|
|
10119
10005
|
) {
|
|
10120
10006
|
// it's src is a base64 image or video; we need to create a new asset without the src,
|
|
10121
|
-
// then create a new asset from the original src. So we
|
|
10122
|
-
//
|
|
10123
|
-
|
|
10124
|
-
asset
|
|
10007
|
+
// then create a new asset from the original src. So we keep the original asset for the
|
|
10008
|
+
// upload and create a copy with its src removed. Copy rather than mutate: when no
|
|
10009
|
+
// migration applies, migrateStoreSnapshot hands back the caller's own record objects.
|
|
10010
|
+
assetsToUpdate.push(asset as TLImageAsset | TLVideoAsset)
|
|
10011
|
+
const assetWithoutSrc = structuredClone(asset as TLImageAsset | TLVideoAsset)
|
|
10012
|
+
assetWithoutSrc.props.src = null
|
|
10013
|
+
assetsToCreate.push(assetWithoutSrc)
|
|
10014
|
+
continue
|
|
10125
10015
|
}
|
|
10126
10016
|
|
|
10127
10017
|
// Add the asset to the list of assets to create
|
|
@@ -11116,7 +11006,10 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
11116
11006
|
if (info.name === 'cancel' || info.name === 'complete') {
|
|
11117
11007
|
this.inputs.setIsDragging(false)
|
|
11118
11008
|
|
|
11119
|
-
|
|
11009
|
+
// A pan owned by a held spacebar outlives the cancelled interaction;
|
|
11010
|
+
// key_up ends it. Otherwise Escape mid-pan stops the camera until
|
|
11011
|
+
// the user releases and re-presses Space (#10446).
|
|
11012
|
+
if (this.inputs.getIsPanning() && !this.inputs.keys.has('Space')) {
|
|
11120
11013
|
this.inputs.setIsPanning(false)
|
|
11121
11014
|
this.inputs.setIsSpacebarPanning(false)
|
|
11122
11015
|
this.setCursor({ type: this._prevCursor, rotation: 0 })
|
|
@@ -11870,10 +11763,10 @@ function withIsolatedShapes<T>(
|
|
|
11870
11763
|
|
|
11871
11764
|
function getCameraFitXFitY(editor: Editor, cameraOptions: TLCameraOptions) {
|
|
11872
11765
|
if (!cameraOptions.constraints) throw Error('Should have constraints here')
|
|
11873
|
-
const {
|
|
11874
|
-
padding: { x: px, y: py },
|
|
11875
|
-
} = cameraOptions.constraints
|
|
11876
11766
|
const vsb = editor.getViewportScreenBounds()
|
|
11767
|
+
// Clamp padding to half the viewport size on either dimension, as getConstrainedCamera does
|
|
11768
|
+
const px = Math.min(cameraOptions.constraints.padding.x, vsb.w / 2)
|
|
11769
|
+
const py = Math.min(cameraOptions.constraints.padding.y, vsb.h / 2)
|
|
11877
11770
|
const bounds = Box.From(cameraOptions.constraints.bounds)
|
|
11878
11771
|
const zx = (vsb.w - px * 2) / bounds.w
|
|
11879
11772
|
const zy = (vsb.h - py * 2) / bounds.h
|