@tldraw/editor 3.9.0-internal.7f0e15f4f7d9 → 3.10.0-canary.3bf31007c5a7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (63) hide show
  1. package/CHANGELOG.md +90 -0
  2. package/README.md +1 -1
  3. package/dist-cjs/index.d.ts +43 -7
  4. package/dist-cjs/index.js +1 -1
  5. package/dist-cjs/index.js.map +2 -2
  6. package/dist-cjs/lib/TldrawEditor.js +2 -3
  7. package/dist-cjs/lib/TldrawEditor.js.map +2 -2
  8. package/dist-cjs/lib/components/LiveCollaborators.js +5 -0
  9. package/dist-cjs/lib/components/LiveCollaborators.js.map +2 -2
  10. package/dist-cjs/lib/components/default-components/DefaultBrush.js.map +2 -2
  11. package/dist-cjs/lib/components/default-components/DefaultCollaboratorHint.js.map +2 -2
  12. package/dist-cjs/lib/components/default-components/DefaultCursor.js.map +2 -2
  13. package/dist-cjs/lib/components/default-components/DefaultErrorFallback.js +1 -1
  14. package/dist-cjs/lib/components/default-components/DefaultErrorFallback.js.map +2 -2
  15. package/dist-cjs/lib/components/default-components/DefaultScribble.js.map +2 -2
  16. package/dist-cjs/lib/components/default-components/DefaultShapeIndicator.js.map +2 -2
  17. package/dist-cjs/lib/editor/Editor.js +435 -252
  18. package/dist-cjs/lib/editor/Editor.js.map +3 -3
  19. package/dist-cjs/lib/editor/managers/FontManager.js +25 -26
  20. package/dist-cjs/lib/editor/managers/FontManager.js.map +2 -2
  21. package/dist-cjs/lib/editor/shapes/ShapeUtil.js +7 -2
  22. package/dist-cjs/lib/editor/shapes/ShapeUtil.js.map +2 -2
  23. package/dist-cjs/lib/exports/getSvgJsx.js.map +2 -2
  24. package/dist-cjs/version.js +3 -3
  25. package/dist-cjs/version.js.map +1 -1
  26. package/dist-esm/index.d.mts +43 -7
  27. package/dist-esm/index.mjs +1 -1
  28. package/dist-esm/index.mjs.map +2 -2
  29. package/dist-esm/lib/TldrawEditor.mjs +2 -3
  30. package/dist-esm/lib/TldrawEditor.mjs.map +2 -2
  31. package/dist-esm/lib/components/LiveCollaborators.mjs +5 -0
  32. package/dist-esm/lib/components/LiveCollaborators.mjs.map +2 -2
  33. package/dist-esm/lib/components/default-components/DefaultBrush.mjs.map +2 -2
  34. package/dist-esm/lib/components/default-components/DefaultCollaboratorHint.mjs.map +2 -2
  35. package/dist-esm/lib/components/default-components/DefaultCursor.mjs.map +2 -2
  36. package/dist-esm/lib/components/default-components/DefaultErrorFallback.mjs +1 -1
  37. package/dist-esm/lib/components/default-components/DefaultErrorFallback.mjs.map +2 -2
  38. package/dist-esm/lib/components/default-components/DefaultScribble.mjs.map +2 -2
  39. package/dist-esm/lib/components/default-components/DefaultShapeIndicator.mjs.map +2 -2
  40. package/dist-esm/lib/editor/Editor.mjs +431 -248
  41. package/dist-esm/lib/editor/Editor.mjs.map +3 -3
  42. package/dist-esm/lib/editor/managers/FontManager.mjs +26 -27
  43. package/dist-esm/lib/editor/managers/FontManager.mjs.map +2 -2
  44. package/dist-esm/lib/editor/shapes/ShapeUtil.mjs +7 -2
  45. package/dist-esm/lib/editor/shapes/ShapeUtil.mjs.map +2 -2
  46. package/dist-esm/lib/exports/getSvgJsx.mjs.map +2 -2
  47. package/dist-esm/version.mjs +3 -3
  48. package/dist-esm/version.mjs.map +1 -1
  49. package/package.json +7 -7
  50. package/src/index.ts +2 -0
  51. package/src/lib/TldrawEditor.tsx +3 -3
  52. package/src/lib/components/LiveCollaborators.tsx +5 -0
  53. package/src/lib/components/default-components/DefaultBrush.tsx +1 -0
  54. package/src/lib/components/default-components/DefaultCollaboratorHint.tsx +1 -0
  55. package/src/lib/components/default-components/DefaultCursor.tsx +1 -0
  56. package/src/lib/components/default-components/DefaultErrorFallback.tsx +5 -3
  57. package/src/lib/components/default-components/DefaultScribble.tsx +1 -0
  58. package/src/lib/components/default-components/DefaultShapeIndicator.tsx +1 -0
  59. package/src/lib/editor/Editor.ts +560 -276
  60. package/src/lib/editor/managers/FontManager.ts +26 -27
  61. package/src/lib/editor/shapes/ShapeUtil.ts +32 -5
  62. package/src/lib/exports/getSvgJsx.tsx +1 -0
  63. package/src/version.ts +3 -3
@@ -80,6 +80,7 @@ var import_state = require("@tldraw/state");
80
80
  var import_store = require("@tldraw/store");
81
81
  var import_tlschema = require("@tldraw/tlschema");
82
82
  var import_utils = require("@tldraw/utils");
83
+ var import_core_js = require("core-js");
83
84
  var import_eventemitter3 = __toESM(require("eventemitter3"));
84
85
  var import_TLEditorSnapshot = require("../config/TLEditorSnapshot");
85
86
  var import_createTLUser = require("../config/createTLUser");
@@ -122,8 +123,8 @@ var import_TextManager = require("./managers/TextManager");
122
123
  var import_TickManager = require("./managers/TickManager");
123
124
  var import_UserPreferencesManager = require("./managers/UserPreferencesManager");
124
125
  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) {
126
+ 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;
127
+ 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
128
  constructor({
128
129
  store,
129
130
  user,
@@ -142,6 +143,7 @@ class Editor extends (_a = import_eventemitter3.default, _getIsShapeHiddenCache_
142
143
  }) {
143
144
  super();
144
145
  __runInitializers(_init, 5, this);
146
+ __publicField(this, "id", (0, import_utils.uniqueId)());
145
147
  __publicField(this, "_isShapeHiddenPredicate");
146
148
  __publicField(this, "options");
147
149
  __publicField(this, "contextId", (0, import_utils.uniqueId)());
@@ -296,6 +298,8 @@ class Editor extends (_a = import_eventemitter3.default, _getIsShapeHiddenCache_
296
298
  __publicField(this, "_cameraStateTimeoutRemaining", 0);
297
299
  /* @internal */
298
300
  __publicField(this, "_currentPageShapeIds");
301
+ /* --------------------- Shapes --------------------- */
302
+ __publicField(this, "_shapeGeometryCaches", {});
299
303
  // Parents and children
300
304
  /**
301
305
  * A cache of parents to children.
@@ -2143,9 +2147,9 @@ class Editor extends (_a = import_eventemitter3.default, _getIsShapeHiddenCache_
2143
2147
  this.stopFollowingUser();
2144
2148
  }
2145
2149
  const _point = import_Vec.Vec.Cast(point);
2146
- if (!Number.isFinite(_point.x)) _point.x = 0;
2147
- if (!Number.isFinite(_point.y)) _point.y = 0;
2148
- if (_point.z === void 0 || !Number.isFinite(_point.z)) point.z = this.getZoomLevel();
2150
+ if (!import_core_js.Number.isFinite(_point.x)) _point.x = 0;
2151
+ if (!import_core_js.Number.isFinite(_point.y)) _point.y = 0;
2152
+ if (_point.z === void 0 || !import_core_js.Number.isFinite(_point.z)) point.z = this.getZoomLevel();
2149
2153
  const camera = this.getConstrainedCamera(_point, opts);
2150
2154
  if (opts?.animation) {
2151
2155
  const { width, height } = this.getViewportScreenBounds();
@@ -3246,16 +3250,6 @@ class Editor extends (_a = import_eventemitter3.default, _getIsShapeHiddenCache_
3246
3250
  async uploadAsset(asset, file, abortSignal) {
3247
3251
  return await this.store.props.assets.upload(asset, file, abortSignal);
3248
3252
  }
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
3253
  /**
3260
3254
  * Get the geometry of a shape.
3261
3255
  *
@@ -3263,14 +3257,29 @@ class Editor extends (_a = import_eventemitter3.default, _getIsShapeHiddenCache_
3263
3257
  * ```ts
3264
3258
  * editor.getShapeGeometry(myShape)
3265
3259
  * editor.getShapeGeometry(myShapeId)
3260
+ * editor.getShapeGeometry(myShapeId, { context: "arrow" })
3266
3261
  * ```
3267
3262
  *
3268
3263
  * @param shape - The shape (or shape id) to get the geometry for.
3264
+ * @param opts - Additional options about the request for geometry. Passed to {@link ShapeUtil.getGeometry}.
3269
3265
  *
3270
3266
  * @public
3271
3267
  */
3272
- getShapeGeometry(shape) {
3273
- return this._getShapeGeometryCache().get(typeof shape === "string" ? shape : shape.id);
3268
+ getShapeGeometry(shape, opts) {
3269
+ const context = opts?.context ?? "none";
3270
+ if (!this._shapeGeometryCaches[context]) {
3271
+ this._shapeGeometryCaches[context] = this.store.createComputedCache(
3272
+ "bounds",
3273
+ (shape2) => {
3274
+ this.fonts.trackFontsForShape(shape2);
3275
+ return this.getShapeUtil(shape2).getGeometry(shape2, opts);
3276
+ },
3277
+ { areRecordsEqual: (a, b) => a.props === b.props }
3278
+ );
3279
+ }
3280
+ return this._shapeGeometryCaches[context].get(
3281
+ typeof shape === "string" ? shape : shape.id
3282
+ );
3274
3283
  }
3275
3284
  _getShapeHandlesCache() {
3276
3285
  return this.store.createComputedCache("handles", (shape) => {
@@ -4336,27 +4345,28 @@ class Editor extends (_a = import_eventemitter3.default, _getIsShapeHiddenCache_
4336
4345
  });
4337
4346
  return this;
4338
4347
  }
4348
+ // Gets a shape partial that includes life cycle changes: on translate start, on translate, on translate end
4339
4349
  getChangesToTranslateShape(initialShape, newShapeCoords) {
4340
4350
  let workingShape = initialShape;
4341
4351
  const util = this.getShapeUtil(initialShape);
4342
- workingShape = applyPartialToRecordWithProps(
4343
- workingShape,
4344
- util.onTranslateStart?.(workingShape) ?? void 0
4345
- );
4352
+ const afterTranslateStart = util.onTranslateStart?.(workingShape);
4353
+ if (afterTranslateStart) {
4354
+ workingShape = applyPartialToRecordWithProps(workingShape, afterTranslateStart);
4355
+ }
4346
4356
  workingShape = applyPartialToRecordWithProps(workingShape, {
4347
4357
  id: initialShape.id,
4348
4358
  type: initialShape.type,
4349
4359
  x: newShapeCoords.x,
4350
4360
  y: newShapeCoords.y
4351
4361
  });
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
- );
4362
+ const afterTranslate = util.onTranslate?.(initialShape, workingShape);
4363
+ if (afterTranslate) {
4364
+ workingShape = applyPartialToRecordWithProps(workingShape, afterTranslate);
4365
+ }
4366
+ const afterTranslateEnd = util.onTranslateEnd?.(initialShape, workingShape);
4367
+ if (afterTranslateEnd) {
4368
+ workingShape = applyPartialToRecordWithProps(workingShape, afterTranslateEnd);
4369
+ }
4360
4370
  return workingShape;
4361
4371
  }
4362
4372
  /**
@@ -4663,6 +4673,30 @@ class Editor extends (_a = import_eventemitter3.default, _getIsShapeHiddenCache_
4663
4673
  if (changes) this.updateShapes(changes);
4664
4674
  return this;
4665
4675
  }
4676
+ /**
4677
+ * @internal
4678
+ */
4679
+ collectShapesViaArrowBindings(info) {
4680
+ const { initialShapes, resultShapes, resultBounds, bindings, visited } = info;
4681
+ for (const binding of bindings) {
4682
+ for (const id of [binding.fromId, binding.toId]) {
4683
+ if (!visited.has(id)) {
4684
+ const aligningShape = initialShapes.find((s) => s.id === id);
4685
+ if (aligningShape && !visited.has(aligningShape.id)) {
4686
+ visited.add(aligningShape.id);
4687
+ const shapePageBounds = this.getShapePageBounds(aligningShape);
4688
+ if (!shapePageBounds) continue;
4689
+ resultShapes.push(aligningShape);
4690
+ resultBounds.push(shapePageBounds);
4691
+ this.collectShapesViaArrowBindings({
4692
+ ...info,
4693
+ bindings: this.getBindingsInvolvingShape(aligningShape, "arrow")
4694
+ });
4695
+ }
4696
+ }
4697
+ }
4698
+ }
4699
+ }
4666
4700
  /**
4667
4701
  * Flip shape positions.
4668
4702
  *
@@ -4678,35 +4712,52 @@ class Editor extends (_a = import_eventemitter3.default, _getIsShapeHiddenCache_
4678
4712
  * @public
4679
4713
  */
4680
4714
  flipShapes(shapes, operation) {
4681
- const ids = typeof shapes[0] === "string" ? shapes : shapes.map((s) => s.id);
4682
4715
  if (this.getIsReadonly()) return this;
4683
- let shapesToFlip = (0, import_utils.compact)(ids.map((id) => this.getShape(id)));
4716
+ const ids = typeof shapes[0] === "string" ? shapes : shapes.map((s) => s.id);
4717
+ const shapesToFlipFirstPass = (0, import_utils.compact)(ids.map((id) => this.getShape(id)));
4718
+ for (const shape of shapesToFlipFirstPass) {
4719
+ if (this.isShapeOfType(shape, "group")) {
4720
+ const childrenOfGroups = (0, import_utils.compact)(
4721
+ this.getSortedChildIdsForParent(shape.id).map((id) => this.getShape(id))
4722
+ );
4723
+ shapesToFlipFirstPass.push(...childrenOfGroups);
4724
+ }
4725
+ }
4726
+ const shapesToFlip = [];
4727
+ const allBounds = [];
4728
+ for (const shape of shapesToFlipFirstPass) {
4729
+ const util = this.getShapeUtil(shape);
4730
+ if (!util.canBeLaidOut(shape, {
4731
+ type: "flip",
4732
+ shapes: shapesToFlipFirstPass
4733
+ })) {
4734
+ continue;
4735
+ }
4736
+ const pageBounds = this.getShapePageBounds(shape);
4737
+ const localBounds = this.getShapeGeometry(shape).bounds;
4738
+ const pageTransform = this.getShapePageTransform(shape.id);
4739
+ if (!(pageBounds && localBounds && pageTransform)) continue;
4740
+ shapesToFlip.push({
4741
+ shape,
4742
+ localBounds,
4743
+ pageTransform,
4744
+ isAspectRatioLocked: util.isAspectRatioLocked(shape)
4745
+ });
4746
+ allBounds.push(pageBounds);
4747
+ }
4684
4748
  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;
4749
+ const scaleOriginPage = import_Box.Box.Common(allBounds).center;
4696
4750
  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;
4751
+ for (const { shape, localBounds, pageTransform, isAspectRatioLocked } of shapesToFlip) {
4701
4752
  this.resizeShape(
4702
4753
  shape.id,
4703
4754
  { x: operation === "horizontal" ? -1 : 1, y: operation === "vertical" ? -1 : 1 },
4704
4755
  {
4705
- initialBounds: bounds,
4706
- initialPageTransform,
4756
+ initialBounds: localBounds,
4757
+ initialPageTransform: pageTransform,
4707
4758
  initialShape: shape,
4759
+ isAspectRatioLocked,
4708
4760
  mode: "scale_shape",
4709
- isAspectRatioLocked: this.getShapeUtil(shape).isAspectRatioLocked(shape),
4710
4761
  scaleOrigin: scaleOriginPage,
4711
4762
  scaleAxisRotation: 0
4712
4763
  }
@@ -4733,15 +4784,40 @@ class Editor extends (_a = import_eventemitter3.default, _getIsShapeHiddenCache_
4733
4784
  stackShapes(shapes, operation, gap) {
4734
4785
  const ids = typeof shapes[0] === "string" ? shapes : shapes.map((s) => s.id);
4735
4786
  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;
4787
+ const shapesToStackFirstPass = (0, import_utils.compact)(ids.map((id) => this.getShape(id)));
4788
+ const shapeClustersToStack = [];
4789
+ const allBounds = [];
4790
+ const visited = /* @__PURE__ */ new Set();
4791
+ for (const shape of shapesToStackFirstPass) {
4792
+ if (visited.has(shape.id)) continue;
4793
+ visited.add(shape.id);
4794
+ const shapePageBounds = this.getShapePageBounds(shape);
4795
+ if (!shapePageBounds) continue;
4796
+ if (!this.getShapeUtil(shape).canBeLaidOut?.(shape, {
4797
+ type: "stack",
4798
+ shapes: shapesToStackFirstPass
4799
+ })) {
4800
+ continue;
4801
+ }
4802
+ const shapesMovingTogether = [shape];
4803
+ const boundsOfShapesMovingTogether = [shapePageBounds];
4804
+ this.collectShapesViaArrowBindings({
4805
+ bindings: this.getBindingsToShape(shape.id, "arrow"),
4806
+ initialShapes: shapesToStackFirstPass,
4807
+ resultShapes: shapesMovingTogether,
4808
+ resultBounds: boundsOfShapesMovingTogether,
4809
+ visited
4810
+ });
4811
+ const commonPageBounds = import_Box.Box.Common(boundsOfShapesMovingTogether);
4812
+ if (!commonPageBounds) continue;
4813
+ shapeClustersToStack.push({
4814
+ shapes: shapesMovingTogether,
4815
+ pageBounds: commonPageBounds
4816
+ });
4817
+ allBounds.push(commonPageBounds);
4818
+ }
4819
+ const len = shapeClustersToStack.length;
4741
4820
  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
4821
  let val;
4746
4822
  let min;
4747
4823
  let max;
@@ -4757,57 +4833,55 @@ class Editor extends (_a = import_eventemitter3.default, _getIsShapeHiddenCache_
4757
4833
  max = "maxY";
4758
4834
  dim = "height";
4759
4835
  }
4760
- let shapeGap;
4836
+ let shapeGap = 0;
4761
4837
  if (gap === 0) {
4762
- const gaps = [];
4763
- shapesToStack.sort((a, b) => pageBounds[a.id][min] - pageBounds[b.id][min]);
4838
+ const gaps = {};
4839
+ shapeClustersToStack.sort((a, b) => a.pageBounds[min] - b.pageBounds[min]);
4764
4840
  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 });
4841
+ const currCluster = shapeClustersToStack[i];
4842
+ const nextCluster = shapeClustersToStack[i + 1];
4843
+ const gap2 = nextCluster.pageBounds[min] - currCluster.pageBounds[max];
4844
+ if (!gaps[gap2]) {
4845
+ gaps[gap2] = 0;
4775
4846
  }
4847
+ gaps[gap2]++;
4776
4848
  }
4777
- let maxCount = 0;
4778
- gaps.forEach((g) => {
4779
- if (g.count > maxCount) {
4780
- maxCount = g.count;
4781
- shapeGap = g.gap;
4849
+ let maxCount = 1;
4850
+ for (const [gap2, count] of Object.entries(gaps)) {
4851
+ if (count > maxCount) {
4852
+ maxCount = count;
4853
+ shapeGap = parseFloat(gap2);
4782
4854
  }
4783
- });
4855
+ }
4784
4856
  if (maxCount === 1) {
4785
- shapeGap = Math.max(0, gaps.reduce((a, c) => a + c.gap * c.count, 0) / (len - 1));
4857
+ let totalCount = 0;
4858
+ for (const [gap2, count] of Object.entries(gaps)) {
4859
+ shapeGap += parseFloat(gap2) * count;
4860
+ totalCount += count;
4861
+ }
4862
+ shapeGap /= totalCount;
4786
4863
  }
4787
4864
  } else {
4788
4865
  shapeGap = gap;
4789
4866
  }
4790
4867
  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]
4868
+ let v = shapeClustersToStack[0].pageBounds[max];
4869
+ for (let i = 1; i < shapeClustersToStack.length; i++) {
4870
+ const { shapes: shapes2, pageBounds } = shapeClustersToStack[i];
4871
+ const delta = new import_Vec.Vec();
4872
+ delta[val] = v + shapeGap - pageBounds[val];
4873
+ for (const shape of shapes2) {
4874
+ const shapeDelta = delta.clone();
4875
+ const parent = this.getShapeParent(shape);
4876
+ if (parent) {
4877
+ const parentTransform = this.getShapePageTransform(parent);
4878
+ if (parentTransform) shapeDelta.rot(-parentTransform.rotation());
4807
4879
  }
4808
- );
4809
- v += pageBounds[shape.id][dim] + shapeGap;
4810
- });
4880
+ shapeDelta.add(shape);
4881
+ changes.push(this.getChangesToTranslateShape(shape, shapeDelta));
4882
+ }
4883
+ v += pageBounds[dim] + shapeGap;
4884
+ }
4811
4885
  this.updateShapes(changes);
4812
4886
  return this;
4813
4887
  }
@@ -4825,91 +4899,101 @@ class Editor extends (_a = import_eventemitter3.default, _getIsShapeHiddenCache_
4825
4899
  * @param gap - The padding to apply to the packed shapes. Defaults to 16.
4826
4900
  */
4827
4901
  packShapes(shapes, gap) {
4828
- const ids = typeof shapes[0] === "string" ? shapes : shapes.map((s) => s.id);
4829
4902
  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)));
4903
+ const ids = typeof shapes[0] === "string" ? shapes : shapes.map((s) => s.id);
4904
+ const shapesToPackFirstPass = (0, import_utils.compact)(ids.map((id) => this.getShape(id)));
4905
+ const shapeClustersToPack = [];
4906
+ const allBounds = [];
4907
+ const visited = /* @__PURE__ */ new Set();
4908
+ for (const shape of shapesToPackFirstPass) {
4909
+ if (visited.has(shape.id)) continue;
4910
+ visited.add(shape.id);
4911
+ const shapePageBounds = this.getShapePageBounds(shape);
4912
+ if (!shapePageBounds) continue;
4913
+ if (!this.getShapeUtil(shape).canBeLaidOut?.(shape, {
4914
+ type: "pack",
4915
+ shapes: shapesToPackFirstPass
4916
+ })) {
4917
+ continue;
4918
+ }
4919
+ const shapesMovingTogether = [shape];
4920
+ const boundsOfShapesMovingTogether = [shapePageBounds];
4921
+ this.collectShapesViaArrowBindings({
4922
+ bindings: this.getBindingsToShape(shape.id, "arrow"),
4923
+ initialShapes: shapesToPackFirstPass,
4924
+ resultShapes: shapesMovingTogether,
4925
+ resultBounds: boundsOfShapesMovingTogether,
4926
+ visited
4927
+ });
4928
+ const commonPageBounds = import_Box.Box.Common(boundsOfShapesMovingTogether);
4929
+ if (!commonPageBounds) continue;
4930
+ shapeClustersToPack.push({
4931
+ shapes: shapesMovingTogether,
4932
+ pageBounds: commonPageBounds,
4933
+ nextPageBounds: commonPageBounds.clone()
4934
+ });
4935
+ allBounds.push(commonPageBounds);
4936
+ }
4937
+ if (shapeClustersToPack.length < 2) return this;
4938
+ let area = 0;
4939
+ for (const { pageBounds } of shapeClustersToPack) {
4940
+ area += pageBounds.width * pageBounds.height;
4941
+ }
4942
+ const commonBounds = import_Box.Box.Common(allBounds);
4846
4943
  const maxWidth = commonBounds.width;
4847
- shapesToPack.sort((a, b) => shapePageBounds[b.id].height - shapePageBounds[a.id].height);
4944
+ shapeClustersToPack.sort((a, b) => a.pageBounds.width - b.pageBounds.width).sort((a, b) => a.pageBounds.height - b.pageBounds.height);
4848
4945
  const startWidth = Math.max(Math.ceil(Math.sqrt(area / 0.95)), maxWidth);
4849
4946
  const spaces = [new import_Box.Box(commonBounds.x, commonBounds.y, startWidth, Infinity)];
4850
4947
  let width = 0;
4851
4948
  let height = 0;
4852
4949
  let space;
4853
4950
  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) {
4951
+ for (const { nextPageBounds } of shapeClustersToPack) {
4952
+ for (let i = spaces.length - 1; i >= 0; i--) {
4953
+ space = spaces[i];
4954
+ if (nextPageBounds.width > space.width || nextPageBounds.height > space.height) continue;
4955
+ nextPageBounds.x = space.x;
4956
+ nextPageBounds.y = space.y;
4957
+ height = Math.max(height, nextPageBounds.maxY);
4958
+ width = Math.max(width, nextPageBounds.maxX);
4959
+ if (nextPageBounds.width === space.width && nextPageBounds.height === space.height) {
4865
4960
  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;
4961
+ if (i < spaces.length) spaces[i] = last2;
4962
+ } else if (nextPageBounds.height === space.height) {
4963
+ space.x += nextPageBounds.width + gap;
4964
+ space.width -= nextPageBounds.width + gap;
4965
+ } else if (nextPageBounds.width === space.width) {
4966
+ space.y += nextPageBounds.height + gap;
4967
+ space.height -= nextPageBounds.height + gap;
4873
4968
  } else {
4874
4969
  spaces.push(
4875
4970
  new import_Box.Box(
4876
- space.x + (bounds.width + gap),
4971
+ space.x + (nextPageBounds.width + gap),
4877
4972
  space.y,
4878
- space.width - (bounds.width + gap),
4879
- bounds.height
4973
+ space.width - (nextPageBounds.width + gap),
4974
+ nextPageBounds.height
4880
4975
  )
4881
4976
  );
4882
- space.y += bounds.height + gap;
4883
- space.height -= bounds.height + gap;
4977
+ space.y += nextPageBounds.height + gap;
4978
+ space.height -= nextPageBounds.height + gap;
4884
4979
  }
4885
4980
  break;
4886
4981
  }
4887
4982
  }
4888
- const commonAfter = import_Box.Box.Common(Object.values(nextShapePageBounds));
4983
+ const commonAfter = import_Box.Box.Common(shapeClustersToPack.map((s) => s.nextPageBounds));
4889
4984
  const centerDelta = import_Vec.Vec.Sub(commonBounds.center, commonAfter.center);
4890
- let nextBounds;
4891
4985
  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);
4986
+ for (const { shapes: shapes2, pageBounds, nextPageBounds } of shapeClustersToPack) {
4987
+ const delta = import_Vec.Vec.Sub(nextPageBounds.point, pageBounds.point).add(centerDelta);
4988
+ for (const shape of shapes2) {
4989
+ const shapeDelta = delta.clone();
4990
+ const parent = this.getShapeParent(shape);
4991
+ if (parent) {
4992
+ const parentTransform = this.getShapeParentTransform(shape);
4993
+ if (parentTransform) shapeDelta.rot(-parentTransform.rotation());
4994
+ }
4995
+ shapeDelta.add(shape);
4996
+ changes.push(this.getChangesToTranslateShape(shape, shapeDelta));
4913
4997
  }
4914
4998
  }
4915
4999
  if (changes.length) {
@@ -4932,19 +5016,45 @@ class Editor extends (_a = import_eventemitter3.default, _getIsShapeHiddenCache_
4932
5016
  * @public
4933
5017
  */
4934
5018
  alignShapes(shapes, operation) {
4935
- const ids = typeof shapes[0] === "string" ? shapes : shapes.map((s) => s.id);
4936
5019
  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)));
5020
+ const ids = typeof shapes[0] === "string" ? shapes : shapes.map((s) => s.id);
5021
+ const shapesToAlignFirstPass = (0, import_utils.compact)(ids.map((id) => this.getShape(id)));
5022
+ const shapeClustersToAlign = [];
5023
+ const allBounds = [];
5024
+ const visited = /* @__PURE__ */ new Set();
5025
+ for (const shape of shapesToAlignFirstPass) {
5026
+ if (visited.has(shape.id)) continue;
5027
+ visited.add(shape.id);
5028
+ const shapePageBounds = this.getShapePageBounds(shape);
5029
+ if (!shapePageBounds) continue;
5030
+ if (!this.getShapeUtil(shape).canBeLaidOut?.(shape, {
5031
+ type: "align",
5032
+ shapes: shapesToAlignFirstPass
5033
+ })) {
5034
+ continue;
5035
+ }
5036
+ const shapesMovingTogether = [shape];
5037
+ const boundsOfShapesMovingTogether = [shapePageBounds];
5038
+ this.collectShapesViaArrowBindings({
5039
+ bindings: this.getBindingsToShape(shape.id, "arrow"),
5040
+ initialShapes: shapesToAlignFirstPass,
5041
+ resultShapes: shapesMovingTogether,
5042
+ resultBounds: boundsOfShapesMovingTogether,
5043
+ visited
5044
+ });
5045
+ const commonPageBounds = import_Box.Box.Common(boundsOfShapesMovingTogether);
5046
+ if (!commonPageBounds) continue;
5047
+ shapeClustersToAlign.push({
5048
+ shapes: shapesMovingTogether,
5049
+ pageBounds: commonPageBounds
5050
+ });
5051
+ allBounds.push(commonPageBounds);
5052
+ }
5053
+ if (shapeClustersToAlign.length < 2) return this;
5054
+ const commonBounds = import_Box.Box.Common(allBounds);
4943
5055
  const changes = [];
4944
- shapesToAlign.forEach((shape) => {
4945
- const pageBounds = shapePageBounds[shape.id];
4946
- if (!pageBounds) return;
4947
- const delta = { x: 0, y: 0 };
5056
+ shapeClustersToAlign.forEach(({ shapes: shapes2, pageBounds }) => {
5057
+ const delta = new import_Vec.Vec();
4948
5058
  switch (operation) {
4949
5059
  case "top": {
4950
5060
  delta.y = commonBounds.minY - pageBounds.minY;
@@ -4971,9 +5081,16 @@ class Editor extends (_a = import_eventemitter3.default, _getIsShapeHiddenCache_
4971
5081
  break;
4972
5082
  }
4973
5083
  }
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)));
5084
+ for (const shape of shapes2) {
5085
+ const shapeDelta = delta.clone();
5086
+ const parent = this.getShapeParent(shape);
5087
+ if (parent) {
5088
+ const parentTransform = this.getShapePageTransform(parent);
5089
+ if (parentTransform) shapeDelta.rot(-parentTransform.rotation());
5090
+ }
5091
+ shapeDelta.add(shape);
5092
+ changes.push(this.getChangesToTranslateShape(shape, shapeDelta));
5093
+ }
4977
5094
  });
4978
5095
  this.updateShapes(changes);
4979
5096
  return this;
@@ -4993,47 +5110,95 @@ class Editor extends (_a = import_eventemitter3.default, _getIsShapeHiddenCache_
4993
5110
  * @public
4994
5111
  */
4995
5112
  distributeShapes(shapes, operation) {
4996
- const ids = typeof shapes[0] === "string" ? shapes : shapes.map((s) => s.id);
4997
5113
  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
- );
5114
+ const ids = typeof shapes[0] === "string" ? shapes : shapes.map((s) => s.id);
5115
+ const shapesToDistributeFirstPass = (0, import_utils.compact)(ids.map((id) => this.getShape(id)));
5116
+ const shapeClustersToDistribute = [];
5117
+ const allBounds = [];
5118
+ const visited = /* @__PURE__ */ new Set();
5119
+ for (const shape of shapesToDistributeFirstPass) {
5120
+ if (visited.has(shape.id)) continue;
5121
+ visited.add(shape.id);
5122
+ const shapePageBounds = this.getShapePageBounds(shape);
5123
+ if (!shapePageBounds) continue;
5124
+ if (!this.getShapeUtil(shape).canBeLaidOut?.(shape, {
5125
+ type: "distribute",
5126
+ shapes: shapesToDistributeFirstPass
5127
+ })) {
5128
+ continue;
5129
+ }
5130
+ const shapesMovingTogether = [shape];
5131
+ const boundsOfShapesMovingTogether = [shapePageBounds];
5132
+ this.collectShapesViaArrowBindings({
5133
+ bindings: this.getBindingsToShape(shape.id, "arrow"),
5134
+ initialShapes: shapesToDistributeFirstPass,
5135
+ resultShapes: shapesMovingTogether,
5136
+ resultBounds: boundsOfShapesMovingTogether,
5137
+ visited
5138
+ });
5139
+ const commonPageBounds = import_Box.Box.Common(boundsOfShapesMovingTogether);
5140
+ if (!commonPageBounds) continue;
5141
+ shapeClustersToDistribute.push({
5142
+ shapes: shapesMovingTogether,
5143
+ pageBounds: commonPageBounds
5144
+ });
5145
+ allBounds.push(commonPageBounds);
5146
+ }
5147
+ if (shapeClustersToDistribute.length < 3) return this;
5004
5148
  let val;
5005
5149
  let min;
5006
5150
  let max;
5007
- let mid;
5008
5151
  let dim;
5009
5152
  if (operation === "horizontal") {
5010
5153
  val = "x";
5011
5154
  min = "minX";
5012
5155
  max = "maxX";
5013
- mid = "midX";
5014
5156
  dim = "width";
5015
5157
  } else {
5016
5158
  val = "y";
5017
5159
  min = "minY";
5018
5160
  max = "maxY";
5019
- mid = "midY";
5020
5161
  dim = "height";
5021
5162
  }
5022
5163
  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)));
5164
+ const first = shapeClustersToDistribute.sort((a, b) => a.pageBounds[min] - b.pageBounds[min])[0];
5165
+ const last2 = shapeClustersToDistribute.sort((a, b) => b.pageBounds[max] - a.pageBounds[max])[0];
5166
+ if (first === last2) {
5167
+ const excludedShapeIds = new Set(first.shapes.map((s) => s.id));
5168
+ return this.distributeShapes(
5169
+ ids.filter((id) => !excludedShapeIds.has(id)),
5170
+ operation
5171
+ );
5172
+ }
5173
+ const shapeClustersToMove = shapeClustersToDistribute.filter((shape) => shape !== first && shape !== last2).sort((a, b) => {
5174
+ if (a.pageBounds[min] === b.pageBounds[min]) {
5175
+ return a.shapes[0].id < b.shapes[0].id ? -1 : 1;
5176
+ }
5177
+ return a.pageBounds[min] - b.pageBounds[min];
5036
5178
  });
5179
+ const maxFirst = first.pageBounds[max];
5180
+ const range = last2.pageBounds[min] - maxFirst;
5181
+ const summedShapeDimensions = shapeClustersToMove.reduce((acc, s) => acc + s.pageBounds[dim], 0);
5182
+ const gap = (range - summedShapeDimensions) / (shapeClustersToMove.length + 1);
5183
+ for (let v = maxFirst + gap, i = 0; i < shapeClustersToMove.length; i++) {
5184
+ const { shapes: shapes2, pageBounds } = shapeClustersToMove[i];
5185
+ const delta = new import_Vec.Vec();
5186
+ delta[val] = v - pageBounds[val];
5187
+ if (v + pageBounds[dim] > last2.pageBounds[max] - 1) {
5188
+ delta[val] = last2.pageBounds[max] - pageBounds[max] - 1;
5189
+ }
5190
+ for (const shape of shapes2) {
5191
+ const shapeDelta = delta.clone();
5192
+ const parent = this.getShapeParent(shape);
5193
+ if (parent) {
5194
+ const parentTransform = this.getShapePageTransform(parent);
5195
+ if (parentTransform) shapeDelta.rot(-parentTransform.rotation());
5196
+ }
5197
+ shapeDelta.add(shape);
5198
+ changes.push(this.getChangesToTranslateShape(shape, shapeDelta));
5199
+ }
5200
+ v += pageBounds[dim] + gap;
5201
+ }
5037
5202
  this.updateShapes(changes);
5038
5203
  return this;
5039
5204
  }
@@ -5054,59 +5219,78 @@ class Editor extends (_a = import_eventemitter3.default, _getIsShapeHiddenCache_
5054
5219
  stretchShapes(shapes, operation) {
5055
5220
  const ids = typeof shapes[0] === "string" ? shapes : shapes.map((s) => s.id);
5056
5221
  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;
5222
+ const shapesToStretchFirstPass = (0, import_utils.compact)(ids.map((id) => this.getShape(id))).filter(
5223
+ (s) => this.getShapePageTransform(s)?.rotation() % (import_utils2.PI / 2) === 0
5224
+ );
5225
+ const shapeClustersToStretch = [];
5226
+ const allBounds = [];
5227
+ const visited = /* @__PURE__ */ new Set();
5228
+ for (const shape of shapesToStretchFirstPass) {
5229
+ if (visited.has(shape.id)) continue;
5230
+ visited.add(shape.id);
5231
+ const shapePageBounds = this.getShapePageBounds(shape);
5232
+ if (!shapePageBounds) continue;
5233
+ const shapesMovingTogether = [shape];
5234
+ const boundsOfShapesMovingTogether = [shapePageBounds];
5235
+ if (!this.getShapeUtil(shape).canBeLaidOut?.(shape, {
5236
+ type: "stretch",
5237
+ shapes: shapesToStretchFirstPass
5238
+ })) {
5239
+ continue;
5108
5240
  }
5241
+ this.collectShapesViaArrowBindings({
5242
+ bindings: this.getBindingsToShape(shape.id, "arrow"),
5243
+ initialShapes: shapesToStretchFirstPass,
5244
+ resultShapes: shapesMovingTogether,
5245
+ resultBounds: boundsOfShapesMovingTogether,
5246
+ visited
5247
+ });
5248
+ const commonPageBounds = import_Box.Box.Common(boundsOfShapesMovingTogether);
5249
+ if (!commonPageBounds) continue;
5250
+ shapeClustersToStretch.push({
5251
+ shapes: shapesMovingTogether,
5252
+ pageBounds: commonPageBounds
5253
+ });
5254
+ allBounds.push(commonPageBounds);
5255
+ }
5256
+ if (shapeClustersToStretch.length < 2) return this;
5257
+ const commonBounds = import_Box.Box.Common(allBounds);
5258
+ let val;
5259
+ let min;
5260
+ let dim;
5261
+ if (operation === "horizontal") {
5262
+ val = "x";
5263
+ min = "minX";
5264
+ dim = "width";
5265
+ } else {
5266
+ val = "y";
5267
+ min = "minY";
5268
+ dim = "height";
5109
5269
  }
5270
+ this.run(() => {
5271
+ shapeClustersToStretch.forEach(({ shapes: shapes2, pageBounds }) => {
5272
+ const localOffset = new import_Vec.Vec();
5273
+ localOffset[val] = commonBounds[min] - pageBounds[min];
5274
+ const scaleOrigin = pageBounds.center.clone();
5275
+ scaleOrigin[val] = commonBounds[min];
5276
+ const scale = new import_Vec.Vec(1, 1);
5277
+ scale[val] = commonBounds[dim] / pageBounds[dim];
5278
+ for (const shape of shapes2) {
5279
+ const shapeLocalOffset = localOffset.clone();
5280
+ const parentTransform = this.getShapeParentTransform(shape);
5281
+ if (parentTransform) localOffset.rot(-parentTransform.rotation());
5282
+ shapeLocalOffset.add(shape);
5283
+ const changes = this.getChangesToTranslateShape(shape, shapeLocalOffset);
5284
+ this.updateShape(changes);
5285
+ this.resizeShape(shape.id, scale, {
5286
+ initialBounds: this.getShapeGeometry(shape).bounds,
5287
+ scaleOrigin,
5288
+ isAspectRatioLocked: this.getShapeUtil(shape).isAspectRatioLocked(shape),
5289
+ scaleAxisRotation: 0
5290
+ });
5291
+ }
5292
+ });
5293
+ });
5110
5294
  return this;
5111
5295
  }
5112
5296
  /**
@@ -5121,8 +5305,8 @@ class Editor extends (_a = import_eventemitter3.default, _getIsShapeHiddenCache_
5121
5305
  resizeShape(shape, scale, opts = {}) {
5122
5306
  const id = typeof shape === "string" ? shape : shape.id;
5123
5307
  if (this.getIsReadonly()) return this;
5124
- if (!Number.isFinite(scale.x)) scale = new import_Vec.Vec(1, scale.y);
5125
- if (!Number.isFinite(scale.y)) scale = new import_Vec.Vec(scale.x, 1);
5308
+ if (!import_core_js.Number.isFinite(scale.x)) scale = new import_Vec.Vec(1, scale.y);
5309
+ if (!import_core_js.Number.isFinite(scale.y)) scale = new import_Vec.Vec(scale.x, 1);
5126
5310
  const initialShape = opts.initialShape ?? this.getShape(id);
5127
5311
  if (!initialShape) return this;
5128
5312
  const scaleOrigin = opts.scaleOrigin ?? this.getShapePageBounds(id)?.center;
@@ -7356,7 +7540,6 @@ __decorateElement(_init, 1, "getPages", _getPages_dec, Editor);
7356
7540
  __decorateElement(_init, 1, "getCurrentPageId", _getCurrentPageId_dec, Editor);
7357
7541
  __decorateElement(_init, 1, "getCurrentPageShapeIdsSorted", _getCurrentPageShapeIdsSorted_dec, Editor);
7358
7542
  __decorateElement(_init, 1, "_getAllAssetsQuery", __getAllAssetsQuery_dec, Editor);
7359
- __decorateElement(_init, 1, "_getShapeGeometryCache", __getShapeGeometryCache_dec, Editor);
7360
7543
  __decorateElement(_init, 1, "_getShapeHandlesCache", __getShapeHandlesCache_dec, Editor);
7361
7544
  __decorateElement(_init, 1, "_getShapePageTransformCache", __getShapePageTransformCache_dec, Editor);
7362
7545
  __decorateElement(_init, 1, "_getShapePageBoundsCache", __getShapePageBoundsCache_dec, Editor);
@@ -7446,7 +7629,7 @@ function withIsolatedShapes(editor, shapeIds, callback) {
7446
7629
  result = import_utils.Result.err(error);
7447
7630
  }
7448
7631
  });
7449
- editor.store.applyDiff((0, import_store.reverseRecordsDiff)(changes));
7632
+ editor.store.applyDiff((0, import_store.reverseRecordsDiff)(changes), { runCallbacks: false });
7450
7633
  },
7451
7634
  { history: "ignore" }
7452
7635
  );