@tldraw/editor 3.9.0-internal.7f0e15f4f7d9 → 3.10.0-canary.15f6aaa3d2d3

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.
Files changed (63) hide show
  1. package/CHANGELOG.md +90 -0
  2. package/README.md +1 -1
  3. package/dist-cjs/index.d.ts +43 -7
  4. package/dist-cjs/index.js +1 -1
  5. package/dist-cjs/index.js.map +2 -2
  6. package/dist-cjs/lib/TldrawEditor.js +2 -3
  7. package/dist-cjs/lib/TldrawEditor.js.map +2 -2
  8. package/dist-cjs/lib/components/LiveCollaborators.js +5 -0
  9. package/dist-cjs/lib/components/LiveCollaborators.js.map +2 -2
  10. package/dist-cjs/lib/components/default-components/DefaultBrush.js.map +2 -2
  11. package/dist-cjs/lib/components/default-components/DefaultCollaboratorHint.js.map +2 -2
  12. package/dist-cjs/lib/components/default-components/DefaultCursor.js.map +2 -2
  13. package/dist-cjs/lib/components/default-components/DefaultErrorFallback.js +1 -1
  14. package/dist-cjs/lib/components/default-components/DefaultErrorFallback.js.map +2 -2
  15. package/dist-cjs/lib/components/default-components/DefaultScribble.js.map +2 -2
  16. package/dist-cjs/lib/components/default-components/DefaultShapeIndicator.js.map +2 -2
  17. package/dist-cjs/lib/editor/Editor.js +435 -252
  18. package/dist-cjs/lib/editor/Editor.js.map +3 -3
  19. package/dist-cjs/lib/editor/managers/FontManager.js +25 -26
  20. package/dist-cjs/lib/editor/managers/FontManager.js.map +2 -2
  21. package/dist-cjs/lib/editor/shapes/ShapeUtil.js +7 -2
  22. package/dist-cjs/lib/editor/shapes/ShapeUtil.js.map +2 -2
  23. package/dist-cjs/lib/exports/getSvgJsx.js.map +2 -2
  24. package/dist-cjs/version.js +3 -3
  25. package/dist-cjs/version.js.map +1 -1
  26. package/dist-esm/index.d.mts +43 -7
  27. package/dist-esm/index.mjs +1 -1
  28. package/dist-esm/index.mjs.map +2 -2
  29. package/dist-esm/lib/TldrawEditor.mjs +2 -3
  30. package/dist-esm/lib/TldrawEditor.mjs.map +2 -2
  31. package/dist-esm/lib/components/LiveCollaborators.mjs +5 -0
  32. package/dist-esm/lib/components/LiveCollaborators.mjs.map +2 -2
  33. package/dist-esm/lib/components/default-components/DefaultBrush.mjs.map +2 -2
  34. package/dist-esm/lib/components/default-components/DefaultCollaboratorHint.mjs.map +2 -2
  35. package/dist-esm/lib/components/default-components/DefaultCursor.mjs.map +2 -2
  36. package/dist-esm/lib/components/default-components/DefaultErrorFallback.mjs +1 -1
  37. package/dist-esm/lib/components/default-components/DefaultErrorFallback.mjs.map +2 -2
  38. package/dist-esm/lib/components/default-components/DefaultScribble.mjs.map +2 -2
  39. package/dist-esm/lib/components/default-components/DefaultShapeIndicator.mjs.map +2 -2
  40. package/dist-esm/lib/editor/Editor.mjs +431 -248
  41. package/dist-esm/lib/editor/Editor.mjs.map +3 -3
  42. package/dist-esm/lib/editor/managers/FontManager.mjs +26 -27
  43. package/dist-esm/lib/editor/managers/FontManager.mjs.map +2 -2
  44. package/dist-esm/lib/editor/shapes/ShapeUtil.mjs +7 -2
  45. package/dist-esm/lib/editor/shapes/ShapeUtil.mjs.map +2 -2
  46. package/dist-esm/lib/exports/getSvgJsx.mjs.map +2 -2
  47. package/dist-esm/version.mjs +3 -3
  48. package/dist-esm/version.mjs.map +1 -1
  49. package/package.json +7 -7
  50. package/src/index.ts +2 -0
  51. package/src/lib/TldrawEditor.tsx +3 -3
  52. package/src/lib/components/LiveCollaborators.tsx +5 -0
  53. package/src/lib/components/default-components/DefaultBrush.tsx +1 -0
  54. package/src/lib/components/default-components/DefaultCollaboratorHint.tsx +1 -0
  55. package/src/lib/components/default-components/DefaultCursor.tsx +1 -0
  56. package/src/lib/components/default-components/DefaultErrorFallback.tsx +5 -3
  57. package/src/lib/components/default-components/DefaultScribble.tsx +1 -0
  58. package/src/lib/components/default-components/DefaultShapeIndicator.tsx +1 -0
  59. package/src/lib/editor/Editor.ts +560 -276
  60. package/src/lib/editor/managers/FontManager.ts +26 -27
  61. package/src/lib/editor/shapes/ShapeUtil.ts +32 -5
  62. package/src/lib/exports/getSvgJsx.tsx +1 -0
  63. package/src/version.ts +3 -3
@@ -46,7 +46,7 @@ var __privateIn = (member, obj) => Object(obj) !== obj ? __typeError('Cannot use
46
46
  var __privateGet = (obj, member, getter) => (__accessCheck(obj, member, "read from private field"), getter ? getter.call(obj) : member.get(obj));
47
47
  var __privateSet = (obj, member, value, setter) => (__accessCheck(obj, member, "write to private field"), setter ? setter.call(obj, value) : member.set(obj, value), value);
48
48
  var __privateMethod = (obj, member, method) => (__accessCheck(obj, member, "access private method"), method);
49
- var __setMetaKeyTimeout_dec, __setCtrlKeyTimeout_dec, __setAltKeyTimeout_dec, __setShiftKeyTimeout_dec, _getIsReadonly_dec, _getIsFocused_dec, _getSharedOpacity_dec, _getSharedStyles_dec, __getSelectionSharedStyles_dec, __getBindingsIndexCache_dec, _getCurrentPageRenderingShapesSorted_dec, _getCurrentPageShapesSorted_dec, _getCurrentPageShapes_dec, _getCurrentPageBounds_dec, _getCulledShapes_dec, __notVisibleShapes_dec, __getShapeMaskedPageBoundsCache_dec, __getShapeMaskCache_dec, __getShapeClipPathCache_dec, __getShapePageBoundsCache_dec, __getShapePageTransformCache_dec, __getShapeHandlesCache_dec, __getShapeGeometryCache_dec, __getAllAssetsQuery_dec, _getCurrentPageShapeIdsSorted_dec, _getCurrentPageId_dec, _getPages_dec, __getAllPagesQuery_dec, _getRenderingShapes_dec, _getCollaboratorsOnCurrentPage_dec, _getCollaborators_dec, __getCollaboratorsQuery_dec, _getViewportPageBounds_dec, _getViewportScreenCenter_dec, _getViewportScreenBounds_dec, _getZoomLevel_dec, _getCameraForFollowing_dec, _getViewportPageBoundsForFollowing_dec, _getCamera_dec, __unsafe_getCameraId_dec, _getErasingShapes_dec, _getErasingShapeIds_dec, _getHintingShape_dec, _getHintingShapeIds_dec, _getHoveredShape_dec, _getHoveredShapeId_dec, _getRichTextEditor_dec, _getEditingShape_dec, _getEditingShapeId_dec, _getFocusedGroup_dec, _getFocusedGroupId_dec, _getSelectionRotatedScreenBounds_dec, _getSelectionRotatedPageBounds_dec, _getSelectionRotation_dec, _getSelectionPageBounds_dec, _getOnlySelectedShape_dec, _getOnlySelectedShapeId_dec, _getSelectedShapes_dec, _getSelectedShapeIds_dec, __getCurrentPageStateId_dec, _getCurrentPageState_dec, __getPageStatesQuery_dec, _getPageStates_dec, _getIsMenuOpen_dec, _getOpenMenus_dec, _getInstanceState_dec, _getDocumentSettings_dec, _getCurrentToolId_dec, _getCurrentTool_dec, _getPath_dec, _getCanRedo_dec, _getCanUndo_dec, _getIsShapeHiddenCache_dec, _a, _init;
49
+ var __setMetaKeyTimeout_dec, __setCtrlKeyTimeout_dec, __setAltKeyTimeout_dec, __setShiftKeyTimeout_dec, _getIsReadonly_dec, _getIsFocused_dec, _getSharedOpacity_dec, _getSharedStyles_dec, __getSelectionSharedStyles_dec, __getBindingsIndexCache_dec, _getCurrentPageRenderingShapesSorted_dec, _getCurrentPageShapesSorted_dec, _getCurrentPageShapes_dec, _getCurrentPageBounds_dec, _getCulledShapes_dec, __notVisibleShapes_dec, __getShapeMaskedPageBoundsCache_dec, __getShapeMaskCache_dec, __getShapeClipPathCache_dec, __getShapePageBoundsCache_dec, __getShapePageTransformCache_dec, __getShapeHandlesCache_dec, __getAllAssetsQuery_dec, _getCurrentPageShapeIdsSorted_dec, _getCurrentPageId_dec, _getPages_dec, __getAllPagesQuery_dec, _getRenderingShapes_dec, _getCollaboratorsOnCurrentPage_dec, _getCollaborators_dec, __getCollaboratorsQuery_dec, _getViewportPageBounds_dec, _getViewportScreenCenter_dec, _getViewportScreenBounds_dec, _getZoomLevel_dec, _getCameraForFollowing_dec, _getViewportPageBoundsForFollowing_dec, _getCamera_dec, __unsafe_getCameraId_dec, _getErasingShapes_dec, _getErasingShapeIds_dec, _getHintingShape_dec, _getHintingShapeIds_dec, _getHoveredShape_dec, _getHoveredShapeId_dec, _getRichTextEditor_dec, _getEditingShape_dec, _getEditingShapeId_dec, _getFocusedGroup_dec, _getFocusedGroupId_dec, _getSelectionRotatedScreenBounds_dec, _getSelectionRotatedPageBounds_dec, _getSelectionRotation_dec, _getSelectionPageBounds_dec, _getOnlySelectedShape_dec, _getOnlySelectedShapeId_dec, _getSelectedShapes_dec, _getSelectedShapeIds_dec, __getCurrentPageStateId_dec, _getCurrentPageState_dec, __getPageStatesQuery_dec, _getPageStates_dec, _getIsMenuOpen_dec, _getOpenMenus_dec, _getInstanceState_dec, _getDocumentSettings_dec, _getCurrentToolId_dec, _getCurrentTool_dec, _getPath_dec, _getCanRedo_dec, _getCanUndo_dec, _getIsShapeHiddenCache_dec, _a, _init;
50
50
  import {
51
51
  EMPTY_ARRAY,
52
52
  atom,
@@ -99,6 +99,7 @@ import {
99
99
  structuredClone,
100
100
  uniqueId
101
101
  } from "@tldraw/utils";
102
+ import { Number } from "core-js";
102
103
  import EventEmitter from "eventemitter3";
103
104
  import {
104
105
  getSnapshot,
@@ -129,7 +130,7 @@ import { Vec } from "../primitives/Vec.mjs";
129
130
  import { EASINGS } from "../primitives/easings.mjs";
130
131
  import { Group2d } from "../primitives/geometry/Group2d.mjs";
131
132
  import { intersectPolygonPolygon } from "../primitives/intersect.mjs";
132
- import { PI2, approximately, areAnglesCompatible, clamp, pointInPolygon } from "../primitives/utils.mjs";
133
+ import { PI, approximately, areAnglesCompatible, clamp, pointInPolygon } from "../primitives/utils.mjs";
133
134
  import { SharedStyleMap } from "../utils/SharedStylesMap.mjs";
134
135
  import { dataUrlToFile } from "../utils/assets.mjs";
135
136
  import { debugFlags } from "../utils/debug-flags.mjs";
@@ -156,7 +157,7 @@ import { TextManager } from "./managers/TextManager.mjs";
156
157
  import { TickManager } from "./managers/TickManager.mjs";
157
158
  import { UserPreferencesManager } from "./managers/UserPreferencesManager.mjs";
158
159
  import { RootState } from "./tools/RootState.mjs";
159
- class Editor extends (_a = EventEmitter, _getIsShapeHiddenCache_dec = [computed], _getCanUndo_dec = [computed], _getCanRedo_dec = [computed], _getPath_dec = [computed], _getCurrentTool_dec = [computed], _getCurrentToolId_dec = [computed], _getDocumentSettings_dec = [computed], _getInstanceState_dec = [computed], _getOpenMenus_dec = [computed], _getIsMenuOpen_dec = [computed], _getPageStates_dec = [computed], __getPageStatesQuery_dec = [computed], _getCurrentPageState_dec = [computed], __getCurrentPageStateId_dec = [computed], _getSelectedShapeIds_dec = [computed], _getSelectedShapes_dec = [computed], _getOnlySelectedShapeId_dec = [computed], _getOnlySelectedShape_dec = [computed], _getSelectionPageBounds_dec = [computed], _getSelectionRotation_dec = [computed], _getSelectionRotatedPageBounds_dec = [computed], _getSelectionRotatedScreenBounds_dec = [computed], _getFocusedGroupId_dec = [computed], _getFocusedGroup_dec = [computed], _getEditingShapeId_dec = [computed], _getEditingShape_dec = [computed], _getRichTextEditor_dec = [computed], _getHoveredShapeId_dec = [computed], _getHoveredShape_dec = [computed], _getHintingShapeIds_dec = [computed], _getHintingShape_dec = [computed], _getErasingShapeIds_dec = [computed], _getErasingShapes_dec = [computed], __unsafe_getCameraId_dec = [computed], _getCamera_dec = [computed], _getViewportPageBoundsForFollowing_dec = [computed], _getCameraForFollowing_dec = [computed], _getZoomLevel_dec = [computed], _getViewportScreenBounds_dec = [computed], _getViewportScreenCenter_dec = [computed], _getViewportPageBounds_dec = [computed], __getCollaboratorsQuery_dec = [computed], _getCollaborators_dec = [computed], _getCollaboratorsOnCurrentPage_dec = [computed], _getRenderingShapes_dec = [computed], __getAllPagesQuery_dec = [computed], _getPages_dec = [computed], _getCurrentPageId_dec = [computed], _getCurrentPageShapeIdsSorted_dec = [computed], __getAllAssetsQuery_dec = [computed], __getShapeGeometryCache_dec = [computed], __getShapeHandlesCache_dec = [computed], __getShapePageTransformCache_dec = [computed], __getShapePageBoundsCache_dec = [computed], __getShapeClipPathCache_dec = [computed], __getShapeMaskCache_dec = [computed], __getShapeMaskedPageBoundsCache_dec = [computed], __notVisibleShapes_dec = [computed], _getCulledShapes_dec = [computed], _getCurrentPageBounds_dec = [computed], _getCurrentPageShapes_dec = [computed], _getCurrentPageShapesSorted_dec = [computed], _getCurrentPageRenderingShapesSorted_dec = [computed], __getBindingsIndexCache_dec = [computed], __getSelectionSharedStyles_dec = [computed], _getSharedStyles_dec = [computed({ isEqual: (a, b) => a.equals(b) })], _getSharedOpacity_dec = [computed], _getIsFocused_dec = [computed], _getIsReadonly_dec = [computed], __setShiftKeyTimeout_dec = [bind], __setAltKeyTimeout_dec = [bind], __setCtrlKeyTimeout_dec = [bind], __setMetaKeyTimeout_dec = [bind], _a) {
160
+ class Editor extends (_a = EventEmitter, _getIsShapeHiddenCache_dec = [computed], _getCanUndo_dec = [computed], _getCanRedo_dec = [computed], _getPath_dec = [computed], _getCurrentTool_dec = [computed], _getCurrentToolId_dec = [computed], _getDocumentSettings_dec = [computed], _getInstanceState_dec = [computed], _getOpenMenus_dec = [computed], _getIsMenuOpen_dec = [computed], _getPageStates_dec = [computed], __getPageStatesQuery_dec = [computed], _getCurrentPageState_dec = [computed], __getCurrentPageStateId_dec = [computed], _getSelectedShapeIds_dec = [computed], _getSelectedShapes_dec = [computed], _getOnlySelectedShapeId_dec = [computed], _getOnlySelectedShape_dec = [computed], _getSelectionPageBounds_dec = [computed], _getSelectionRotation_dec = [computed], _getSelectionRotatedPageBounds_dec = [computed], _getSelectionRotatedScreenBounds_dec = [computed], _getFocusedGroupId_dec = [computed], _getFocusedGroup_dec = [computed], _getEditingShapeId_dec = [computed], _getEditingShape_dec = [computed], _getRichTextEditor_dec = [computed], _getHoveredShapeId_dec = [computed], _getHoveredShape_dec = [computed], _getHintingShapeIds_dec = [computed], _getHintingShape_dec = [computed], _getErasingShapeIds_dec = [computed], _getErasingShapes_dec = [computed], __unsafe_getCameraId_dec = [computed], _getCamera_dec = [computed], _getViewportPageBoundsForFollowing_dec = [computed], _getCameraForFollowing_dec = [computed], _getZoomLevel_dec = [computed], _getViewportScreenBounds_dec = [computed], _getViewportScreenCenter_dec = [computed], _getViewportPageBounds_dec = [computed], __getCollaboratorsQuery_dec = [computed], _getCollaborators_dec = [computed], _getCollaboratorsOnCurrentPage_dec = [computed], _getRenderingShapes_dec = [computed], __getAllPagesQuery_dec = [computed], _getPages_dec = [computed], _getCurrentPageId_dec = [computed], _getCurrentPageShapeIdsSorted_dec = [computed], __getAllAssetsQuery_dec = [computed], __getShapeHandlesCache_dec = [computed], __getShapePageTransformCache_dec = [computed], __getShapePageBoundsCache_dec = [computed], __getShapeClipPathCache_dec = [computed], __getShapeMaskCache_dec = [computed], __getShapeMaskedPageBoundsCache_dec = [computed], __notVisibleShapes_dec = [computed], _getCulledShapes_dec = [computed], _getCurrentPageBounds_dec = [computed], _getCurrentPageShapes_dec = [computed], _getCurrentPageShapesSorted_dec = [computed], _getCurrentPageRenderingShapesSorted_dec = [computed], __getBindingsIndexCache_dec = [computed], __getSelectionSharedStyles_dec = [computed], _getSharedStyles_dec = [computed({ isEqual: (a, b) => a.equals(b) })], _getSharedOpacity_dec = [computed], _getIsFocused_dec = [computed], _getIsReadonly_dec = [computed], __setShiftKeyTimeout_dec = [bind], __setAltKeyTimeout_dec = [bind], __setCtrlKeyTimeout_dec = [bind], __setMetaKeyTimeout_dec = [bind], _a) {
160
161
  constructor({
161
162
  store,
162
163
  user,
@@ -175,6 +176,7 @@ class Editor extends (_a = EventEmitter, _getIsShapeHiddenCache_dec = [computed]
175
176
  }) {
176
177
  super();
177
178
  __runInitializers(_init, 5, this);
179
+ __publicField(this, "id", uniqueId());
178
180
  __publicField(this, "_isShapeHiddenPredicate");
179
181
  __publicField(this, "options");
180
182
  __publicField(this, "contextId", uniqueId());
@@ -329,6 +331,8 @@ class Editor extends (_a = EventEmitter, _getIsShapeHiddenCache_dec = [computed]
329
331
  __publicField(this, "_cameraStateTimeoutRemaining", 0);
330
332
  /* @internal */
331
333
  __publicField(this, "_currentPageShapeIds");
334
+ /* --------------------- Shapes --------------------- */
335
+ __publicField(this, "_shapeGeometryCaches", {});
332
336
  // Parents and children
333
337
  /**
334
338
  * A cache of parents to children.
@@ -3279,16 +3283,6 @@ class Editor extends (_a = EventEmitter, _getIsShapeHiddenCache_dec = [computed]
3279
3283
  async uploadAsset(asset, file, abortSignal) {
3280
3284
  return await this.store.props.assets.upload(asset, file, abortSignal);
3281
3285
  }
3282
- _getShapeGeometryCache() {
3283
- return this.store.createComputedCache(
3284
- "bounds",
3285
- (shape) => {
3286
- this.fonts.trackFontsForShape(shape);
3287
- return this.getShapeUtil(shape).getGeometry(shape);
3288
- },
3289
- { areRecordsEqual: (a, b) => a.props === b.props }
3290
- );
3291
- }
3292
3286
  /**
3293
3287
  * Get the geometry of a shape.
3294
3288
  *
@@ -3296,14 +3290,29 @@ class Editor extends (_a = EventEmitter, _getIsShapeHiddenCache_dec = [computed]
3296
3290
  * ```ts
3297
3291
  * editor.getShapeGeometry(myShape)
3298
3292
  * editor.getShapeGeometry(myShapeId)
3293
+ * editor.getShapeGeometry(myShapeId, { context: "arrow" })
3299
3294
  * ```
3300
3295
  *
3301
3296
  * @param shape - The shape (or shape id) to get the geometry for.
3297
+ * @param opts - Additional options about the request for geometry. Passed to {@link ShapeUtil.getGeometry}.
3302
3298
  *
3303
3299
  * @public
3304
3300
  */
3305
- getShapeGeometry(shape) {
3306
- return this._getShapeGeometryCache().get(typeof shape === "string" ? shape : shape.id);
3301
+ getShapeGeometry(shape, opts) {
3302
+ const context = opts?.context ?? "none";
3303
+ if (!this._shapeGeometryCaches[context]) {
3304
+ this._shapeGeometryCaches[context] = this.store.createComputedCache(
3305
+ "bounds",
3306
+ (shape2) => {
3307
+ this.fonts.trackFontsForShape(shape2);
3308
+ return this.getShapeUtil(shape2).getGeometry(shape2, opts);
3309
+ },
3310
+ { areRecordsEqual: (a, b) => a.props === b.props }
3311
+ );
3312
+ }
3313
+ return this._shapeGeometryCaches[context].get(
3314
+ typeof shape === "string" ? shape : shape.id
3315
+ );
3307
3316
  }
3308
3317
  _getShapeHandlesCache() {
3309
3318
  return this.store.createComputedCache("handles", (shape) => {
@@ -4369,27 +4378,28 @@ class Editor extends (_a = EventEmitter, _getIsShapeHiddenCache_dec = [computed]
4369
4378
  });
4370
4379
  return this;
4371
4380
  }
4381
+ // Gets a shape partial that includes life cycle changes: on translate start, on translate, on translate end
4372
4382
  getChangesToTranslateShape(initialShape, newShapeCoords) {
4373
4383
  let workingShape = initialShape;
4374
4384
  const util = this.getShapeUtil(initialShape);
4375
- workingShape = applyPartialToRecordWithProps(
4376
- workingShape,
4377
- util.onTranslateStart?.(workingShape) ?? void 0
4378
- );
4385
+ const afterTranslateStart = util.onTranslateStart?.(workingShape);
4386
+ if (afterTranslateStart) {
4387
+ workingShape = applyPartialToRecordWithProps(workingShape, afterTranslateStart);
4388
+ }
4379
4389
  workingShape = applyPartialToRecordWithProps(workingShape, {
4380
4390
  id: initialShape.id,
4381
4391
  type: initialShape.type,
4382
4392
  x: newShapeCoords.x,
4383
4393
  y: newShapeCoords.y
4384
4394
  });
4385
- workingShape = applyPartialToRecordWithProps(
4386
- workingShape,
4387
- util.onTranslate?.(initialShape, workingShape) ?? void 0
4388
- );
4389
- workingShape = applyPartialToRecordWithProps(
4390
- workingShape,
4391
- util.onTranslateEnd?.(initialShape, workingShape) ?? void 0
4392
- );
4395
+ const afterTranslate = util.onTranslate?.(initialShape, workingShape);
4396
+ if (afterTranslate) {
4397
+ workingShape = applyPartialToRecordWithProps(workingShape, afterTranslate);
4398
+ }
4399
+ const afterTranslateEnd = util.onTranslateEnd?.(initialShape, workingShape);
4400
+ if (afterTranslateEnd) {
4401
+ workingShape = applyPartialToRecordWithProps(workingShape, afterTranslateEnd);
4402
+ }
4393
4403
  return workingShape;
4394
4404
  }
4395
4405
  /**
@@ -4696,6 +4706,30 @@ class Editor extends (_a = EventEmitter, _getIsShapeHiddenCache_dec = [computed]
4696
4706
  if (changes) this.updateShapes(changes);
4697
4707
  return this;
4698
4708
  }
4709
+ /**
4710
+ * @internal
4711
+ */
4712
+ collectShapesViaArrowBindings(info) {
4713
+ const { initialShapes, resultShapes, resultBounds, bindings, visited } = info;
4714
+ for (const binding of bindings) {
4715
+ for (const id of [binding.fromId, binding.toId]) {
4716
+ if (!visited.has(id)) {
4717
+ const aligningShape = initialShapes.find((s) => s.id === id);
4718
+ if (aligningShape && !visited.has(aligningShape.id)) {
4719
+ visited.add(aligningShape.id);
4720
+ const shapePageBounds = this.getShapePageBounds(aligningShape);
4721
+ if (!shapePageBounds) continue;
4722
+ resultShapes.push(aligningShape);
4723
+ resultBounds.push(shapePageBounds);
4724
+ this.collectShapesViaArrowBindings({
4725
+ ...info,
4726
+ bindings: this.getBindingsInvolvingShape(aligningShape, "arrow")
4727
+ });
4728
+ }
4729
+ }
4730
+ }
4731
+ }
4732
+ }
4699
4733
  /**
4700
4734
  * Flip shape positions.
4701
4735
  *
@@ -4711,35 +4745,52 @@ class Editor extends (_a = EventEmitter, _getIsShapeHiddenCache_dec = [computed]
4711
4745
  * @public
4712
4746
  */
4713
4747
  flipShapes(shapes, operation) {
4714
- const ids = typeof shapes[0] === "string" ? shapes : shapes.map((s) => s.id);
4715
4748
  if (this.getIsReadonly()) return this;
4716
- let shapesToFlip = compact(ids.map((id) => this.getShape(id)));
4749
+ const ids = typeof shapes[0] === "string" ? shapes : shapes.map((s) => s.id);
4750
+ const shapesToFlipFirstPass = compact(ids.map((id) => this.getShape(id)));
4751
+ for (const shape of shapesToFlipFirstPass) {
4752
+ if (this.isShapeOfType(shape, "group")) {
4753
+ const childrenOfGroups = compact(
4754
+ this.getSortedChildIdsForParent(shape.id).map((id) => this.getShape(id))
4755
+ );
4756
+ shapesToFlipFirstPass.push(...childrenOfGroups);
4757
+ }
4758
+ }
4759
+ const shapesToFlip = [];
4760
+ const allBounds = [];
4761
+ for (const shape of shapesToFlipFirstPass) {
4762
+ const util = this.getShapeUtil(shape);
4763
+ if (!util.canBeLaidOut(shape, {
4764
+ type: "flip",
4765
+ shapes: shapesToFlipFirstPass
4766
+ })) {
4767
+ continue;
4768
+ }
4769
+ const pageBounds = this.getShapePageBounds(shape);
4770
+ const localBounds = this.getShapeGeometry(shape).bounds;
4771
+ const pageTransform = this.getShapePageTransform(shape.id);
4772
+ if (!(pageBounds && localBounds && pageTransform)) continue;
4773
+ shapesToFlip.push({
4774
+ shape,
4775
+ localBounds,
4776
+ pageTransform,
4777
+ isAspectRatioLocked: util.isAspectRatioLocked(shape)
4778
+ });
4779
+ allBounds.push(pageBounds);
4780
+ }
4717
4781
  if (!shapesToFlip.length) return this;
4718
- shapesToFlip = compact(
4719
- shapesToFlip.map((shape) => {
4720
- if (this.isShapeOfType(shape, "group")) {
4721
- return this.getSortedChildIdsForParent(shape.id).map((id) => this.getShape(id));
4722
- }
4723
- return shape;
4724
- }).flat()
4725
- );
4726
- const scaleOriginPage = Box.Common(
4727
- compact(shapesToFlip.map((id) => this.getShapePageBounds(id)))
4728
- ).center;
4782
+ const scaleOriginPage = Box.Common(allBounds).center;
4729
4783
  this.run(() => {
4730
- for (const shape of shapesToFlip) {
4731
- const bounds = this.getShapeGeometry(shape).bounds;
4732
- const initialPageTransform = this.getShapePageTransform(shape.id);
4733
- if (!initialPageTransform) continue;
4784
+ for (const { shape, localBounds, pageTransform, isAspectRatioLocked } of shapesToFlip) {
4734
4785
  this.resizeShape(
4735
4786
  shape.id,
4736
4787
  { x: operation === "horizontal" ? -1 : 1, y: operation === "vertical" ? -1 : 1 },
4737
4788
  {
4738
- initialBounds: bounds,
4739
- initialPageTransform,
4789
+ initialBounds: localBounds,
4790
+ initialPageTransform: pageTransform,
4740
4791
  initialShape: shape,
4792
+ isAspectRatioLocked,
4741
4793
  mode: "scale_shape",
4742
- isAspectRatioLocked: this.getShapeUtil(shape).isAspectRatioLocked(shape),
4743
4794
  scaleOrigin: scaleOriginPage,
4744
4795
  scaleAxisRotation: 0
4745
4796
  }
@@ -4766,15 +4817,40 @@ class Editor extends (_a = EventEmitter, _getIsShapeHiddenCache_dec = [computed]
4766
4817
  stackShapes(shapes, operation, gap) {
4767
4818
  const ids = typeof shapes[0] === "string" ? shapes : shapes.map((s) => s.id);
4768
4819
  if (this.getIsReadonly()) return this;
4769
- const shapesToStack = ids.map((id) => this.getShape(id)).filter((shape) => {
4770
- if (!shape) return false;
4771
- return this.getShapeUtil(shape).canBeLaidOut(shape);
4772
- });
4773
- const len = shapesToStack.length;
4820
+ const shapesToStackFirstPass = compact(ids.map((id) => this.getShape(id)));
4821
+ const shapeClustersToStack = [];
4822
+ const allBounds = [];
4823
+ const visited = /* @__PURE__ */ new Set();
4824
+ for (const shape of shapesToStackFirstPass) {
4825
+ if (visited.has(shape.id)) continue;
4826
+ visited.add(shape.id);
4827
+ const shapePageBounds = this.getShapePageBounds(shape);
4828
+ if (!shapePageBounds) continue;
4829
+ if (!this.getShapeUtil(shape).canBeLaidOut?.(shape, {
4830
+ type: "stack",
4831
+ shapes: shapesToStackFirstPass
4832
+ })) {
4833
+ continue;
4834
+ }
4835
+ const shapesMovingTogether = [shape];
4836
+ const boundsOfShapesMovingTogether = [shapePageBounds];
4837
+ this.collectShapesViaArrowBindings({
4838
+ bindings: this.getBindingsToShape(shape.id, "arrow"),
4839
+ initialShapes: shapesToStackFirstPass,
4840
+ resultShapes: shapesMovingTogether,
4841
+ resultBounds: boundsOfShapesMovingTogether,
4842
+ visited
4843
+ });
4844
+ const commonPageBounds = Box.Common(boundsOfShapesMovingTogether);
4845
+ if (!commonPageBounds) continue;
4846
+ shapeClustersToStack.push({
4847
+ shapes: shapesMovingTogether,
4848
+ pageBounds: commonPageBounds
4849
+ });
4850
+ allBounds.push(commonPageBounds);
4851
+ }
4852
+ const len = shapeClustersToStack.length;
4774
4853
  if (gap === 0 && len < 3 || len < 2) return this;
4775
- const pageBounds = Object.fromEntries(
4776
- shapesToStack.map((shape) => [shape.id, this.getShapePageBounds(shape)])
4777
- );
4778
4854
  let val;
4779
4855
  let min;
4780
4856
  let max;
@@ -4790,57 +4866,55 @@ class Editor extends (_a = EventEmitter, _getIsShapeHiddenCache_dec = [computed]
4790
4866
  max = "maxY";
4791
4867
  dim = "height";
4792
4868
  }
4793
- let shapeGap;
4869
+ let shapeGap = 0;
4794
4870
  if (gap === 0) {
4795
- const gaps = [];
4796
- shapesToStack.sort((a, b) => pageBounds[a.id][min] - pageBounds[b.id][min]);
4871
+ const gaps = {};
4872
+ shapeClustersToStack.sort((a, b) => a.pageBounds[min] - b.pageBounds[min]);
4797
4873
  for (let i = 0; i < len - 1; i++) {
4798
- const shape = shapesToStack[i];
4799
- const nextShape = shapesToStack[i + 1];
4800
- const bounds = pageBounds[shape.id];
4801
- const nextBounds = pageBounds[nextShape.id];
4802
- const gap2 = nextBounds[min] - bounds[max];
4803
- const current = gaps.find((g) => g.gap === gap2);
4804
- if (current) {
4805
- current.count++;
4806
- } else {
4807
- gaps.push({ gap: gap2, count: 1 });
4874
+ const currCluster = shapeClustersToStack[i];
4875
+ const nextCluster = shapeClustersToStack[i + 1];
4876
+ const gap2 = nextCluster.pageBounds[min] - currCluster.pageBounds[max];
4877
+ if (!gaps[gap2]) {
4878
+ gaps[gap2] = 0;
4808
4879
  }
4880
+ gaps[gap2]++;
4809
4881
  }
4810
- let maxCount = 0;
4811
- gaps.forEach((g) => {
4812
- if (g.count > maxCount) {
4813
- maxCount = g.count;
4814
- shapeGap = g.gap;
4882
+ let maxCount = 1;
4883
+ for (const [gap2, count] of Object.entries(gaps)) {
4884
+ if (count > maxCount) {
4885
+ maxCount = count;
4886
+ shapeGap = parseFloat(gap2);
4815
4887
  }
4816
- });
4888
+ }
4817
4889
  if (maxCount === 1) {
4818
- shapeGap = Math.max(0, gaps.reduce((a, c) => a + c.gap * c.count, 0) / (len - 1));
4890
+ let totalCount = 0;
4891
+ for (const [gap2, count] of Object.entries(gaps)) {
4892
+ shapeGap += parseFloat(gap2) * count;
4893
+ totalCount += count;
4894
+ }
4895
+ shapeGap /= totalCount;
4819
4896
  }
4820
4897
  } else {
4821
4898
  shapeGap = gap;
4822
4899
  }
4823
4900
  const changes = [];
4824
- let v = pageBounds[shapesToStack[0].id][max];
4825
- shapesToStack.forEach((shape, i) => {
4826
- if (i === 0) return;
4827
- const delta = { x: 0, y: 0 };
4828
- delta[val] = v + shapeGap - pageBounds[shape.id][val];
4829
- const parent = this.getShapeParent(shape);
4830
- const localDelta = parent ? Vec.Rot(delta, -this.getShapePageTransform(parent).decompose().rotation) : delta;
4831
- const translateStartChanges = this.getShapeUtil(shape).onTranslateStart?.(shape);
4832
- changes.push(
4833
- translateStartChanges ? {
4834
- ...translateStartChanges,
4835
- [val]: shape[val] + localDelta[val]
4836
- } : {
4837
- id: shape.id,
4838
- type: shape.type,
4839
- [val]: shape[val] + localDelta[val]
4901
+ let v = shapeClustersToStack[0].pageBounds[max];
4902
+ for (let i = 1; i < shapeClustersToStack.length; i++) {
4903
+ const { shapes: shapes2, pageBounds } = shapeClustersToStack[i];
4904
+ const delta = new Vec();
4905
+ delta[val] = v + shapeGap - pageBounds[val];
4906
+ for (const shape of shapes2) {
4907
+ const shapeDelta = delta.clone();
4908
+ const parent = this.getShapeParent(shape);
4909
+ if (parent) {
4910
+ const parentTransform = this.getShapePageTransform(parent);
4911
+ if (parentTransform) shapeDelta.rot(-parentTransform.rotation());
4840
4912
  }
4841
- );
4842
- v += pageBounds[shape.id][dim] + shapeGap;
4843
- });
4913
+ shapeDelta.add(shape);
4914
+ changes.push(this.getChangesToTranslateShape(shape, shapeDelta));
4915
+ }
4916
+ v += pageBounds[dim] + shapeGap;
4917
+ }
4844
4918
  this.updateShapes(changes);
4845
4919
  return this;
4846
4920
  }
@@ -4858,91 +4932,101 @@ class Editor extends (_a = EventEmitter, _getIsShapeHiddenCache_dec = [computed]
4858
4932
  * @param gap - The padding to apply to the packed shapes. Defaults to 16.
4859
4933
  */
4860
4934
  packShapes(shapes, gap) {
4861
- const ids = typeof shapes[0] === "string" ? shapes : shapes.map((s) => s.id);
4862
4935
  if (this.getIsReadonly()) return this;
4863
- if (ids.length < 2) return this;
4864
- const shapesToPack = ids.map((id) => this.getShape(id)).filter((shape2) => {
4865
- if (!shape2) return false;
4866
- return this.getShapeUtil(shape2).canBeLaidOut(shape2);
4867
- });
4868
- const shapePageBounds = {};
4869
- const nextShapePageBounds = {};
4870
- let shape, bounds, area = 0;
4871
- for (let i = 0; i < shapesToPack.length; i++) {
4872
- shape = shapesToPack[i];
4873
- bounds = this.getShapePageBounds(shape);
4874
- shapePageBounds[shape.id] = bounds;
4875
- nextShapePageBounds[shape.id] = bounds.clone();
4876
- area += bounds.width * bounds.height;
4877
- }
4878
- const commonBounds = Box.Common(compact(Object.values(shapePageBounds)));
4936
+ const ids = typeof shapes[0] === "string" ? shapes : shapes.map((s) => s.id);
4937
+ const shapesToPackFirstPass = compact(ids.map((id) => this.getShape(id)));
4938
+ const shapeClustersToPack = [];
4939
+ const allBounds = [];
4940
+ const visited = /* @__PURE__ */ new Set();
4941
+ for (const shape of shapesToPackFirstPass) {
4942
+ if (visited.has(shape.id)) continue;
4943
+ visited.add(shape.id);
4944
+ const shapePageBounds = this.getShapePageBounds(shape);
4945
+ if (!shapePageBounds) continue;
4946
+ if (!this.getShapeUtil(shape).canBeLaidOut?.(shape, {
4947
+ type: "pack",
4948
+ shapes: shapesToPackFirstPass
4949
+ })) {
4950
+ continue;
4951
+ }
4952
+ const shapesMovingTogether = [shape];
4953
+ const boundsOfShapesMovingTogether = [shapePageBounds];
4954
+ this.collectShapesViaArrowBindings({
4955
+ bindings: this.getBindingsToShape(shape.id, "arrow"),
4956
+ initialShapes: shapesToPackFirstPass,
4957
+ resultShapes: shapesMovingTogether,
4958
+ resultBounds: boundsOfShapesMovingTogether,
4959
+ visited
4960
+ });
4961
+ const commonPageBounds = Box.Common(boundsOfShapesMovingTogether);
4962
+ if (!commonPageBounds) continue;
4963
+ shapeClustersToPack.push({
4964
+ shapes: shapesMovingTogether,
4965
+ pageBounds: commonPageBounds,
4966
+ nextPageBounds: commonPageBounds.clone()
4967
+ });
4968
+ allBounds.push(commonPageBounds);
4969
+ }
4970
+ if (shapeClustersToPack.length < 2) return this;
4971
+ let area = 0;
4972
+ for (const { pageBounds } of shapeClustersToPack) {
4973
+ area += pageBounds.width * pageBounds.height;
4974
+ }
4975
+ const commonBounds = Box.Common(allBounds);
4879
4976
  const maxWidth = commonBounds.width;
4880
- shapesToPack.sort((a, b) => shapePageBounds[b.id].height - shapePageBounds[a.id].height);
4977
+ shapeClustersToPack.sort((a, b) => a.pageBounds.width - b.pageBounds.width).sort((a, b) => a.pageBounds.height - b.pageBounds.height);
4881
4978
  const startWidth = Math.max(Math.ceil(Math.sqrt(area / 0.95)), maxWidth);
4882
4979
  const spaces = [new Box(commonBounds.x, commonBounds.y, startWidth, Infinity)];
4883
4980
  let width = 0;
4884
4981
  let height = 0;
4885
4982
  let space;
4886
4983
  let last2;
4887
- for (let i = 0; i < shapesToPack.length; i++) {
4888
- shape = shapesToPack[i];
4889
- bounds = nextShapePageBounds[shape.id];
4890
- for (let i2 = spaces.length - 1; i2 >= 0; i2--) {
4891
- space = spaces[i2];
4892
- if (bounds.width > space.width || bounds.height > space.height) continue;
4893
- bounds.x = space.x;
4894
- bounds.y = space.y;
4895
- height = Math.max(height, bounds.maxY);
4896
- width = Math.max(width, bounds.maxX);
4897
- if (bounds.width === space.width && bounds.height === space.height) {
4984
+ for (const { nextPageBounds } of shapeClustersToPack) {
4985
+ for (let i = spaces.length - 1; i >= 0; i--) {
4986
+ space = spaces[i];
4987
+ if (nextPageBounds.width > space.width || nextPageBounds.height > space.height) continue;
4988
+ nextPageBounds.x = space.x;
4989
+ nextPageBounds.y = space.y;
4990
+ height = Math.max(height, nextPageBounds.maxY);
4991
+ width = Math.max(width, nextPageBounds.maxX);
4992
+ if (nextPageBounds.width === space.width && nextPageBounds.height === space.height) {
4898
4993
  last2 = spaces.pop();
4899
- if (i2 < spaces.length) spaces[i2] = last2;
4900
- } else if (bounds.height === space.height) {
4901
- space.x += bounds.width + gap;
4902
- space.width -= bounds.width + gap;
4903
- } else if (bounds.width === space.width) {
4904
- space.y += bounds.height + gap;
4905
- space.height -= bounds.height + gap;
4994
+ if (i < spaces.length) spaces[i] = last2;
4995
+ } else if (nextPageBounds.height === space.height) {
4996
+ space.x += nextPageBounds.width + gap;
4997
+ space.width -= nextPageBounds.width + gap;
4998
+ } else if (nextPageBounds.width === space.width) {
4999
+ space.y += nextPageBounds.height + gap;
5000
+ space.height -= nextPageBounds.height + gap;
4906
5001
  } else {
4907
5002
  spaces.push(
4908
5003
  new Box(
4909
- space.x + (bounds.width + gap),
5004
+ space.x + (nextPageBounds.width + gap),
4910
5005
  space.y,
4911
- space.width - (bounds.width + gap),
4912
- bounds.height
5006
+ space.width - (nextPageBounds.width + gap),
5007
+ nextPageBounds.height
4913
5008
  )
4914
5009
  );
4915
- space.y += bounds.height + gap;
4916
- space.height -= bounds.height + gap;
5010
+ space.y += nextPageBounds.height + gap;
5011
+ space.height -= nextPageBounds.height + gap;
4917
5012
  }
4918
5013
  break;
4919
5014
  }
4920
5015
  }
4921
- const commonAfter = Box.Common(Object.values(nextShapePageBounds));
5016
+ const commonAfter = Box.Common(shapeClustersToPack.map((s) => s.nextPageBounds));
4922
5017
  const centerDelta = Vec.Sub(commonBounds.center, commonAfter.center);
4923
- let nextBounds;
4924
5018
  const changes = [];
4925
- for (let i = 0; i < shapesToPack.length; i++) {
4926
- shape = shapesToPack[i];
4927
- bounds = shapePageBounds[shape.id];
4928
- nextBounds = nextShapePageBounds[shape.id];
4929
- const delta = Vec.Sub(nextBounds.point, bounds.point).add(centerDelta);
4930
- const parentTransform = this.getShapeParentTransform(shape);
4931
- if (parentTransform) delta.rot(-parentTransform.rotation());
4932
- const change = {
4933
- id: shape.id,
4934
- type: shape.type,
4935
- x: shape.x + delta.x,
4936
- y: shape.y + delta.y
4937
- };
4938
- const translateStartChange = this.getShapeUtil(shape).onTranslateStart?.({
4939
- ...shape,
4940
- ...change
4941
- });
4942
- if (translateStartChange) {
4943
- changes.push({ ...change, ...translateStartChange });
4944
- } else {
4945
- changes.push(change);
5019
+ for (const { shapes: shapes2, pageBounds, nextPageBounds } of shapeClustersToPack) {
5020
+ const delta = Vec.Sub(nextPageBounds.point, pageBounds.point).add(centerDelta);
5021
+ for (const shape of shapes2) {
5022
+ const shapeDelta = delta.clone();
5023
+ const parent = this.getShapeParent(shape);
5024
+ if (parent) {
5025
+ const parentTransform = this.getShapeParentTransform(shape);
5026
+ if (parentTransform) shapeDelta.rot(-parentTransform.rotation());
5027
+ }
5028
+ shapeDelta.add(shape);
5029
+ changes.push(this.getChangesToTranslateShape(shape, shapeDelta));
4946
5030
  }
4947
5031
  }
4948
5032
  if (changes.length) {
@@ -4965,19 +5049,45 @@ class Editor extends (_a = EventEmitter, _getIsShapeHiddenCache_dec = [computed]
4965
5049
  * @public
4966
5050
  */
4967
5051
  alignShapes(shapes, operation) {
4968
- const ids = typeof shapes[0] === "string" ? shapes : shapes.map((s) => s.id);
4969
5052
  if (this.getIsReadonly()) return this;
4970
- if (ids.length < 2) return this;
4971
- const shapesToAlign = compact(ids.map((id) => this.getShape(id)));
4972
- const shapePageBounds = Object.fromEntries(
4973
- shapesToAlign.map((shape) => [shape.id, this.getShapePageBounds(shape)])
4974
- );
4975
- const commonBounds = Box.Common(compact(Object.values(shapePageBounds)));
5053
+ const ids = typeof shapes[0] === "string" ? shapes : shapes.map((s) => s.id);
5054
+ const shapesToAlignFirstPass = compact(ids.map((id) => this.getShape(id)));
5055
+ const shapeClustersToAlign = [];
5056
+ const allBounds = [];
5057
+ const visited = /* @__PURE__ */ new Set();
5058
+ for (const shape of shapesToAlignFirstPass) {
5059
+ if (visited.has(shape.id)) continue;
5060
+ visited.add(shape.id);
5061
+ const shapePageBounds = this.getShapePageBounds(shape);
5062
+ if (!shapePageBounds) continue;
5063
+ if (!this.getShapeUtil(shape).canBeLaidOut?.(shape, {
5064
+ type: "align",
5065
+ shapes: shapesToAlignFirstPass
5066
+ })) {
5067
+ continue;
5068
+ }
5069
+ const shapesMovingTogether = [shape];
5070
+ const boundsOfShapesMovingTogether = [shapePageBounds];
5071
+ this.collectShapesViaArrowBindings({
5072
+ bindings: this.getBindingsToShape(shape.id, "arrow"),
5073
+ initialShapes: shapesToAlignFirstPass,
5074
+ resultShapes: shapesMovingTogether,
5075
+ resultBounds: boundsOfShapesMovingTogether,
5076
+ visited
5077
+ });
5078
+ const commonPageBounds = Box.Common(boundsOfShapesMovingTogether);
5079
+ if (!commonPageBounds) continue;
5080
+ shapeClustersToAlign.push({
5081
+ shapes: shapesMovingTogether,
5082
+ pageBounds: commonPageBounds
5083
+ });
5084
+ allBounds.push(commonPageBounds);
5085
+ }
5086
+ if (shapeClustersToAlign.length < 2) return this;
5087
+ const commonBounds = Box.Common(allBounds);
4976
5088
  const changes = [];
4977
- shapesToAlign.forEach((shape) => {
4978
- const pageBounds = shapePageBounds[shape.id];
4979
- if (!pageBounds) return;
4980
- const delta = { x: 0, y: 0 };
5089
+ shapeClustersToAlign.forEach(({ shapes: shapes2, pageBounds }) => {
5090
+ const delta = new Vec();
4981
5091
  switch (operation) {
4982
5092
  case "top": {
4983
5093
  delta.y = commonBounds.minY - pageBounds.minY;
@@ -5004,9 +5114,16 @@ class Editor extends (_a = EventEmitter, _getIsShapeHiddenCache_dec = [computed]
5004
5114
  break;
5005
5115
  }
5006
5116
  }
5007
- const parent = this.getShapeParent(shape);
5008
- const localDelta = parent ? Vec.Rot(delta, -this.getShapePageTransform(parent).decompose().rotation) : delta;
5009
- changes.push(this.getChangesToTranslateShape(shape, Vec.Add(shape, localDelta)));
5117
+ for (const shape of shapes2) {
5118
+ const shapeDelta = delta.clone();
5119
+ const parent = this.getShapeParent(shape);
5120
+ if (parent) {
5121
+ const parentTransform = this.getShapePageTransform(parent);
5122
+ if (parentTransform) shapeDelta.rot(-parentTransform.rotation());
5123
+ }
5124
+ shapeDelta.add(shape);
5125
+ changes.push(this.getChangesToTranslateShape(shape, shapeDelta));
5126
+ }
5010
5127
  });
5011
5128
  this.updateShapes(changes);
5012
5129
  return this;
@@ -5026,47 +5143,95 @@ class Editor extends (_a = EventEmitter, _getIsShapeHiddenCache_dec = [computed]
5026
5143
  * @public
5027
5144
  */
5028
5145
  distributeShapes(shapes, operation) {
5029
- const ids = typeof shapes[0] === "string" ? shapes : shapes.map((s) => s.id);
5030
5146
  if (this.getIsReadonly()) return this;
5031
- if (ids.length < 3) return this;
5032
- const len = ids.length;
5033
- const shapesToDistribute = compact(ids.map((id) => this.getShape(id)));
5034
- const pageBounds = Object.fromEntries(
5035
- shapesToDistribute.map((shape) => [shape.id, this.getShapePageBounds(shape)])
5036
- );
5147
+ const ids = typeof shapes[0] === "string" ? shapes : shapes.map((s) => s.id);
5148
+ const shapesToDistributeFirstPass = compact(ids.map((id) => this.getShape(id)));
5149
+ const shapeClustersToDistribute = [];
5150
+ const allBounds = [];
5151
+ const visited = /* @__PURE__ */ new Set();
5152
+ for (const shape of shapesToDistributeFirstPass) {
5153
+ if (visited.has(shape.id)) continue;
5154
+ visited.add(shape.id);
5155
+ const shapePageBounds = this.getShapePageBounds(shape);
5156
+ if (!shapePageBounds) continue;
5157
+ if (!this.getShapeUtil(shape).canBeLaidOut?.(shape, {
5158
+ type: "distribute",
5159
+ shapes: shapesToDistributeFirstPass
5160
+ })) {
5161
+ continue;
5162
+ }
5163
+ const shapesMovingTogether = [shape];
5164
+ const boundsOfShapesMovingTogether = [shapePageBounds];
5165
+ this.collectShapesViaArrowBindings({
5166
+ bindings: this.getBindingsToShape(shape.id, "arrow"),
5167
+ initialShapes: shapesToDistributeFirstPass,
5168
+ resultShapes: shapesMovingTogether,
5169
+ resultBounds: boundsOfShapesMovingTogether,
5170
+ visited
5171
+ });
5172
+ const commonPageBounds = Box.Common(boundsOfShapesMovingTogether);
5173
+ if (!commonPageBounds) continue;
5174
+ shapeClustersToDistribute.push({
5175
+ shapes: shapesMovingTogether,
5176
+ pageBounds: commonPageBounds
5177
+ });
5178
+ allBounds.push(commonPageBounds);
5179
+ }
5180
+ if (shapeClustersToDistribute.length < 3) return this;
5037
5181
  let val;
5038
5182
  let min;
5039
5183
  let max;
5040
- let mid;
5041
5184
  let dim;
5042
5185
  if (operation === "horizontal") {
5043
5186
  val = "x";
5044
5187
  min = "minX";
5045
5188
  max = "maxX";
5046
- mid = "midX";
5047
5189
  dim = "width";
5048
5190
  } else {
5049
5191
  val = "y";
5050
5192
  min = "minY";
5051
5193
  max = "maxY";
5052
- mid = "midY";
5053
5194
  dim = "height";
5054
5195
  }
5055
5196
  const changes = [];
5056
- const first = shapesToDistribute.sort(
5057
- (a, b) => pageBounds[a.id][min] - pageBounds[b.id][min]
5058
- )[0];
5059
- const last2 = shapesToDistribute.sort((a, b) => pageBounds[b.id][max] - pageBounds[a.id][max])[0];
5060
- const midFirst = pageBounds[first.id][mid];
5061
- const step = (pageBounds[last2.id][mid] - midFirst) / (len - 1);
5062
- const v = midFirst + step;
5063
- shapesToDistribute.filter((shape) => shape !== first && shape !== last2).sort((a, b) => pageBounds[a.id][mid] - pageBounds[b.id][mid]).forEach((shape, i) => {
5064
- const delta = { x: 0, y: 0 };
5065
- delta[val] = v + step * i - pageBounds[shape.id][dim] / 2 - pageBounds[shape.id][val];
5066
- const parent = this.getShapeParent(shape);
5067
- const localDelta = parent ? Vec.Rot(delta, -this.getShapePageTransform(parent).rotation()) : delta;
5068
- changes.push(this.getChangesToTranslateShape(shape, Vec.Add(shape, localDelta)));
5197
+ const first = shapeClustersToDistribute.sort((a, b) => a.pageBounds[min] - b.pageBounds[min])[0];
5198
+ const last2 = shapeClustersToDistribute.sort((a, b) => b.pageBounds[max] - a.pageBounds[max])[0];
5199
+ if (first === last2) {
5200
+ const excludedShapeIds = new Set(first.shapes.map((s) => s.id));
5201
+ return this.distributeShapes(
5202
+ ids.filter((id) => !excludedShapeIds.has(id)),
5203
+ operation
5204
+ );
5205
+ }
5206
+ const shapeClustersToMove = shapeClustersToDistribute.filter((shape) => shape !== first && shape !== last2).sort((a, b) => {
5207
+ if (a.pageBounds[min] === b.pageBounds[min]) {
5208
+ return a.shapes[0].id < b.shapes[0].id ? -1 : 1;
5209
+ }
5210
+ return a.pageBounds[min] - b.pageBounds[min];
5069
5211
  });
5212
+ const maxFirst = first.pageBounds[max];
5213
+ const range = last2.pageBounds[min] - maxFirst;
5214
+ const summedShapeDimensions = shapeClustersToMove.reduce((acc, s) => acc + s.pageBounds[dim], 0);
5215
+ const gap = (range - summedShapeDimensions) / (shapeClustersToMove.length + 1);
5216
+ for (let v = maxFirst + gap, i = 0; i < shapeClustersToMove.length; i++) {
5217
+ const { shapes: shapes2, pageBounds } = shapeClustersToMove[i];
5218
+ const delta = new Vec();
5219
+ delta[val] = v - pageBounds[val];
5220
+ if (v + pageBounds[dim] > last2.pageBounds[max] - 1) {
5221
+ delta[val] = last2.pageBounds[max] - pageBounds[max] - 1;
5222
+ }
5223
+ for (const shape of shapes2) {
5224
+ const shapeDelta = delta.clone();
5225
+ const parent = this.getShapeParent(shape);
5226
+ if (parent) {
5227
+ const parentTransform = this.getShapePageTransform(parent);
5228
+ if (parentTransform) shapeDelta.rot(-parentTransform.rotation());
5229
+ }
5230
+ shapeDelta.add(shape);
5231
+ changes.push(this.getChangesToTranslateShape(shape, shapeDelta));
5232
+ }
5233
+ v += pageBounds[dim] + gap;
5234
+ }
5070
5235
  this.updateShapes(changes);
5071
5236
  return this;
5072
5237
  }
@@ -5087,59 +5252,78 @@ class Editor extends (_a = EventEmitter, _getIsShapeHiddenCache_dec = [computed]
5087
5252
  stretchShapes(shapes, operation) {
5088
5253
  const ids = typeof shapes[0] === "string" ? shapes : shapes.map((s) => s.id);
5089
5254
  if (this.getIsReadonly()) return this;
5090
- if (ids.length < 2) return this;
5091
- const shapesToStretch = compact(ids.map((id) => this.getShape(id)));
5092
- const shapeBounds = Object.fromEntries(ids.map((id) => [id, this.getShapeGeometry(id).bounds]));
5093
- const shapePageBounds = Object.fromEntries(ids.map((id) => [id, this.getShapePageBounds(id)]));
5094
- const commonBounds = Box.Common(compact(Object.values(shapePageBounds)));
5095
- switch (operation) {
5096
- case "vertical": {
5097
- this.run(() => {
5098
- for (const shape of shapesToStretch) {
5099
- const pageRotation = this.getShapePageTransform(shape).rotation();
5100
- if (pageRotation % PI2) continue;
5101
- const bounds = shapeBounds[shape.id];
5102
- const pageBounds = shapePageBounds[shape.id];
5103
- const localOffset = new Vec(0, commonBounds.minY - pageBounds.minY);
5104
- const parentTransform = this.getShapeParentTransform(shape);
5105
- if (parentTransform) localOffset.rot(-parentTransform.rotation());
5106
- const { x, y } = Vec.Add(localOffset, shape);
5107
- this.updateShapes([{ id: shape.id, type: shape.type, x, y }]);
5108
- const scale = new Vec(1, commonBounds.height / pageBounds.height);
5109
- this.resizeShape(shape.id, scale, {
5110
- initialBounds: bounds,
5111
- scaleOrigin: new Vec(pageBounds.center.x, commonBounds.minY),
5112
- isAspectRatioLocked: this.getShapeUtil(shape).isAspectRatioLocked(shape),
5113
- scaleAxisRotation: 0
5114
- });
5115
- }
5116
- });
5117
- break;
5118
- }
5119
- case "horizontal": {
5120
- this.run(() => {
5121
- for (const shape of shapesToStretch) {
5122
- const bounds = shapeBounds[shape.id];
5123
- const pageBounds = shapePageBounds[shape.id];
5124
- const pageRotation = this.getShapePageTransform(shape).rotation();
5125
- if (pageRotation % PI2) continue;
5126
- const localOffset = new Vec(commonBounds.minX - pageBounds.minX, 0);
5127
- const parentTransform = this.getShapeParentTransform(shape);
5128
- if (parentTransform) localOffset.rot(-parentTransform.rotation());
5129
- const { x, y } = Vec.Add(localOffset, shape);
5130
- this.updateShapes([{ id: shape.id, type: shape.type, x, y }]);
5131
- const scale = new Vec(commonBounds.width / pageBounds.width, 1);
5132
- this.resizeShape(shape.id, scale, {
5133
- initialBounds: bounds,
5134
- scaleOrigin: new Vec(commonBounds.minX, pageBounds.center.y),
5135
- isAspectRatioLocked: this.getShapeUtil(shape).isAspectRatioLocked(shape),
5136
- scaleAxisRotation: 0
5137
- });
5138
- }
5139
- });
5140
- break;
5255
+ const shapesToStretchFirstPass = compact(ids.map((id) => this.getShape(id))).filter(
5256
+ (s) => this.getShapePageTransform(s)?.rotation() % (PI / 2) === 0
5257
+ );
5258
+ const shapeClustersToStretch = [];
5259
+ const allBounds = [];
5260
+ const visited = /* @__PURE__ */ new Set();
5261
+ for (const shape of shapesToStretchFirstPass) {
5262
+ if (visited.has(shape.id)) continue;
5263
+ visited.add(shape.id);
5264
+ const shapePageBounds = this.getShapePageBounds(shape);
5265
+ if (!shapePageBounds) continue;
5266
+ const shapesMovingTogether = [shape];
5267
+ const boundsOfShapesMovingTogether = [shapePageBounds];
5268
+ if (!this.getShapeUtil(shape).canBeLaidOut?.(shape, {
5269
+ type: "stretch",
5270
+ shapes: shapesToStretchFirstPass
5271
+ })) {
5272
+ continue;
5141
5273
  }
5274
+ this.collectShapesViaArrowBindings({
5275
+ bindings: this.getBindingsToShape(shape.id, "arrow"),
5276
+ initialShapes: shapesToStretchFirstPass,
5277
+ resultShapes: shapesMovingTogether,
5278
+ resultBounds: boundsOfShapesMovingTogether,
5279
+ visited
5280
+ });
5281
+ const commonPageBounds = Box.Common(boundsOfShapesMovingTogether);
5282
+ if (!commonPageBounds) continue;
5283
+ shapeClustersToStretch.push({
5284
+ shapes: shapesMovingTogether,
5285
+ pageBounds: commonPageBounds
5286
+ });
5287
+ allBounds.push(commonPageBounds);
5288
+ }
5289
+ if (shapeClustersToStretch.length < 2) return this;
5290
+ const commonBounds = Box.Common(allBounds);
5291
+ let val;
5292
+ let min;
5293
+ let dim;
5294
+ if (operation === "horizontal") {
5295
+ val = "x";
5296
+ min = "minX";
5297
+ dim = "width";
5298
+ } else {
5299
+ val = "y";
5300
+ min = "minY";
5301
+ dim = "height";
5142
5302
  }
5303
+ this.run(() => {
5304
+ shapeClustersToStretch.forEach(({ shapes: shapes2, pageBounds }) => {
5305
+ const localOffset = new Vec();
5306
+ localOffset[val] = commonBounds[min] - pageBounds[min];
5307
+ const scaleOrigin = pageBounds.center.clone();
5308
+ scaleOrigin[val] = commonBounds[min];
5309
+ const scale = new Vec(1, 1);
5310
+ scale[val] = commonBounds[dim] / pageBounds[dim];
5311
+ for (const shape of shapes2) {
5312
+ const shapeLocalOffset = localOffset.clone();
5313
+ const parentTransform = this.getShapeParentTransform(shape);
5314
+ if (parentTransform) localOffset.rot(-parentTransform.rotation());
5315
+ shapeLocalOffset.add(shape);
5316
+ const changes = this.getChangesToTranslateShape(shape, shapeLocalOffset);
5317
+ this.updateShape(changes);
5318
+ this.resizeShape(shape.id, scale, {
5319
+ initialBounds: this.getShapeGeometry(shape).bounds,
5320
+ scaleOrigin,
5321
+ isAspectRatioLocked: this.getShapeUtil(shape).isAspectRatioLocked(shape),
5322
+ scaleAxisRotation: 0
5323
+ });
5324
+ }
5325
+ });
5326
+ });
5143
5327
  return this;
5144
5328
  }
5145
5329
  /**
@@ -7389,7 +7573,6 @@ __decorateElement(_init, 1, "getPages", _getPages_dec, Editor);
7389
7573
  __decorateElement(_init, 1, "getCurrentPageId", _getCurrentPageId_dec, Editor);
7390
7574
  __decorateElement(_init, 1, "getCurrentPageShapeIdsSorted", _getCurrentPageShapeIdsSorted_dec, Editor);
7391
7575
  __decorateElement(_init, 1, "_getAllAssetsQuery", __getAllAssetsQuery_dec, Editor);
7392
- __decorateElement(_init, 1, "_getShapeGeometryCache", __getShapeGeometryCache_dec, Editor);
7393
7576
  __decorateElement(_init, 1, "_getShapeHandlesCache", __getShapeHandlesCache_dec, Editor);
7394
7577
  __decorateElement(_init, 1, "_getShapePageTransformCache", __getShapePageTransformCache_dec, Editor);
7395
7578
  __decorateElement(_init, 1, "_getShapePageBoundsCache", __getShapePageBoundsCache_dec, Editor);
@@ -7479,7 +7662,7 @@ function withIsolatedShapes(editor, shapeIds, callback) {
7479
7662
  result = Result.err(error);
7480
7663
  }
7481
7664
  });
7482
- editor.store.applyDiff(reverseRecordsDiff(changes));
7665
+ editor.store.applyDiff(reverseRecordsDiff(changes), { runCallbacks: false });
7483
7666
  },
7484
7667
  { history: "ignore" }
7485
7668
  );