@tldraw/editor 3.9.0-internal.7f0e15f4f7d9 → 3.10.0-canary.12c0cb0549ca

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 (74) hide show
  1. package/CHANGELOG.md +90 -0
  2. package/README.md +1 -1
  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/getSvgJsx.js.map +2 -2
  24. package/dist-cjs/lib/hooks/usePeerIds.js.map +1 -1
  25. package/dist-cjs/lib/hooks/usePresence.js.map +1 -1
  26. package/dist-cjs/lib/utils/browserCanvasMaxSize.js +5 -0
  27. package/dist-cjs/lib/utils/browserCanvasMaxSize.js.map +2 -2
  28. package/dist-cjs/version.js +3 -3
  29. package/dist-cjs/version.js.map +1 -1
  30. package/dist-esm/index.d.mts +54 -9
  31. package/dist-esm/index.mjs +1 -1
  32. package/dist-esm/index.mjs.map +2 -2
  33. package/dist-esm/lib/TldrawEditor.mjs +2 -3
  34. package/dist-esm/lib/TldrawEditor.mjs.map +2 -2
  35. package/dist-esm/lib/components/LiveCollaborators.mjs +5 -0
  36. package/dist-esm/lib/components/LiveCollaborators.mjs.map +2 -2
  37. package/dist-esm/lib/components/default-components/DefaultBrush.mjs.map +2 -2
  38. package/dist-esm/lib/components/default-components/DefaultCollaboratorHint.mjs.map +2 -2
  39. package/dist-esm/lib/components/default-components/DefaultCursor.mjs.map +2 -2
  40. package/dist-esm/lib/components/default-components/DefaultErrorFallback.mjs +1 -1
  41. package/dist-esm/lib/components/default-components/DefaultErrorFallback.mjs.map +2 -2
  42. package/dist-esm/lib/components/default-components/DefaultScribble.mjs.map +2 -2
  43. package/dist-esm/lib/components/default-components/DefaultShapeIndicator.mjs.map +2 -2
  44. package/dist-esm/lib/editor/Editor.mjs +431 -249
  45. package/dist-esm/lib/editor/Editor.mjs.map +3 -3
  46. package/dist-esm/lib/editor/managers/FontManager.mjs +26 -27
  47. package/dist-esm/lib/editor/managers/FontManager.mjs.map +2 -2
  48. package/dist-esm/lib/editor/shapes/ShapeUtil.mjs +7 -2
  49. package/dist-esm/lib/editor/shapes/ShapeUtil.mjs.map +2 -2
  50. package/dist-esm/lib/exports/getSvgJsx.mjs.map +2 -2
  51. package/dist-esm/lib/hooks/usePeerIds.mjs.map +1 -1
  52. package/dist-esm/lib/hooks/usePresence.mjs.map +1 -1
  53. package/dist-esm/lib/utils/browserCanvasMaxSize.mjs +5 -0
  54. package/dist-esm/lib/utils/browserCanvasMaxSize.mjs.map +2 -2
  55. package/dist-esm/version.mjs +3 -3
  56. package/dist-esm/version.mjs.map +1 -1
  57. package/package.json +7 -7
  58. package/src/index.ts +2 -0
  59. package/src/lib/TldrawEditor.tsx +3 -3
  60. package/src/lib/components/LiveCollaborators.tsx +5 -0
  61. package/src/lib/components/default-components/DefaultBrush.tsx +1 -0
  62. package/src/lib/components/default-components/DefaultCollaboratorHint.tsx +1 -0
  63. package/src/lib/components/default-components/DefaultCursor.tsx +1 -0
  64. package/src/lib/components/default-components/DefaultErrorFallback.tsx +5 -3
  65. package/src/lib/components/default-components/DefaultScribble.tsx +1 -0
  66. package/src/lib/components/default-components/DefaultShapeIndicator.tsx +1 -0
  67. package/src/lib/editor/Editor.ts +560 -277
  68. package/src/lib/editor/managers/FontManager.ts +26 -27
  69. package/src/lib/editor/shapes/ShapeUtil.ts +32 -5
  70. package/src/lib/exports/getSvgJsx.tsx +1 -0
  71. package/src/lib/hooks/usePeerIds.ts +1 -1
  72. package/src/lib/hooks/usePresence.ts +2 -2
  73. package/src/lib/utils/browserCanvasMaxSize.ts +5 -3
  74. package/src/version.ts +3 -3
@@ -122,8 +122,8 @@ var import_TextManager = require("./managers/TextManager");
122
122
  var import_TickManager = require("./managers/TickManager");
123
123
  var import_UserPreferencesManager = require("./managers/UserPreferencesManager");
124
124
  var import_RootState = require("./tools/RootState");
125
- 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;
126
- class Editor extends (_a = import_eventemitter3.default, _getIsShapeHiddenCache_dec = [import_state.computed], _getCanUndo_dec = [import_state.computed], _getCanRedo_dec = [import_state.computed], _getPath_dec = [import_state.computed], _getCurrentTool_dec = [import_state.computed], _getCurrentToolId_dec = [import_state.computed], _getDocumentSettings_dec = [import_state.computed], _getInstanceState_dec = [import_state.computed], _getOpenMenus_dec = [import_state.computed], _getIsMenuOpen_dec = [import_state.computed], _getPageStates_dec = [import_state.computed], __getPageStatesQuery_dec = [import_state.computed], _getCurrentPageState_dec = [import_state.computed], __getCurrentPageStateId_dec = [import_state.computed], _getSelectedShapeIds_dec = [import_state.computed], _getSelectedShapes_dec = [import_state.computed], _getOnlySelectedShapeId_dec = [import_state.computed], _getOnlySelectedShape_dec = [import_state.computed], _getSelectionPageBounds_dec = [import_state.computed], _getSelectionRotation_dec = [import_state.computed], _getSelectionRotatedPageBounds_dec = [import_state.computed], _getSelectionRotatedScreenBounds_dec = [import_state.computed], _getFocusedGroupId_dec = [import_state.computed], _getFocusedGroup_dec = [import_state.computed], _getEditingShapeId_dec = [import_state.computed], _getEditingShape_dec = [import_state.computed], _getRichTextEditor_dec = [import_state.computed], _getHoveredShapeId_dec = [import_state.computed], _getHoveredShape_dec = [import_state.computed], _getHintingShapeIds_dec = [import_state.computed], _getHintingShape_dec = [import_state.computed], _getErasingShapeIds_dec = [import_state.computed], _getErasingShapes_dec = [import_state.computed], __unsafe_getCameraId_dec = [import_state.computed], _getCamera_dec = [import_state.computed], _getViewportPageBoundsForFollowing_dec = [import_state.computed], _getCameraForFollowing_dec = [import_state.computed], _getZoomLevel_dec = [import_state.computed], _getViewportScreenBounds_dec = [import_state.computed], _getViewportScreenCenter_dec = [import_state.computed], _getViewportPageBounds_dec = [import_state.computed], __getCollaboratorsQuery_dec = [import_state.computed], _getCollaborators_dec = [import_state.computed], _getCollaboratorsOnCurrentPage_dec = [import_state.computed], _getRenderingShapes_dec = [import_state.computed], __getAllPagesQuery_dec = [import_state.computed], _getPages_dec = [import_state.computed], _getCurrentPageId_dec = [import_state.computed], _getCurrentPageShapeIdsSorted_dec = [import_state.computed], __getAllAssetsQuery_dec = [import_state.computed], __getShapeGeometryCache_dec = [import_state.computed], __getShapeHandlesCache_dec = [import_state.computed], __getShapePageTransformCache_dec = [import_state.computed], __getShapePageBoundsCache_dec = [import_state.computed], __getShapeClipPathCache_dec = [import_state.computed], __getShapeMaskCache_dec = [import_state.computed], __getShapeMaskedPageBoundsCache_dec = [import_state.computed], __notVisibleShapes_dec = [import_state.computed], _getCulledShapes_dec = [import_state.computed], _getCurrentPageBounds_dec = [import_state.computed], _getCurrentPageShapes_dec = [import_state.computed], _getCurrentPageShapesSorted_dec = [import_state.computed], _getCurrentPageRenderingShapesSorted_dec = [import_state.computed], __getBindingsIndexCache_dec = [import_state.computed], __getSelectionSharedStyles_dec = [import_state.computed], _getSharedStyles_dec = [(0, import_state.computed)({ isEqual: (a, b) => a.equals(b) })], _getSharedOpacity_dec = [import_state.computed], _getIsFocused_dec = [import_state.computed], _getIsReadonly_dec = [import_state.computed], __setShiftKeyTimeout_dec = [import_utils.bind], __setAltKeyTimeout_dec = [import_utils.bind], __setCtrlKeyTimeout_dec = [import_utils.bind], __setMetaKeyTimeout_dec = [import_utils.bind], _a) {
125
+ 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;
126
+ class Editor extends (_a = import_eventemitter3.default, _getIsShapeHiddenCache_dec = [import_state.computed], _getCanUndo_dec = [import_state.computed], _getCanRedo_dec = [import_state.computed], _getPath_dec = [import_state.computed], _getCurrentTool_dec = [import_state.computed], _getCurrentToolId_dec = [import_state.computed], _getDocumentSettings_dec = [import_state.computed], _getInstanceState_dec = [import_state.computed], _getOpenMenus_dec = [import_state.computed], _getIsMenuOpen_dec = [import_state.computed], _getPageStates_dec = [import_state.computed], __getPageStatesQuery_dec = [import_state.computed], _getCurrentPageState_dec = [import_state.computed], __getCurrentPageStateId_dec = [import_state.computed], _getSelectedShapeIds_dec = [import_state.computed], _getSelectedShapes_dec = [import_state.computed], _getOnlySelectedShapeId_dec = [import_state.computed], _getOnlySelectedShape_dec = [import_state.computed], _getSelectionPageBounds_dec = [import_state.computed], _getSelectionRotation_dec = [import_state.computed], _getSelectionRotatedPageBounds_dec = [import_state.computed], _getSelectionRotatedScreenBounds_dec = [import_state.computed], _getFocusedGroupId_dec = [import_state.computed], _getFocusedGroup_dec = [import_state.computed], _getEditingShapeId_dec = [import_state.computed], _getEditingShape_dec = [import_state.computed], _getRichTextEditor_dec = [import_state.computed], _getHoveredShapeId_dec = [import_state.computed], _getHoveredShape_dec = [import_state.computed], _getHintingShapeIds_dec = [import_state.computed], _getHintingShape_dec = [import_state.computed], _getErasingShapeIds_dec = [import_state.computed], _getErasingShapes_dec = [import_state.computed], __unsafe_getCameraId_dec = [import_state.computed], _getCamera_dec = [import_state.computed], _getViewportPageBoundsForFollowing_dec = [import_state.computed], _getCameraForFollowing_dec = [import_state.computed], _getZoomLevel_dec = [import_state.computed], _getViewportScreenBounds_dec = [import_state.computed], _getViewportScreenCenter_dec = [import_state.computed], _getViewportPageBounds_dec = [import_state.computed], __getCollaboratorsQuery_dec = [import_state.computed], _getCollaborators_dec = [import_state.computed], _getCollaboratorsOnCurrentPage_dec = [import_state.computed], _getRenderingShapes_dec = [import_state.computed], __getAllPagesQuery_dec = [import_state.computed], _getPages_dec = [import_state.computed], _getCurrentPageId_dec = [import_state.computed], _getCurrentPageShapeIdsSorted_dec = [import_state.computed], __getAllAssetsQuery_dec = [import_state.computed], __getShapeHandlesCache_dec = [import_state.computed], __getShapePageTransformCache_dec = [import_state.computed], __getShapePageBoundsCache_dec = [import_state.computed], __getShapeClipPathCache_dec = [import_state.computed], __getShapeMaskCache_dec = [import_state.computed], __getShapeMaskedPageBoundsCache_dec = [import_state.computed], __notVisibleShapes_dec = [import_state.computed], _getCulledShapes_dec = [import_state.computed], _getCurrentPageBounds_dec = [import_state.computed], _getCurrentPageShapes_dec = [import_state.computed], _getCurrentPageShapesSorted_dec = [import_state.computed], _getCurrentPageRenderingShapesSorted_dec = [import_state.computed], __getBindingsIndexCache_dec = [import_state.computed], __getSelectionSharedStyles_dec = [import_state.computed], _getSharedStyles_dec = [(0, import_state.computed)({ isEqual: (a, b) => a.equals(b) })], _getSharedOpacity_dec = [import_state.computed], _getIsFocused_dec = [import_state.computed], _getIsReadonly_dec = [import_state.computed], __setShiftKeyTimeout_dec = [import_utils.bind], __setAltKeyTimeout_dec = [import_utils.bind], __setCtrlKeyTimeout_dec = [import_utils.bind], __setMetaKeyTimeout_dec = [import_utils.bind], _a) {
127
127
  constructor({
128
128
  store,
129
129
  user,
@@ -142,6 +142,7 @@ class Editor extends (_a = import_eventemitter3.default, _getIsShapeHiddenCache_
142
142
  }) {
143
143
  super();
144
144
  __runInitializers(_init, 5, this);
145
+ __publicField(this, "id", (0, import_utils.uniqueId)());
145
146
  __publicField(this, "_isShapeHiddenPredicate");
146
147
  __publicField(this, "options");
147
148
  __publicField(this, "contextId", (0, import_utils.uniqueId)());
@@ -296,6 +297,8 @@ class Editor extends (_a = import_eventemitter3.default, _getIsShapeHiddenCache_
296
297
  __publicField(this, "_cameraStateTimeoutRemaining", 0);
297
298
  /* @internal */
298
299
  __publicField(this, "_currentPageShapeIds");
300
+ /* --------------------- Shapes --------------------- */
301
+ __publicField(this, "_shapeGeometryCaches", {});
299
302
  // Parents and children
300
303
  /**
301
304
  * A cache of parents to children.
@@ -3246,16 +3249,6 @@ class Editor extends (_a = import_eventemitter3.default, _getIsShapeHiddenCache_
3246
3249
  async uploadAsset(asset, file, abortSignal) {
3247
3250
  return await this.store.props.assets.upload(asset, file, abortSignal);
3248
3251
  }
3249
- _getShapeGeometryCache() {
3250
- return this.store.createComputedCache(
3251
- "bounds",
3252
- (shape) => {
3253
- this.fonts.trackFontsForShape(shape);
3254
- return this.getShapeUtil(shape).getGeometry(shape);
3255
- },
3256
- { areRecordsEqual: (a, b) => a.props === b.props }
3257
- );
3258
- }
3259
3252
  /**
3260
3253
  * Get the geometry of a shape.
3261
3254
  *
@@ -3263,14 +3256,29 @@ class Editor extends (_a = import_eventemitter3.default, _getIsShapeHiddenCache_
3263
3256
  * ```ts
3264
3257
  * editor.getShapeGeometry(myShape)
3265
3258
  * editor.getShapeGeometry(myShapeId)
3259
+ * editor.getShapeGeometry(myShapeId, { context: "arrow" })
3266
3260
  * ```
3267
3261
  *
3268
3262
  * @param shape - The shape (or shape id) to get the geometry for.
3263
+ * @param opts - Additional options about the request for geometry. Passed to {@link ShapeUtil.getGeometry}.
3269
3264
  *
3270
3265
  * @public
3271
3266
  */
3272
- getShapeGeometry(shape) {
3273
- return this._getShapeGeometryCache().get(typeof shape === "string" ? shape : shape.id);
3267
+ getShapeGeometry(shape, opts) {
3268
+ const context = opts?.context ?? "none";
3269
+ if (!this._shapeGeometryCaches[context]) {
3270
+ this._shapeGeometryCaches[context] = this.store.createComputedCache(
3271
+ "bounds",
3272
+ (shape2) => {
3273
+ this.fonts.trackFontsForShape(shape2);
3274
+ return this.getShapeUtil(shape2).getGeometry(shape2, opts);
3275
+ },
3276
+ { areRecordsEqual: (a, b) => a.props === b.props }
3277
+ );
3278
+ }
3279
+ return this._shapeGeometryCaches[context].get(
3280
+ typeof shape === "string" ? shape : shape.id
3281
+ );
3274
3282
  }
3275
3283
  _getShapeHandlesCache() {
3276
3284
  return this.store.createComputedCache("handles", (shape) => {
@@ -4001,7 +4009,7 @@ class Editor extends (_a = import_eventemitter3.default, _getIsShapeHiddenCache_
4001
4009
  indices = sib ? (0, import_utils.getIndicesAbove)(sib.index, ids.length) : (0, import_utils.getIndices)(ids.length);
4002
4010
  }
4003
4011
  const invertedParentTransform = parentTransform.clone().invert();
4004
- const shapesToReparent = (0, import_utils.compact)(ids.map((id) => this.getShape(id)));
4012
+ const shapesToReparent = (0, import_utils.compact)(ids.map((id) => this.getShape(id))).sort(import_utils.sortByIndex);
4005
4013
  this.run(
4006
4014
  () => {
4007
4015
  for (let i = 0; i < shapesToReparent.length; i++) {
@@ -4336,27 +4344,28 @@ class Editor extends (_a = import_eventemitter3.default, _getIsShapeHiddenCache_
4336
4344
  });
4337
4345
  return this;
4338
4346
  }
4347
+ // Gets a shape partial that includes life cycle changes: on translate start, on translate, on translate end
4339
4348
  getChangesToTranslateShape(initialShape, newShapeCoords) {
4340
4349
  let workingShape = initialShape;
4341
4350
  const util = this.getShapeUtil(initialShape);
4342
- workingShape = applyPartialToRecordWithProps(
4343
- workingShape,
4344
- util.onTranslateStart?.(workingShape) ?? void 0
4345
- );
4351
+ const afterTranslateStart = util.onTranslateStart?.(workingShape);
4352
+ if (afterTranslateStart) {
4353
+ workingShape = applyPartialToRecordWithProps(workingShape, afterTranslateStart);
4354
+ }
4346
4355
  workingShape = applyPartialToRecordWithProps(workingShape, {
4347
4356
  id: initialShape.id,
4348
4357
  type: initialShape.type,
4349
4358
  x: newShapeCoords.x,
4350
4359
  y: newShapeCoords.y
4351
4360
  });
4352
- workingShape = applyPartialToRecordWithProps(
4353
- workingShape,
4354
- util.onTranslate?.(initialShape, workingShape) ?? void 0
4355
- );
4356
- workingShape = applyPartialToRecordWithProps(
4357
- workingShape,
4358
- util.onTranslateEnd?.(initialShape, workingShape) ?? void 0
4359
- );
4361
+ const afterTranslate = util.onTranslate?.(initialShape, workingShape);
4362
+ if (afterTranslate) {
4363
+ workingShape = applyPartialToRecordWithProps(workingShape, afterTranslate);
4364
+ }
4365
+ const afterTranslateEnd = util.onTranslateEnd?.(initialShape, workingShape);
4366
+ if (afterTranslateEnd) {
4367
+ workingShape = applyPartialToRecordWithProps(workingShape, afterTranslateEnd);
4368
+ }
4360
4369
  return workingShape;
4361
4370
  }
4362
4371
  /**
@@ -4663,6 +4672,30 @@ class Editor extends (_a = import_eventemitter3.default, _getIsShapeHiddenCache_
4663
4672
  if (changes) this.updateShapes(changes);
4664
4673
  return this;
4665
4674
  }
4675
+ /**
4676
+ * @internal
4677
+ */
4678
+ collectShapesViaArrowBindings(info) {
4679
+ const { initialShapes, resultShapes, resultBounds, bindings, visited } = info;
4680
+ for (const binding of bindings) {
4681
+ for (const id of [binding.fromId, binding.toId]) {
4682
+ if (!visited.has(id)) {
4683
+ const aligningShape = initialShapes.find((s) => s.id === id);
4684
+ if (aligningShape && !visited.has(aligningShape.id)) {
4685
+ visited.add(aligningShape.id);
4686
+ const shapePageBounds = this.getShapePageBounds(aligningShape);
4687
+ if (!shapePageBounds) continue;
4688
+ resultShapes.push(aligningShape);
4689
+ resultBounds.push(shapePageBounds);
4690
+ this.collectShapesViaArrowBindings({
4691
+ ...info,
4692
+ bindings: this.getBindingsInvolvingShape(aligningShape, "arrow")
4693
+ });
4694
+ }
4695
+ }
4696
+ }
4697
+ }
4698
+ }
4666
4699
  /**
4667
4700
  * Flip shape positions.
4668
4701
  *
@@ -4678,35 +4711,52 @@ class Editor extends (_a = import_eventemitter3.default, _getIsShapeHiddenCache_
4678
4711
  * @public
4679
4712
  */
4680
4713
  flipShapes(shapes, operation) {
4681
- const ids = typeof shapes[0] === "string" ? shapes : shapes.map((s) => s.id);
4682
4714
  if (this.getIsReadonly()) return this;
4683
- let shapesToFlip = (0, import_utils.compact)(ids.map((id) => this.getShape(id)));
4715
+ const ids = typeof shapes[0] === "string" ? shapes : shapes.map((s) => s.id);
4716
+ const shapesToFlipFirstPass = (0, import_utils.compact)(ids.map((id) => this.getShape(id)));
4717
+ for (const shape of shapesToFlipFirstPass) {
4718
+ if (this.isShapeOfType(shape, "group")) {
4719
+ const childrenOfGroups = (0, import_utils.compact)(
4720
+ this.getSortedChildIdsForParent(shape.id).map((id) => this.getShape(id))
4721
+ );
4722
+ shapesToFlipFirstPass.push(...childrenOfGroups);
4723
+ }
4724
+ }
4725
+ const shapesToFlip = [];
4726
+ const allBounds = [];
4727
+ for (const shape of shapesToFlipFirstPass) {
4728
+ const util = this.getShapeUtil(shape);
4729
+ if (!util.canBeLaidOut(shape, {
4730
+ type: "flip",
4731
+ shapes: shapesToFlipFirstPass
4732
+ })) {
4733
+ continue;
4734
+ }
4735
+ const pageBounds = this.getShapePageBounds(shape);
4736
+ const localBounds = this.getShapeGeometry(shape).bounds;
4737
+ const pageTransform = this.getShapePageTransform(shape.id);
4738
+ if (!(pageBounds && localBounds && pageTransform)) continue;
4739
+ shapesToFlip.push({
4740
+ shape,
4741
+ localBounds,
4742
+ pageTransform,
4743
+ isAspectRatioLocked: util.isAspectRatioLocked(shape)
4744
+ });
4745
+ allBounds.push(pageBounds);
4746
+ }
4684
4747
  if (!shapesToFlip.length) return this;
4685
- shapesToFlip = (0, import_utils.compact)(
4686
- shapesToFlip.map((shape) => {
4687
- if (this.isShapeOfType(shape, "group")) {
4688
- return this.getSortedChildIdsForParent(shape.id).map((id) => this.getShape(id));
4689
- }
4690
- return shape;
4691
- }).flat()
4692
- );
4693
- const scaleOriginPage = import_Box.Box.Common(
4694
- (0, import_utils.compact)(shapesToFlip.map((id) => this.getShapePageBounds(id)))
4695
- ).center;
4748
+ const scaleOriginPage = import_Box.Box.Common(allBounds).center;
4696
4749
  this.run(() => {
4697
- for (const shape of shapesToFlip) {
4698
- const bounds = this.getShapeGeometry(shape).bounds;
4699
- const initialPageTransform = this.getShapePageTransform(shape.id);
4700
- if (!initialPageTransform) continue;
4750
+ for (const { shape, localBounds, pageTransform, isAspectRatioLocked } of shapesToFlip) {
4701
4751
  this.resizeShape(
4702
4752
  shape.id,
4703
4753
  { x: operation === "horizontal" ? -1 : 1, y: operation === "vertical" ? -1 : 1 },
4704
4754
  {
4705
- initialBounds: bounds,
4706
- initialPageTransform,
4755
+ initialBounds: localBounds,
4756
+ initialPageTransform: pageTransform,
4707
4757
  initialShape: shape,
4758
+ isAspectRatioLocked,
4708
4759
  mode: "scale_shape",
4709
- isAspectRatioLocked: this.getShapeUtil(shape).isAspectRatioLocked(shape),
4710
4760
  scaleOrigin: scaleOriginPage,
4711
4761
  scaleAxisRotation: 0
4712
4762
  }
@@ -4733,15 +4783,40 @@ class Editor extends (_a = import_eventemitter3.default, _getIsShapeHiddenCache_
4733
4783
  stackShapes(shapes, operation, gap) {
4734
4784
  const ids = typeof shapes[0] === "string" ? shapes : shapes.map((s) => s.id);
4735
4785
  if (this.getIsReadonly()) return this;
4736
- const shapesToStack = ids.map((id) => this.getShape(id)).filter((shape) => {
4737
- if (!shape) return false;
4738
- return this.getShapeUtil(shape).canBeLaidOut(shape);
4739
- });
4740
- const len = shapesToStack.length;
4786
+ const shapesToStackFirstPass = (0, import_utils.compact)(ids.map((id) => this.getShape(id)));
4787
+ const shapeClustersToStack = [];
4788
+ const allBounds = [];
4789
+ const visited = /* @__PURE__ */ new Set();
4790
+ for (const shape of shapesToStackFirstPass) {
4791
+ if (visited.has(shape.id)) continue;
4792
+ visited.add(shape.id);
4793
+ const shapePageBounds = this.getShapePageBounds(shape);
4794
+ if (!shapePageBounds) continue;
4795
+ if (!this.getShapeUtil(shape).canBeLaidOut?.(shape, {
4796
+ type: "stack",
4797
+ shapes: shapesToStackFirstPass
4798
+ })) {
4799
+ continue;
4800
+ }
4801
+ const shapesMovingTogether = [shape];
4802
+ const boundsOfShapesMovingTogether = [shapePageBounds];
4803
+ this.collectShapesViaArrowBindings({
4804
+ bindings: this.getBindingsToShape(shape.id, "arrow"),
4805
+ initialShapes: shapesToStackFirstPass,
4806
+ resultShapes: shapesMovingTogether,
4807
+ resultBounds: boundsOfShapesMovingTogether,
4808
+ visited
4809
+ });
4810
+ const commonPageBounds = import_Box.Box.Common(boundsOfShapesMovingTogether);
4811
+ if (!commonPageBounds) continue;
4812
+ shapeClustersToStack.push({
4813
+ shapes: shapesMovingTogether,
4814
+ pageBounds: commonPageBounds
4815
+ });
4816
+ allBounds.push(commonPageBounds);
4817
+ }
4818
+ const len = shapeClustersToStack.length;
4741
4819
  if (gap === 0 && len < 3 || len < 2) return this;
4742
- const pageBounds = Object.fromEntries(
4743
- shapesToStack.map((shape) => [shape.id, this.getShapePageBounds(shape)])
4744
- );
4745
4820
  let val;
4746
4821
  let min;
4747
4822
  let max;
@@ -4757,57 +4832,55 @@ class Editor extends (_a = import_eventemitter3.default, _getIsShapeHiddenCache_
4757
4832
  max = "maxY";
4758
4833
  dim = "height";
4759
4834
  }
4760
- let shapeGap;
4835
+ let shapeGap = 0;
4761
4836
  if (gap === 0) {
4762
- const gaps = [];
4763
- shapesToStack.sort((a, b) => pageBounds[a.id][min] - pageBounds[b.id][min]);
4837
+ const gaps = {};
4838
+ shapeClustersToStack.sort((a, b) => a.pageBounds[min] - b.pageBounds[min]);
4764
4839
  for (let i = 0; i < len - 1; i++) {
4765
- const shape = shapesToStack[i];
4766
- const nextShape = shapesToStack[i + 1];
4767
- const bounds = pageBounds[shape.id];
4768
- const nextBounds = pageBounds[nextShape.id];
4769
- const gap2 = nextBounds[min] - bounds[max];
4770
- const current = gaps.find((g) => g.gap === gap2);
4771
- if (current) {
4772
- current.count++;
4773
- } else {
4774
- gaps.push({ gap: gap2, count: 1 });
4840
+ const currCluster = shapeClustersToStack[i];
4841
+ const nextCluster = shapeClustersToStack[i + 1];
4842
+ const gap2 = nextCluster.pageBounds[min] - currCluster.pageBounds[max];
4843
+ if (!gaps[gap2]) {
4844
+ gaps[gap2] = 0;
4775
4845
  }
4846
+ gaps[gap2]++;
4776
4847
  }
4777
- let maxCount = 0;
4778
- gaps.forEach((g) => {
4779
- if (g.count > maxCount) {
4780
- maxCount = g.count;
4781
- shapeGap = g.gap;
4848
+ let maxCount = 1;
4849
+ for (const [gap2, count] of Object.entries(gaps)) {
4850
+ if (count > maxCount) {
4851
+ maxCount = count;
4852
+ shapeGap = parseFloat(gap2);
4782
4853
  }
4783
- });
4854
+ }
4784
4855
  if (maxCount === 1) {
4785
- shapeGap = Math.max(0, gaps.reduce((a, c) => a + c.gap * c.count, 0) / (len - 1));
4856
+ let totalCount = 0;
4857
+ for (const [gap2, count] of Object.entries(gaps)) {
4858
+ shapeGap += parseFloat(gap2) * count;
4859
+ totalCount += count;
4860
+ }
4861
+ shapeGap /= totalCount;
4786
4862
  }
4787
4863
  } else {
4788
4864
  shapeGap = gap;
4789
4865
  }
4790
4866
  const changes = [];
4791
- let v = pageBounds[shapesToStack[0].id][max];
4792
- shapesToStack.forEach((shape, i) => {
4793
- if (i === 0) return;
4794
- const delta = { x: 0, y: 0 };
4795
- delta[val] = v + shapeGap - pageBounds[shape.id][val];
4796
- const parent = this.getShapeParent(shape);
4797
- const localDelta = parent ? import_Vec.Vec.Rot(delta, -this.getShapePageTransform(parent).decompose().rotation) : delta;
4798
- const translateStartChanges = this.getShapeUtil(shape).onTranslateStart?.(shape);
4799
- changes.push(
4800
- translateStartChanges ? {
4801
- ...translateStartChanges,
4802
- [val]: shape[val] + localDelta[val]
4803
- } : {
4804
- id: shape.id,
4805
- type: shape.type,
4806
- [val]: shape[val] + localDelta[val]
4867
+ let v = shapeClustersToStack[0].pageBounds[max];
4868
+ for (let i = 1; i < shapeClustersToStack.length; i++) {
4869
+ const { shapes: shapes2, pageBounds } = shapeClustersToStack[i];
4870
+ const delta = new import_Vec.Vec();
4871
+ delta[val] = v + shapeGap - pageBounds[val];
4872
+ for (const shape of shapes2) {
4873
+ const shapeDelta = delta.clone();
4874
+ const parent = this.getShapeParent(shape);
4875
+ if (parent) {
4876
+ const parentTransform = this.getShapePageTransform(parent);
4877
+ if (parentTransform) shapeDelta.rot(-parentTransform.rotation());
4807
4878
  }
4808
- );
4809
- v += pageBounds[shape.id][dim] + shapeGap;
4810
- });
4879
+ shapeDelta.add(shape);
4880
+ changes.push(this.getChangesToTranslateShape(shape, shapeDelta));
4881
+ }
4882
+ v += pageBounds[dim] + shapeGap;
4883
+ }
4811
4884
  this.updateShapes(changes);
4812
4885
  return this;
4813
4886
  }
@@ -4825,91 +4898,101 @@ class Editor extends (_a = import_eventemitter3.default, _getIsShapeHiddenCache_
4825
4898
  * @param gap - The padding to apply to the packed shapes. Defaults to 16.
4826
4899
  */
4827
4900
  packShapes(shapes, gap) {
4828
- const ids = typeof shapes[0] === "string" ? shapes : shapes.map((s) => s.id);
4829
4901
  if (this.getIsReadonly()) return this;
4830
- if (ids.length < 2) return this;
4831
- const shapesToPack = ids.map((id) => this.getShape(id)).filter((shape2) => {
4832
- if (!shape2) return false;
4833
- return this.getShapeUtil(shape2).canBeLaidOut(shape2);
4834
- });
4835
- const shapePageBounds = {};
4836
- const nextShapePageBounds = {};
4837
- let shape, bounds, area = 0;
4838
- for (let i = 0; i < shapesToPack.length; i++) {
4839
- shape = shapesToPack[i];
4840
- bounds = this.getShapePageBounds(shape);
4841
- shapePageBounds[shape.id] = bounds;
4842
- nextShapePageBounds[shape.id] = bounds.clone();
4843
- area += bounds.width * bounds.height;
4844
- }
4845
- const commonBounds = import_Box.Box.Common((0, import_utils.compact)(Object.values(shapePageBounds)));
4902
+ const ids = typeof shapes[0] === "string" ? shapes : shapes.map((s) => s.id);
4903
+ const shapesToPackFirstPass = (0, import_utils.compact)(ids.map((id) => this.getShape(id)));
4904
+ const shapeClustersToPack = [];
4905
+ const allBounds = [];
4906
+ const visited = /* @__PURE__ */ new Set();
4907
+ for (const shape of shapesToPackFirstPass) {
4908
+ if (visited.has(shape.id)) continue;
4909
+ visited.add(shape.id);
4910
+ const shapePageBounds = this.getShapePageBounds(shape);
4911
+ if (!shapePageBounds) continue;
4912
+ if (!this.getShapeUtil(shape).canBeLaidOut?.(shape, {
4913
+ type: "pack",
4914
+ shapes: shapesToPackFirstPass
4915
+ })) {
4916
+ continue;
4917
+ }
4918
+ const shapesMovingTogether = [shape];
4919
+ const boundsOfShapesMovingTogether = [shapePageBounds];
4920
+ this.collectShapesViaArrowBindings({
4921
+ bindings: this.getBindingsToShape(shape.id, "arrow"),
4922
+ initialShapes: shapesToPackFirstPass,
4923
+ resultShapes: shapesMovingTogether,
4924
+ resultBounds: boundsOfShapesMovingTogether,
4925
+ visited
4926
+ });
4927
+ const commonPageBounds = import_Box.Box.Common(boundsOfShapesMovingTogether);
4928
+ if (!commonPageBounds) continue;
4929
+ shapeClustersToPack.push({
4930
+ shapes: shapesMovingTogether,
4931
+ pageBounds: commonPageBounds,
4932
+ nextPageBounds: commonPageBounds.clone()
4933
+ });
4934
+ allBounds.push(commonPageBounds);
4935
+ }
4936
+ if (shapeClustersToPack.length < 2) return this;
4937
+ let area = 0;
4938
+ for (const { pageBounds } of shapeClustersToPack) {
4939
+ area += pageBounds.width * pageBounds.height;
4940
+ }
4941
+ const commonBounds = import_Box.Box.Common(allBounds);
4846
4942
  const maxWidth = commonBounds.width;
4847
- shapesToPack.sort((a, b) => shapePageBounds[b.id].height - shapePageBounds[a.id].height);
4943
+ shapeClustersToPack.sort((a, b) => a.pageBounds.width - b.pageBounds.width).sort((a, b) => a.pageBounds.height - b.pageBounds.height);
4848
4944
  const startWidth = Math.max(Math.ceil(Math.sqrt(area / 0.95)), maxWidth);
4849
4945
  const spaces = [new import_Box.Box(commonBounds.x, commonBounds.y, startWidth, Infinity)];
4850
4946
  let width = 0;
4851
4947
  let height = 0;
4852
4948
  let space;
4853
4949
  let last2;
4854
- for (let i = 0; i < shapesToPack.length; i++) {
4855
- shape = shapesToPack[i];
4856
- bounds = nextShapePageBounds[shape.id];
4857
- for (let i2 = spaces.length - 1; i2 >= 0; i2--) {
4858
- space = spaces[i2];
4859
- if (bounds.width > space.width || bounds.height > space.height) continue;
4860
- bounds.x = space.x;
4861
- bounds.y = space.y;
4862
- height = Math.max(height, bounds.maxY);
4863
- width = Math.max(width, bounds.maxX);
4864
- if (bounds.width === space.width && bounds.height === space.height) {
4950
+ for (const { nextPageBounds } of shapeClustersToPack) {
4951
+ for (let i = spaces.length - 1; i >= 0; i--) {
4952
+ space = spaces[i];
4953
+ if (nextPageBounds.width > space.width || nextPageBounds.height > space.height) continue;
4954
+ nextPageBounds.x = space.x;
4955
+ nextPageBounds.y = space.y;
4956
+ height = Math.max(height, nextPageBounds.maxY);
4957
+ width = Math.max(width, nextPageBounds.maxX);
4958
+ if (nextPageBounds.width === space.width && nextPageBounds.height === space.height) {
4865
4959
  last2 = spaces.pop();
4866
- if (i2 < spaces.length) spaces[i2] = last2;
4867
- } else if (bounds.height === space.height) {
4868
- space.x += bounds.width + gap;
4869
- space.width -= bounds.width + gap;
4870
- } else if (bounds.width === space.width) {
4871
- space.y += bounds.height + gap;
4872
- space.height -= bounds.height + gap;
4960
+ if (i < spaces.length) spaces[i] = last2;
4961
+ } else if (nextPageBounds.height === space.height) {
4962
+ space.x += nextPageBounds.width + gap;
4963
+ space.width -= nextPageBounds.width + gap;
4964
+ } else if (nextPageBounds.width === space.width) {
4965
+ space.y += nextPageBounds.height + gap;
4966
+ space.height -= nextPageBounds.height + gap;
4873
4967
  } else {
4874
4968
  spaces.push(
4875
4969
  new import_Box.Box(
4876
- space.x + (bounds.width + gap),
4970
+ space.x + (nextPageBounds.width + gap),
4877
4971
  space.y,
4878
- space.width - (bounds.width + gap),
4879
- bounds.height
4972
+ space.width - (nextPageBounds.width + gap),
4973
+ nextPageBounds.height
4880
4974
  )
4881
4975
  );
4882
- space.y += bounds.height + gap;
4883
- space.height -= bounds.height + gap;
4976
+ space.y += nextPageBounds.height + gap;
4977
+ space.height -= nextPageBounds.height + gap;
4884
4978
  }
4885
4979
  break;
4886
4980
  }
4887
4981
  }
4888
- const commonAfter = import_Box.Box.Common(Object.values(nextShapePageBounds));
4982
+ const commonAfter = import_Box.Box.Common(shapeClustersToPack.map((s) => s.nextPageBounds));
4889
4983
  const centerDelta = import_Vec.Vec.Sub(commonBounds.center, commonAfter.center);
4890
- let nextBounds;
4891
4984
  const changes = [];
4892
- for (let i = 0; i < shapesToPack.length; i++) {
4893
- shape = shapesToPack[i];
4894
- bounds = shapePageBounds[shape.id];
4895
- nextBounds = nextShapePageBounds[shape.id];
4896
- const delta = import_Vec.Vec.Sub(nextBounds.point, bounds.point).add(centerDelta);
4897
- const parentTransform = this.getShapeParentTransform(shape);
4898
- if (parentTransform) delta.rot(-parentTransform.rotation());
4899
- const change = {
4900
- id: shape.id,
4901
- type: shape.type,
4902
- x: shape.x + delta.x,
4903
- y: shape.y + delta.y
4904
- };
4905
- const translateStartChange = this.getShapeUtil(shape).onTranslateStart?.({
4906
- ...shape,
4907
- ...change
4908
- });
4909
- if (translateStartChange) {
4910
- changes.push({ ...change, ...translateStartChange });
4911
- } else {
4912
- changes.push(change);
4985
+ for (const { shapes: shapes2, pageBounds, nextPageBounds } of shapeClustersToPack) {
4986
+ const delta = import_Vec.Vec.Sub(nextPageBounds.point, pageBounds.point).add(centerDelta);
4987
+ for (const shape of shapes2) {
4988
+ const shapeDelta = delta.clone();
4989
+ const parent = this.getShapeParent(shape);
4990
+ if (parent) {
4991
+ const parentTransform = this.getShapeParentTransform(shape);
4992
+ if (parentTransform) shapeDelta.rot(-parentTransform.rotation());
4993
+ }
4994
+ shapeDelta.add(shape);
4995
+ changes.push(this.getChangesToTranslateShape(shape, shapeDelta));
4913
4996
  }
4914
4997
  }
4915
4998
  if (changes.length) {
@@ -4932,19 +5015,45 @@ class Editor extends (_a = import_eventemitter3.default, _getIsShapeHiddenCache_
4932
5015
  * @public
4933
5016
  */
4934
5017
  alignShapes(shapes, operation) {
4935
- const ids = typeof shapes[0] === "string" ? shapes : shapes.map((s) => s.id);
4936
5018
  if (this.getIsReadonly()) return this;
4937
- if (ids.length < 2) return this;
4938
- const shapesToAlign = (0, import_utils.compact)(ids.map((id) => this.getShape(id)));
4939
- const shapePageBounds = Object.fromEntries(
4940
- shapesToAlign.map((shape) => [shape.id, this.getShapePageBounds(shape)])
4941
- );
4942
- const commonBounds = import_Box.Box.Common((0, import_utils.compact)(Object.values(shapePageBounds)));
5019
+ const ids = typeof shapes[0] === "string" ? shapes : shapes.map((s) => s.id);
5020
+ const shapesToAlignFirstPass = (0, import_utils.compact)(ids.map((id) => this.getShape(id)));
5021
+ const shapeClustersToAlign = [];
5022
+ const allBounds = [];
5023
+ const visited = /* @__PURE__ */ new Set();
5024
+ for (const shape of shapesToAlignFirstPass) {
5025
+ if (visited.has(shape.id)) continue;
5026
+ visited.add(shape.id);
5027
+ const shapePageBounds = this.getShapePageBounds(shape);
5028
+ if (!shapePageBounds) continue;
5029
+ if (!this.getShapeUtil(shape).canBeLaidOut?.(shape, {
5030
+ type: "align",
5031
+ shapes: shapesToAlignFirstPass
5032
+ })) {
5033
+ continue;
5034
+ }
5035
+ const shapesMovingTogether = [shape];
5036
+ const boundsOfShapesMovingTogether = [shapePageBounds];
5037
+ this.collectShapesViaArrowBindings({
5038
+ bindings: this.getBindingsToShape(shape.id, "arrow"),
5039
+ initialShapes: shapesToAlignFirstPass,
5040
+ resultShapes: shapesMovingTogether,
5041
+ resultBounds: boundsOfShapesMovingTogether,
5042
+ visited
5043
+ });
5044
+ const commonPageBounds = import_Box.Box.Common(boundsOfShapesMovingTogether);
5045
+ if (!commonPageBounds) continue;
5046
+ shapeClustersToAlign.push({
5047
+ shapes: shapesMovingTogether,
5048
+ pageBounds: commonPageBounds
5049
+ });
5050
+ allBounds.push(commonPageBounds);
5051
+ }
5052
+ if (shapeClustersToAlign.length < 2) return this;
5053
+ const commonBounds = import_Box.Box.Common(allBounds);
4943
5054
  const changes = [];
4944
- shapesToAlign.forEach((shape) => {
4945
- const pageBounds = shapePageBounds[shape.id];
4946
- if (!pageBounds) return;
4947
- const delta = { x: 0, y: 0 };
5055
+ shapeClustersToAlign.forEach(({ shapes: shapes2, pageBounds }) => {
5056
+ const delta = new import_Vec.Vec();
4948
5057
  switch (operation) {
4949
5058
  case "top": {
4950
5059
  delta.y = commonBounds.minY - pageBounds.minY;
@@ -4971,9 +5080,16 @@ class Editor extends (_a = import_eventemitter3.default, _getIsShapeHiddenCache_
4971
5080
  break;
4972
5081
  }
4973
5082
  }
4974
- const parent = this.getShapeParent(shape);
4975
- const localDelta = parent ? import_Vec.Vec.Rot(delta, -this.getShapePageTransform(parent).decompose().rotation) : delta;
4976
- changes.push(this.getChangesToTranslateShape(shape, import_Vec.Vec.Add(shape, localDelta)));
5083
+ for (const shape of shapes2) {
5084
+ const shapeDelta = delta.clone();
5085
+ const parent = this.getShapeParent(shape);
5086
+ if (parent) {
5087
+ const parentTransform = this.getShapePageTransform(parent);
5088
+ if (parentTransform) shapeDelta.rot(-parentTransform.rotation());
5089
+ }
5090
+ shapeDelta.add(shape);
5091
+ changes.push(this.getChangesToTranslateShape(shape, shapeDelta));
5092
+ }
4977
5093
  });
4978
5094
  this.updateShapes(changes);
4979
5095
  return this;
@@ -4993,47 +5109,95 @@ class Editor extends (_a = import_eventemitter3.default, _getIsShapeHiddenCache_
4993
5109
  * @public
4994
5110
  */
4995
5111
  distributeShapes(shapes, operation) {
4996
- const ids = typeof shapes[0] === "string" ? shapes : shapes.map((s) => s.id);
4997
5112
  if (this.getIsReadonly()) return this;
4998
- if (ids.length < 3) return this;
4999
- const len = ids.length;
5000
- const shapesToDistribute = (0, import_utils.compact)(ids.map((id) => this.getShape(id)));
5001
- const pageBounds = Object.fromEntries(
5002
- shapesToDistribute.map((shape) => [shape.id, this.getShapePageBounds(shape)])
5003
- );
5113
+ const ids = typeof shapes[0] === "string" ? shapes : shapes.map((s) => s.id);
5114
+ const shapesToDistributeFirstPass = (0, import_utils.compact)(ids.map((id) => this.getShape(id)));
5115
+ const shapeClustersToDistribute = [];
5116
+ const allBounds = [];
5117
+ const visited = /* @__PURE__ */ new Set();
5118
+ for (const shape of shapesToDistributeFirstPass) {
5119
+ if (visited.has(shape.id)) continue;
5120
+ visited.add(shape.id);
5121
+ const shapePageBounds = this.getShapePageBounds(shape);
5122
+ if (!shapePageBounds) continue;
5123
+ if (!this.getShapeUtil(shape).canBeLaidOut?.(shape, {
5124
+ type: "distribute",
5125
+ shapes: shapesToDistributeFirstPass
5126
+ })) {
5127
+ continue;
5128
+ }
5129
+ const shapesMovingTogether = [shape];
5130
+ const boundsOfShapesMovingTogether = [shapePageBounds];
5131
+ this.collectShapesViaArrowBindings({
5132
+ bindings: this.getBindingsToShape(shape.id, "arrow"),
5133
+ initialShapes: shapesToDistributeFirstPass,
5134
+ resultShapes: shapesMovingTogether,
5135
+ resultBounds: boundsOfShapesMovingTogether,
5136
+ visited
5137
+ });
5138
+ const commonPageBounds = import_Box.Box.Common(boundsOfShapesMovingTogether);
5139
+ if (!commonPageBounds) continue;
5140
+ shapeClustersToDistribute.push({
5141
+ shapes: shapesMovingTogether,
5142
+ pageBounds: commonPageBounds
5143
+ });
5144
+ allBounds.push(commonPageBounds);
5145
+ }
5146
+ if (shapeClustersToDistribute.length < 3) return this;
5004
5147
  let val;
5005
5148
  let min;
5006
5149
  let max;
5007
- let mid;
5008
5150
  let dim;
5009
5151
  if (operation === "horizontal") {
5010
5152
  val = "x";
5011
5153
  min = "minX";
5012
5154
  max = "maxX";
5013
- mid = "midX";
5014
5155
  dim = "width";
5015
5156
  } else {
5016
5157
  val = "y";
5017
5158
  min = "minY";
5018
5159
  max = "maxY";
5019
- mid = "midY";
5020
5160
  dim = "height";
5021
5161
  }
5022
5162
  const changes = [];
5023
- const first = shapesToDistribute.sort(
5024
- (a, b) => pageBounds[a.id][min] - pageBounds[b.id][min]
5025
- )[0];
5026
- const last2 = shapesToDistribute.sort((a, b) => pageBounds[b.id][max] - pageBounds[a.id][max])[0];
5027
- const midFirst = pageBounds[first.id][mid];
5028
- const step = (pageBounds[last2.id][mid] - midFirst) / (len - 1);
5029
- const v = midFirst + step;
5030
- shapesToDistribute.filter((shape) => shape !== first && shape !== last2).sort((a, b) => pageBounds[a.id][mid] - pageBounds[b.id][mid]).forEach((shape, i) => {
5031
- const delta = { x: 0, y: 0 };
5032
- delta[val] = v + step * i - pageBounds[shape.id][dim] / 2 - pageBounds[shape.id][val];
5033
- const parent = this.getShapeParent(shape);
5034
- const localDelta = parent ? import_Vec.Vec.Rot(delta, -this.getShapePageTransform(parent).rotation()) : delta;
5035
- changes.push(this.getChangesToTranslateShape(shape, import_Vec.Vec.Add(shape, localDelta)));
5163
+ const first = shapeClustersToDistribute.sort((a, b) => a.pageBounds[min] - b.pageBounds[min])[0];
5164
+ const last2 = shapeClustersToDistribute.sort((a, b) => b.pageBounds[max] - a.pageBounds[max])[0];
5165
+ if (first === last2) {
5166
+ const excludedShapeIds = new Set(first.shapes.map((s) => s.id));
5167
+ return this.distributeShapes(
5168
+ ids.filter((id) => !excludedShapeIds.has(id)),
5169
+ operation
5170
+ );
5171
+ }
5172
+ const shapeClustersToMove = shapeClustersToDistribute.filter((shape) => shape !== first && shape !== last2).sort((a, b) => {
5173
+ if (a.pageBounds[min] === b.pageBounds[min]) {
5174
+ return a.shapes[0].id < b.shapes[0].id ? -1 : 1;
5175
+ }
5176
+ return a.pageBounds[min] - b.pageBounds[min];
5036
5177
  });
5178
+ const maxFirst = first.pageBounds[max];
5179
+ const range = last2.pageBounds[min] - maxFirst;
5180
+ const summedShapeDimensions = shapeClustersToMove.reduce((acc, s) => acc + s.pageBounds[dim], 0);
5181
+ const gap = (range - summedShapeDimensions) / (shapeClustersToMove.length + 1);
5182
+ for (let v = maxFirst + gap, i = 0; i < shapeClustersToMove.length; i++) {
5183
+ const { shapes: shapes2, pageBounds } = shapeClustersToMove[i];
5184
+ const delta = new import_Vec.Vec();
5185
+ delta[val] = v - pageBounds[val];
5186
+ if (v + pageBounds[dim] > last2.pageBounds[max] - 1) {
5187
+ delta[val] = last2.pageBounds[max] - pageBounds[max] - 1;
5188
+ }
5189
+ for (const shape of shapes2) {
5190
+ const shapeDelta = delta.clone();
5191
+ const parent = this.getShapeParent(shape);
5192
+ if (parent) {
5193
+ const parentTransform = this.getShapePageTransform(parent);
5194
+ if (parentTransform) shapeDelta.rot(-parentTransform.rotation());
5195
+ }
5196
+ shapeDelta.add(shape);
5197
+ changes.push(this.getChangesToTranslateShape(shape, shapeDelta));
5198
+ }
5199
+ v += pageBounds[dim] + gap;
5200
+ }
5037
5201
  this.updateShapes(changes);
5038
5202
  return this;
5039
5203
  }
@@ -5054,59 +5218,78 @@ class Editor extends (_a = import_eventemitter3.default, _getIsShapeHiddenCache_
5054
5218
  stretchShapes(shapes, operation) {
5055
5219
  const ids = typeof shapes[0] === "string" ? shapes : shapes.map((s) => s.id);
5056
5220
  if (this.getIsReadonly()) return this;
5057
- if (ids.length < 2) return this;
5058
- const shapesToStretch = (0, import_utils.compact)(ids.map((id) => this.getShape(id)));
5059
- const shapeBounds = Object.fromEntries(ids.map((id) => [id, this.getShapeGeometry(id).bounds]));
5060
- const shapePageBounds = Object.fromEntries(ids.map((id) => [id, this.getShapePageBounds(id)]));
5061
- const commonBounds = import_Box.Box.Common((0, import_utils.compact)(Object.values(shapePageBounds)));
5062
- switch (operation) {
5063
- case "vertical": {
5064
- this.run(() => {
5065
- for (const shape of shapesToStretch) {
5066
- const pageRotation = this.getShapePageTransform(shape).rotation();
5067
- if (pageRotation % import_utils2.PI2) continue;
5068
- const bounds = shapeBounds[shape.id];
5069
- const pageBounds = shapePageBounds[shape.id];
5070
- const localOffset = new import_Vec.Vec(0, commonBounds.minY - pageBounds.minY);
5071
- const parentTransform = this.getShapeParentTransform(shape);
5072
- if (parentTransform) localOffset.rot(-parentTransform.rotation());
5073
- const { x, y } = import_Vec.Vec.Add(localOffset, shape);
5074
- this.updateShapes([{ id: shape.id, type: shape.type, x, y }]);
5075
- const scale = new import_Vec.Vec(1, commonBounds.height / pageBounds.height);
5076
- this.resizeShape(shape.id, scale, {
5077
- initialBounds: bounds,
5078
- scaleOrigin: new import_Vec.Vec(pageBounds.center.x, commonBounds.minY),
5079
- isAspectRatioLocked: this.getShapeUtil(shape).isAspectRatioLocked(shape),
5080
- scaleAxisRotation: 0
5081
- });
5082
- }
5083
- });
5084
- break;
5085
- }
5086
- case "horizontal": {
5087
- this.run(() => {
5088
- for (const shape of shapesToStretch) {
5089
- const bounds = shapeBounds[shape.id];
5090
- const pageBounds = shapePageBounds[shape.id];
5091
- const pageRotation = this.getShapePageTransform(shape).rotation();
5092
- if (pageRotation % import_utils2.PI2) continue;
5093
- const localOffset = new import_Vec.Vec(commonBounds.minX - pageBounds.minX, 0);
5094
- const parentTransform = this.getShapeParentTransform(shape);
5095
- if (parentTransform) localOffset.rot(-parentTransform.rotation());
5096
- const { x, y } = import_Vec.Vec.Add(localOffset, shape);
5097
- this.updateShapes([{ id: shape.id, type: shape.type, x, y }]);
5098
- const scale = new import_Vec.Vec(commonBounds.width / pageBounds.width, 1);
5099
- this.resizeShape(shape.id, scale, {
5100
- initialBounds: bounds,
5101
- scaleOrigin: new import_Vec.Vec(commonBounds.minX, pageBounds.center.y),
5102
- isAspectRatioLocked: this.getShapeUtil(shape).isAspectRatioLocked(shape),
5103
- scaleAxisRotation: 0
5104
- });
5105
- }
5106
- });
5107
- break;
5221
+ const shapesToStretchFirstPass = (0, import_utils.compact)(ids.map((id) => this.getShape(id))).filter(
5222
+ (s) => this.getShapePageTransform(s)?.rotation() % (import_utils2.PI / 2) === 0
5223
+ );
5224
+ const shapeClustersToStretch = [];
5225
+ const allBounds = [];
5226
+ const visited = /* @__PURE__ */ new Set();
5227
+ for (const shape of shapesToStretchFirstPass) {
5228
+ if (visited.has(shape.id)) continue;
5229
+ visited.add(shape.id);
5230
+ const shapePageBounds = this.getShapePageBounds(shape);
5231
+ if (!shapePageBounds) continue;
5232
+ const shapesMovingTogether = [shape];
5233
+ const boundsOfShapesMovingTogether = [shapePageBounds];
5234
+ if (!this.getShapeUtil(shape).canBeLaidOut?.(shape, {
5235
+ type: "stretch",
5236
+ shapes: shapesToStretchFirstPass
5237
+ })) {
5238
+ continue;
5108
5239
  }
5240
+ this.collectShapesViaArrowBindings({
5241
+ bindings: this.getBindingsToShape(shape.id, "arrow"),
5242
+ initialShapes: shapesToStretchFirstPass,
5243
+ resultShapes: shapesMovingTogether,
5244
+ resultBounds: boundsOfShapesMovingTogether,
5245
+ visited
5246
+ });
5247
+ const commonPageBounds = import_Box.Box.Common(boundsOfShapesMovingTogether);
5248
+ if (!commonPageBounds) continue;
5249
+ shapeClustersToStretch.push({
5250
+ shapes: shapesMovingTogether,
5251
+ pageBounds: commonPageBounds
5252
+ });
5253
+ allBounds.push(commonPageBounds);
5254
+ }
5255
+ if (shapeClustersToStretch.length < 2) return this;
5256
+ const commonBounds = import_Box.Box.Common(allBounds);
5257
+ let val;
5258
+ let min;
5259
+ let dim;
5260
+ if (operation === "horizontal") {
5261
+ val = "x";
5262
+ min = "minX";
5263
+ dim = "width";
5264
+ } else {
5265
+ val = "y";
5266
+ min = "minY";
5267
+ dim = "height";
5109
5268
  }
5269
+ this.run(() => {
5270
+ shapeClustersToStretch.forEach(({ shapes: shapes2, pageBounds }) => {
5271
+ const localOffset = new import_Vec.Vec();
5272
+ localOffset[val] = commonBounds[min] - pageBounds[min];
5273
+ const scaleOrigin = pageBounds.center.clone();
5274
+ scaleOrigin[val] = commonBounds[min];
5275
+ const scale = new import_Vec.Vec(1, 1);
5276
+ scale[val] = commonBounds[dim] / pageBounds[dim];
5277
+ for (const shape of shapes2) {
5278
+ const shapeLocalOffset = localOffset.clone();
5279
+ const parentTransform = this.getShapeParentTransform(shape);
5280
+ if (parentTransform) localOffset.rot(-parentTransform.rotation());
5281
+ shapeLocalOffset.add(shape);
5282
+ const changes = this.getChangesToTranslateShape(shape, shapeLocalOffset);
5283
+ this.updateShape(changes);
5284
+ this.resizeShape(shape.id, scale, {
5285
+ initialBounds: this.getShapeGeometry(shape).bounds,
5286
+ scaleOrigin,
5287
+ isAspectRatioLocked: this.getShapeUtil(shape).isAspectRatioLocked(shape),
5288
+ scaleAxisRotation: 0
5289
+ });
5290
+ }
5291
+ });
5292
+ });
5110
5293
  return this;
5111
5294
  }
5112
5295
  /**
@@ -7356,7 +7539,6 @@ __decorateElement(_init, 1, "getPages", _getPages_dec, Editor);
7356
7539
  __decorateElement(_init, 1, "getCurrentPageId", _getCurrentPageId_dec, Editor);
7357
7540
  __decorateElement(_init, 1, "getCurrentPageShapeIdsSorted", _getCurrentPageShapeIdsSorted_dec, Editor);
7358
7541
  __decorateElement(_init, 1, "_getAllAssetsQuery", __getAllAssetsQuery_dec, Editor);
7359
- __decorateElement(_init, 1, "_getShapeGeometryCache", __getShapeGeometryCache_dec, Editor);
7360
7542
  __decorateElement(_init, 1, "_getShapeHandlesCache", __getShapeHandlesCache_dec, Editor);
7361
7543
  __decorateElement(_init, 1, "_getShapePageTransformCache", __getShapePageTransformCache_dec, Editor);
7362
7544
  __decorateElement(_init, 1, "_getShapePageBoundsCache", __getShapePageBoundsCache_dec, Editor);
@@ -7446,7 +7628,7 @@ function withIsolatedShapes(editor, shapeIds, callback) {
7446
7628
  result = import_utils.Result.err(error);
7447
7629
  }
7448
7630
  });
7449
- editor.store.applyDiff((0, import_store.reverseRecordsDiff)(changes));
7631
+ editor.store.applyDiff((0, import_store.reverseRecordsDiff)(changes), { runCallbacks: false });
7450
7632
  },
7451
7633
  { history: "ignore" }
7452
7634
  );