ooxml-core 0.21.2 → 0.22.0

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 (78) hide show
  1. package/README.md +3 -4
  2. package/dist/automation/index.cjs +34 -13
  3. package/dist/automation/index.mjs +34 -13
  4. package/dist/chart/index.cjs +242 -0
  5. package/dist/chart/index.d.ts +5 -0
  6. package/dist/chart/index.d.ts.map +1 -1
  7. package/dist/chart/index.mjs +227 -1
  8. package/dist/chart/pie-best-fit.d.ts +51 -0
  9. package/dist/chart/pie-best-fit.d.ts.map +1 -0
  10. package/dist/chart/pie-label-collision.d.ts +48 -0
  11. package/dist/chart/pie-label-collision.d.ts.map +1 -0
  12. package/dist/chart/radar-geometry.d.ts +19 -0
  13. package/dist/chart/radar-geometry.d.ts.map +1 -0
  14. package/dist/chart/treemap-squarify.d.ts +31 -0
  15. package/dist/chart/treemap-squarify.d.ts.map +1 -0
  16. package/dist/chart/trendline-equation.d.ts +39 -0
  17. package/dist/chart/trendline-equation.d.ts.map +1 -0
  18. package/dist/collab/index.cjs +6 -1
  19. package/dist/collab/index.mjs +6 -1
  20. package/dist/color/contrast.d.ts +18 -0
  21. package/dist/color/contrast.d.ts.map +1 -0
  22. package/dist/color/index.cjs +33 -0
  23. package/dist/color/index.d.ts +2 -0
  24. package/dist/color/index.d.ts.map +1 -1
  25. package/dist/color/index.mjs +31 -1
  26. package/dist/color/unit-rgb.d.ts +21 -0
  27. package/dist/color/unit-rgb.d.ts.map +1 -0
  28. package/dist/docx/embedded.cjs +1 -1
  29. package/dist/docx/embedded.mjs +1 -1
  30. package/dist/docx/index.cjs +1 -1
  31. package/dist/docx/index.mjs +1 -1
  32. package/dist/docx/load/index.cjs +1 -1
  33. package/dist/docx/load/index.mjs +1 -1
  34. package/dist/geometry/align-distribute.d.ts +145 -0
  35. package/dist/geometry/align-distribute.d.ts.map +1 -0
  36. package/dist/geometry/index.cjs +318 -0
  37. package/dist/geometry/index.d.ts +2 -0
  38. package/dist/geometry/index.d.ts.map +1 -1
  39. package/dist/geometry/index.mjs +307 -1
  40. package/dist/geometry/snap-guides.d.ts +125 -0
  41. package/dist/geometry/snap-guides.d.ts.map +1 -0
  42. package/dist/index.cjs +665 -0
  43. package/dist/index.mjs +665 -0
  44. package/dist/pptx/automation/index.cjs +24 -1
  45. package/dist/pptx/automation/index.d.ts +3 -3
  46. package/dist/pptx/automation/index.mjs +24 -1
  47. package/dist/pptx/chunks/{index-JaNVJ6Tf.d.ts → index-D2y7ehZ5.d.ts} +3 -3
  48. package/dist/pptx/chunks/{index-JaNVJ6Tf.d.ts.map → index-D2y7ehZ5.d.ts.map} +1 -1
  49. package/dist/pptx/chunks/{index-DHdHU9Qr.d.ts → index-ucqKcqvI.d.ts} +10 -2
  50. package/dist/pptx/chunks/{index-DHdHU9Qr.d.ts.map → index-ucqKcqvI.d.ts.map} +1 -1
  51. package/dist/pptx/chunks/{text-operations-DUEhSW5Z.d.ts → text-operations-CR3pUJi-.d.ts} +14 -2
  52. package/dist/pptx/chunks/text-operations-CR3pUJi-.d.ts.map +1 -0
  53. package/dist/pptx/cli/index.cjs +23 -0
  54. package/dist/pptx/cli/index.d.ts +1 -1
  55. package/dist/pptx/cli/index.mjs +23 -0
  56. package/dist/pptx/converter/index.d.ts +1 -1
  57. package/dist/pptx/index.cjs +33 -3
  58. package/dist/pptx/index.d.ts +3 -3
  59. package/dist/pptx/index.mjs +33 -3
  60. package/dist/teams/index.cjs +7 -2
  61. package/dist/teams/index.mjs +7 -2
  62. package/dist/text/decimal-tab.d.ts +24 -0
  63. package/dist/text/decimal-tab.d.ts.map +1 -0
  64. package/dist/text/index.cjs +72 -0
  65. package/dist/text/index.d.ts +2 -0
  66. package/dist/text/index.d.ts.map +1 -1
  67. package/dist/text/index.mjs +68 -1
  68. package/dist/text/tab-leader.d.ts +36 -0
  69. package/dist/text/tab-leader.d.ts.map +1 -0
  70. package/dist/xlsx/edit/charts.d.ts.map +1 -1
  71. package/dist/xlsx/edit/clipboard-html-parse.d.ts.map +1 -1
  72. package/dist/xlsx/index.cjs +33 -12
  73. package/dist/xlsx/index.mjs +33 -12
  74. package/dist/xlsx/ui/grid/cell-paint.d.ts.map +1 -1
  75. package/dist/xlsx/ui/index.cjs +37 -16
  76. package/dist/xlsx/ui/index.mjs +37 -16
  77. package/package.json +2 -2
  78. package/dist/pptx/chunks/text-operations-DUEhSW5Z.d.ts.map +0 -1
@@ -1787,7 +1787,7 @@ async function applyCoreProperties(zip, next, prior) {
1787
1787
  await ensureContentTypeOverride(zip, CORE_PATH, CORE_TYPE);
1788
1788
  const rels = zip.file("_rels/.rels");
1789
1789
  const relsXml = await rels?.async("string") ?? '<?xml version="1.0" encoding="UTF-8" standalone="yes"?><Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships"></Relationships>';
1790
- if (!relsXml.includes(CORE_REL)) {
1790
+ if (!relsXml.includes(`Type="${CORE_REL}"`)) {
1791
1791
  const ids = [...relsXml.matchAll(/Id="rId(\d+)"/g)].map((match) => Number(match[1]));
1792
1792
  const id = `rId${Math.max(0, ...ids) + 1}`;
1793
1793
  zip.file(
@@ -1781,7 +1781,7 @@ async function applyCoreProperties(zip, next, prior) {
1781
1781
  await ensureContentTypeOverride(zip, CORE_PATH, CORE_TYPE);
1782
1782
  const rels = zip.file("_rels/.rels");
1783
1783
  const relsXml = await rels?.async("string") ?? '<?xml version="1.0" encoding="UTF-8" standalone="yes"?><Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships"></Relationships>';
1784
- if (!relsXml.includes(CORE_REL)) {
1784
+ if (!relsXml.includes(`Type="${CORE_REL}"`)) {
1785
1785
  const ids = [...relsXml.matchAll(/Id="rId(\d+)"/g)].map((match) => Number(match[1]));
1786
1786
  const id = `rId${Math.max(0, ...ids) + 1}`;
1787
1787
  zip.file(
@@ -2110,7 +2110,7 @@ async function applyCoreProperties(zip, next, prior) {
2110
2110
  await ensureContentTypeOverride(zip, CORE_PATH, CORE_TYPE);
2111
2111
  const rels = zip.file("_rels/.rels");
2112
2112
  const relsXml = await rels?.async("string") ?? '<?xml version="1.0" encoding="UTF-8" standalone="yes"?><Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships"></Relationships>';
2113
- if (!relsXml.includes(CORE_REL)) {
2113
+ if (!relsXml.includes(`Type="${CORE_REL}"`)) {
2114
2114
  const ids = [...relsXml.matchAll(/Id="rId(\d+)"/g)].map((match) => Number(match[1]));
2115
2115
  const id = `rId${Math.max(0, ...ids) + 1}`;
2116
2116
  zip.file(
@@ -2104,7 +2104,7 @@ async function applyCoreProperties(zip, next, prior) {
2104
2104
  await ensureContentTypeOverride(zip, CORE_PATH, CORE_TYPE);
2105
2105
  const rels = zip.file("_rels/.rels");
2106
2106
  const relsXml = await rels?.async("string") ?? '<?xml version="1.0" encoding="UTF-8" standalone="yes"?><Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships"></Relationships>';
2107
- if (!relsXml.includes(CORE_REL)) {
2107
+ if (!relsXml.includes(`Type="${CORE_REL}"`)) {
2108
2108
  const ids = [...relsXml.matchAll(/Id="rId(\d+)"/g)].map((match) => Number(match[1]));
2109
2109
  const id = `rId${Math.max(0, ...ids) + 1}`;
2110
2110
  zip.file(
@@ -0,0 +1,145 @@
1
+ /**
2
+ * Pure geometry helpers for the align / distribute editor operations.
3
+ *
4
+ * These functions operate over a list of slide elements (anything carrying the
5
+ * `{ id, x, y, width, height }` bounding-box fields of {@link PptxElement}) and
6
+ * return a `Map` keyed by element `id` describing the *new* position(s) for the
7
+ * elements that need to move. Elements that already sit on the target edge (or
8
+ * the two outer-most elements during distribution) are still included with
9
+ * their unchanged coordinate so callers can apply the whole map uniformly; the
10
+ * map only ever contains the axis that the operation touches.
11
+ *
12
+ * The reference box the selection is aligned against is either the union of
13
+ * the selected boxes (PowerPoint's "Align Selected Objects") or the slide
14
+ * itself ("Align to Slide"). PowerPoint defaults to the slide whenever a
15
+ * single object is selected, which is why a one-element selection is not a
16
+ * no-op once a `slideSize` is supplied: Align Center on a lone title is one
17
+ * of the commonest arrange gestures there is.
18
+ *
19
+ * The helpers are deliberately framework-agnostic: no DOM, no Vue reactivity,
20
+ * no side effects. The host wires them into the editor by feeding the current
21
+ * selection in and applying the returned `Map` via its element-transform
22
+ * operation (one batched history entry per call).
23
+ */
24
+ /** Edge / centre that {@link alignElements} can snap a selection to. */
25
+ export type AlignEdge = 'left' | 'centerH' | 'right' | 'top' | 'middle' | 'bottom';
26
+ /** Axis along which {@link distributeElements} evenly spaces a selection. */
27
+ export type DistributeAxis = 'horizontal' | 'vertical';
28
+ /**
29
+ * What a selection is aligned / distributed against.
30
+ *
31
+ * - `'selection'`: the union bounding box of the selected elements
32
+ * (PowerPoint's "Align Selected Objects"). Needs at least two elements.
33
+ * - `'slide'`: the slide bounds (`0, 0, slideSize.width, slideSize.height`),
34
+ * PowerPoint's "Align to Slide". Works for any non-empty selection.
35
+ */
36
+ export type AlignReference = 'selection' | 'slide';
37
+ /** Slide dimensions in the same (pixel) units as the element boxes. */
38
+ export interface AlignSlideSize {
39
+ width: number;
40
+ height: number;
41
+ }
42
+ /** Optional reference selection for {@link alignElements} and friends. */
43
+ export interface AlignOptions {
44
+ /**
45
+ * Explicit reference. When omitted, `'slide'` is assumed for a single
46
+ * element (PowerPoint's default) and `'selection'` otherwise. Either way
47
+ * the slide is only used when `slideSize` is supplied.
48
+ */
49
+ reference?: AlignReference;
50
+ /** Slide bounds; required for the slide to act as the reference. */
51
+ slideSize?: AlignSlideSize;
52
+ }
53
+ /** New position for a single element. Only the affected axis is populated. */
54
+ export interface ElementPosition {
55
+ x?: number;
56
+ y?: number;
57
+ }
58
+ /**
59
+ * The minimal bounding-box shape the helpers need. {@link PptxElement} already
60
+ * satisfies this, but accepting the structural subset keeps the helpers usable
61
+ * (and testable) with plain geometry objects too.
62
+ */
63
+ export interface BoundingBoxElement {
64
+ id: string;
65
+ x: number;
66
+ y: number;
67
+ width: number;
68
+ height: number;
69
+ }
70
+ /** An axis-aligned reference rectangle. */
71
+ export interface AlignReferenceBox {
72
+ minX: number;
73
+ minY: number;
74
+ maxX: number;
75
+ maxY: number;
76
+ }
77
+ /**
78
+ * Decide whether the slide (rather than the selection) is the reference for a
79
+ * given selection + options. The slide is chosen when asked for explicitly or
80
+ * when only one element is selected, but only when its size is known.
81
+ */
82
+ export declare function usesSlideReference(elementCount: number, options: AlignOptions | undefined): options is AlignOptions & {
83
+ slideSize: AlignSlideSize;
84
+ };
85
+ /**
86
+ * The rectangle a selection is aligned against, or `null` when there is
87
+ * nothing to align to (an empty selection, or a lone element with no slide
88
+ * size to fall back on).
89
+ */
90
+ export declare function resolveAlignReferenceBox(elements: readonly BoundingBoxElement[], options?: AlignOptions): AlignReferenceBox | null;
91
+ /**
92
+ * Align every element in the selection to a shared edge (or centre line) of
93
+ * the reference box (see {@link resolveAlignReferenceBox}).
94
+ *
95
+ * - `left` / `right` / `centerH` move elements horizontally (set `x`).
96
+ * - `top` / `bottom` / `middle` move elements vertically (set `y`).
97
+ *
98
+ * Without a `slideSize` the behaviour is unchanged from before the option
99
+ * existed: the reference is the selection's union box and fewer than two
100
+ * elements yields an empty map.
101
+ *
102
+ * @param elements - The selected elements.
103
+ * @param edge - The edge / centre line to align to.
104
+ * @param options - Reference selection; see {@link AlignOptions}.
105
+ * @returns A `Map<id, {x?, y?}>` of new positions (only the touched axis set).
106
+ */
107
+ export declare function alignElements(elements: readonly BoundingBoxElement[], edge: AlignEdge, options?: AlignOptions): Map<string, ElementPosition>;
108
+ /**
109
+ * Distribute a selection so the gaps between adjacent elements are equal.
110
+ *
111
+ * Elements are ordered by their leading edge on the chosen axis. Against the
112
+ * selection, the two outer-most elements are pinned and the free space (span
113
+ * minus the sum of element sizes) is divided evenly into the gaps between
114
+ * consecutive elements; this needs at least three elements (two outer + one
115
+ * interior to move). Against the slide (`'slide'` reference, or a single
116
+ * element with a `slideSize`), the span is the whole slide and the free space
117
+ * is divided into `n + 1` equal gaps so the margins to both slide edges match
118
+ * the gaps between elements, as PowerPoint's "Align to Slide" distribution
119
+ * does; any non-empty selection qualifies.
120
+ *
121
+ * @param elements - The selected elements.
122
+ * @param axis - `'horizontal'` distributes along `x`; `'vertical'` along `y`.
123
+ * @param options - Reference selection; see {@link AlignOptions}.
124
+ * @returns A `Map<id, {x?, y?}>` of new positions (only the touched axis set).
125
+ */
126
+ export declare function distributeElements(elements: readonly BoundingBoxElement[], axis: DistributeAxis, options?: AlignOptions): Map<string, ElementPosition>;
127
+ /** Alias of {@link AlignEdge}. */
128
+ export type AlignMode = AlignEdge;
129
+ /** Alias of {@link DistributeAxis}. */
130
+ export type DistributeMode = DistributeAxis;
131
+ /** Alias of {@link BoundingBoxElement}. */
132
+ export type AlignBox = BoundingBoxElement;
133
+ /** Alias of {@link ElementPosition}. */
134
+ export type PositionUpdate = ElementPosition;
135
+ /**
136
+ * {@link alignElements} (slide reference included) filtered to the boxes that
137
+ * actually move.
138
+ */
139
+ export declare function computeAlign(boxes: readonly AlignBox[], mode: AlignMode, options?: AlignOptions): Map<string, PositionUpdate>;
140
+ /**
141
+ * {@link distributeElements} (slide reference included) filtered to the boxes
142
+ * that actually move.
143
+ */
144
+ export declare function computeDistribute(boxes: readonly AlignBox[], mode: DistributeMode, options?: AlignOptions): Map<string, PositionUpdate>;
145
+ //# sourceMappingURL=align-distribute.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"align-distribute.d.ts","sourceRoot":"","sources":["../../geometry/align-distribute.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAEH,wEAAwE;AACxE,MAAM,MAAM,SAAS,GAAG,MAAM,GAAG,SAAS,GAAG,OAAO,GAAG,KAAK,GAAG,QAAQ,GAAG,QAAQ,CAAC;AAEnF,6EAA6E;AAC7E,MAAM,MAAM,cAAc,GAAG,YAAY,GAAG,UAAU,CAAC;AAEvD;;;;;;;GAOG;AACH,MAAM,MAAM,cAAc,GAAG,WAAW,GAAG,OAAO,CAAC;AAEnD,uEAAuE;AACvE,MAAM,WAAW,cAAc;IAC9B,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;CACf;AAED,0EAA0E;AAC1E,MAAM,WAAW,YAAY;IAC5B;;;;OAIG;IACH,SAAS,CAAC,EAAE,cAAc,CAAC;IAC3B,oEAAoE;IACpE,SAAS,CAAC,EAAE,cAAc,CAAC;CAC3B;AAED,8EAA8E;AAC9E,MAAM,WAAW,eAAe;IAC/B,CAAC,CAAC,EAAE,MAAM,CAAC;IACX,CAAC,CAAC,EAAE,MAAM,CAAC;CACX;AAED;;;;GAIG;AACH,MAAM,WAAW,kBAAkB;IAClC,EAAE,EAAE,MAAM,CAAC;IACX,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,MAAM,CAAC;IACV,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;CACf;AAED,2CAA2C;AAC3C,MAAM,WAAW,iBAAiB;IACjC,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;CACb;AAED;;;;GAIG;AACH,wBAAgB,kBAAkB,CACjC,YAAY,EAAE,MAAM,EACpB,OAAO,EAAE,YAAY,GAAG,SAAS,GAC/B,OAAO,IAAI,YAAY,GAAG;IAAE,SAAS,EAAE,cAAc,CAAA;CAAE,CAKzD;AAED;;;;GAIG;AACH,wBAAgB,wBAAwB,CACvC,QAAQ,EAAE,SAAS,kBAAkB,EAAE,EACvC,OAAO,CAAC,EAAE,YAAY,GACpB,iBAAiB,GAAG,IAAI,CAgB1B;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,aAAa,CAC5B,QAAQ,EAAE,SAAS,kBAAkB,EAAE,EACvC,IAAI,EAAE,SAAS,EACf,OAAO,CAAC,EAAE,YAAY,GACpB,GAAG,CAAC,MAAM,EAAE,eAAe,CAAC,CAkC9B;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,kBAAkB,CACjC,QAAQ,EAAE,SAAS,kBAAkB,EAAE,EACvC,IAAI,EAAE,cAAc,EACpB,OAAO,CAAC,EAAE,YAAY,GACpB,GAAG,CAAC,MAAM,EAAE,eAAe,CAAC,CAkC9B;AASD,kCAAkC;AAClC,MAAM,MAAM,SAAS,GAAG,SAAS,CAAC;AAClC,uCAAuC;AACvC,MAAM,MAAM,cAAc,GAAG,cAAc,CAAC;AAC5C,2CAA2C;AAC3C,MAAM,MAAM,QAAQ,GAAG,kBAAkB,CAAC;AAC1C,wCAAwC;AACxC,MAAM,MAAM,cAAc,GAAG,eAAe,CAAC;AAuB7C;;;GAGG;AACH,wBAAgB,YAAY,CAC3B,KAAK,EAAE,SAAS,QAAQ,EAAE,EAC1B,IAAI,EAAE,SAAS,EACf,OAAO,CAAC,EAAE,YAAY,GACpB,GAAG,CAAC,MAAM,EAAE,cAAc,CAAC,CAE7B;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,CAChC,KAAK,EAAE,SAAS,QAAQ,EAAE,EAC1B,IAAI,EAAE,cAAc,EACpB,OAAO,CAAC,EAAE,YAAY,GACpB,GAAG,CAAC,MAAM,EAAE,cAAc,CAAC,CAE7B"}
@@ -15087,6 +15087,312 @@ function flattenSvgPath(d, curveSegments = 16) {
15087
15087
  return loops;
15088
15088
  }
15089
15089
 
15090
+ // geometry/snap-guides.ts
15091
+ var SNAP_THRESHOLD = 6;
15092
+ function computeSnapToShape(dragX, dragY, dragW, dragH, siblings, draggedIds, guides, threshold = SNAP_THRESHOLD) {
15093
+ let bestDx = Infinity;
15094
+ let bestDy = Infinity;
15095
+ let snapX = dragX;
15096
+ let snapY = dragY;
15097
+ const lines = [];
15098
+ const dragCx = dragX + dragW / 2;
15099
+ const dragCy = dragY + dragH / 2;
15100
+ const dragRight = dragX + dragW;
15101
+ const dragBottom = dragY + dragH;
15102
+ const hRefs = [dragX, dragCx, dragRight];
15103
+ const vRefs = [dragY, dragCy, dragBottom];
15104
+ for (const sib of siblings) {
15105
+ if (draggedIds.has(sib.id)) {
15106
+ continue;
15107
+ }
15108
+ const sibCx = sib.x + sib.width / 2;
15109
+ const sibCy = sib.y + sib.height / 2;
15110
+ const sibRight = sib.x + sib.width;
15111
+ const sibBottom = sib.y + sib.height;
15112
+ for (const ref of [sib.x, sibCx, sibRight]) {
15113
+ for (const hr of hRefs) {
15114
+ const dx = Math.abs(ref - hr);
15115
+ if (dx < threshold && dx < bestDx) {
15116
+ bestDx = dx;
15117
+ snapX = dragX + (ref - hr);
15118
+ }
15119
+ }
15120
+ }
15121
+ for (const ref of [sib.y, sibCy, sibBottom]) {
15122
+ for (const vr of vRefs) {
15123
+ const dy = Math.abs(ref - vr);
15124
+ if (dy < threshold && dy < bestDy) {
15125
+ bestDy = dy;
15126
+ snapY = dragY + (ref - vr);
15127
+ }
15128
+ }
15129
+ }
15130
+ }
15131
+ for (const guide of guides) {
15132
+ if (guide.axis === "v") {
15133
+ for (const hr of hRefs) {
15134
+ const dx = Math.abs(guide.position - hr);
15135
+ if (dx < threshold && dx < bestDx) {
15136
+ bestDx = dx;
15137
+ snapX = dragX + (guide.position - hr);
15138
+ }
15139
+ }
15140
+ } else {
15141
+ for (const vr of vRefs) {
15142
+ const dy = Math.abs(guide.position - vr);
15143
+ if (dy < threshold && dy < bestDy) {
15144
+ bestDy = dy;
15145
+ snapY = dragY + (guide.position - vr);
15146
+ }
15147
+ }
15148
+ }
15149
+ }
15150
+ if (bestDx < threshold) {
15151
+ const snappedCx = snapX + dragW / 2;
15152
+ const snappedRight = snapX + dragW;
15153
+ for (const sib of siblings) {
15154
+ if (draggedIds.has(sib.id)) {
15155
+ continue;
15156
+ }
15157
+ for (const ref of [sib.x, sib.x + sib.width / 2, sib.x + sib.width]) {
15158
+ if (Math.abs(ref - snapX) < 1 || Math.abs(ref - snappedCx) < 1 || Math.abs(ref - snappedRight) < 1) {
15159
+ lines.push({ axis: "v", position: ref });
15160
+ }
15161
+ }
15162
+ }
15163
+ for (const g of guides) {
15164
+ if (g.axis === "v" && (Math.abs(g.position - snapX) < 1 || Math.abs(g.position - snappedCx) < 1 || Math.abs(g.position - snappedRight) < 1)) {
15165
+ lines.push({ axis: "v", position: g.position });
15166
+ }
15167
+ }
15168
+ }
15169
+ if (bestDy < threshold) {
15170
+ const snappedCy = snapY + dragH / 2;
15171
+ const snappedBottom = snapY + dragH;
15172
+ for (const sib of siblings) {
15173
+ if (draggedIds.has(sib.id)) {
15174
+ continue;
15175
+ }
15176
+ for (const ref of [sib.y, sib.y + sib.height / 2, sib.y + sib.height]) {
15177
+ if (Math.abs(ref - snapY) < 1 || Math.abs(ref - snappedCy) < 1 || Math.abs(ref - snappedBottom) < 1) {
15178
+ lines.push({ axis: "h", position: ref });
15179
+ }
15180
+ }
15181
+ }
15182
+ for (const g of guides) {
15183
+ if (g.axis === "h" && (Math.abs(g.position - snapY) < 1 || Math.abs(g.position - snappedCy) < 1 || Math.abs(g.position - snappedBottom) < 1)) {
15184
+ lines.push({ axis: "h", position: g.position });
15185
+ }
15186
+ }
15187
+ }
15188
+ return { x: snapX, y: snapY, lines };
15189
+ }
15190
+ function xCandidates(box) {
15191
+ return {
15192
+ leading: box.x,
15193
+ centre: box.x + box.width / 2,
15194
+ trailing: box.x + box.width
15195
+ };
15196
+ }
15197
+ function yCandidates(box) {
15198
+ return {
15199
+ leading: box.y,
15200
+ centre: box.y + box.height / 2,
15201
+ trailing: box.y + box.height
15202
+ };
15203
+ }
15204
+ function findSnapHit(movingValues, others, axisCandidates, threshold) {
15205
+ let best = null;
15206
+ let bestDist = threshold + 1;
15207
+ for (const other of others) {
15208
+ const target = axisCandidates(other);
15209
+ const targetValues = [target.leading, target.centre, target.trailing];
15210
+ for (const mv of movingValues) {
15211
+ for (const tv of targetValues) {
15212
+ const dist = mv > tv ? mv - tv : tv - mv;
15213
+ if (dist <= threshold && dist < bestDist) {
15214
+ bestDist = dist;
15215
+ best = { delta: tv - mv, matchedLine: tv, otherBox: other };
15216
+ }
15217
+ }
15218
+ }
15219
+ }
15220
+ return best;
15221
+ }
15222
+ function unionExtent(aStart, aEnd, bStart, bEnd) {
15223
+ const start = aStart < bStart ? aStart : bStart;
15224
+ const end = aEnd > bEnd ? aEnd : bEnd;
15225
+ return { start, end };
15226
+ }
15227
+ function computeSnap(box, others, threshold) {
15228
+ const guides = [];
15229
+ let snappedX = box.x;
15230
+ let snappedY = box.y;
15231
+ const xMoving = xCandidates(box);
15232
+ const hitX = findSnapHit(
15233
+ [xMoving.leading, xMoving.centre, xMoving.trailing],
15234
+ others,
15235
+ xCandidates,
15236
+ threshold
15237
+ );
15238
+ if (hitX !== null) {
15239
+ snappedX = box.x + hitX.delta;
15240
+ const { start, end } = unionExtent(
15241
+ box.y,
15242
+ box.y + box.height,
15243
+ hitX.otherBox.y,
15244
+ hitX.otherBox.y + hitX.otherBox.height
15245
+ );
15246
+ guides.push({ axis: "x", pos: hitX.matchedLine, start, end });
15247
+ }
15248
+ const yMoving = yCandidates(box);
15249
+ const hitY = findSnapHit(
15250
+ [yMoving.leading, yMoving.centre, yMoving.trailing],
15251
+ others,
15252
+ yCandidates,
15253
+ threshold
15254
+ );
15255
+ if (hitY !== null) {
15256
+ snappedY = box.y + hitY.delta;
15257
+ const { start, end } = unionExtent(
15258
+ snappedX,
15259
+ snappedX + box.width,
15260
+ hitY.otherBox.x,
15261
+ hitY.otherBox.x + hitY.otherBox.width
15262
+ );
15263
+ guides.push({ axis: "y", pos: hitY.matchedLine, start, end });
15264
+ }
15265
+ return { x: snappedX, y: snappedY, guides };
15266
+ }
15267
+ function snapToGridStep(value, step) {
15268
+ if (step <= 0) {
15269
+ return value;
15270
+ }
15271
+ return Math.round(value / step) * step || 0;
15272
+ }
15273
+ function snapValue(value, size) {
15274
+ return Math.round(value / size) * size;
15275
+ }
15276
+ function snapBox(box, size) {
15277
+ return {
15278
+ x: snapValue(box.x, size),
15279
+ y: snapValue(box.y, size),
15280
+ width: Math.max(size, snapValue(box.width, size)),
15281
+ height: Math.max(size, snapValue(box.height, size))
15282
+ };
15283
+ }
15284
+
15285
+ // geometry/align-distribute.ts
15286
+ function usesSlideReference(elementCount, options) {
15287
+ if (!options?.slideSize) {
15288
+ return false;
15289
+ }
15290
+ return options.reference === "slide" || options.reference === void 0 && elementCount === 1;
15291
+ }
15292
+ function resolveAlignReferenceBox(elements, options) {
15293
+ if (elements.length === 0) {
15294
+ return null;
15295
+ }
15296
+ if (usesSlideReference(elements.length, options)) {
15297
+ return { minX: 0, minY: 0, maxX: options.slideSize.width, maxY: options.slideSize.height };
15298
+ }
15299
+ if (elements.length < 2) {
15300
+ return null;
15301
+ }
15302
+ return {
15303
+ minX: Math.min(...elements.map((e) => e.x)),
15304
+ minY: Math.min(...elements.map((e) => e.y)),
15305
+ maxX: Math.max(...elements.map((e) => e.x + e.width)),
15306
+ maxY: Math.max(...elements.map((e) => e.y + e.height))
15307
+ };
15308
+ }
15309
+ function alignElements(elements, edge, options) {
15310
+ const result = /* @__PURE__ */ new Map();
15311
+ const ref = resolveAlignReferenceBox(elements, options);
15312
+ if (!ref) {
15313
+ return result;
15314
+ }
15315
+ const { minX, minY, maxX, maxY } = ref;
15316
+ const centerX = (minX + maxX) / 2;
15317
+ const centerY = (minY + maxY) / 2;
15318
+ for (const el of elements) {
15319
+ switch (edge) {
15320
+ case "left":
15321
+ result.set(el.id, { x: minX });
15322
+ break;
15323
+ case "right":
15324
+ result.set(el.id, { x: maxX - el.width });
15325
+ break;
15326
+ case "centerH":
15327
+ result.set(el.id, { x: centerX - el.width / 2 });
15328
+ break;
15329
+ case "top":
15330
+ result.set(el.id, { y: minY });
15331
+ break;
15332
+ case "bottom":
15333
+ result.set(el.id, { y: maxY - el.height });
15334
+ break;
15335
+ case "middle":
15336
+ result.set(el.id, { y: centerY - el.height / 2 });
15337
+ break;
15338
+ }
15339
+ }
15340
+ return result;
15341
+ }
15342
+ function distributeElements(elements, axis, options) {
15343
+ const result = /* @__PURE__ */ new Map();
15344
+ const horizontal = axis === "horizontal";
15345
+ const start = (e) => horizontal ? e.x : e.y;
15346
+ const size = (e) => horizontal ? e.width : e.height;
15347
+ const sorted = [...elements].sort((a, b) => start(a) - start(b));
15348
+ const totalSize = sorted.reduce((sum, e) => sum + size(e), 0);
15349
+ let cursor;
15350
+ let gap;
15351
+ if (usesSlideReference(elements.length, options)) {
15352
+ if (sorted.length === 0) {
15353
+ return result;
15354
+ }
15355
+ const span = horizontal ? options.slideSize.width : options.slideSize.height;
15356
+ gap = (span - totalSize) / (sorted.length + 1);
15357
+ cursor = gap;
15358
+ } else {
15359
+ if (sorted.length < 3) {
15360
+ return result;
15361
+ }
15362
+ const first = sorted[0];
15363
+ const last = sorted[sorted.length - 1];
15364
+ const span = start(last) + size(last) - start(first);
15365
+ gap = (span - totalSize) / (sorted.length - 1);
15366
+ cursor = start(first);
15367
+ }
15368
+ for (const el of sorted) {
15369
+ result.set(el.id, horizontal ? { x: cursor } : { y: cursor });
15370
+ cursor += size(el) + gap;
15371
+ }
15372
+ return result;
15373
+ }
15374
+ function onlyMoved(boxes, positions) {
15375
+ const byId = new Map(boxes.map((box) => [box.id, box]));
15376
+ const result = /* @__PURE__ */ new Map();
15377
+ for (const [id, pos] of positions) {
15378
+ const box = byId.get(id);
15379
+ if (!box) {
15380
+ continue;
15381
+ }
15382
+ const moved = pos.x !== void 0 && pos.x !== box.x || pos.y !== void 0 && pos.y !== box.y;
15383
+ if (moved) {
15384
+ result.set(id, pos);
15385
+ }
15386
+ }
15387
+ return result;
15388
+ }
15389
+ function computeAlign(boxes, mode, options) {
15390
+ return onlyMoved(boxes, alignElements(boxes, mode, options));
15391
+ }
15392
+ function computeDistribute(boxes, mode, options) {
15393
+ return onlyMoved(boxes, distributeElements(boxes, mode, options));
15394
+ }
15395
+
15090
15396
  exports.ACTION_BUTTON_CONNECTION_SITES = ACTION_BUTTON_CONNECTION_SITES;
15091
15397
  exports.ACTION_BUTTON_PRESET_DEFINITIONS = ACTION_BUTTON_PRESET_DEFINITIONS;
15092
15398
  exports.ACTION_BUTTON_TEXT_RECTS = ACTION_BUTTON_TEXT_RECTS;
@@ -15146,6 +15452,7 @@ exports.REFINED_ARROW_PRESET_DEFINITIONS = REFINED_ARROW_PRESET_DEFINITIONS;
15146
15452
  exports.RIBBON_CONNECTION_SITES = RIBBON_CONNECTION_SITES;
15147
15453
  exports.RIBBON_TEXT_RECTS = RIBBON_TEXT_RECTS;
15148
15454
  exports.SCROLLS_ACCENT_CALLOUTS_PRESET_DEFINITIONS = SCROLLS_ACCENT_CALLOUTS_PRESET_DEFINITIONS;
15455
+ exports.SNAP_THRESHOLD = SNAP_THRESHOLD;
15149
15456
  exports.STAR_CONNECTION_SITES = STAR_CONNECTION_SITES;
15150
15457
  exports.STAR_TEXT_RECTS = STAR_TEXT_RECTS;
15151
15458
  exports.ST_SHAPE_TYPE_VALUES = ST_SHAPE_TYPE_VALUES;
@@ -15153,17 +15460,23 @@ exports.TABS_DECORATIONS_PRESET_DEFINITIONS = TABS_DECORATIONS_PRESET_DEFINITION
15153
15460
  exports.TAB_CONNECTION_SITES = TAB_CONNECTION_SITES;
15154
15461
  exports.TAB_TEXT_RECTS = TAB_TEXT_RECTS;
15155
15462
  exports._internalForTests = _internalForTests;
15463
+ exports.alignElements = alignElements;
15156
15464
  exports.angleToRadians = angleToRadians;
15157
15465
  exports.at = at;
15158
15466
  exports.buildCalloutLeaderLineSvgPath = buildCalloutLeaderLineSvgPath;
15159
15467
  exports.clipPolygons = clipPolygons;
15160
15468
  exports.combineShapes = combineShapes;
15469
+ exports.computeAlign = computeAlign;
15470
+ exports.computeDistribute = computeDistribute;
15471
+ exports.computeSnap = computeSnap;
15472
+ exports.computeSnapToShape = computeSnapToShape;
15161
15473
  exports.computeUnionWalk = computeUnionWalk;
15162
15474
  exports.convexHull = convexHull;
15163
15475
  exports.createBuiltinVariables = createBuiltinVariables;
15164
15476
  exports.cross2 = cross2;
15165
15477
  exports.cxn = cxn;
15166
15478
  exports.dedupPoly = dedupPoly;
15479
+ exports.distributeElements = distributeElements;
15167
15480
  exports.elbowCurveSegments = elbowCurveSegments;
15168
15481
  exports.elbowWaypoints = elbowWaypoints;
15169
15482
  exports.ensureCCW = ensureCCW;
@@ -15207,10 +15520,14 @@ exports.pointInPolygon = pointInPolygon;
15207
15520
  exports.pointInTriangle = pointInTriangle;
15208
15521
  exports.polygonArea = polygonArea;
15209
15522
  exports.polygonsToSvgPath = polygonsToSvgPath;
15523
+ exports.resolveAlignReferenceBox = resolveAlignReferenceBox;
15210
15524
  exports.resolveCoordinate = resolveCoordinate;
15211
15525
  exports.resolveOperand = resolveOperand;
15212
15526
  exports.segmentIntersection = segmentIntersection;
15213
15527
  exports.signedArea = signedArea;
15528
+ exports.snapBox = snapBox;
15529
+ exports.snapToGridStep = snapToGridStep;
15530
+ exports.snapValue = snapValue;
15214
15531
  exports.subtractPolygons = subtractPolygons;
15215
15532
  exports.subtractShapes = subtractShapes;
15216
15533
  exports.subtractSvgPaths = subtractSvgPaths;
@@ -15220,4 +15537,5 @@ exports.triangulate = triangulate;
15220
15537
  exports.unionPolygons = unionPolygons;
15221
15538
  exports.unionShapes = unionShapes;
15222
15539
  exports.unionSvgPaths = unionSvgPaths;
15540
+ exports.usesSlideReference = usesSlideReference;
15223
15541
  exports.vec2Eq = vec2Eq;
@@ -78,4 +78,6 @@ export * from './shape-definitions-extended.js';
78
78
  export * from './shape-definitions-primary.js';
79
79
  export { at } from './indexed.js';
80
80
  export * from './svg-path-flatten.js';
81
+ export * from './snap-guides.js';
82
+ export * from './align-distribute.js';
81
83
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../geometry/index.ts"],"names":[],"mappings":"AAAA,cAAc,8BAA8B,CAAC;AAC7C,cAAc,uBAAuB,CAAC;AACtC,cAAc,yBAAyB,CAAC;AACxC,cAAc,6BAA6B,CAAC;AAC5C,cAAc,+BAA+B,CAAC;AAC9C,cAAc,wBAAwB,CAAC;AACvC,cAAc,yBAAyB,CAAC;AACxC,cAAc,6BAA6B,CAAC;AAC5C,cAAc,iCAAiC,CAAC;AAChD,cAAc,6CAA6C,CAAC;AAC5D,cAAc,4CAA4C,CAAC;AAC3D,cAAc,0CAA0C,CAAC;AACzD,cAAc,qCAAqC,CAAC;AACpD,cAAc,sCAAsC,CAAC;AACrD,cAAc,sCAAsC,CAAC;AACrD,cAAc,qCAAqC,CAAC;AACpD,cAAc,6CAA6C,CAAC;AAC5D,cAAc,6CAA6C,CAAC;AAC5D,cAAc,6CAA6C,CAAC;AAC5D,cAAc,wCAAwC,CAAC;AACvD,cAAc,oCAAoC,CAAC;AACnD,cAAc,kDAAkD,CAAC;AACjE,cAAc,wDAAwD,CAAC;AACvE,cAAc,mCAAmC,CAAC;AAClD,cAAc,mCAAmC,CAAC;AAClD,cAAc,uCAAuC,CAAC;AACtD,cAAc,oCAAoC,CAAC;AACnD,cAAc,oCAAoC,CAAC;AACnD,cAAc,sCAAsC,CAAC;AACrD,cAAc,oCAAoC,CAAC;AACnD,cAAc,oCAAoC,CAAC;AACnD,cAAc,mCAAmC,CAAC;AAClD,cAAc,oCAAoC,CAAC;AACnD,cAAc,4BAA4B,CAAC;AAC3C,cAAc,8BAA8B,CAAC;AAC7C,cAAc,8CAA8C,CAAC;AAC7D,cAAc,8CAA8C,CAAC;AAC7D,cAAc,8CAA8C,CAAC;AAC7D,cAAc,sCAAsC,CAAC;AACrD,cAAc,2CAA2C,CAAC;AAC1D,cAAc,6CAA6C,CAAC;AAC5D,cAAc,mDAAmD,CAAC;AAClE,cAAc,wDAAwD,CAAC;AACvE,cAAc,mDAAmD,CAAC;AAClE,cAAc,yCAAyC,CAAC;AACxD,cAAc,qCAAqC,CAAC;AACpD,cAAc,qCAAqC,CAAC;AACpD,cAAc,oCAAoC,CAAC;AACnD,cAAc,2CAA2C,CAAC;AAC1D,cAAc,uDAAuD,CAAC;AACtE,cAAc,qCAAqC,CAAC;AACpD,cAAc,gDAAgD,CAAC;AAC/D,cAAc,+BAA+B,CAAC;AAC9C,cAAc,yBAAyB,CAAC;AACxC,cAAc,yBAAyB,CAAC;AACxC,cAAc,sCAAsC,CAAC;AACrD,cAAc,8BAA8B,CAAC;AAC7C,cAAc,8BAA8B,CAAC;AAC7C,cAAc,gCAAgC,CAAC;AAC/C,cAAc,uCAAuC,CAAC;AACtD,cAAc,iCAAiC,CAAC;AAChD,cAAc,8BAA8B,CAAC;AAC7C,cAAc,8BAA8B,CAAC;AAC7C,cAAc,gCAAgC,CAAC;AAC/C,cAAc,6BAA6B,CAAC;AAC5C,cAAc,+BAA+B,CAAC;AAC9C,cAAc,6BAA6B,CAAC;AAC5C,cAAc,6BAA6B,CAAC;AAC5C,cAAc,4BAA4B,CAAC;AAC3C,cAAc,6BAA6B,CAAC;AAC5C,cAAc,6BAA6B,CAAC;AAC5C,cAAc,wBAAwB,CAAC;AACvC,cAAc,wBAAwB,CAAC;AACvC,cAAc,0BAA0B,CAAC;AACzC,cAAc,0BAA0B,CAAC;AACzC,cAAc,oBAAoB,CAAC;AACnC,cAAc,iCAAiC,CAAC;AAChD,cAAc,gCAAgC,CAAC;AAC/C,OAAO,EAAE,EAAE,EAAE,MAAM,cAAc,CAAC;AAClC,cAAc,uBAAuB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../geometry/index.ts"],"names":[],"mappings":"AAAA,cAAc,8BAA8B,CAAC;AAC7C,cAAc,uBAAuB,CAAC;AACtC,cAAc,yBAAyB,CAAC;AACxC,cAAc,6BAA6B,CAAC;AAC5C,cAAc,+BAA+B,CAAC;AAC9C,cAAc,wBAAwB,CAAC;AACvC,cAAc,yBAAyB,CAAC;AACxC,cAAc,6BAA6B,CAAC;AAC5C,cAAc,iCAAiC,CAAC;AAChD,cAAc,6CAA6C,CAAC;AAC5D,cAAc,4CAA4C,CAAC;AAC3D,cAAc,0CAA0C,CAAC;AACzD,cAAc,qCAAqC,CAAC;AACpD,cAAc,sCAAsC,CAAC;AACrD,cAAc,sCAAsC,CAAC;AACrD,cAAc,qCAAqC,CAAC;AACpD,cAAc,6CAA6C,CAAC;AAC5D,cAAc,6CAA6C,CAAC;AAC5D,cAAc,6CAA6C,CAAC;AAC5D,cAAc,wCAAwC,CAAC;AACvD,cAAc,oCAAoC,CAAC;AACnD,cAAc,kDAAkD,CAAC;AACjE,cAAc,wDAAwD,CAAC;AACvE,cAAc,mCAAmC,CAAC;AAClD,cAAc,mCAAmC,CAAC;AAClD,cAAc,uCAAuC,CAAC;AACtD,cAAc,oCAAoC,CAAC;AACnD,cAAc,oCAAoC,CAAC;AACnD,cAAc,sCAAsC,CAAC;AACrD,cAAc,oCAAoC,CAAC;AACnD,cAAc,oCAAoC,CAAC;AACnD,cAAc,mCAAmC,CAAC;AAClD,cAAc,oCAAoC,CAAC;AACnD,cAAc,4BAA4B,CAAC;AAC3C,cAAc,8BAA8B,CAAC;AAC7C,cAAc,8CAA8C,CAAC;AAC7D,cAAc,8CAA8C,CAAC;AAC7D,cAAc,8CAA8C,CAAC;AAC7D,cAAc,sCAAsC,CAAC;AACrD,cAAc,2CAA2C,CAAC;AAC1D,cAAc,6CAA6C,CAAC;AAC5D,cAAc,mDAAmD,CAAC;AAClE,cAAc,wDAAwD,CAAC;AACvE,cAAc,mDAAmD,CAAC;AAClE,cAAc,yCAAyC,CAAC;AACxD,cAAc,qCAAqC,CAAC;AACpD,cAAc,qCAAqC,CAAC;AACpD,cAAc,oCAAoC,CAAC;AACnD,cAAc,2CAA2C,CAAC;AAC1D,cAAc,uDAAuD,CAAC;AACtE,cAAc,qCAAqC,CAAC;AACpD,cAAc,gDAAgD,CAAC;AAC/D,cAAc,+BAA+B,CAAC;AAC9C,cAAc,yBAAyB,CAAC;AACxC,cAAc,yBAAyB,CAAC;AACxC,cAAc,sCAAsC,CAAC;AACrD,cAAc,8BAA8B,CAAC;AAC7C,cAAc,8BAA8B,CAAC;AAC7C,cAAc,gCAAgC,CAAC;AAC/C,cAAc,uCAAuC,CAAC;AACtD,cAAc,iCAAiC,CAAC;AAChD,cAAc,8BAA8B,CAAC;AAC7C,cAAc,8BAA8B,CAAC;AAC7C,cAAc,gCAAgC,CAAC;AAC/C,cAAc,6BAA6B,CAAC;AAC5C,cAAc,+BAA+B,CAAC;AAC9C,cAAc,6BAA6B,CAAC;AAC5C,cAAc,6BAA6B,CAAC;AAC5C,cAAc,4BAA4B,CAAC;AAC3C,cAAc,6BAA6B,CAAC;AAC5C,cAAc,6BAA6B,CAAC;AAC5C,cAAc,wBAAwB,CAAC;AACvC,cAAc,wBAAwB,CAAC;AACvC,cAAc,0BAA0B,CAAC;AACzC,cAAc,0BAA0B,CAAC;AACzC,cAAc,oBAAoB,CAAC;AACnC,cAAc,iCAAiC,CAAC;AAChD,cAAc,gCAAgC,CAAC;AAC/C,OAAO,EAAE,EAAE,EAAE,MAAM,cAAc,CAAC;AAClC,cAAc,uBAAuB,CAAC;AACtC,cAAc,kBAAkB,CAAC;AACjC,cAAc,uBAAuB,CAAC"}