@tldraw/editor 5.4.2 → 5.5.0-canary.003bf5951274
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 +37 -14
- package/dist-cjs/index.js +1 -1
- package/dist-cjs/index.js.map +2 -2
- package/dist-cjs/lib/TldrawEditor.js +16 -12
- package/dist-cjs/lib/TldrawEditor.js.map +2 -2
- package/dist-cjs/lib/components/MenuClickCapture.js +42 -3
- package/dist-cjs/lib/components/MenuClickCapture.js.map +2 -2
- package/dist-cjs/lib/components/default-components/DefaultCanvas.js +5 -1
- package/dist-cjs/lib/components/default-components/DefaultCanvas.js.map +2 -2
- package/dist-cjs/lib/components/default-components/DefaultErrorFallback.js +2 -5
- package/dist-cjs/lib/components/default-components/DefaultErrorFallback.js.map +3 -3
- package/dist-cjs/lib/components/default-components/DefaultSpinner.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 +147 -157
- package/dist-cjs/lib/editor/Editor.js.map +3 -3
- package/dist-cjs/lib/editor/derivations/bindingsIndex.js +2 -0
- package/dist-cjs/lib/editor/derivations/bindingsIndex.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/TextManager/TextManager.js +4 -4
- 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/shapes/group/GroupShapeUtil.js +14 -6
- package/dist-cjs/lib/editor/shapes/group/GroupShapeUtil.js.map +3 -3
- package/dist-cjs/lib/editor/types/external-content.js.map +1 -1
- package/dist-cjs/lib/exports/ExportDelay.js +10 -2
- package/dist-cjs/lib/exports/ExportDelay.js.map +2 -2
- package/dist-cjs/lib/exports/FontEmbedder.js +25 -11
- package/dist-cjs/lib/exports/FontEmbedder.js.map +2 -2
- package/dist-cjs/lib/exports/StyleEmbedder.js +9 -4
- package/dist-cjs/lib/exports/StyleEmbedder.js.map +2 -2
- package/dist-cjs/lib/exports/fetchCache.js +3 -0
- package/dist-cjs/lib/exports/fetchCache.js.map +2 -2
- package/dist-cjs/lib/exports/getSvgAsImage.js +10 -4
- package/dist-cjs/lib/exports/getSvgAsImage.js.map +2 -2
- package/dist-cjs/lib/exports/getSvgJsx.js +18 -9
- package/dist-cjs/lib/exports/getSvgJsx.js.map +2 -2
- package/dist-cjs/lib/exports/parseCss.js +1 -1
- package/dist-cjs/lib/exports/parseCss.js.map +1 -1
- package/dist-cjs/lib/hooks/EditorComponentsContext.js.map +2 -2
- package/dist-cjs/lib/hooks/useCanvasEvents.js +24 -0
- package/dist-cjs/lib/hooks/useCanvasEvents.js.map +2 -2
- package/dist-cjs/lib/hooks/useIdentity.js +6 -2
- package/dist-cjs/lib/hooks/useIdentity.js.map +3 -3
- package/dist-cjs/lib/license/LicenseManager.js +9 -1
- package/dist-cjs/lib/license/LicenseManager.js.map +2 -2
- package/dist-cjs/lib/license/LicenseProvider.js +12 -4
- package/dist-cjs/lib/license/LicenseProvider.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 +8 -5
- 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/utils/debug-flags.js +0 -6
- package/dist-cjs/lib/utils/debug-flags.js.map +2 -2
- package/dist-cjs/lib/utils/deepLinks.js +2 -1
- package/dist-cjs/lib/utils/deepLinks.js.map +2 -2
- package/dist-cjs/lib/utils/sync/LocalIndexedDb.js +22 -24
- package/dist-cjs/lib/utils/sync/LocalIndexedDb.js.map +2 -2
- package/dist-cjs/lib/utils/sync/TLLocalSyncClient.js +81 -40
- 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 +37 -14
- package/dist-esm/index.mjs +4 -2
- package/dist-esm/index.mjs.map +2 -2
- package/dist-esm/lib/TldrawEditor.mjs +17 -13
- package/dist-esm/lib/TldrawEditor.mjs.map +2 -2
- package/dist-esm/lib/components/MenuClickCapture.mjs +42 -3
- package/dist-esm/lib/components/MenuClickCapture.mjs.map +2 -2
- package/dist-esm/lib/components/default-components/DefaultCanvas.mjs +5 -1
- package/dist-esm/lib/components/default-components/DefaultCanvas.mjs.map +2 -2
- package/dist-esm/lib/components/default-components/DefaultErrorFallback.mjs +2 -5
- package/dist-esm/lib/components/default-components/DefaultErrorFallback.mjs.map +3 -3
- package/dist-esm/lib/components/default-components/DefaultSpinner.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 +148 -157
- package/dist-esm/lib/editor/Editor.mjs.map +3 -3
- package/dist-esm/lib/editor/derivations/bindingsIndex.mjs +2 -0
- package/dist-esm/lib/editor/derivations/bindingsIndex.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/TextManager/TextManager.mjs +4 -4
- 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/shapes/group/GroupShapeUtil.mjs +14 -6
- package/dist-esm/lib/editor/shapes/group/GroupShapeUtil.mjs.map +3 -3
- package/dist-esm/lib/exports/ExportDelay.mjs +11 -3
- package/dist-esm/lib/exports/ExportDelay.mjs.map +2 -2
- package/dist-esm/lib/exports/FontEmbedder.mjs +25 -11
- package/dist-esm/lib/exports/FontEmbedder.mjs.map +2 -2
- package/dist-esm/lib/exports/StyleEmbedder.mjs +9 -4
- package/dist-esm/lib/exports/StyleEmbedder.mjs.map +2 -2
- package/dist-esm/lib/exports/fetchCache.mjs +3 -0
- package/dist-esm/lib/exports/fetchCache.mjs.map +2 -2
- package/dist-esm/lib/exports/getSvgAsImage.mjs +10 -4
- package/dist-esm/lib/exports/getSvgAsImage.mjs.map +2 -2
- package/dist-esm/lib/exports/getSvgJsx.mjs +18 -9
- package/dist-esm/lib/exports/getSvgJsx.mjs.map +2 -2
- package/dist-esm/lib/exports/parseCss.mjs +1 -1
- package/dist-esm/lib/exports/parseCss.mjs.map +1 -1
- package/dist-esm/lib/hooks/EditorComponentsContext.mjs.map +2 -2
- package/dist-esm/lib/hooks/useCanvasEvents.mjs +24 -0
- package/dist-esm/lib/hooks/useCanvasEvents.mjs.map +2 -2
- package/dist-esm/lib/hooks/useIdentity.mjs +7 -3
- package/dist-esm/lib/hooks/useIdentity.mjs.map +3 -3
- package/dist-esm/lib/license/LicenseManager.mjs +9 -1
- package/dist-esm/lib/license/LicenseManager.mjs.map +2 -2
- package/dist-esm/lib/license/LicenseProvider.mjs +13 -5
- package/dist-esm/lib/license/LicenseProvider.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 +12 -6
- 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/utils/debug-flags.mjs +0 -6
- package/dist-esm/lib/utils/debug-flags.mjs.map +2 -2
- package/dist-esm/lib/utils/deepLinks.mjs +2 -1
- package/dist-esm/lib/utils/deepLinks.mjs.map +2 -2
- package/dist-esm/lib/utils/sync/LocalIndexedDb.mjs +22 -24
- package/dist-esm/lib/utils/sync/LocalIndexedDb.mjs.map +2 -2
- package/dist-esm/lib/utils/sync/TLLocalSyncClient.mjs +81 -40
- 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 +9 -1
- package/package.json +8 -11
- package/src/index.ts +5 -1
- package/src/lib/TldrawEditor.tsx +35 -21
- package/src/lib/components/MenuClickCapture.tsx +55 -4
- package/src/lib/components/default-components/DefaultCanvas.tsx +7 -1
- package/src/lib/components/default-components/DefaultErrorFallback.tsx +13 -5
- package/src/lib/components/default-components/DefaultSpinner.tsx +4 -1
- package/src/lib/constants.ts +8 -0
- package/src/lib/editor/Editor.test.ts +20 -0
- package/src/lib/editor/Editor.ts +228 -262
- package/src/lib/editor/derivations/bindingsIndex.ts +3 -0
- 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/TextManager/TextManager.test.ts +74 -0
- package/src/lib/editor/managers/TextManager/TextManager.ts +6 -5
- 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/shapes/group/GroupShapeUtil.tsx +23 -8
- package/src/lib/editor/types/external-content.ts +5 -0
- package/src/lib/exports/ExportDelay.tsx +16 -4
- package/src/lib/exports/FontEmbedder.test.ts +99 -0
- package/src/lib/exports/FontEmbedder.ts +32 -11
- package/src/lib/exports/StyleEmbedder.ts +13 -4
- package/src/lib/exports/fetchCache.test.ts +22 -0
- package/src/lib/exports/fetchCache.ts +4 -0
- package/src/lib/exports/getSvgAsImage.ts +13 -5
- package/src/lib/exports/getSvgJsx.test.ts +43 -1
- package/src/lib/exports/getSvgJsx.tsx +28 -16
- package/src/lib/exports/parseCss.test.ts +4 -0
- package/src/lib/exports/parseCss.ts +2 -2
- package/src/lib/hooks/EditorComponentsContext.tsx +2 -1
- package/src/lib/hooks/useCanvasEvents.ts +42 -0
- package/src/lib/hooks/useIdentity.tsx +12 -1
- package/src/lib/license/LicenseManager.ts +13 -1
- package/src/lib/license/LicenseProvider.test.tsx +76 -0
- package/src/lib/license/LicenseProvider.tsx +19 -5
- package/src/lib/primitives/geometry/Arc2d.test.ts +9 -0
- package/src/lib/primitives/geometry/Arc2d.ts +1 -1
- package/src/lib/primitives/geometry/CubicBezier2d.test.ts +13 -0
- package/src/lib/primitives/geometry/CubicBezier2d.ts +1 -1
- package/src/lib/primitives/geometry/Edge2d.test.ts +21 -3
- package/src/lib/primitives/geometry/Edge2d.ts +1 -1
- package/src/lib/primitives/geometry/Group2d.test.ts +26 -5
- package/src/lib/primitives/geometry/Group2d.ts +18 -7
- package/src/lib/primitives/geometry/Point2d.test.ts +14 -3
- package/src/lib/primitives/geometry/Point2d.ts +1 -1
- package/src/lib/test/preserveFocus.test.tsx +81 -0
- package/src/lib/utils/debug-flags.ts +0 -6
- package/src/lib/utils/deepLinks.test.ts +15 -0
- package/src/lib/utils/deepLinks.ts +8 -3
- package/src/lib/utils/sync/LocalIndexedDb.test.ts +95 -0
- package/src/lib/utils/sync/LocalIndexedDb.ts +31 -27
- package/src/lib/utils/sync/TLLocalSyncClient.test.ts +257 -1
- package/src/lib/utils/sync/TLLocalSyncClient.ts +112 -54
- package/src/version.ts +3 -3
- package/dist-cjs/lib/primitives/geometry/geometry.bench.js +0 -133
- package/dist-cjs/lib/primitives/geometry/geometry.bench.js.map +0 -7
- package/dist-esm/lib/primitives/geometry/geometry.bench.mjs +0 -132
- package/dist-esm/lib/primitives/geometry/geometry.bench.mjs.map +0 -7
- package/src/lib/primitives/geometry/geometry.bench.ts +0 -179
|
@@ -75,6 +75,7 @@ import {
|
|
|
75
75
|
import {
|
|
76
76
|
DEFAULT_ANIMATION_OPTIONS,
|
|
77
77
|
DEFAULT_CAMERA_OPTIONS,
|
|
78
|
+
FRAME_MS_60HZ,
|
|
78
79
|
INTERNAL_POINTER_IDS,
|
|
79
80
|
LEFT_MOUSE_BUTTON,
|
|
80
81
|
MIDDLE_MOUSE_BUTTON,
|
|
@@ -135,6 +136,10 @@ import { UserPreferencesManager } from "./managers/UserPreferencesManager/UserPr
|
|
|
135
136
|
import { OverlayManager } from "./overlays/OverlayManager.mjs";
|
|
136
137
|
import { RootState } from "./tools/RootState.mjs";
|
|
137
138
|
const RENDERING_SHAPES_SORT_CACHE_THRESHOLD = 100;
|
|
139
|
+
const AXIS = {
|
|
140
|
+
horizontal: { val: "x", min: "minX", max: "maxX", dim: "width" },
|
|
141
|
+
vertical: { val: "y", min: "minY", max: "maxY", dim: "height" }
|
|
142
|
+
};
|
|
138
143
|
class Editor extends EventEmitter {
|
|
139
144
|
id = uniqueId();
|
|
140
145
|
constructor({
|
|
@@ -461,7 +466,7 @@ class Editor extends EventEmitter {
|
|
|
461
466
|
}
|
|
462
467
|
}
|
|
463
468
|
if (deleteBindingIds.length) {
|
|
464
|
-
this.
|
|
469
|
+
this.store.remove(deleteBindingIds);
|
|
465
470
|
}
|
|
466
471
|
}
|
|
467
472
|
},
|
|
@@ -1421,6 +1426,7 @@ class Editor extends EventEmitter {
|
|
|
1421
1426
|
* @public
|
|
1422
1427
|
**/
|
|
1423
1428
|
updateDocumentSettings(settings) {
|
|
1429
|
+
if (this.getIsReadonly()) return this;
|
|
1424
1430
|
this.run(
|
|
1425
1431
|
() => {
|
|
1426
1432
|
this.store.put([{ ...this.getDocumentSettings(), ...settings }]);
|
|
@@ -1580,7 +1586,7 @@ class Editor extends EventEmitter {
|
|
|
1580
1586
|
* @public
|
|
1581
1587
|
*/
|
|
1582
1588
|
select(...shapes) {
|
|
1583
|
-
const ids =
|
|
1589
|
+
const ids = toShapeIds(shapes);
|
|
1584
1590
|
this.setSelectedShapes(ids);
|
|
1585
1591
|
return this;
|
|
1586
1592
|
}
|
|
@@ -1595,7 +1601,7 @@ class Editor extends EventEmitter {
|
|
|
1595
1601
|
* @public
|
|
1596
1602
|
*/
|
|
1597
1603
|
deselect(...shapes) {
|
|
1598
|
-
const ids =
|
|
1604
|
+
const ids = toShapeIds(shapes);
|
|
1599
1605
|
const selectedShapeIds = this.getSelectedShapeIds();
|
|
1600
1606
|
if (selectedShapeIds.length > 0 && ids.length > 0) {
|
|
1601
1607
|
this.setSelectedShapes(selectedShapeIds.filter((id) => !ids.includes(id)));
|
|
@@ -1633,7 +1639,7 @@ class Editor extends EventEmitter {
|
|
|
1633
1639
|
}
|
|
1634
1640
|
const ids = this.getSortedChildIdsForParent(parentToSelectWithinId);
|
|
1635
1641
|
if (ids.length <= 0) return this;
|
|
1636
|
-
this.setSelectedShapes(this.
|
|
1642
|
+
this.setSelectedShapes(ids.filter((id) => !this.getShape(id)?.isLocked));
|
|
1637
1643
|
return this;
|
|
1638
1644
|
}
|
|
1639
1645
|
/**
|
|
@@ -1650,14 +1656,18 @@ class Editor extends EventEmitter {
|
|
|
1650
1656
|
const selectedShapeIds = this.getSelectedShapeIds();
|
|
1651
1657
|
const firstParentId = selectedShapeIds[0] ? this.getShape(selectedShapeIds[0])?.parentId : null;
|
|
1652
1658
|
const isSelectedWithinContainer = firstParentId && selectedShapeIds.every((shapeId) => this.getShape(shapeId)?.parentId === firstParentId) && !isPageId(firstParentId);
|
|
1653
|
-
const filteredShapes =
|
|
1654
|
-
|
|
1659
|
+
const filteredShapes = this.getCurrentPageShapes().filter(
|
|
1660
|
+
(shape2) => !this.isShapeOrAncestorLocked(shape2) && (isSelectedWithinContainer ? shape2.parentId === firstParentId : isPageId(shape2.parentId))
|
|
1661
|
+
);
|
|
1662
|
+
const readingOrderShapes = this._getShapesInReadingOrder(filteredShapes);
|
|
1655
1663
|
const currentShapeId = selectedShapeIds.length === 1 ? selectedShapeIds[0] : readingOrderShapes.find((shape2) => selectedShapeIds.includes(shape2.id))?.id;
|
|
1656
1664
|
let adjacentShapeId;
|
|
1657
1665
|
if (direction === "next" || direction === "prev") {
|
|
1658
1666
|
const shapeIds = readingOrderShapes.map((shape2) => shape2.id);
|
|
1667
|
+
if (shapeIds.length === 0) return;
|
|
1659
1668
|
const currentIndex = currentShapeId ? shapeIds.indexOf(currentShapeId) : -1;
|
|
1660
|
-
const
|
|
1669
|
+
const startIndex = currentIndex === -1 && direction === "prev" ? 0 : currentIndex;
|
|
1670
|
+
const adjacentIndex = (startIndex + (direction === "next" ? 1 : -1) + shapeIds.length) % shapeIds.length;
|
|
1661
1671
|
adjacentShapeId = shapeIds[adjacentIndex];
|
|
1662
1672
|
} else {
|
|
1663
1673
|
if (!currentShapeId) return;
|
|
@@ -1756,13 +1766,14 @@ class Editor extends EventEmitter {
|
|
|
1756
1766
|
});
|
|
1757
1767
|
if (shapesInDirection.length === 0) return currentShapeId;
|
|
1758
1768
|
const lowestScoringShape = minBy(shapesInDirection, ({ center }) => {
|
|
1759
|
-
const distance = Vec.
|
|
1769
|
+
const distance = Vec.Dist(currentCenter, center);
|
|
1760
1770
|
const dirProp = ["left", "right"].includes(direction) ? "x" : "y";
|
|
1761
1771
|
const directionalDistance = Math.abs(center[dirProp] - currentCenter[dirProp]);
|
|
1762
1772
|
const offProp = ["left", "right"].includes(direction) ? "y" : "x";
|
|
1763
1773
|
const offAxisDeviation = Math.abs(center[offProp] - currentCenter[offProp]);
|
|
1764
|
-
const angle =
|
|
1765
|
-
const
|
|
1774
|
+
const angle = (Vec.Angle(currentCenter, center) * (180 / Math.PI) + 360) % 360;
|
|
1775
|
+
const rawAngleDeviation = Math.abs(angle - directionToAngle[direction]);
|
|
1776
|
+
const angleDeviation = Math.min(rawAngleDeviation, 360 - rawAngleDeviation);
|
|
1766
1777
|
return distance * 1 + // Base distance
|
|
1767
1778
|
offAxisDeviation * 2 + // Heavy penalty for off-axis deviation
|
|
1768
1779
|
(distance - directionalDistance) * 1.5 + // Penalty for diagonal distance
|
|
@@ -2100,7 +2111,7 @@ class Editor extends EventEmitter {
|
|
|
2100
2111
|
* @public
|
|
2101
2112
|
*/
|
|
2102
2113
|
setHintingShapes(shapes) {
|
|
2103
|
-
const ids =
|
|
2114
|
+
const ids = toShapeIds(shapes);
|
|
2104
2115
|
this.run(
|
|
2105
2116
|
() => {
|
|
2106
2117
|
this._updateCurrentPageState({ hintingShapeIds: dedupe(ids) });
|
|
@@ -2130,7 +2141,7 @@ class Editor extends EventEmitter {
|
|
|
2130
2141
|
* @public
|
|
2131
2142
|
*/
|
|
2132
2143
|
setErasingShapes(shapes) {
|
|
2133
|
-
const ids =
|
|
2144
|
+
const ids = toShapeIds(shapes).slice();
|
|
2134
2145
|
ids.sort();
|
|
2135
2146
|
const erasingShapeIds = this.getErasingShapeIds();
|
|
2136
2147
|
this.run(
|
|
@@ -2537,14 +2548,16 @@ class Editor extends EventEmitter {
|
|
|
2537
2548
|
setCamera(point, opts) {
|
|
2538
2549
|
const { isLocked } = this._cameraOptions.__unsafe__getWithoutCapture();
|
|
2539
2550
|
if (isLocked && !opts?.force) return this;
|
|
2551
|
+
const _point = new Vec(point.x, point.y, point.z ?? this.getZoomLevel());
|
|
2552
|
+
if (!Number.isFinite(_point.x) || !Number.isFinite(_point.y) || !Number.isFinite(_point.z)) {
|
|
2553
|
+
throw Error(
|
|
2554
|
+
`Editor.setCamera: expected finite values, got (${_point.x}, ${_point.y}, ${_point.z}).`
|
|
2555
|
+
);
|
|
2556
|
+
}
|
|
2540
2557
|
this.stopCameraAnimation();
|
|
2541
2558
|
if (this.getInstanceState().followingUserId) {
|
|
2542
2559
|
this.stopFollowingUser();
|
|
2543
2560
|
}
|
|
2544
|
-
const _point = Vec.Cast(point);
|
|
2545
|
-
if (!Number.isFinite(_point.x)) _point.x = 0;
|
|
2546
|
-
if (!Number.isFinite(_point.y)) _point.y = 0;
|
|
2547
|
-
if (_point.z === void 0 || !Number.isFinite(_point.z)) point.z = this.getZoomLevel();
|
|
2548
2561
|
const camera = this.getConstrainedCamera(_point, opts);
|
|
2549
2562
|
if (opts?.animation) {
|
|
2550
2563
|
const { width, height } = this.getViewportScreenBounds();
|
|
@@ -2829,11 +2842,14 @@ class Editor extends EventEmitter {
|
|
|
2829
2842
|
_animateViewport(ms) {
|
|
2830
2843
|
if (!this._viewportAnimation) return;
|
|
2831
2844
|
this._viewportAnimation.elapsed += ms;
|
|
2832
|
-
const { elapsed, easing, duration, start, end } = this._viewportAnimation;
|
|
2845
|
+
const { elapsed, easing, duration, start, end, opts } = this._viewportAnimation;
|
|
2833
2846
|
if (elapsed > duration) {
|
|
2834
2847
|
this.off("tick", this._animateViewport);
|
|
2835
2848
|
this._viewportAnimation = null;
|
|
2836
|
-
this._setCamera(
|
|
2849
|
+
this._setCamera(
|
|
2850
|
+
new Vec(-end.x, -end.y, this.getViewportScreenBounds().width / end.width),
|
|
2851
|
+
opts
|
|
2852
|
+
);
|
|
2837
2853
|
return;
|
|
2838
2854
|
}
|
|
2839
2855
|
const remaining = duration - elapsed;
|
|
@@ -2871,7 +2887,8 @@ class Editor extends EventEmitter {
|
|
|
2871
2887
|
duration: duration / animationSpeed,
|
|
2872
2888
|
easing,
|
|
2873
2889
|
start: viewportPageBounds.clone(),
|
|
2874
|
-
end: targetViewportPage.clone()
|
|
2890
|
+
end: targetViewportPage.clone(),
|
|
2891
|
+
opts: rest
|
|
2875
2892
|
};
|
|
2876
2893
|
this.once("stop-camera-animation", () => {
|
|
2877
2894
|
this.off("tick", this._animateViewport);
|
|
@@ -2923,7 +2940,7 @@ class Editor extends EventEmitter {
|
|
|
2923
2940
|
newCx += center.x / newCz - center.x / cz;
|
|
2924
2941
|
newCy += center.y / newCz - center.y / cz;
|
|
2925
2942
|
}
|
|
2926
|
-
currentSpeed *= 1 - friction;
|
|
2943
|
+
currentSpeed *= (1 - friction) ** (elapsed / FRAME_MS_60HZ);
|
|
2927
2944
|
if (currentSpeed < speedThreshold) {
|
|
2928
2945
|
cancel();
|
|
2929
2946
|
} else {
|
|
@@ -2957,6 +2974,7 @@ class Editor extends EventEmitter {
|
|
|
2957
2974
|
}
|
|
2958
2975
|
const isOnSamePage = presence.currentPageId === this.getCurrentPageId();
|
|
2959
2976
|
if (!isOnSamePage) {
|
|
2977
|
+
this.markHistoryStoppingPoint("change-page");
|
|
2960
2978
|
this.setCurrentPage(presence.currentPageId);
|
|
2961
2979
|
}
|
|
2962
2980
|
if (opts && opts.animation && !isOnSamePage) {
|
|
@@ -3788,7 +3806,9 @@ class Editor extends EventEmitter {
|
|
|
3788
3806
|
if (ids.length <= 0) return this;
|
|
3789
3807
|
this.run(
|
|
3790
3808
|
() => {
|
|
3791
|
-
this.store.props.assets.remove?.(ids)
|
|
3809
|
+
Promise.resolve(this.store.props.assets.remove?.(ids)).catch(
|
|
3810
|
+
(err) => console.error("Error while removing assets from the asset store:", err)
|
|
3811
|
+
);
|
|
3792
3812
|
this.store.remove(ids);
|
|
3793
3813
|
},
|
|
3794
3814
|
{ history: "ignore" }
|
|
@@ -4170,7 +4190,7 @@ class Editor extends EventEmitter {
|
|
|
4170
4190
|
if (shapes.length === 0) {
|
|
4171
4191
|
return;
|
|
4172
4192
|
}
|
|
4173
|
-
const ids =
|
|
4193
|
+
const ids = toShapeIds(shapes);
|
|
4174
4194
|
const freshShapes = compact(ids.map((id) => this.getShape(id)));
|
|
4175
4195
|
if (freshShapes.length === 1) {
|
|
4176
4196
|
const parentId = freshShapes[0].parentId;
|
|
@@ -4267,12 +4287,14 @@ class Editor extends EventEmitter {
|
|
|
4267
4287
|
*/
|
|
4268
4288
|
getSelectedShapeAtPoint(point) {
|
|
4269
4289
|
const selectedShapeIds = this.getSelectedShapeIds();
|
|
4270
|
-
|
|
4290
|
+
if (selectedShapeIds.length === 0) return void 0;
|
|
4291
|
+
const selectedShapeIdSet = new Set(selectedShapeIds);
|
|
4292
|
+
const margin = this.getHitTestMargin();
|
|
4271
4293
|
const sortedShapes = this.getCurrentPageShapesSorted();
|
|
4272
4294
|
for (let i = sortedShapes.length - 1; i >= 0; i--) {
|
|
4273
4295
|
const shape = sortedShapes[i];
|
|
4274
4296
|
if (shape.type === "group") continue;
|
|
4275
|
-
if (!
|
|
4297
|
+
if (!selectedShapeIdSet.has(shape.id)) continue;
|
|
4276
4298
|
if (this.getShapeGeometry(shape).hitTestPoint(
|
|
4277
4299
|
this.getPointInShapeSpace(shape, point),
|
|
4278
4300
|
margin,
|
|
@@ -4370,7 +4392,6 @@ class Editor extends EventEmitter {
|
|
|
4370
4392
|
}
|
|
4371
4393
|
return inMarginClosestToEdgeHit || shape;
|
|
4372
4394
|
} else {
|
|
4373
|
-
if (this.getShapePageBounds(shape).contains(viewportPageBounds)) continue;
|
|
4374
4395
|
if (hitInside ? (
|
|
4375
4396
|
// On hitInside, the distance will be negative for hits inside
|
|
4376
4397
|
// If the distance is positive, check against the outer margin
|
|
@@ -4387,6 +4408,7 @@ class Editor extends EventEmitter {
|
|
|
4387
4408
|
inMarginClosestToEdgeHit = shape;
|
|
4388
4409
|
}
|
|
4389
4410
|
} else if (!inMarginClosestToEdgeHit) {
|
|
4411
|
+
if (this.getShapePageBounds(shape).contains(viewportPageBounds)) continue;
|
|
4390
4412
|
const { area } = geometry;
|
|
4391
4413
|
if (area < inHollowSmallestArea) {
|
|
4392
4414
|
inHollowSmallestArea = area;
|
|
@@ -4397,6 +4419,9 @@ class Editor extends EventEmitter {
|
|
|
4397
4419
|
}
|
|
4398
4420
|
} else {
|
|
4399
4421
|
if (distance < this.getHitTestMargin()) {
|
|
4422
|
+
if (inMarginClosestToEdgeHit && inMarginClosestToEdgeDistance <= distance) {
|
|
4423
|
+
return inMarginClosestToEdgeHit;
|
|
4424
|
+
}
|
|
4400
4425
|
return shape;
|
|
4401
4426
|
}
|
|
4402
4427
|
}
|
|
@@ -4673,7 +4698,7 @@ class Editor extends EventEmitter {
|
|
|
4673
4698
|
* @public
|
|
4674
4699
|
*/
|
|
4675
4700
|
reparentShapes(shapes, parentId, insertIndex) {
|
|
4676
|
-
const ids =
|
|
4701
|
+
const ids = toShapeIds(shapes);
|
|
4677
4702
|
if (ids.length === 0) return this;
|
|
4678
4703
|
const changes = [];
|
|
4679
4704
|
const parentTransform = isPageId(parentId) ? Mat.Identity() : this.getShapePageTransform(parentId);
|
|
@@ -4902,6 +4927,7 @@ class Editor extends EventEmitter {
|
|
|
4902
4927
|
* binding, but the `type`, `toId`, and `fromId` must all be provided.
|
|
4903
4928
|
*/
|
|
4904
4929
|
createBindings(partials) {
|
|
4930
|
+
if (this.getIsReadonly()) return this;
|
|
4905
4931
|
const bindings = [];
|
|
4906
4932
|
for (const partial of partials) {
|
|
4907
4933
|
const fromShape = this.getShape(partial.fromId);
|
|
@@ -4936,6 +4962,7 @@ class Editor extends EventEmitter {
|
|
|
4936
4962
|
* binding is skipped. The changes from the partial are merged into the existing record.
|
|
4937
4963
|
*/
|
|
4938
4964
|
updateBindings(partials) {
|
|
4965
|
+
if (this.getIsReadonly()) return this;
|
|
4939
4966
|
const updated = [];
|
|
4940
4967
|
for (const partial of partials) {
|
|
4941
4968
|
if (!partial) continue;
|
|
@@ -4964,6 +4991,16 @@ class Editor extends EventEmitter {
|
|
|
4964
4991
|
* Delete several bindings by their IDs. If a binding ID doesn't exist, it's ignored.
|
|
4965
4992
|
*/
|
|
4966
4993
|
deleteBindings(bindings, { isolateShapes = false } = {}) {
|
|
4994
|
+
if (this.getIsReadonly()) return this;
|
|
4995
|
+
return this._deleteBindings(bindings, { isolateShapes });
|
|
4996
|
+
}
|
|
4997
|
+
/**
|
|
4998
|
+
* Unguarded so that withIsolatedShapes can transiently isolate shapes for copy and export in
|
|
4999
|
+
* readonly mode; the public deleteBindings is what readonly blocks.
|
|
5000
|
+
*
|
|
5001
|
+
* @internal
|
|
5002
|
+
*/
|
|
5003
|
+
_deleteBindings(bindings, { isolateShapes = false } = {}) {
|
|
4967
5004
|
const ids = bindings.map((binding) => typeof binding === "string" ? binding : binding.id);
|
|
4968
5005
|
if (isolateShapes) {
|
|
4969
5006
|
this.store.atomic(() => {
|
|
@@ -5022,7 +5059,7 @@ class Editor extends EventEmitter {
|
|
|
5022
5059
|
* @param opts - The options for the rotation.
|
|
5023
5060
|
*/
|
|
5024
5061
|
rotateShapesBy(shapes, delta, opts) {
|
|
5025
|
-
const ids =
|
|
5062
|
+
const ids = toShapeIds(shapes);
|
|
5026
5063
|
if (ids.length <= 0) return this;
|
|
5027
5064
|
const snapshot = getRotationSnapshot({ editor: this, ids });
|
|
5028
5065
|
if (!snapshot) return this;
|
|
@@ -5059,6 +5096,14 @@ class Editor extends EventEmitter {
|
|
|
5059
5096
|
}
|
|
5060
5097
|
return workingShape;
|
|
5061
5098
|
}
|
|
5099
|
+
// Counter-rotates the page-space delta into the parent's space; without that a child of a rotated
|
|
5100
|
+
// frame or group would move along its parent's axes instead of the page's.
|
|
5101
|
+
// todo: a shape laid out together with its own parent moves twice, once with the parent and once
|
|
5102
|
+
// on its own; the layout commands should skip shapes whose ancestors are also being laid out
|
|
5103
|
+
getChangesToTranslateShapeByPageDelta(shape, pageDelta) {
|
|
5104
|
+
const localDelta = Vec.From(pageDelta).rot(-this.getShapeParentTransform(shape).rotation());
|
|
5105
|
+
return this.getChangesToTranslateShape(shape, localDelta.add(shape));
|
|
5106
|
+
}
|
|
5062
5107
|
/**
|
|
5063
5108
|
* Move shapes by a delta.
|
|
5064
5109
|
*
|
|
@@ -5071,14 +5116,12 @@ class Editor extends EventEmitter {
|
|
|
5071
5116
|
* @param offset - The offset to apply to the shapes.
|
|
5072
5117
|
*/
|
|
5073
5118
|
nudgeShapes(shapes, offset) {
|
|
5074
|
-
const ids =
|
|
5119
|
+
const ids = toShapeIds(shapes);
|
|
5075
5120
|
if (ids.length <= 0) return this;
|
|
5076
5121
|
const changes = [];
|
|
5077
5122
|
for (const id of ids) {
|
|
5078
5123
|
const shape = this.getShape(id);
|
|
5079
|
-
|
|
5080
|
-
localDelta.rot(-this.getShapeParentTransform(shape).rotation());
|
|
5081
|
-
changes.push(this.getChangesToTranslateShape(shape, localDelta.add(shape)));
|
|
5124
|
+
changes.push(this.getChangesToTranslateShapeByPageDelta(shape, offset));
|
|
5082
5125
|
}
|
|
5083
5126
|
this.updateShapes(changes);
|
|
5084
5127
|
return this;
|
|
@@ -5099,7 +5142,7 @@ class Editor extends EventEmitter {
|
|
|
5099
5142
|
*/
|
|
5100
5143
|
duplicateShapes(shapes, offset) {
|
|
5101
5144
|
this.run(() => {
|
|
5102
|
-
const _ids =
|
|
5145
|
+
const _ids = toShapeIds(shapes);
|
|
5103
5146
|
const ids = this._shouldIgnoreShapeLock ? _ids : this._getUnlockedShapeIds(_ids);
|
|
5104
5147
|
if (ids.length <= 0) return this;
|
|
5105
5148
|
const initialIds = new Set(ids);
|
|
@@ -5208,7 +5251,7 @@ class Editor extends EventEmitter {
|
|
|
5208
5251
|
* @public
|
|
5209
5252
|
*/
|
|
5210
5253
|
moveShapesToPage(shapes, pageId) {
|
|
5211
|
-
const ids =
|
|
5254
|
+
const ids = toShapeIds(shapes);
|
|
5212
5255
|
if (ids.length === 0) return this;
|
|
5213
5256
|
if (this.getIsReadonly()) return this;
|
|
5214
5257
|
const currentPageId = this.getCurrentPageId();
|
|
@@ -5244,7 +5287,7 @@ class Editor extends EventEmitter {
|
|
|
5244
5287
|
* @public
|
|
5245
5288
|
*/
|
|
5246
5289
|
toggleLock(shapes) {
|
|
5247
|
-
const ids =
|
|
5290
|
+
const ids = toShapeIds(shapes);
|
|
5248
5291
|
if (this.getIsReadonly() || ids.length === 0) return this;
|
|
5249
5292
|
let allLocked = true;
|
|
5250
5293
|
const shapesToToggle = [];
|
|
@@ -5283,7 +5326,7 @@ class Editor extends EventEmitter {
|
|
|
5283
5326
|
* @public
|
|
5284
5327
|
*/
|
|
5285
5328
|
sendToBack(shapes) {
|
|
5286
|
-
const ids =
|
|
5329
|
+
const ids = toShapeIds(shapes);
|
|
5287
5330
|
const changes = getReorderingShapesChanges(this, "toBack", ids, {
|
|
5288
5331
|
considerAllShapes: true
|
|
5289
5332
|
});
|
|
@@ -5313,7 +5356,7 @@ class Editor extends EventEmitter {
|
|
|
5313
5356
|
* @public
|
|
5314
5357
|
*/
|
|
5315
5358
|
sendBackward(shapes, opts = {}) {
|
|
5316
|
-
const ids =
|
|
5359
|
+
const ids = toShapeIds(shapes);
|
|
5317
5360
|
const changes = getReorderingShapesChanges(this, "backward", ids, opts);
|
|
5318
5361
|
if (changes) this.updateShapes(changes);
|
|
5319
5362
|
return this;
|
|
@@ -5341,7 +5384,7 @@ class Editor extends EventEmitter {
|
|
|
5341
5384
|
* @public
|
|
5342
5385
|
*/
|
|
5343
5386
|
bringForward(shapes, opts = {}) {
|
|
5344
|
-
const ids =
|
|
5387
|
+
const ids = toShapeIds(shapes);
|
|
5345
5388
|
const changes = getReorderingShapesChanges(this, "forward", ids, opts);
|
|
5346
5389
|
if (changes) this.updateShapes(changes);
|
|
5347
5390
|
return this;
|
|
@@ -5360,7 +5403,7 @@ class Editor extends EventEmitter {
|
|
|
5360
5403
|
* @public
|
|
5361
5404
|
*/
|
|
5362
5405
|
bringToFront(shapes) {
|
|
5363
|
-
const ids =
|
|
5406
|
+
const ids = toShapeIds(shapes);
|
|
5364
5407
|
const changes = getReorderingShapesChanges(this, "toFront", ids);
|
|
5365
5408
|
if (changes) this.updateShapes(changes);
|
|
5366
5409
|
return this;
|
|
@@ -5372,7 +5415,7 @@ class Editor extends EventEmitter {
|
|
|
5372
5415
|
* @internal
|
|
5373
5416
|
*/
|
|
5374
5417
|
getShapeClusters(shapes, type, opts) {
|
|
5375
|
-
const ids =
|
|
5418
|
+
const ids = toShapeIds(shapes);
|
|
5376
5419
|
let freshShapes = compact(ids.map((id) => this.getShape(id)));
|
|
5377
5420
|
if (opts?.filterAxisAligned) {
|
|
5378
5421
|
freshShapes = freshShapes.filter((s) => {
|
|
@@ -5397,7 +5440,7 @@ class Editor extends EventEmitter {
|
|
|
5397
5440
|
const shapesMovingTogether = [shape];
|
|
5398
5441
|
const boundsOfShapesMovingTogether = [shapePageBounds];
|
|
5399
5442
|
this.collectShapesViaArrowBindings({
|
|
5400
|
-
bindings: this.
|
|
5443
|
+
bindings: this.getBindingsInvolvingShape(shape.id, "arrow"),
|
|
5401
5444
|
initialShapes: freshShapes,
|
|
5402
5445
|
resultShapes: shapesMovingTogether,
|
|
5403
5446
|
resultBounds: boundsOfShapesMovingTogether,
|
|
@@ -5453,7 +5496,7 @@ class Editor extends EventEmitter {
|
|
|
5453
5496
|
*/
|
|
5454
5497
|
flipShapes(shapes, operation) {
|
|
5455
5498
|
if (this.getIsReadonly()) return this;
|
|
5456
|
-
const ids =
|
|
5499
|
+
const ids = toShapeIds(shapes);
|
|
5457
5500
|
const shapesToFlipFirstPass = compact(ids.map((id) => this.getShape(id)));
|
|
5458
5501
|
for (const shape of shapesToFlipFirstPass) {
|
|
5459
5502
|
if (this.isShapeOfType(shape, "group")) {
|
|
@@ -5527,21 +5570,7 @@ class Editor extends EventEmitter {
|
|
|
5527
5570
|
const { clusters: shapeClustersToStack } = this.getShapeClusters(shapes, "stack");
|
|
5528
5571
|
const len = shapeClustersToStack.length;
|
|
5529
5572
|
if (_gap === 0 && len < 3 || len < 2) return this;
|
|
5530
|
-
|
|
5531
|
-
let min;
|
|
5532
|
-
let max;
|
|
5533
|
-
let dim;
|
|
5534
|
-
if (operation === "horizontal") {
|
|
5535
|
-
val = "x";
|
|
5536
|
-
min = "minX";
|
|
5537
|
-
max = "maxX";
|
|
5538
|
-
dim = "width";
|
|
5539
|
-
} else {
|
|
5540
|
-
val = "y";
|
|
5541
|
-
min = "minY";
|
|
5542
|
-
max = "maxY";
|
|
5543
|
-
dim = "height";
|
|
5544
|
-
}
|
|
5573
|
+
const { val, min, max, dim } = AXIS[operation];
|
|
5545
5574
|
let shapeGap = 0;
|
|
5546
5575
|
shapeClustersToStack.sort((a, b) => a.pageBounds[min] - b.pageBounds[min]);
|
|
5547
5576
|
if (_gap === 0) {
|
|
@@ -5580,13 +5609,7 @@ class Editor extends EventEmitter {
|
|
|
5580
5609
|
const delta = new Vec();
|
|
5581
5610
|
delta[val] = v + shapeGap - pageBounds[val];
|
|
5582
5611
|
for (const shape of shapes2) {
|
|
5583
|
-
|
|
5584
|
-
const parent = this.getShapeParent(shape);
|
|
5585
|
-
if (parent) {
|
|
5586
|
-
shapeDelta.rot(-this.getShapePageTransform(parent).rotation());
|
|
5587
|
-
}
|
|
5588
|
-
shapeDelta.add(shape);
|
|
5589
|
-
changes.push(this.getChangesToTranslateShape(shape, shapeDelta));
|
|
5612
|
+
changes.push(this.getChangesToTranslateShapeByPageDelta(shape, delta));
|
|
5590
5613
|
}
|
|
5591
5614
|
v += pageBounds[dim] + shapeGap;
|
|
5592
5615
|
}
|
|
@@ -5666,13 +5689,7 @@ class Editor extends EventEmitter {
|
|
|
5666
5689
|
for (const { shapes: shapes2, pageBounds, nextPageBounds } of shapeClustersToPack) {
|
|
5667
5690
|
const delta = Vec.Sub(nextPageBounds.point, pageBounds.point).add(centerDelta);
|
|
5668
5691
|
for (const shape of shapes2) {
|
|
5669
|
-
|
|
5670
|
-
const parent = this.getShapeParent(shape);
|
|
5671
|
-
if (parent) {
|
|
5672
|
-
shapeDelta.rot(-this.getShapeParentTransform(shape).rotation());
|
|
5673
|
-
}
|
|
5674
|
-
shapeDelta.add(shape);
|
|
5675
|
-
changes.push(this.getChangesToTranslateShape(shape, shapeDelta));
|
|
5692
|
+
changes.push(this.getChangesToTranslateShapeByPageDelta(shape, delta));
|
|
5676
5693
|
}
|
|
5677
5694
|
}
|
|
5678
5695
|
if (changes.length) {
|
|
@@ -5732,13 +5749,7 @@ class Editor extends EventEmitter {
|
|
|
5732
5749
|
}
|
|
5733
5750
|
}
|
|
5734
5751
|
for (const shape of shapes2) {
|
|
5735
|
-
|
|
5736
|
-
const parent = this.getShapeParent(shape);
|
|
5737
|
-
if (parent) {
|
|
5738
|
-
shapeDelta.rot(-this.getShapePageTransform(parent).rotation());
|
|
5739
|
-
}
|
|
5740
|
-
shapeDelta.add(shape);
|
|
5741
|
-
changes.push(this.getChangesToTranslateShape(shape, shapeDelta));
|
|
5752
|
+
changes.push(this.getChangesToTranslateShapeByPageDelta(shape, delta));
|
|
5742
5753
|
}
|
|
5743
5754
|
});
|
|
5744
5755
|
this.updateShapes(changes);
|
|
@@ -5762,27 +5773,13 @@ class Editor extends EventEmitter {
|
|
|
5762
5773
|
if (this.getIsReadonly()) return this;
|
|
5763
5774
|
const { clusters: shapeClustersToDistribute } = this.getShapeClusters(shapes, "distribute");
|
|
5764
5775
|
if (shapeClustersToDistribute.length < 3) return this;
|
|
5765
|
-
|
|
5766
|
-
let min;
|
|
5767
|
-
let max;
|
|
5768
|
-
let dim;
|
|
5769
|
-
if (operation === "horizontal") {
|
|
5770
|
-
val = "x";
|
|
5771
|
-
min = "minX";
|
|
5772
|
-
max = "maxX";
|
|
5773
|
-
dim = "width";
|
|
5774
|
-
} else {
|
|
5775
|
-
val = "y";
|
|
5776
|
-
min = "minY";
|
|
5777
|
-
max = "maxY";
|
|
5778
|
-
dim = "height";
|
|
5779
|
-
}
|
|
5776
|
+
const { val, min, max, dim } = AXIS[operation];
|
|
5780
5777
|
const changes = [];
|
|
5781
5778
|
const first = shapeClustersToDistribute.sort((a, b) => a.pageBounds[min] - b.pageBounds[min])[0];
|
|
5782
5779
|
const last2 = shapeClustersToDistribute.sort((a, b) => b.pageBounds[max] - a.pageBounds[max])[0];
|
|
5783
5780
|
if (first === last2) {
|
|
5784
5781
|
const excludedShapeIds = new Set(first.shapes.map((s) => s.id));
|
|
5785
|
-
const ids =
|
|
5782
|
+
const ids = toShapeIds(shapes);
|
|
5786
5783
|
return this.distributeShapes(
|
|
5787
5784
|
ids.filter((id) => !excludedShapeIds.has(id)),
|
|
5788
5785
|
operation
|
|
@@ -5806,13 +5803,7 @@ class Editor extends EventEmitter {
|
|
|
5806
5803
|
delta[val] = last2.pageBounds[max] - pageBounds[max] - 1;
|
|
5807
5804
|
}
|
|
5808
5805
|
for (const shape of shapes2) {
|
|
5809
|
-
|
|
5810
|
-
const parent = this.getShapeParent(shape);
|
|
5811
|
-
if (parent) {
|
|
5812
|
-
shapeDelta.rot(-this.getShapePageTransform(parent).rotation());
|
|
5813
|
-
}
|
|
5814
|
-
shapeDelta.add(shape);
|
|
5815
|
-
changes.push(this.getChangesToTranslateShape(shape, shapeDelta));
|
|
5806
|
+
changes.push(this.getChangesToTranslateShapeByPageDelta(shape, delta));
|
|
5816
5807
|
}
|
|
5817
5808
|
v += pageBounds[dim] + gap;
|
|
5818
5809
|
}
|
|
@@ -5842,32 +5833,17 @@ class Editor extends EventEmitter {
|
|
|
5842
5833
|
);
|
|
5843
5834
|
if (shapeClustersToStretch.length < 2) return this;
|
|
5844
5835
|
const commonBounds = Box.Common(allBounds);
|
|
5845
|
-
|
|
5846
|
-
let min;
|
|
5847
|
-
let dim;
|
|
5848
|
-
if (operation === "horizontal") {
|
|
5849
|
-
val = "x";
|
|
5850
|
-
min = "minX";
|
|
5851
|
-
dim = "width";
|
|
5852
|
-
} else {
|
|
5853
|
-
val = "y";
|
|
5854
|
-
min = "minY";
|
|
5855
|
-
dim = "height";
|
|
5856
|
-
}
|
|
5836
|
+
const { val, min, dim } = AXIS[operation];
|
|
5857
5837
|
this.run(() => {
|
|
5858
5838
|
shapeClustersToStretch.forEach(({ shapes: shapes2, pageBounds }) => {
|
|
5859
|
-
const
|
|
5860
|
-
|
|
5839
|
+
const pageOffset = new Vec();
|
|
5840
|
+
pageOffset[val] = commonBounds[min] - pageBounds[min];
|
|
5861
5841
|
const scaleOrigin = pageBounds.center.clone();
|
|
5862
5842
|
scaleOrigin[val] = commonBounds[min];
|
|
5863
5843
|
const scale = new Vec(1, 1);
|
|
5864
5844
|
scale[val] = commonBounds[dim] / pageBounds[dim];
|
|
5865
5845
|
for (const shape of shapes2) {
|
|
5866
|
-
|
|
5867
|
-
shapeLocalOffset.rot(-this.getShapeParentTransform(shape).rotation());
|
|
5868
|
-
shapeLocalOffset.add(shape);
|
|
5869
|
-
const changes = this.getChangesToTranslateShape(shape, shapeLocalOffset);
|
|
5870
|
-
this.updateShape(changes);
|
|
5846
|
+
this.updateShape(this.getChangesToTranslateShapeByPageDelta(shape, pageOffset));
|
|
5871
5847
|
this.resizeShape(shape.id, scale, {
|
|
5872
5848
|
initialBounds: this.getShapeGeometry(shape).bounds,
|
|
5873
5849
|
scaleOrigin,
|
|
@@ -5910,7 +5886,7 @@ class Editor extends EventEmitter {
|
|
|
5910
5886
|
const scaleY = targetBounds.height / commonBounds.height;
|
|
5911
5887
|
const scale = new Vec(scaleX, scaleY);
|
|
5912
5888
|
shapeClusters.forEach(({ shapes: shapes2, pageBounds }) => {
|
|
5913
|
-
const
|
|
5889
|
+
const pageOffset = new Vec(
|
|
5914
5890
|
targetBounds.minX - commonBounds.minX + (pageBounds.minX - commonBounds.minX) * (scaleX - 1),
|
|
5915
5891
|
targetBounds.minY - commonBounds.minY + (pageBounds.minY - commonBounds.minY) * (scaleY - 1)
|
|
5916
5892
|
);
|
|
@@ -5919,11 +5895,7 @@ class Editor extends EventEmitter {
|
|
|
5919
5895
|
targetBounds.minY + (pageBounds.minY - commonBounds.minY) * scaleY
|
|
5920
5896
|
);
|
|
5921
5897
|
for (const shape of shapes2) {
|
|
5922
|
-
|
|
5923
|
-
shapeLocalOffset.rot(-this.getShapeParentTransform(shape).rotation());
|
|
5924
|
-
shapeLocalOffset.add(shape);
|
|
5925
|
-
const changes = this.getChangesToTranslateShape(shape, shapeLocalOffset);
|
|
5926
|
-
this.updateShape(changes);
|
|
5898
|
+
this.updateShape(this.getChangesToTranslateShapeByPageDelta(shape, pageOffset));
|
|
5927
5899
|
this.resizeShape(shape.id, scale, {
|
|
5928
5900
|
initialBounds: this.getShapeGeometry(shape).bounds,
|
|
5929
5901
|
scaleOrigin,
|
|
@@ -6342,12 +6314,17 @@ class Editor extends EventEmitter {
|
|
|
6342
6314
|
let remaining = duration;
|
|
6343
6315
|
let t;
|
|
6344
6316
|
const animations = [];
|
|
6317
|
+
const ignoreShapeLock = this._shouldIgnoreShapeLock;
|
|
6345
6318
|
let partial, result;
|
|
6346
6319
|
for (let i = 0, n = partials.length; i < n; i++) {
|
|
6347
6320
|
partial = partials[i];
|
|
6348
6321
|
if (!partial) continue;
|
|
6349
6322
|
const shape = this.getShape(partial.id);
|
|
6350
6323
|
if (!shape) continue;
|
|
6324
|
+
const unlocks = shape.isLocked && Object.hasOwn(partial, "isLocked") && !partial.isLocked;
|
|
6325
|
+
if (!ignoreShapeLock && !unlocks && this.isShapeOrAncestorLocked(shape)) {
|
|
6326
|
+
continue;
|
|
6327
|
+
}
|
|
6351
6328
|
result = {
|
|
6352
6329
|
start: structuredClone(shape),
|
|
6353
6330
|
end: applyPartialToRecordWithProps(structuredClone(shape), partial)
|
|
@@ -6363,7 +6340,7 @@ class Editor extends EventEmitter {
|
|
|
6363
6340
|
(p) => p && animatingShapes2.get(p.id) === animationId
|
|
6364
6341
|
);
|
|
6365
6342
|
if (partialsToUpdate.length) {
|
|
6366
|
-
this.updateShapes(partialsToUpdate);
|
|
6343
|
+
this.run(() => this.updateShapes(partialsToUpdate), { ignoreShapeLock });
|
|
6367
6344
|
}
|
|
6368
6345
|
this.off("tick", handleTick);
|
|
6369
6346
|
return;
|
|
@@ -6396,13 +6373,13 @@ class Editor extends EventEmitter {
|
|
|
6396
6373
|
throw Error("Editor.groupShapes: must provide an array of shapes or shape ids");
|
|
6397
6374
|
}
|
|
6398
6375
|
if (this.getIsReadonly()) return this;
|
|
6399
|
-
const ids =
|
|
6400
|
-
if (ids.length <= 1) return this;
|
|
6376
|
+
const ids = toShapeIds(shapes);
|
|
6401
6377
|
const shapesToGroup = compact(
|
|
6402
6378
|
(this._shouldIgnoreShapeLock ? ids : this._getUnlockedShapeIds(ids)).map(
|
|
6403
6379
|
(id) => this.getShape(id)
|
|
6404
6380
|
)
|
|
6405
6381
|
);
|
|
6382
|
+
if (shapesToGroup.length <= 1) return this;
|
|
6406
6383
|
const sortedShapeIds = shapesToGroup.sort(sortByIndex).map((s) => s.id);
|
|
6407
6384
|
const childBounds = compact(shapesToGroup.map((shape) => this.getShapePageBounds(shape)));
|
|
6408
6385
|
const pageBounds = Box.Common(childBounds);
|
|
@@ -6411,8 +6388,11 @@ class Editor extends EventEmitter {
|
|
|
6411
6388
|
}
|
|
6412
6389
|
const { x, y } = pageBounds.point;
|
|
6413
6390
|
const parentId = this.findCommonAncestor(shapesToGroup) ?? this.getCurrentPageId();
|
|
6414
|
-
if (this.
|
|
6415
|
-
|
|
6391
|
+
if (!this.canCreateShapes([groupId])) {
|
|
6392
|
+
alertMaxShapes(this);
|
|
6393
|
+
return this;
|
|
6394
|
+
}
|
|
6395
|
+
if (this.isIn("select") && !this.isIn("select.idle")) {
|
|
6416
6396
|
this.cancel();
|
|
6417
6397
|
}
|
|
6418
6398
|
const shapesWithRootParent = shapesToGroup.filter((shape) => shape.parentId === parentId).sort(sortByIndex);
|
|
@@ -6440,15 +6420,14 @@ class Editor extends EventEmitter {
|
|
|
6440
6420
|
ungroupShapes(shapes, opts = {}) {
|
|
6441
6421
|
if (this.getIsReadonly()) return this;
|
|
6442
6422
|
const { select = true } = opts;
|
|
6443
|
-
const ids =
|
|
6423
|
+
const ids = toShapeIds(shapes);
|
|
6444
6424
|
const shapesToUngroup = compact(
|
|
6445
6425
|
(this._shouldIgnoreShapeLock ? ids : this._getUnlockedShapeIds(ids)).map(
|
|
6446
6426
|
(id) => this.getShape(id)
|
|
6447
6427
|
)
|
|
6448
6428
|
);
|
|
6449
6429
|
if (shapesToUngroup.length === 0) return this;
|
|
6450
|
-
if (this.
|
|
6451
|
-
if (!this.isIn("select.idle")) {
|
|
6430
|
+
if (this.isIn("select") && !this.isIn("select.idle")) {
|
|
6452
6431
|
this.cancel();
|
|
6453
6432
|
}
|
|
6454
6433
|
const idsToSelect = /* @__PURE__ */ new Set();
|
|
@@ -6462,16 +6441,16 @@ class Editor extends EventEmitter {
|
|
|
6462
6441
|
});
|
|
6463
6442
|
if (groups.length === 0) return this;
|
|
6464
6443
|
this.run(() => {
|
|
6465
|
-
let group;
|
|
6466
6444
|
for (let i = 0, n = groups.length; i < n; i++) {
|
|
6467
|
-
group = groups[i];
|
|
6445
|
+
const group = this.getShape(groups[i].id);
|
|
6446
|
+
if (!group) continue;
|
|
6468
6447
|
const childIds = this.getSortedChildIdsForParent(group.id);
|
|
6469
6448
|
for (let j = 0, n2 = childIds.length; j < n2; j++) {
|
|
6470
6449
|
idsToSelect.add(childIds[j]);
|
|
6471
6450
|
}
|
|
6472
6451
|
this.reparentShapes(childIds, group.parentId, group.index);
|
|
6473
6452
|
}
|
|
6474
|
-
this.deleteShapes(groups.map((
|
|
6453
|
+
this.deleteShapes(groups.map((group) => group.id));
|
|
6475
6454
|
if (select) {
|
|
6476
6455
|
this.select(...idsToSelect);
|
|
6477
6456
|
}
|
|
@@ -6552,14 +6531,14 @@ class Editor extends EventEmitter {
|
|
|
6552
6531
|
}
|
|
6553
6532
|
/** @internal */
|
|
6554
6533
|
_getUnlockedShapeIds(ids) {
|
|
6555
|
-
return ids.filter((id) => !this.
|
|
6534
|
+
return ids.filter((id) => !this.isShapeOrAncestorLocked(id));
|
|
6556
6535
|
}
|
|
6557
6536
|
deleteShapes(_ids) {
|
|
6558
6537
|
if (this.getIsReadonly()) return this;
|
|
6559
6538
|
if (!Array.isArray(_ids)) {
|
|
6560
6539
|
throw Error("Editor.deleteShapes: must provide an array of shapes or shapeIds");
|
|
6561
6540
|
}
|
|
6562
|
-
const shapeIds =
|
|
6541
|
+
const shapeIds = toShapeIds(_ids);
|
|
6563
6542
|
const shapeIdsToDelete = this._shouldIgnoreShapeLock ? shapeIds : this._getUnlockedShapeIds(shapeIds);
|
|
6564
6543
|
if (shapeIdsToDelete.length === 0) return this;
|
|
6565
6544
|
const allShapeIdsToDelete = new Set(shapeIdsToDelete);
|
|
@@ -6904,7 +6883,7 @@ class Editor extends EventEmitter {
|
|
|
6904
6883
|
* @public
|
|
6905
6884
|
*/
|
|
6906
6885
|
getContentFromCurrentPage(shapes) {
|
|
6907
|
-
const ids =
|
|
6886
|
+
const ids = toShapeIds(shapes);
|
|
6908
6887
|
if (!ids) return;
|
|
6909
6888
|
if (ids.length === 0) return;
|
|
6910
6889
|
const shapeIds = this.getShapeAndDescendantIds(ids);
|
|
@@ -6972,21 +6951,29 @@ class Editor extends EventEmitter {
|
|
|
6972
6951
|
await Promise.allSettled(
|
|
6973
6952
|
content.assets.map(async (asset) => {
|
|
6974
6953
|
if ((asset.type === "image" || asset.type === "video") && !asset.props.src?.startsWith("data:image") && !asset.props.src?.startsWith("data:video") && !asset.props.src?.startsWith("http")) {
|
|
6975
|
-
|
|
6976
|
-
|
|
6977
|
-
|
|
6978
|
-
|
|
6979
|
-
|
|
6980
|
-
|
|
6981
|
-
|
|
6982
|
-
|
|
6983
|
-
|
|
6984
|
-
|
|
6985
|
-
|
|
6986
|
-
|
|
6987
|
-
|
|
6988
|
-
|
|
6954
|
+
try {
|
|
6955
|
+
const objectUrl = await this.store.props.assets.resolve(asset, {
|
|
6956
|
+
screenScale: 1,
|
|
6957
|
+
steppedScreenScale: 1,
|
|
6958
|
+
dpr: 1,
|
|
6959
|
+
networkEffectiveType: null,
|
|
6960
|
+
shouldResolveToOriginal: true
|
|
6961
|
+
});
|
|
6962
|
+
if (objectUrl) {
|
|
6963
|
+
const response = await fetch(objectUrl);
|
|
6964
|
+
if (response.ok) {
|
|
6965
|
+
const assetWithDataUrl = structuredClone(asset);
|
|
6966
|
+
assetWithDataUrl.props.src = await FileHelpers.blobToDataUrl(await response.blob());
|
|
6967
|
+
assets.push(assetWithDataUrl);
|
|
6968
|
+
return;
|
|
6969
|
+
}
|
|
6970
|
+
console.warn(`Could not inline asset ${asset.id}: fetch returned ${response.status}`);
|
|
6971
|
+
}
|
|
6972
|
+
} catch (err) {
|
|
6973
|
+
console.warn(`Could not inline asset ${asset.id}`, err);
|
|
6974
|
+
}
|
|
6989
6975
|
}
|
|
6976
|
+
assets.push(asset);
|
|
6990
6977
|
})
|
|
6991
6978
|
);
|
|
6992
6979
|
content.assets = assets;
|
|
@@ -7280,7 +7267,7 @@ class Editor extends EventEmitter {
|
|
|
7280
7267
|
* @public
|
|
7281
7268
|
*/
|
|
7282
7269
|
async getSvgElement(shapes, opts = {}) {
|
|
7283
|
-
const ids = shapes.length === 0 ? this.getCurrentPageShapeIdsSorted() :
|
|
7270
|
+
const ids = shapes.length === 0 ? this.getCurrentPageShapeIdsSorted() : toShapeIds(shapes);
|
|
7284
7271
|
if (ids.length === 0) return void 0;
|
|
7285
7272
|
await this.fonts.loadRequiredFontsForCurrentPage(this.options.maxFontsToLoadBeforeRender);
|
|
7286
7273
|
return exportToSvg(this, ids, opts);
|
|
@@ -8198,6 +8185,7 @@ class Editor extends EventEmitter {
|
|
|
8198
8185
|
inputs.buttons.add(info.button);
|
|
8199
8186
|
inputs.setIsPointing(true);
|
|
8200
8187
|
inputs.setIsDragging(false);
|
|
8188
|
+
this.stopCameraAnimation();
|
|
8201
8189
|
if (!isPenMode && info.isPenDirect) {
|
|
8202
8190
|
this.updateInstanceState({ isPenMode: true });
|
|
8203
8191
|
this.interrupt();
|
|
@@ -8334,8 +8322,8 @@ class Editor extends EventEmitter {
|
|
|
8334
8322
|
break;
|
|
8335
8323
|
}
|
|
8336
8324
|
case "keyboard": {
|
|
8337
|
-
if (info.
|
|
8338
|
-
if (info.
|
|
8325
|
+
if (info.code === "ShiftRight") info.code = "ShiftLeft";
|
|
8326
|
+
if (info.code === "AltRight") info.code = "AltLeft";
|
|
8339
8327
|
if (info.code === "ControlRight") info.code = "ControlLeft";
|
|
8340
8328
|
if (info.code === "MetaRight") info.code = "MetaLeft";
|
|
8341
8329
|
switch (info.name) {
|
|
@@ -8705,6 +8693,9 @@ function pushShapeWithDescendants(editor, id, result) {
|
|
|
8705
8693
|
pushShapeWithDescendants(editor, childIds[i], result);
|
|
8706
8694
|
}
|
|
8707
8695
|
}
|
|
8696
|
+
function toShapeIds(shapes) {
|
|
8697
|
+
return typeof shapes[0] === "string" ? shapes : shapes.map((shape) => shape.id);
|
|
8698
|
+
}
|
|
8708
8699
|
function withIsolatedShapes(editor, shapeIds, callback) {
|
|
8709
8700
|
let result;
|
|
8710
8701
|
editor.run(
|
|
@@ -8727,7 +8718,7 @@ function withIsolatedShapes(editor, shapeIds, callback) {
|
|
|
8727
8718
|
}
|
|
8728
8719
|
}
|
|
8729
8720
|
}
|
|
8730
|
-
editor.
|
|
8721
|
+
editor._deleteBindings([...bindingsToRemove], { isolateShapes: true });
|
|
8731
8722
|
try {
|
|
8732
8723
|
result = Result.ok(callback(bindingsWithBoth));
|
|
8733
8724
|
} catch (error) {
|