@tldraw/editor 3.9.0-canary.d799df28e99e → 3.9.0-canary.d81de7fd0bea
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist-cjs/index.d.ts +23 -6
- package/dist-cjs/index.js +1 -1
- package/dist-cjs/index.js.map +2 -2
- package/dist-cjs/lib/editor/Editor.js +412 -236
- package/dist-cjs/lib/editor/Editor.js.map +3 -3
- package/dist-cjs/lib/editor/shapes/ShapeUtil.js +7 -2
- package/dist-cjs/lib/editor/shapes/ShapeUtil.js.map +2 -2
- package/dist-cjs/lib/exports/getSvgJsx.js.map +2 -2
- package/dist-cjs/version.js +3 -3
- package/dist-cjs/version.js.map +1 -1
- package/dist-esm/index.d.mts +23 -6
- package/dist-esm/index.mjs +1 -1
- package/dist-esm/index.mjs.map +2 -2
- package/dist-esm/lib/editor/Editor.mjs +408 -232
- package/dist-esm/lib/editor/Editor.mjs.map +3 -3
- package/dist-esm/lib/editor/shapes/ShapeUtil.mjs +7 -2
- package/dist-esm/lib/editor/shapes/ShapeUtil.mjs.map +2 -2
- package/dist-esm/lib/exports/getSvgJsx.mjs.map +2 -2
- package/dist-esm/version.mjs +3 -3
- package/dist-esm/version.mjs.map +1 -1
- package/package.json +7 -7
- package/src/index.ts +1 -0
- package/src/lib/editor/Editor.ts +539 -261
- package/src/lib/editor/shapes/ShapeUtil.ts +21 -5
- package/src/lib/exports/getSvgJsx.tsx +1 -0
- package/src/version.ts +3 -3
|
@@ -92,6 +92,7 @@ import {
|
|
|
92
92
|
structuredClone,
|
|
93
93
|
uniqueId
|
|
94
94
|
} from "@tldraw/utils";
|
|
95
|
+
import { Number } from "core-js";
|
|
95
96
|
import EventEmitter from "eventemitter3";
|
|
96
97
|
import {
|
|
97
98
|
getSnapshot,
|
|
@@ -122,7 +123,7 @@ import { Vec } from "../primitives/Vec.mjs";
|
|
|
122
123
|
import { EASINGS } from "../primitives/easings.mjs";
|
|
123
124
|
import { Group2d } from "../primitives/geometry/Group2d.mjs";
|
|
124
125
|
import { intersectPolygonPolygon } from "../primitives/intersect.mjs";
|
|
125
|
-
import {
|
|
126
|
+
import { PI, approximately, areAnglesCompatible, clamp, pointInPolygon } from "../primitives/utils.mjs";
|
|
126
127
|
import { SharedStyleMap } from "../utils/SharedStylesMap.mjs";
|
|
127
128
|
import { dataUrlToFile } from "../utils/assets.mjs";
|
|
128
129
|
import { debugFlags } from "../utils/debug-flags.mjs";
|
|
@@ -4315,27 +4316,28 @@ class Editor extends (_a = EventEmitter, _getIsShapeHiddenCache_dec = [computed]
|
|
|
4315
4316
|
});
|
|
4316
4317
|
return this;
|
|
4317
4318
|
}
|
|
4319
|
+
// Gets a shape partial that includes life cycle changes: on translate start, on translate, on translate end
|
|
4318
4320
|
getChangesToTranslateShape(initialShape, newShapeCoords) {
|
|
4319
4321
|
let workingShape = initialShape;
|
|
4320
4322
|
const util = this.getShapeUtil(initialShape);
|
|
4321
|
-
|
|
4322
|
-
|
|
4323
|
-
|
|
4324
|
-
|
|
4323
|
+
const afterTranslateStart = util.onTranslateStart?.(workingShape);
|
|
4324
|
+
if (afterTranslateStart) {
|
|
4325
|
+
workingShape = applyPartialToRecordWithProps(workingShape, afterTranslateStart);
|
|
4326
|
+
}
|
|
4325
4327
|
workingShape = applyPartialToRecordWithProps(workingShape, {
|
|
4326
4328
|
id: initialShape.id,
|
|
4327
4329
|
type: initialShape.type,
|
|
4328
4330
|
x: newShapeCoords.x,
|
|
4329
4331
|
y: newShapeCoords.y
|
|
4330
4332
|
});
|
|
4331
|
-
|
|
4332
|
-
|
|
4333
|
-
|
|
4334
|
-
|
|
4335
|
-
|
|
4336
|
-
|
|
4337
|
-
|
|
4338
|
-
|
|
4333
|
+
const afterTranslate = util.onTranslate?.(initialShape, workingShape);
|
|
4334
|
+
if (afterTranslate) {
|
|
4335
|
+
workingShape = applyPartialToRecordWithProps(workingShape, afterTranslate);
|
|
4336
|
+
}
|
|
4337
|
+
const afterTranslateEnd = util.onTranslateEnd?.(initialShape, workingShape);
|
|
4338
|
+
if (afterTranslateEnd) {
|
|
4339
|
+
workingShape = applyPartialToRecordWithProps(workingShape, afterTranslateEnd);
|
|
4340
|
+
}
|
|
4339
4341
|
return workingShape;
|
|
4340
4342
|
}
|
|
4341
4343
|
/**
|
|
@@ -4642,6 +4644,30 @@ class Editor extends (_a = EventEmitter, _getIsShapeHiddenCache_dec = [computed]
|
|
|
4642
4644
|
if (changes) this.updateShapes(changes);
|
|
4643
4645
|
return this;
|
|
4644
4646
|
}
|
|
4647
|
+
/**
|
|
4648
|
+
* @internal
|
|
4649
|
+
*/
|
|
4650
|
+
collectShapesViaArrowBindings(info) {
|
|
4651
|
+
const { initialShapes, resultShapes, resultBounds, bindings, visited } = info;
|
|
4652
|
+
for (const binding of bindings) {
|
|
4653
|
+
for (const id of [binding.fromId, binding.toId]) {
|
|
4654
|
+
if (!visited.has(id)) {
|
|
4655
|
+
const aligningShape = initialShapes.find((s) => s.id === id);
|
|
4656
|
+
if (aligningShape && !visited.has(aligningShape.id)) {
|
|
4657
|
+
visited.add(aligningShape.id);
|
|
4658
|
+
const shapePageBounds = this.getShapePageBounds(aligningShape);
|
|
4659
|
+
if (!shapePageBounds) continue;
|
|
4660
|
+
resultShapes.push(aligningShape);
|
|
4661
|
+
resultBounds.push(shapePageBounds);
|
|
4662
|
+
this.collectShapesViaArrowBindings({
|
|
4663
|
+
...info,
|
|
4664
|
+
bindings: this.getBindingsInvolvingShape(aligningShape, "arrow")
|
|
4665
|
+
});
|
|
4666
|
+
}
|
|
4667
|
+
}
|
|
4668
|
+
}
|
|
4669
|
+
}
|
|
4670
|
+
}
|
|
4645
4671
|
/**
|
|
4646
4672
|
* Flip shape positions.
|
|
4647
4673
|
*
|
|
@@ -4657,35 +4683,52 @@ class Editor extends (_a = EventEmitter, _getIsShapeHiddenCache_dec = [computed]
|
|
|
4657
4683
|
* @public
|
|
4658
4684
|
*/
|
|
4659
4685
|
flipShapes(shapes, operation) {
|
|
4660
|
-
const ids = typeof shapes[0] === "string" ? shapes : shapes.map((s) => s.id);
|
|
4661
4686
|
if (this.getIsReadonly()) return this;
|
|
4662
|
-
|
|
4687
|
+
const ids = typeof shapes[0] === "string" ? shapes : shapes.map((s) => s.id);
|
|
4688
|
+
const shapesToFlipFirstPass = compact(ids.map((id) => this.getShape(id)));
|
|
4689
|
+
for (const shape of shapesToFlipFirstPass) {
|
|
4690
|
+
if (this.isShapeOfType(shape, "group")) {
|
|
4691
|
+
const childrenOfGroups = compact(
|
|
4692
|
+
this.getSortedChildIdsForParent(shape.id).map((id) => this.getShape(id))
|
|
4693
|
+
);
|
|
4694
|
+
shapesToFlipFirstPass.push(...childrenOfGroups);
|
|
4695
|
+
}
|
|
4696
|
+
}
|
|
4697
|
+
const shapesToFlip = [];
|
|
4698
|
+
const allBounds = [];
|
|
4699
|
+
for (const shape of shapesToFlipFirstPass) {
|
|
4700
|
+
const util = this.getShapeUtil(shape);
|
|
4701
|
+
if (!util.canBeLaidOut(shape, {
|
|
4702
|
+
type: "flip",
|
|
4703
|
+
shapes: shapesToFlipFirstPass
|
|
4704
|
+
})) {
|
|
4705
|
+
continue;
|
|
4706
|
+
}
|
|
4707
|
+
const pageBounds = this.getShapePageBounds(shape);
|
|
4708
|
+
const localBounds = this.getShapeGeometry(shape).bounds;
|
|
4709
|
+
const pageTransform = this.getShapePageTransform(shape.id);
|
|
4710
|
+
if (!(pageBounds && localBounds && pageTransform)) continue;
|
|
4711
|
+
shapesToFlip.push({
|
|
4712
|
+
shape,
|
|
4713
|
+
localBounds,
|
|
4714
|
+
pageTransform,
|
|
4715
|
+
isAspectRatioLocked: util.isAspectRatioLocked(shape)
|
|
4716
|
+
});
|
|
4717
|
+
allBounds.push(pageBounds);
|
|
4718
|
+
}
|
|
4663
4719
|
if (!shapesToFlip.length) return this;
|
|
4664
|
-
|
|
4665
|
-
shapesToFlip.map((shape) => {
|
|
4666
|
-
if (this.isShapeOfType(shape, "group")) {
|
|
4667
|
-
return this.getSortedChildIdsForParent(shape.id).map((id) => this.getShape(id));
|
|
4668
|
-
}
|
|
4669
|
-
return shape;
|
|
4670
|
-
}).flat()
|
|
4671
|
-
);
|
|
4672
|
-
const scaleOriginPage = Box.Common(
|
|
4673
|
-
compact(shapesToFlip.map((id) => this.getShapePageBounds(id)))
|
|
4674
|
-
).center;
|
|
4720
|
+
const scaleOriginPage = Box.Common(allBounds).center;
|
|
4675
4721
|
this.run(() => {
|
|
4676
|
-
for (const shape of shapesToFlip) {
|
|
4677
|
-
const bounds = this.getShapeGeometry(shape).bounds;
|
|
4678
|
-
const initialPageTransform = this.getShapePageTransform(shape.id);
|
|
4679
|
-
if (!initialPageTransform) continue;
|
|
4722
|
+
for (const { shape, localBounds, pageTransform, isAspectRatioLocked } of shapesToFlip) {
|
|
4680
4723
|
this.resizeShape(
|
|
4681
4724
|
shape.id,
|
|
4682
4725
|
{ x: operation === "horizontal" ? -1 : 1, y: operation === "vertical" ? -1 : 1 },
|
|
4683
4726
|
{
|
|
4684
|
-
initialBounds:
|
|
4685
|
-
initialPageTransform,
|
|
4727
|
+
initialBounds: localBounds,
|
|
4728
|
+
initialPageTransform: pageTransform,
|
|
4686
4729
|
initialShape: shape,
|
|
4730
|
+
isAspectRatioLocked,
|
|
4687
4731
|
mode: "scale_shape",
|
|
4688
|
-
isAspectRatioLocked: this.getShapeUtil(shape).isAspectRatioLocked(shape),
|
|
4689
4732
|
scaleOrigin: scaleOriginPage,
|
|
4690
4733
|
scaleAxisRotation: 0
|
|
4691
4734
|
}
|
|
@@ -4712,15 +4755,40 @@ class Editor extends (_a = EventEmitter, _getIsShapeHiddenCache_dec = [computed]
|
|
|
4712
4755
|
stackShapes(shapes, operation, gap) {
|
|
4713
4756
|
const ids = typeof shapes[0] === "string" ? shapes : shapes.map((s) => s.id);
|
|
4714
4757
|
if (this.getIsReadonly()) return this;
|
|
4715
|
-
const
|
|
4716
|
-
|
|
4717
|
-
|
|
4718
|
-
|
|
4719
|
-
const
|
|
4758
|
+
const shapesToStackFirstPass = compact(ids.map((id) => this.getShape(id)));
|
|
4759
|
+
const shapeClustersToStack = [];
|
|
4760
|
+
const allBounds = [];
|
|
4761
|
+
const visited = /* @__PURE__ */ new Set();
|
|
4762
|
+
for (const shape of shapesToStackFirstPass) {
|
|
4763
|
+
if (visited.has(shape.id)) continue;
|
|
4764
|
+
visited.add(shape.id);
|
|
4765
|
+
const shapePageBounds = this.getShapePageBounds(shape);
|
|
4766
|
+
if (!shapePageBounds) continue;
|
|
4767
|
+
if (!this.getShapeUtil(shape).canBeLaidOut?.(shape, {
|
|
4768
|
+
type: "stack",
|
|
4769
|
+
shapes: shapesToStackFirstPass
|
|
4770
|
+
})) {
|
|
4771
|
+
continue;
|
|
4772
|
+
}
|
|
4773
|
+
const shapesMovingTogether = [shape];
|
|
4774
|
+
const boundsOfShapesMovingTogether = [shapePageBounds];
|
|
4775
|
+
this.collectShapesViaArrowBindings({
|
|
4776
|
+
bindings: this.getBindingsToShape(shape.id, "arrow"),
|
|
4777
|
+
initialShapes: shapesToStackFirstPass,
|
|
4778
|
+
resultShapes: shapesMovingTogether,
|
|
4779
|
+
resultBounds: boundsOfShapesMovingTogether,
|
|
4780
|
+
visited
|
|
4781
|
+
});
|
|
4782
|
+
const commonPageBounds = Box.Common(boundsOfShapesMovingTogether);
|
|
4783
|
+
if (!commonPageBounds) continue;
|
|
4784
|
+
shapeClustersToStack.push({
|
|
4785
|
+
shapes: shapesMovingTogether,
|
|
4786
|
+
pageBounds: commonPageBounds
|
|
4787
|
+
});
|
|
4788
|
+
allBounds.push(commonPageBounds);
|
|
4789
|
+
}
|
|
4790
|
+
const len = shapeClustersToStack.length;
|
|
4720
4791
|
if (gap === 0 && len < 3 || len < 2) return this;
|
|
4721
|
-
const pageBounds = Object.fromEntries(
|
|
4722
|
-
shapesToStack.map((shape) => [shape.id, this.getShapePageBounds(shape)])
|
|
4723
|
-
);
|
|
4724
4792
|
let val;
|
|
4725
4793
|
let min;
|
|
4726
4794
|
let max;
|
|
@@ -4736,57 +4804,55 @@ class Editor extends (_a = EventEmitter, _getIsShapeHiddenCache_dec = [computed]
|
|
|
4736
4804
|
max = "maxY";
|
|
4737
4805
|
dim = "height";
|
|
4738
4806
|
}
|
|
4739
|
-
let shapeGap;
|
|
4807
|
+
let shapeGap = 0;
|
|
4740
4808
|
if (gap === 0) {
|
|
4741
|
-
const gaps =
|
|
4742
|
-
|
|
4809
|
+
const gaps = {};
|
|
4810
|
+
shapeClustersToStack.sort((a, b) => a.pageBounds[min] - b.pageBounds[min]);
|
|
4743
4811
|
for (let i = 0; i < len - 1; i++) {
|
|
4744
|
-
const
|
|
4745
|
-
const
|
|
4746
|
-
const
|
|
4747
|
-
|
|
4748
|
-
|
|
4749
|
-
const current = gaps.find((g) => g.gap === gap2);
|
|
4750
|
-
if (current) {
|
|
4751
|
-
current.count++;
|
|
4752
|
-
} else {
|
|
4753
|
-
gaps.push({ gap: gap2, count: 1 });
|
|
4812
|
+
const currCluster = shapeClustersToStack[i];
|
|
4813
|
+
const nextCluster = shapeClustersToStack[i + 1];
|
|
4814
|
+
const gap2 = nextCluster.pageBounds[min] - currCluster.pageBounds[max];
|
|
4815
|
+
if (!gaps[gap2]) {
|
|
4816
|
+
gaps[gap2] = 0;
|
|
4754
4817
|
}
|
|
4818
|
+
gaps[gap2]++;
|
|
4755
4819
|
}
|
|
4756
|
-
let maxCount =
|
|
4757
|
-
|
|
4758
|
-
if (
|
|
4759
|
-
maxCount =
|
|
4760
|
-
shapeGap =
|
|
4820
|
+
let maxCount = 1;
|
|
4821
|
+
for (const [gap2, count] of Object.entries(gaps)) {
|
|
4822
|
+
if (count > maxCount) {
|
|
4823
|
+
maxCount = count;
|
|
4824
|
+
shapeGap = parseFloat(gap2);
|
|
4761
4825
|
}
|
|
4762
|
-
}
|
|
4826
|
+
}
|
|
4763
4827
|
if (maxCount === 1) {
|
|
4764
|
-
|
|
4828
|
+
let totalCount = 0;
|
|
4829
|
+
for (const [gap2, count] of Object.entries(gaps)) {
|
|
4830
|
+
shapeGap += parseFloat(gap2) * count;
|
|
4831
|
+
totalCount += count;
|
|
4832
|
+
}
|
|
4833
|
+
shapeGap /= totalCount;
|
|
4765
4834
|
}
|
|
4766
4835
|
} else {
|
|
4767
4836
|
shapeGap = gap;
|
|
4768
4837
|
}
|
|
4769
4838
|
const changes = [];
|
|
4770
|
-
let v =
|
|
4771
|
-
|
|
4772
|
-
|
|
4773
|
-
const delta =
|
|
4774
|
-
delta[val] = v + shapeGap - pageBounds[
|
|
4775
|
-
const
|
|
4776
|
-
|
|
4777
|
-
|
|
4778
|
-
|
|
4779
|
-
|
|
4780
|
-
|
|
4781
|
-
[val]: shape[val] + localDelta[val]
|
|
4782
|
-
} : {
|
|
4783
|
-
id: shape.id,
|
|
4784
|
-
type: shape.type,
|
|
4785
|
-
[val]: shape[val] + localDelta[val]
|
|
4839
|
+
let v = shapeClustersToStack[0].pageBounds[max];
|
|
4840
|
+
for (let i = 1; i < shapeClustersToStack.length; i++) {
|
|
4841
|
+
const { shapes: shapes2, pageBounds } = shapeClustersToStack[i];
|
|
4842
|
+
const delta = new Vec();
|
|
4843
|
+
delta[val] = v + shapeGap - pageBounds[val];
|
|
4844
|
+
for (const shape of shapes2) {
|
|
4845
|
+
const shapeDelta = delta.clone();
|
|
4846
|
+
const parent = this.getShapeParent(shape);
|
|
4847
|
+
if (parent) {
|
|
4848
|
+
const parentTransform = this.getShapePageTransform(parent);
|
|
4849
|
+
if (parentTransform) shapeDelta.rot(-parentTransform.rotation());
|
|
4786
4850
|
}
|
|
4787
|
-
|
|
4788
|
-
|
|
4789
|
-
|
|
4851
|
+
shapeDelta.add(shape);
|
|
4852
|
+
changes.push(this.getChangesToTranslateShape(shape, shapeDelta));
|
|
4853
|
+
}
|
|
4854
|
+
v += pageBounds[dim] + shapeGap;
|
|
4855
|
+
}
|
|
4790
4856
|
this.updateShapes(changes);
|
|
4791
4857
|
return this;
|
|
4792
4858
|
}
|
|
@@ -4804,91 +4870,101 @@ class Editor extends (_a = EventEmitter, _getIsShapeHiddenCache_dec = [computed]
|
|
|
4804
4870
|
* @param gap - The padding to apply to the packed shapes. Defaults to 16.
|
|
4805
4871
|
*/
|
|
4806
4872
|
packShapes(shapes, gap) {
|
|
4807
|
-
const ids = typeof shapes[0] === "string" ? shapes : shapes.map((s) => s.id);
|
|
4808
4873
|
if (this.getIsReadonly()) return this;
|
|
4809
|
-
|
|
4810
|
-
const
|
|
4811
|
-
|
|
4812
|
-
|
|
4813
|
-
|
|
4814
|
-
const
|
|
4815
|
-
|
|
4816
|
-
|
|
4817
|
-
|
|
4818
|
-
|
|
4819
|
-
|
|
4820
|
-
|
|
4821
|
-
|
|
4822
|
-
|
|
4823
|
-
|
|
4824
|
-
|
|
4874
|
+
const ids = typeof shapes[0] === "string" ? shapes : shapes.map((s) => s.id);
|
|
4875
|
+
const shapesToPackFirstPass = compact(ids.map((id) => this.getShape(id)));
|
|
4876
|
+
const shapeClustersToPack = [];
|
|
4877
|
+
const allBounds = [];
|
|
4878
|
+
const visited = /* @__PURE__ */ new Set();
|
|
4879
|
+
for (const shape of shapesToPackFirstPass) {
|
|
4880
|
+
if (visited.has(shape.id)) continue;
|
|
4881
|
+
visited.add(shape.id);
|
|
4882
|
+
const shapePageBounds = this.getShapePageBounds(shape);
|
|
4883
|
+
if (!shapePageBounds) continue;
|
|
4884
|
+
if (!this.getShapeUtil(shape).canBeLaidOut?.(shape, {
|
|
4885
|
+
type: "pack",
|
|
4886
|
+
shapes: shapesToPackFirstPass
|
|
4887
|
+
})) {
|
|
4888
|
+
continue;
|
|
4889
|
+
}
|
|
4890
|
+
const shapesMovingTogether = [shape];
|
|
4891
|
+
const boundsOfShapesMovingTogether = [shapePageBounds];
|
|
4892
|
+
this.collectShapesViaArrowBindings({
|
|
4893
|
+
bindings: this.getBindingsToShape(shape.id, "arrow"),
|
|
4894
|
+
initialShapes: shapesToPackFirstPass,
|
|
4895
|
+
resultShapes: shapesMovingTogether,
|
|
4896
|
+
resultBounds: boundsOfShapesMovingTogether,
|
|
4897
|
+
visited
|
|
4898
|
+
});
|
|
4899
|
+
const commonPageBounds = Box.Common(boundsOfShapesMovingTogether);
|
|
4900
|
+
if (!commonPageBounds) continue;
|
|
4901
|
+
shapeClustersToPack.push({
|
|
4902
|
+
shapes: shapesMovingTogether,
|
|
4903
|
+
pageBounds: commonPageBounds,
|
|
4904
|
+
nextPageBounds: commonPageBounds.clone()
|
|
4905
|
+
});
|
|
4906
|
+
allBounds.push(commonPageBounds);
|
|
4907
|
+
}
|
|
4908
|
+
if (shapeClustersToPack.length < 2) return this;
|
|
4909
|
+
let area = 0;
|
|
4910
|
+
for (const { pageBounds } of shapeClustersToPack) {
|
|
4911
|
+
area += pageBounds.width * pageBounds.height;
|
|
4912
|
+
}
|
|
4913
|
+
const commonBounds = Box.Common(allBounds);
|
|
4825
4914
|
const maxWidth = commonBounds.width;
|
|
4826
|
-
|
|
4915
|
+
shapeClustersToPack.sort((a, b) => a.pageBounds.width - b.pageBounds.width).sort((a, b) => a.pageBounds.height - b.pageBounds.height);
|
|
4827
4916
|
const startWidth = Math.max(Math.ceil(Math.sqrt(area / 0.95)), maxWidth);
|
|
4828
4917
|
const spaces = [new Box(commonBounds.x, commonBounds.y, startWidth, Infinity)];
|
|
4829
4918
|
let width = 0;
|
|
4830
4919
|
let height = 0;
|
|
4831
4920
|
let space;
|
|
4832
4921
|
let last2;
|
|
4833
|
-
for (
|
|
4834
|
-
|
|
4835
|
-
|
|
4836
|
-
|
|
4837
|
-
|
|
4838
|
-
|
|
4839
|
-
|
|
4840
|
-
|
|
4841
|
-
|
|
4842
|
-
width = Math.max(width, bounds.maxX);
|
|
4843
|
-
if (bounds.width === space.width && bounds.height === space.height) {
|
|
4922
|
+
for (const { nextPageBounds } of shapeClustersToPack) {
|
|
4923
|
+
for (let i = spaces.length - 1; i >= 0; i--) {
|
|
4924
|
+
space = spaces[i];
|
|
4925
|
+
if (nextPageBounds.width > space.width || nextPageBounds.height > space.height) continue;
|
|
4926
|
+
nextPageBounds.x = space.x;
|
|
4927
|
+
nextPageBounds.y = space.y;
|
|
4928
|
+
height = Math.max(height, nextPageBounds.maxY);
|
|
4929
|
+
width = Math.max(width, nextPageBounds.maxX);
|
|
4930
|
+
if (nextPageBounds.width === space.width && nextPageBounds.height === space.height) {
|
|
4844
4931
|
last2 = spaces.pop();
|
|
4845
|
-
if (
|
|
4846
|
-
} else if (
|
|
4847
|
-
space.x +=
|
|
4848
|
-
space.width -=
|
|
4849
|
-
} else if (
|
|
4850
|
-
space.y +=
|
|
4851
|
-
space.height -=
|
|
4932
|
+
if (i < spaces.length) spaces[i] = last2;
|
|
4933
|
+
} else if (nextPageBounds.height === space.height) {
|
|
4934
|
+
space.x += nextPageBounds.width + gap;
|
|
4935
|
+
space.width -= nextPageBounds.width + gap;
|
|
4936
|
+
} else if (nextPageBounds.width === space.width) {
|
|
4937
|
+
space.y += nextPageBounds.height + gap;
|
|
4938
|
+
space.height -= nextPageBounds.height + gap;
|
|
4852
4939
|
} else {
|
|
4853
4940
|
spaces.push(
|
|
4854
4941
|
new Box(
|
|
4855
|
-
space.x + (
|
|
4942
|
+
space.x + (nextPageBounds.width + gap),
|
|
4856
4943
|
space.y,
|
|
4857
|
-
space.width - (
|
|
4858
|
-
|
|
4944
|
+
space.width - (nextPageBounds.width + gap),
|
|
4945
|
+
nextPageBounds.height
|
|
4859
4946
|
)
|
|
4860
4947
|
);
|
|
4861
|
-
space.y +=
|
|
4862
|
-
space.height -=
|
|
4948
|
+
space.y += nextPageBounds.height + gap;
|
|
4949
|
+
space.height -= nextPageBounds.height + gap;
|
|
4863
4950
|
}
|
|
4864
4951
|
break;
|
|
4865
4952
|
}
|
|
4866
4953
|
}
|
|
4867
|
-
const commonAfter = Box.Common(
|
|
4954
|
+
const commonAfter = Box.Common(shapeClustersToPack.map((s) => s.nextPageBounds));
|
|
4868
4955
|
const centerDelta = Vec.Sub(commonBounds.center, commonAfter.center);
|
|
4869
|
-
let nextBounds;
|
|
4870
4956
|
const changes = [];
|
|
4871
|
-
for (
|
|
4872
|
-
|
|
4873
|
-
|
|
4874
|
-
|
|
4875
|
-
|
|
4876
|
-
|
|
4877
|
-
|
|
4878
|
-
|
|
4879
|
-
|
|
4880
|
-
|
|
4881
|
-
|
|
4882
|
-
y: shape.y + delta.y
|
|
4883
|
-
};
|
|
4884
|
-
const translateStartChange = this.getShapeUtil(shape).onTranslateStart?.({
|
|
4885
|
-
...shape,
|
|
4886
|
-
...change
|
|
4887
|
-
});
|
|
4888
|
-
if (translateStartChange) {
|
|
4889
|
-
changes.push({ ...change, ...translateStartChange });
|
|
4890
|
-
} else {
|
|
4891
|
-
changes.push(change);
|
|
4957
|
+
for (const { shapes: shapes2, pageBounds, nextPageBounds } of shapeClustersToPack) {
|
|
4958
|
+
const delta = Vec.Sub(nextPageBounds.point, pageBounds.point).add(centerDelta);
|
|
4959
|
+
for (const shape of shapes2) {
|
|
4960
|
+
const shapeDelta = delta.clone();
|
|
4961
|
+
const parent = this.getShapeParent(shape);
|
|
4962
|
+
if (parent) {
|
|
4963
|
+
const parentTransform = this.getShapeParentTransform(shape);
|
|
4964
|
+
if (parentTransform) shapeDelta.rot(-parentTransform.rotation());
|
|
4965
|
+
}
|
|
4966
|
+
shapeDelta.add(shape);
|
|
4967
|
+
changes.push(this.getChangesToTranslateShape(shape, shapeDelta));
|
|
4892
4968
|
}
|
|
4893
4969
|
}
|
|
4894
4970
|
if (changes.length) {
|
|
@@ -4911,19 +4987,45 @@ class Editor extends (_a = EventEmitter, _getIsShapeHiddenCache_dec = [computed]
|
|
|
4911
4987
|
* @public
|
|
4912
4988
|
*/
|
|
4913
4989
|
alignShapes(shapes, operation) {
|
|
4914
|
-
const ids = typeof shapes[0] === "string" ? shapes : shapes.map((s) => s.id);
|
|
4915
4990
|
if (this.getIsReadonly()) return this;
|
|
4916
|
-
|
|
4917
|
-
const
|
|
4918
|
-
const
|
|
4919
|
-
|
|
4920
|
-
);
|
|
4921
|
-
const
|
|
4991
|
+
const ids = typeof shapes[0] === "string" ? shapes : shapes.map((s) => s.id);
|
|
4992
|
+
const shapesToAlignFirstPass = compact(ids.map((id) => this.getShape(id)));
|
|
4993
|
+
const shapeClustersToAlign = [];
|
|
4994
|
+
const allBounds = [];
|
|
4995
|
+
const visited = /* @__PURE__ */ new Set();
|
|
4996
|
+
for (const shape of shapesToAlignFirstPass) {
|
|
4997
|
+
if (visited.has(shape.id)) continue;
|
|
4998
|
+
visited.add(shape.id);
|
|
4999
|
+
const shapePageBounds = this.getShapePageBounds(shape);
|
|
5000
|
+
if (!shapePageBounds) continue;
|
|
5001
|
+
if (!this.getShapeUtil(shape).canBeLaidOut?.(shape, {
|
|
5002
|
+
type: "align",
|
|
5003
|
+
shapes: shapesToAlignFirstPass
|
|
5004
|
+
})) {
|
|
5005
|
+
continue;
|
|
5006
|
+
}
|
|
5007
|
+
const shapesMovingTogether = [shape];
|
|
5008
|
+
const boundsOfShapesMovingTogether = [shapePageBounds];
|
|
5009
|
+
this.collectShapesViaArrowBindings({
|
|
5010
|
+
bindings: this.getBindingsToShape(shape.id, "arrow"),
|
|
5011
|
+
initialShapes: shapesToAlignFirstPass,
|
|
5012
|
+
resultShapes: shapesMovingTogether,
|
|
5013
|
+
resultBounds: boundsOfShapesMovingTogether,
|
|
5014
|
+
visited
|
|
5015
|
+
});
|
|
5016
|
+
const commonPageBounds = Box.Common(boundsOfShapesMovingTogether);
|
|
5017
|
+
if (!commonPageBounds) continue;
|
|
5018
|
+
shapeClustersToAlign.push({
|
|
5019
|
+
shapes: shapesMovingTogether,
|
|
5020
|
+
pageBounds: commonPageBounds
|
|
5021
|
+
});
|
|
5022
|
+
allBounds.push(commonPageBounds);
|
|
5023
|
+
}
|
|
5024
|
+
if (shapeClustersToAlign.length < 2) return this;
|
|
5025
|
+
const commonBounds = Box.Common(allBounds);
|
|
4922
5026
|
const changes = [];
|
|
4923
|
-
|
|
4924
|
-
const
|
|
4925
|
-
if (!pageBounds) return;
|
|
4926
|
-
const delta = { x: 0, y: 0 };
|
|
5027
|
+
shapeClustersToAlign.forEach(({ shapes: shapes2, pageBounds }) => {
|
|
5028
|
+
const delta = new Vec();
|
|
4927
5029
|
switch (operation) {
|
|
4928
5030
|
case "top": {
|
|
4929
5031
|
delta.y = commonBounds.minY - pageBounds.minY;
|
|
@@ -4950,9 +5052,16 @@ class Editor extends (_a = EventEmitter, _getIsShapeHiddenCache_dec = [computed]
|
|
|
4950
5052
|
break;
|
|
4951
5053
|
}
|
|
4952
5054
|
}
|
|
4953
|
-
const
|
|
4954
|
-
|
|
4955
|
-
|
|
5055
|
+
for (const shape of shapes2) {
|
|
5056
|
+
const shapeDelta = delta.clone();
|
|
5057
|
+
const parent = this.getShapeParent(shape);
|
|
5058
|
+
if (parent) {
|
|
5059
|
+
const parentTransform = this.getShapePageTransform(parent);
|
|
5060
|
+
if (parentTransform) shapeDelta.rot(-parentTransform.rotation());
|
|
5061
|
+
}
|
|
5062
|
+
shapeDelta.add(shape);
|
|
5063
|
+
changes.push(this.getChangesToTranslateShape(shape, shapeDelta));
|
|
5064
|
+
}
|
|
4956
5065
|
});
|
|
4957
5066
|
this.updateShapes(changes);
|
|
4958
5067
|
return this;
|
|
@@ -4972,47 +5081,95 @@ class Editor extends (_a = EventEmitter, _getIsShapeHiddenCache_dec = [computed]
|
|
|
4972
5081
|
* @public
|
|
4973
5082
|
*/
|
|
4974
5083
|
distributeShapes(shapes, operation) {
|
|
4975
|
-
const ids = typeof shapes[0] === "string" ? shapes : shapes.map((s) => s.id);
|
|
4976
5084
|
if (this.getIsReadonly()) return this;
|
|
4977
|
-
|
|
4978
|
-
const
|
|
4979
|
-
const
|
|
4980
|
-
const
|
|
4981
|
-
|
|
4982
|
-
)
|
|
5085
|
+
const ids = typeof shapes[0] === "string" ? shapes : shapes.map((s) => s.id);
|
|
5086
|
+
const shapesToDistributeFirstPass = compact(ids.map((id) => this.getShape(id)));
|
|
5087
|
+
const shapeClustersToDistribute = [];
|
|
5088
|
+
const allBounds = [];
|
|
5089
|
+
const visited = /* @__PURE__ */ new Set();
|
|
5090
|
+
for (const shape of shapesToDistributeFirstPass) {
|
|
5091
|
+
if (visited.has(shape.id)) continue;
|
|
5092
|
+
visited.add(shape.id);
|
|
5093
|
+
const shapePageBounds = this.getShapePageBounds(shape);
|
|
5094
|
+
if (!shapePageBounds) continue;
|
|
5095
|
+
if (!this.getShapeUtil(shape).canBeLaidOut?.(shape, {
|
|
5096
|
+
type: "distribute",
|
|
5097
|
+
shapes: shapesToDistributeFirstPass
|
|
5098
|
+
})) {
|
|
5099
|
+
continue;
|
|
5100
|
+
}
|
|
5101
|
+
const shapesMovingTogether = [shape];
|
|
5102
|
+
const boundsOfShapesMovingTogether = [shapePageBounds];
|
|
5103
|
+
this.collectShapesViaArrowBindings({
|
|
5104
|
+
bindings: this.getBindingsToShape(shape.id, "arrow"),
|
|
5105
|
+
initialShapes: shapesToDistributeFirstPass,
|
|
5106
|
+
resultShapes: shapesMovingTogether,
|
|
5107
|
+
resultBounds: boundsOfShapesMovingTogether,
|
|
5108
|
+
visited
|
|
5109
|
+
});
|
|
5110
|
+
const commonPageBounds = Box.Common(boundsOfShapesMovingTogether);
|
|
5111
|
+
if (!commonPageBounds) continue;
|
|
5112
|
+
shapeClustersToDistribute.push({
|
|
5113
|
+
shapes: shapesMovingTogether,
|
|
5114
|
+
pageBounds: commonPageBounds
|
|
5115
|
+
});
|
|
5116
|
+
allBounds.push(commonPageBounds);
|
|
5117
|
+
}
|
|
5118
|
+
if (shapeClustersToDistribute.length < 3) return this;
|
|
4983
5119
|
let val;
|
|
4984
5120
|
let min;
|
|
4985
5121
|
let max;
|
|
4986
|
-
let mid;
|
|
4987
5122
|
let dim;
|
|
4988
5123
|
if (operation === "horizontal") {
|
|
4989
5124
|
val = "x";
|
|
4990
5125
|
min = "minX";
|
|
4991
5126
|
max = "maxX";
|
|
4992
|
-
mid = "midX";
|
|
4993
5127
|
dim = "width";
|
|
4994
5128
|
} else {
|
|
4995
5129
|
val = "y";
|
|
4996
5130
|
min = "minY";
|
|
4997
5131
|
max = "maxY";
|
|
4998
|
-
mid = "midY";
|
|
4999
5132
|
dim = "height";
|
|
5000
5133
|
}
|
|
5001
5134
|
const changes = [];
|
|
5002
|
-
const first =
|
|
5003
|
-
|
|
5004
|
-
)
|
|
5005
|
-
|
|
5006
|
-
|
|
5007
|
-
|
|
5008
|
-
|
|
5009
|
-
|
|
5010
|
-
|
|
5011
|
-
|
|
5012
|
-
|
|
5013
|
-
|
|
5014
|
-
|
|
5135
|
+
const first = shapeClustersToDistribute.sort((a, b) => a.pageBounds[min] - b.pageBounds[min])[0];
|
|
5136
|
+
const last2 = shapeClustersToDistribute.sort((a, b) => b.pageBounds[max] - a.pageBounds[max])[0];
|
|
5137
|
+
if (first === last2) {
|
|
5138
|
+
const excludedShapeIds = new Set(first.shapes.map((s) => s.id));
|
|
5139
|
+
return this.distributeShapes(
|
|
5140
|
+
ids.filter((id) => !excludedShapeIds.has(id)),
|
|
5141
|
+
operation
|
|
5142
|
+
);
|
|
5143
|
+
}
|
|
5144
|
+
const shapeClustersToMove = shapeClustersToDistribute.filter((shape) => shape !== first && shape !== last2).sort((a, b) => {
|
|
5145
|
+
if (a.pageBounds[min] === b.pageBounds[min]) {
|
|
5146
|
+
return a.shapes[0].id < b.shapes[0].id ? -1 : 1;
|
|
5147
|
+
}
|
|
5148
|
+
return a.pageBounds[min] - b.pageBounds[min];
|
|
5015
5149
|
});
|
|
5150
|
+
const maxFirst = first.pageBounds[max];
|
|
5151
|
+
const range = last2.pageBounds[min] - maxFirst;
|
|
5152
|
+
const summedShapeDimensions = shapeClustersToMove.reduce((acc, s) => acc + s.pageBounds[dim], 0);
|
|
5153
|
+
const gap = (range - summedShapeDimensions) / (shapeClustersToMove.length + 1);
|
|
5154
|
+
for (let v = maxFirst + gap, i = 0; i < shapeClustersToMove.length; i++) {
|
|
5155
|
+
const { shapes: shapes2, pageBounds } = shapeClustersToMove[i];
|
|
5156
|
+
const delta = new Vec();
|
|
5157
|
+
delta[val] = v - pageBounds[val];
|
|
5158
|
+
if (v + pageBounds[dim] > last2.pageBounds[max] - 1) {
|
|
5159
|
+
delta[val] = last2.pageBounds[max] - pageBounds[max] - 1;
|
|
5160
|
+
}
|
|
5161
|
+
for (const shape of shapes2) {
|
|
5162
|
+
const shapeDelta = delta.clone();
|
|
5163
|
+
const parent = this.getShapeParent(shape);
|
|
5164
|
+
if (parent) {
|
|
5165
|
+
const parentTransform = this.getShapePageTransform(parent);
|
|
5166
|
+
if (parentTransform) shapeDelta.rot(-parentTransform.rotation());
|
|
5167
|
+
}
|
|
5168
|
+
shapeDelta.add(shape);
|
|
5169
|
+
changes.push(this.getChangesToTranslateShape(shape, shapeDelta));
|
|
5170
|
+
}
|
|
5171
|
+
v += pageBounds[dim] + gap;
|
|
5172
|
+
}
|
|
5016
5173
|
this.updateShapes(changes);
|
|
5017
5174
|
return this;
|
|
5018
5175
|
}
|
|
@@ -5033,59 +5190,78 @@ class Editor extends (_a = EventEmitter, _getIsShapeHiddenCache_dec = [computed]
|
|
|
5033
5190
|
stretchShapes(shapes, operation) {
|
|
5034
5191
|
const ids = typeof shapes[0] === "string" ? shapes : shapes.map((s) => s.id);
|
|
5035
5192
|
if (this.getIsReadonly()) return this;
|
|
5036
|
-
|
|
5037
|
-
|
|
5038
|
-
|
|
5039
|
-
const
|
|
5040
|
-
const
|
|
5041
|
-
|
|
5042
|
-
|
|
5043
|
-
|
|
5044
|
-
|
|
5045
|
-
|
|
5046
|
-
|
|
5047
|
-
|
|
5048
|
-
|
|
5049
|
-
|
|
5050
|
-
|
|
5051
|
-
|
|
5052
|
-
|
|
5053
|
-
|
|
5054
|
-
const scale = new Vec(1, commonBounds.height / pageBounds.height);
|
|
5055
|
-
this.resizeShape(shape.id, scale, {
|
|
5056
|
-
initialBounds: bounds,
|
|
5057
|
-
scaleOrigin: new Vec(pageBounds.center.x, commonBounds.minY),
|
|
5058
|
-
isAspectRatioLocked: this.getShapeUtil(shape).isAspectRatioLocked(shape),
|
|
5059
|
-
scaleAxisRotation: 0
|
|
5060
|
-
});
|
|
5061
|
-
}
|
|
5062
|
-
});
|
|
5063
|
-
break;
|
|
5064
|
-
}
|
|
5065
|
-
case "horizontal": {
|
|
5066
|
-
this.run(() => {
|
|
5067
|
-
for (const shape of shapesToStretch) {
|
|
5068
|
-
const bounds = shapeBounds[shape.id];
|
|
5069
|
-
const pageBounds = shapePageBounds[shape.id];
|
|
5070
|
-
const pageRotation = this.getShapePageTransform(shape).rotation();
|
|
5071
|
-
if (pageRotation % PI2) continue;
|
|
5072
|
-
const localOffset = new Vec(commonBounds.minX - pageBounds.minX, 0);
|
|
5073
|
-
const parentTransform = this.getShapeParentTransform(shape);
|
|
5074
|
-
if (parentTransform) localOffset.rot(-parentTransform.rotation());
|
|
5075
|
-
const { x, y } = Vec.Add(localOffset, shape);
|
|
5076
|
-
this.updateShapes([{ id: shape.id, type: shape.type, x, y }]);
|
|
5077
|
-
const scale = new Vec(commonBounds.width / pageBounds.width, 1);
|
|
5078
|
-
this.resizeShape(shape.id, scale, {
|
|
5079
|
-
initialBounds: bounds,
|
|
5080
|
-
scaleOrigin: new Vec(commonBounds.minX, pageBounds.center.y),
|
|
5081
|
-
isAspectRatioLocked: this.getShapeUtil(shape).isAspectRatioLocked(shape),
|
|
5082
|
-
scaleAxisRotation: 0
|
|
5083
|
-
});
|
|
5084
|
-
}
|
|
5085
|
-
});
|
|
5086
|
-
break;
|
|
5193
|
+
const shapesToStretchFirstPass = compact(ids.map((id) => this.getShape(id))).filter(
|
|
5194
|
+
(s) => this.getShapePageTransform(s)?.rotation() % (PI / 2) === 0
|
|
5195
|
+
);
|
|
5196
|
+
const shapeClustersToStretch = [];
|
|
5197
|
+
const allBounds = [];
|
|
5198
|
+
const visited = /* @__PURE__ */ new Set();
|
|
5199
|
+
for (const shape of shapesToStretchFirstPass) {
|
|
5200
|
+
if (visited.has(shape.id)) continue;
|
|
5201
|
+
visited.add(shape.id);
|
|
5202
|
+
const shapePageBounds = this.getShapePageBounds(shape);
|
|
5203
|
+
if (!shapePageBounds) continue;
|
|
5204
|
+
const shapesMovingTogether = [shape];
|
|
5205
|
+
const boundsOfShapesMovingTogether = [shapePageBounds];
|
|
5206
|
+
if (!this.getShapeUtil(shape).canBeLaidOut?.(shape, {
|
|
5207
|
+
type: "stretch",
|
|
5208
|
+
shapes: shapesToStretchFirstPass
|
|
5209
|
+
})) {
|
|
5210
|
+
continue;
|
|
5087
5211
|
}
|
|
5212
|
+
this.collectShapesViaArrowBindings({
|
|
5213
|
+
bindings: this.getBindingsToShape(shape.id, "arrow"),
|
|
5214
|
+
initialShapes: shapesToStretchFirstPass,
|
|
5215
|
+
resultShapes: shapesMovingTogether,
|
|
5216
|
+
resultBounds: boundsOfShapesMovingTogether,
|
|
5217
|
+
visited
|
|
5218
|
+
});
|
|
5219
|
+
const commonPageBounds = Box.Common(boundsOfShapesMovingTogether);
|
|
5220
|
+
if (!commonPageBounds) continue;
|
|
5221
|
+
shapeClustersToStretch.push({
|
|
5222
|
+
shapes: shapesMovingTogether,
|
|
5223
|
+
pageBounds: commonPageBounds
|
|
5224
|
+
});
|
|
5225
|
+
allBounds.push(commonPageBounds);
|
|
5226
|
+
}
|
|
5227
|
+
if (shapeClustersToStretch.length < 2) return this;
|
|
5228
|
+
const commonBounds = Box.Common(allBounds);
|
|
5229
|
+
let val;
|
|
5230
|
+
let min;
|
|
5231
|
+
let dim;
|
|
5232
|
+
if (operation === "horizontal") {
|
|
5233
|
+
val = "x";
|
|
5234
|
+
min = "minX";
|
|
5235
|
+
dim = "width";
|
|
5236
|
+
} else {
|
|
5237
|
+
val = "y";
|
|
5238
|
+
min = "minY";
|
|
5239
|
+
dim = "height";
|
|
5088
5240
|
}
|
|
5241
|
+
this.run(() => {
|
|
5242
|
+
shapeClustersToStretch.forEach(({ shapes: shapes2, pageBounds }) => {
|
|
5243
|
+
const localOffset = new Vec();
|
|
5244
|
+
localOffset[val] = commonBounds[min] - pageBounds[min];
|
|
5245
|
+
const scaleOrigin = pageBounds.center.clone();
|
|
5246
|
+
scaleOrigin[val] = commonBounds[min];
|
|
5247
|
+
const scale = new Vec(1, 1);
|
|
5248
|
+
scale[val] = commonBounds[dim] / pageBounds[dim];
|
|
5249
|
+
for (const shape of shapes2) {
|
|
5250
|
+
const shapeLocalOffset = localOffset.clone();
|
|
5251
|
+
const parentTransform = this.getShapeParentTransform(shape);
|
|
5252
|
+
if (parentTransform) localOffset.rot(-parentTransform.rotation());
|
|
5253
|
+
shapeLocalOffset.add(shape);
|
|
5254
|
+
const changes = this.getChangesToTranslateShape(shape, shapeLocalOffset);
|
|
5255
|
+
this.updateShape(changes);
|
|
5256
|
+
this.resizeShape(shape.id, scale, {
|
|
5257
|
+
initialBounds: this.getShapeGeometry(shape).bounds,
|
|
5258
|
+
scaleOrigin,
|
|
5259
|
+
isAspectRatioLocked: this.getShapeUtil(shape).isAspectRatioLocked(shape),
|
|
5260
|
+
scaleAxisRotation: 0
|
|
5261
|
+
});
|
|
5262
|
+
}
|
|
5263
|
+
});
|
|
5264
|
+
});
|
|
5089
5265
|
return this;
|
|
5090
5266
|
}
|
|
5091
5267
|
/**
|