@tldraw/editor 3.9.0-internal.7f0e15f4f7d9 → 3.10.0-canary.075415a2bbc8

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