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
package/README.md CHANGED
@@ -9,7 +9,6 @@
9
9
  **Read, edit, validate and write Office Open XML documents in TypeScript.**
10
10
  One package, one XML model, every format: Word, PowerPoint, Excel and the shared building blocks beneath them. Visio VSDX parsing and bounded editing are available through `ooxml-core/visio`.
11
11
 
12
- [![license](https://img.shields.io/badge/license-Apache--2.0-blue.svg)](LICENSE)
13
12
  [![CI](https://github.com/ChristopherVR/ooxml/actions/workflows/ci.yml/badge.svg)](https://github.com/ChristopherVR/ooxml/actions/workflows/ci.yml)
14
13
  [![Contributor Covenant](https://img.shields.io/badge/Contributor%20Covenant-2.1-4baaaa.svg)](CODE_OF_CONDUCT.md)
15
14
 
@@ -17,7 +16,7 @@ One package, one XML model, every format: Word, PowerPoint, Excel and the shared
17
16
  [**Packages and areas**](#features-and-api-one-package-many-areas)  · 
18
17
  [**Install**](#install)  · 
19
18
  [**Examples**](#quick-start)  · 
20
- [**Contributing**](CONTRIBUTING.md)
19
+ [**Contributing**](../../CONTRIBUTING.md)
21
20
 
22
21
  </div>
23
22
 
@@ -63,7 +62,7 @@ The `chart` subpath provides shared chart data calculations (regression, quartil
63
62
  blank values and stacked series). The `text` subpath provides script-category
64
63
  segmentation primitives. These areas are DOM-free and do not import a product model.
65
64
  SVG curve flattening is available from `geometry`. See the
66
- [PowerPoint reuse audit](docs/pptx-shared-logic-audit.md) for extraction evidence
65
+ [PowerPoint reuse audit](../../docs/pptx-shared-logic-audit.md) for extraction evidence
67
66
  and remaining candidates.
68
67
 
69
68
  ## Install
@@ -110,7 +109,7 @@ const part = resolvePartPath('word/document.xml', rels.get('rId5')!.target);
110
109
 
111
110
  ## Contributing and roadmap
112
111
 
113
- `ooxml-core` is developed in the [ChristopherVR/ooxml](https://github.com/ChristopherVR/ooxml) repository, which also holds [`ooxml-ui`](https://www.npmjs.com/package/ooxml-ui), the MCP servers and the Word, Excel, Visio and OpenTeams viewers. Its README covers the repository layout, the development workflow and releases, and the [roadmap](https://github.com/ChristopherVR/ooxml/blob/main/docs/roadmap.md) says what comes next; the working agreements are in [AGENTS.md](AGENTS.md) and [CONTRIBUTING.md](CONTRIBUTING.md), and [PROVENANCE.md](PROVENANCE.md) records where each module came from.
112
+ `ooxml-core` is developed in the [ChristopherVR/ooxml](https://github.com/ChristopherVR/ooxml) repository, which also holds [`ooxml-ui`](https://www.npmjs.com/package/ooxml-ui), the MCP servers and the Word, Excel, Visio and OpenTeams viewers. Its README covers the repository layout, the development workflow and releases, and the [roadmap](https://github.com/ChristopherVR/ooxml/blob/main/docs/roadmap.md) says what comes next; the working agreements are in [AGENTS.md](../../AGENTS.md) and [CONTRIBUTING.md](../../CONTRIBUTING.md), and [PROVENANCE.md](../../PROVENANCE.md) records where each module came from.
114
113
 
115
114
  ## License
116
115
 
@@ -1404,7 +1404,7 @@ async function applyCoreProperties(zip, next, prior) {
1404
1404
  await ensureContentTypeOverride(zip, CORE_PATH, CORE_TYPE);
1405
1405
  const rels = zip.file("_rels/.rels");
1406
1406
  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>';
1407
- if (!relsXml.includes(CORE_REL)) {
1407
+ if (!relsXml.includes(`Type="${CORE_REL}"`)) {
1408
1408
  const ids = [...relsXml.matchAll(/Id="rId(\d+)"/g)].map((match2) => Number(match2[1]));
1409
1409
  const id = `rId${Math.max(0, ...ids) + 1}`;
1410
1410
  zip.file(
@@ -25586,7 +25586,7 @@ async function saveXlsx(workbook) {
25586
25586
 
25587
25587
  // xlsx/edit/context.ts
25588
25588
  function sheetAt(workbook, index2) {
25589
- const sheet = workbook.sheets[index2];
25589
+ const sheet = Number.isInteger(index2) ? workbook.sheets[index2] : void 0;
25590
25590
  if (!sheet) throw new RangeError(`No sheet at index ${index2}`);
25591
25591
  return sheet;
25592
25592
  }
@@ -26786,9 +26786,27 @@ function stylePatchFromCss(css, inner2) {
26786
26786
  if (numFmt) patch.numFmt = decodeNumberFormat(numFmt);
26787
26787
  return patch;
26788
26788
  }
26789
+ function stripTags(markup) {
26790
+ let previous;
26791
+ let text = markup;
26792
+ do {
26793
+ previous = text;
26794
+ text = text.replace(/<!--[\s\S]*?-->/g, "").replace(/<[^<>]*>/g, "");
26795
+ } while (text !== previous);
26796
+ return removeAll(text, ["<!--", "-->"]);
26797
+ }
26798
+ function removeAll(text, tokens2) {
26799
+ let current = text;
26800
+ let previous;
26801
+ do {
26802
+ previous = current;
26803
+ for (const token of tokens2) current = current.split(token).join("");
26804
+ } while (current !== previous);
26805
+ return current;
26806
+ }
26789
26807
  function cellText(inner2) {
26790
26808
  return decodeEntities(
26791
- inner2.replace(/<!--[\s\S]*?-->/g, "").replace(/<br\s*\/?>/gi, "\0").replace(/<[^>]+>/g, "").replace(/\s+/g, " ").replace(/ ?\u0000 ?/g, "\n")
26809
+ stripTags(inner2.replace(/<br\s*\/?>/gi, "\0")).replace(/\s+/g, " ").replace(/ ?\u0000 ?/g, "\n")
26792
26810
  ).replace(/ /g, " ").trim();
26793
26811
  }
26794
26812
  function parseHtmlTable(html, base) {
@@ -26796,7 +26814,9 @@ function parseHtmlTable(html, base) {
26796
26814
  if (table2 === void 0) return void 0;
26797
26815
  const classes = /* @__PURE__ */ new Map();
26798
26816
  for (const block of html.matchAll(/<style\b[^>]*>([\s\S]*?)<\/style>/gi))
26799
- for (const rule2 of (block[1] ?? "").replace(/<!--|-->/g, "").matchAll(/\.([\w-]+)\s*\{([^}]*)\}/g))
26817
+ for (const rule2 of removeAll(block[1] ?? "", ["<!--", "-->"]).matchAll(
26818
+ /\.([\w-]+)\s*\{([^}]*)\}/g
26819
+ ))
26800
26820
  classes.set(rule2[1] ?? "", rule2[2] ?? "");
26801
26821
  const grid = [];
26802
26822
  const taken = /* @__PURE__ */ new Set();
@@ -27585,7 +27605,7 @@ function setAutoFilter(ctx, s, range) {
27585
27605
  const old = sheet.autoFilter;
27586
27606
  if (old) applyFilter(ctx, sheet, { range: old.range });
27587
27607
  if (r) sheet.autoFilter = { range: r };
27588
- else delete sheet.autoFilter;
27608
+ else Reflect.deleteProperty(sheet, "autoFilter");
27589
27609
  },
27590
27610
  { sheet: s, structural: true }
27591
27611
  );
@@ -28647,9 +28667,10 @@ function updateChart(ctx, s, index2, patch) {
28647
28667
  () => {
28648
28668
  const live = sheet.drawings[index2];
28649
28669
  for (const [key2, value] of Object.entries(structuredClone(patch))) {
28650
- if (key2 === "kind" || key2 === "partName") continue;
28651
- if (value === void 0) delete live[key2];
28652
- else live[key2] = value;
28670
+ if (key2 === "kind" || key2 === "partName" || key2 === "__proto__") continue;
28671
+ if (key2 === "constructor" || key2 === "prototype") continue;
28672
+ if (value === void 0) Reflect.deleteProperty(live, key2);
28673
+ else Reflect.set(live, key2, value);
28653
28674
  }
28654
28675
  },
28655
28676
  { sheet: s }
@@ -29158,7 +29179,7 @@ function setDefaultColumnWidth(ctx, s, width) {
29158
29179
  "format",
29159
29180
  [{ kind: "sheet", sheet: s }],
29160
29181
  () => {
29161
- if (width === void 0) delete sheet.defaultColWidth;
29182
+ if (width === void 0) Reflect.deleteProperty(sheet, "defaultColWidth");
29162
29183
  else sheet.defaultColWidth = width;
29163
29184
  },
29164
29185
  { sheet: s, structural: true }
@@ -29180,7 +29201,7 @@ function setPageSetup(ctx, s, patch) {
29180
29201
  () => {
29181
29202
  const next = compact2({ ...sheet.pageSetup, ...structuredClone(patch) });
29182
29203
  if (Object.keys(next).length) sheet.pageSetup = next;
29183
- else delete sheet.pageSetup;
29204
+ else Reflect.deleteProperty(sheet, "pageSetup");
29184
29205
  },
29185
29206
  { sheet: s }
29186
29207
  );
@@ -29196,7 +29217,7 @@ function setPrintOptions(ctx, s, patch) {
29196
29217
  for (const key2 of Object.keys(next))
29197
29218
  if (!next[key2]) delete next[key2];
29198
29219
  if (Object.keys(next).length) sheet.printOptions = next;
29199
- else delete sheet.printOptions;
29220
+ else Reflect.deleteProperty(sheet, "printOptions");
29200
29221
  sheet.preserved.delete("printOptions");
29201
29222
  },
29202
29223
  { sheet: s }
@@ -29802,7 +29823,7 @@ function setSheetProtection(ctx, s, protection2, password) {
29802
29823
  [{ kind: "sheet", sheet: s }],
29803
29824
  () => {
29804
29825
  if (!protection2) {
29805
- delete sheet.protection;
29826
+ Reflect.deleteProperty(sheet, "protection");
29806
29827
  return;
29807
29828
  }
29808
29829
  const next = structuredClone(protection2);
@@ -30774,7 +30795,7 @@ function setTabColor(ctx, index2, color2) {
30774
30795
  [{ kind: "sheet", sheet: index2 }],
30775
30796
  () => {
30776
30797
  if (color2) sheet.tabColor = { ...color2 };
30777
- else delete sheet.tabColor;
30798
+ else Reflect.deleteProperty(sheet, "tabColor");
30778
30799
  },
30779
30800
  { sheet: index2 }
30780
30801
  );
@@ -1398,7 +1398,7 @@ async function applyCoreProperties(zip, next, prior) {
1398
1398
  await ensureContentTypeOverride(zip, CORE_PATH, CORE_TYPE);
1399
1399
  const rels = zip.file("_rels/.rels");
1400
1400
  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>';
1401
- if (!relsXml.includes(CORE_REL)) {
1401
+ if (!relsXml.includes(`Type="${CORE_REL}"`)) {
1402
1402
  const ids = [...relsXml.matchAll(/Id="rId(\d+)"/g)].map((match2) => Number(match2[1]));
1403
1403
  const id = `rId${Math.max(0, ...ids) + 1}`;
1404
1404
  zip.file(
@@ -25580,7 +25580,7 @@ async function saveXlsx(workbook) {
25580
25580
 
25581
25581
  // xlsx/edit/context.ts
25582
25582
  function sheetAt(workbook, index2) {
25583
- const sheet = workbook.sheets[index2];
25583
+ const sheet = Number.isInteger(index2) ? workbook.sheets[index2] : void 0;
25584
25584
  if (!sheet) throw new RangeError(`No sheet at index ${index2}`);
25585
25585
  return sheet;
25586
25586
  }
@@ -26780,9 +26780,27 @@ function stylePatchFromCss(css, inner2) {
26780
26780
  if (numFmt) patch.numFmt = decodeNumberFormat(numFmt);
26781
26781
  return patch;
26782
26782
  }
26783
+ function stripTags(markup) {
26784
+ let previous;
26785
+ let text = markup;
26786
+ do {
26787
+ previous = text;
26788
+ text = text.replace(/<!--[\s\S]*?-->/g, "").replace(/<[^<>]*>/g, "");
26789
+ } while (text !== previous);
26790
+ return removeAll(text, ["<!--", "-->"]);
26791
+ }
26792
+ function removeAll(text, tokens2) {
26793
+ let current = text;
26794
+ let previous;
26795
+ do {
26796
+ previous = current;
26797
+ for (const token of tokens2) current = current.split(token).join("");
26798
+ } while (current !== previous);
26799
+ return current;
26800
+ }
26783
26801
  function cellText(inner2) {
26784
26802
  return decodeEntities(
26785
- inner2.replace(/<!--[\s\S]*?-->/g, "").replace(/<br\s*\/?>/gi, "\0").replace(/<[^>]+>/g, "").replace(/\s+/g, " ").replace(/ ?\u0000 ?/g, "\n")
26803
+ stripTags(inner2.replace(/<br\s*\/?>/gi, "\0")).replace(/\s+/g, " ").replace(/ ?\u0000 ?/g, "\n")
26786
26804
  ).replace(/ /g, " ").trim();
26787
26805
  }
26788
26806
  function parseHtmlTable(html, base) {
@@ -26790,7 +26808,9 @@ function parseHtmlTable(html, base) {
26790
26808
  if (table2 === void 0) return void 0;
26791
26809
  const classes = /* @__PURE__ */ new Map();
26792
26810
  for (const block of html.matchAll(/<style\b[^>]*>([\s\S]*?)<\/style>/gi))
26793
- for (const rule2 of (block[1] ?? "").replace(/<!--|-->/g, "").matchAll(/\.([\w-]+)\s*\{([^}]*)\}/g))
26811
+ for (const rule2 of removeAll(block[1] ?? "", ["<!--", "-->"]).matchAll(
26812
+ /\.([\w-]+)\s*\{([^}]*)\}/g
26813
+ ))
26794
26814
  classes.set(rule2[1] ?? "", rule2[2] ?? "");
26795
26815
  const grid = [];
26796
26816
  const taken = /* @__PURE__ */ new Set();
@@ -27579,7 +27599,7 @@ function setAutoFilter(ctx, s, range) {
27579
27599
  const old = sheet.autoFilter;
27580
27600
  if (old) applyFilter(ctx, sheet, { range: old.range });
27581
27601
  if (r) sheet.autoFilter = { range: r };
27582
- else delete sheet.autoFilter;
27602
+ else Reflect.deleteProperty(sheet, "autoFilter");
27583
27603
  },
27584
27604
  { sheet: s, structural: true }
27585
27605
  );
@@ -28641,9 +28661,10 @@ function updateChart(ctx, s, index2, patch) {
28641
28661
  () => {
28642
28662
  const live = sheet.drawings[index2];
28643
28663
  for (const [key2, value] of Object.entries(structuredClone(patch))) {
28644
- if (key2 === "kind" || key2 === "partName") continue;
28645
- if (value === void 0) delete live[key2];
28646
- else live[key2] = value;
28664
+ if (key2 === "kind" || key2 === "partName" || key2 === "__proto__") continue;
28665
+ if (key2 === "constructor" || key2 === "prototype") continue;
28666
+ if (value === void 0) Reflect.deleteProperty(live, key2);
28667
+ else Reflect.set(live, key2, value);
28647
28668
  }
28648
28669
  },
28649
28670
  { sheet: s }
@@ -29152,7 +29173,7 @@ function setDefaultColumnWidth(ctx, s, width) {
29152
29173
  "format",
29153
29174
  [{ kind: "sheet", sheet: s }],
29154
29175
  () => {
29155
- if (width === void 0) delete sheet.defaultColWidth;
29176
+ if (width === void 0) Reflect.deleteProperty(sheet, "defaultColWidth");
29156
29177
  else sheet.defaultColWidth = width;
29157
29178
  },
29158
29179
  { sheet: s, structural: true }
@@ -29174,7 +29195,7 @@ function setPageSetup(ctx, s, patch) {
29174
29195
  () => {
29175
29196
  const next = compact2({ ...sheet.pageSetup, ...structuredClone(patch) });
29176
29197
  if (Object.keys(next).length) sheet.pageSetup = next;
29177
- else delete sheet.pageSetup;
29198
+ else Reflect.deleteProperty(sheet, "pageSetup");
29178
29199
  },
29179
29200
  { sheet: s }
29180
29201
  );
@@ -29190,7 +29211,7 @@ function setPrintOptions(ctx, s, patch) {
29190
29211
  for (const key2 of Object.keys(next))
29191
29212
  if (!next[key2]) delete next[key2];
29192
29213
  if (Object.keys(next).length) sheet.printOptions = next;
29193
- else delete sheet.printOptions;
29214
+ else Reflect.deleteProperty(sheet, "printOptions");
29194
29215
  sheet.preserved.delete("printOptions");
29195
29216
  },
29196
29217
  { sheet: s }
@@ -29796,7 +29817,7 @@ function setSheetProtection(ctx, s, protection2, password) {
29796
29817
  [{ kind: "sheet", sheet: s }],
29797
29818
  () => {
29798
29819
  if (!protection2) {
29799
- delete sheet.protection;
29820
+ Reflect.deleteProperty(sheet, "protection");
29800
29821
  return;
29801
29822
  }
29802
29823
  const next = structuredClone(protection2);
@@ -30768,7 +30789,7 @@ function setTabColor(ctx, index2, color2) {
30768
30789
  [{ kind: "sheet", sheet: index2 }],
30769
30790
  () => {
30770
30791
  if (color2) sheet.tabColor = { ...color2 };
30771
- else delete sheet.tabColor;
30792
+ else Reflect.deleteProperty(sheet, "tabColor");
30772
30793
  },
30773
30794
  { sheet: index2 }
30774
30795
  );
@@ -380,17 +380,259 @@ function aggregateByCategory(values, rawCategories) {
380
380
  });
381
381
  }
382
382
 
383
+ // chart/treemap-squarify.ts
384
+ function worstAspectRatio(areas, side) {
385
+ const sum = areas.reduce((total, area) => total + area, 0);
386
+ const max = Math.max(...areas);
387
+ const min = Math.min(...areas);
388
+ const sideSq = side * side;
389
+ const sumSq = sum * sum;
390
+ return Math.max(sideSq * max / sumSq, sumSq / (sideSq * min));
391
+ }
392
+ function squarify(nodes, box) {
393
+ const sorted = [...nodes].sort((a, b) => b.weight - a.weight);
394
+ const total = sorted.reduce((sum, node) => sum + node.weight, 0);
395
+ const scale = total > 0 ? box.w * box.h / total : box.w * box.h / sorted.length;
396
+ const items = sorted.map((node) => ({ node, area: (total > 0 ? node.weight : 1) * scale }));
397
+ const result = [];
398
+ let rect = { ...box };
399
+ let remaining = items;
400
+ for (let first = remaining[0]; first !== void 0; first = remaining[0]) {
401
+ const side = Math.min(rect.w, rect.h);
402
+ let row = [first];
403
+ let rowAreas = [first.area];
404
+ let worst = worstAspectRatio(rowAreas, side);
405
+ let cursor = 1;
406
+ for (let next = remaining[cursor]; next !== void 0; next = remaining[cursor]) {
407
+ const candidateAreas = [...rowAreas, next.area];
408
+ const candidateWorst = worstAspectRatio(candidateAreas, side);
409
+ if (candidateWorst > worst) {
410
+ break;
411
+ }
412
+ row = [...row, next];
413
+ rowAreas = candidateAreas;
414
+ worst = candidateWorst;
415
+ cursor++;
416
+ }
417
+ const rowTotalArea = rowAreas.reduce((sum, area) => sum + area, 0);
418
+ const thickness = side > 0 ? rowTotalArea / side : 0;
419
+ const layoutVertically = rect.w >= rect.h;
420
+ let offset = layoutVertically ? rect.y : rect.x;
421
+ for (const item of row) {
422
+ const length = thickness > 0 ? item.area / thickness : 0;
423
+ if (layoutVertically) {
424
+ result.push([item.node, { x: rect.x, y: offset, w: thickness, h: length }]);
425
+ } else {
426
+ result.push([item.node, { x: offset, y: rect.y, w: length, h: thickness }]);
427
+ }
428
+ offset += length;
429
+ }
430
+ rect = layoutVertically ? { ...rect, x: rect.x + thickness, w: rect.w - thickness } : { ...rect, y: rect.y + thickness, h: rect.h - thickness };
431
+ remaining = remaining.slice(row.length);
432
+ }
433
+ return result;
434
+ }
435
+
436
+ // chart/trendline-equation.ts
437
+ var SIGNIFICANT_DIGITS = 4;
438
+ function formatTrendlineCoefficient(value) {
439
+ if (!Number.isFinite(value) || value === 0) {
440
+ return "0";
441
+ }
442
+ const rounded = Number(value.toPrecision(SIGNIFICANT_DIGITS));
443
+ return rounded === 0 ? "0" : String(rounded);
444
+ }
445
+ function signedTerm(value) {
446
+ return { sign: value < 0 ? "-" : "+", abs: formatTrendlineCoefficient(Math.abs(value)) };
447
+ }
448
+ function formatLinearEquation(slope, intercept) {
449
+ const { sign, abs } = signedTerm(intercept);
450
+ return `y = ${formatTrendlineCoefficient(slope)}x ${sign} ${abs}`;
451
+ }
452
+ function formatExponentialEquation(a, b) {
453
+ return `y = ${formatTrendlineCoefficient(a)} e^(${formatTrendlineCoefficient(b)}x)`;
454
+ }
455
+ function formatLogarithmicEquation(slope, intercept) {
456
+ const { sign, abs } = signedTerm(intercept);
457
+ return `y = ${formatTrendlineCoefficient(slope)}ln(x) ${sign} ${abs}`;
458
+ }
459
+ function formatPowerEquation(a, b) {
460
+ return `y = ${formatTrendlineCoefficient(a)}x^${formatTrendlineCoefficient(b)}`;
461
+ }
462
+ function formatPolynomialEquation(coeffsAscending) {
463
+ const order = coeffsAscending.length - 1;
464
+ let out = "y = ";
465
+ for (let power = order; power >= 0; power--) {
466
+ const coeff = coeffsAscending[power] ?? 0;
467
+ const { sign, abs } = signedTerm(coeff);
468
+ const variable = power === 0 ? "" : power === 1 ? "x" : `x^${power}`;
469
+ out += power === order ? `${sign === "-" ? "-" : ""}${abs}${variable}` : ` ${sign} ${abs}${variable}`;
470
+ }
471
+ return out;
472
+ }
473
+
474
+ // chart/radar-geometry.ts
475
+ function radarAngle(index, catCount) {
476
+ const n = Math.max(catCount, 1);
477
+ return Math.PI * 2 * index / n - Math.PI / 2;
478
+ }
479
+ function computeRadarPoints(values, maxVal, radius, cx, cy, catCount) {
480
+ const denom = maxVal > 0 ? maxVal : 1;
481
+ return values.slice(0, Math.max(catCount, 1)).map((val, i) => {
482
+ const angle = radarAngle(i, catCount), r = Math.abs(val) / denom * radius;
483
+ return { x: cx + r * Math.cos(angle), y: cy + r * Math.sin(angle) };
484
+ });
485
+ }
486
+ function radarRingPoints(cx, cy, rr, catCount) {
487
+ const n = Math.max(catCount, 1);
488
+ return Array.from({ length: n }, (_, i) => {
489
+ const angle = radarAngle(i, n);
490
+ return `${(cx + rr * Math.cos(angle)).toFixed(2)},${(cy + rr * Math.sin(angle)).toFixed(2)}`;
491
+ }).join(" ");
492
+ }
493
+
494
+ // chart/pie-best-fit.ts
495
+ var PX_PER_PT = 4 / 3;
496
+ var BEST_FIT_RIM_INSET = 4 * PX_PER_PT;
497
+ var BEST_FIT_EDGE_TOLERANCE = 1.5 * PX_PER_PT;
498
+ var BEST_FIT_OUTSIDE_GAP = 2 * PX_PER_PT;
499
+ function farCorner(x, y, w, h) {
500
+ return Math.hypot(Math.abs(x) + w / 2, Math.abs(y) + h / 2);
501
+ }
502
+ function insideDistance(ux, uy, w, h, limit) {
503
+ if (farCorner(0, 0, w, h) > limit) {
504
+ return void 0;
505
+ }
506
+ let lo = 0;
507
+ let hi = limit;
508
+ for (let i = 0; i < 40; i++) {
509
+ const mid = (lo + hi) / 2;
510
+ if (farCorner(mid * ux, mid * uy, w, h) > limit) {
511
+ hi = mid;
512
+ } else {
513
+ lo = mid;
514
+ }
515
+ }
516
+ return lo;
517
+ }
518
+ function edgeOverhang(x, y, w, h, slice) {
519
+ let worst = Number.NEGATIVE_INFINITY;
520
+ const corners = [
521
+ [-w / 2, -h / 2],
522
+ [w / 2, -h / 2],
523
+ [-w / 2, h / 2],
524
+ [w / 2, h / 2]
525
+ ];
526
+ for (const [dx, dy] of corners) {
527
+ const px = x + dx;
528
+ const py = y + dy;
529
+ const fromStart = Math.cos(slice.startAngle) * py - Math.sin(slice.startAngle) * px;
530
+ const fromEnd = Math.sin(slice.endAngle) * px - Math.cos(slice.endAngle) * py;
531
+ worst = Math.max(worst, -fromStart, -fromEnd);
532
+ }
533
+ return worst;
534
+ }
535
+ function placeBestFitLabel(slice, w, h) {
536
+ const mid = (slice.startAngle + slice.endAngle) / 2;
537
+ const ux = Math.cos(mid);
538
+ const uy = Math.sin(mid);
539
+ const distance = insideDistance(ux, uy, w, h, slice.outerR - BEST_FIT_RIM_INSET);
540
+ if (distance !== void 0) {
541
+ const x = distance * ux;
542
+ const y = distance * uy;
543
+ const reflex = slice.endAngle - slice.startAngle >= Math.PI;
544
+ if (reflex || edgeOverhang(x, y, w, h, slice) <= BEST_FIT_EDGE_TOLERANCE) {
545
+ return { x: slice.cx + x, y: slice.cy + y, inside: true };
546
+ }
547
+ }
548
+ const extent = Math.abs(ux) * w / 2 + Math.abs(uy) * h / 2;
549
+ const outside = slice.outerR + BEST_FIT_OUTSIDE_GAP + extent;
550
+ return { x: slice.cx + outside * ux, y: slice.cy + outside * uy, inside: false };
551
+ }
552
+
553
+ // chart/pie-label-collision.ts
554
+ var LABEL_COLLISION_GAP = 2;
555
+ function boxDistanceToCentre(box, y, disc) {
556
+ const nearestX = Math.max(box.x - box.w / 2, Math.min(disc.cx, box.x + box.w / 2));
557
+ const nearestY = Math.max(y - box.h / 2, Math.min(disc.cy, y + box.h / 2));
558
+ return Math.hypot(nearestX - disc.cx, nearestY - disc.cy);
559
+ }
560
+ function clearOfDisc(box, y, disc) {
561
+ if (boxDistanceToCentre(box, y, disc) >= disc.r) {
562
+ return 0;
563
+ }
564
+ const sign = box.x >= disc.cx ? 1 : -1;
565
+ let dx = 0;
566
+ while (dx < disc.r * 2 && boxDistanceToCentre({ ...box, x: box.x + sign * dx }, y, disc) < disc.r) {
567
+ dx += 1;
568
+ }
569
+ return sign * dx;
570
+ }
571
+ function overlapsX(a, b) {
572
+ return Math.abs(a.x - b.x) < (a.w + b.w) / 2;
573
+ }
574
+ function sweepSide(boxes, minY, maxY) {
575
+ const sorted = [...boxes].sort((a, b) => a.y - b.y || a.id - b.id);
576
+ const ys = sorted.map((box) => box.y);
577
+ for (let i = 1; i < sorted.length; i++) {
578
+ const prev = sorted[i - 1];
579
+ const box = sorted[i];
580
+ if (overlapsX(prev, box)) {
581
+ ys[i] = Math.max(ys[i], ys[i - 1] + (prev.h + box.h) / 2 + LABEL_COLLISION_GAP);
582
+ }
583
+ }
584
+ for (let i = sorted.length - 1; i >= 0; i--) {
585
+ const box = sorted[i];
586
+ const limit = i === sorted.length - 1 ? maxY - box.h / 2 : ys[i + 1] - (box.h + sorted[i + 1].h) / 2 - LABEL_COLLISION_GAP;
587
+ if (ys[i] > limit) {
588
+ ys[i] = Math.max(limit, minY + box.h / 2);
589
+ }
590
+ }
591
+ return new Map(sorted.map((box, i) => [box.id, ys[i]]));
592
+ }
593
+ function nudgeOutsideLabels(boxes, disc, minY, maxY) {
594
+ const resolved = /* @__PURE__ */ new Map();
595
+ for (const side of [
596
+ boxes.filter((box) => box.x >= disc.cx),
597
+ boxes.filter((box) => box.x < disc.cx)
598
+ ]) {
599
+ for (const [id, y] of sweepSide(side, minY, maxY)) {
600
+ resolved.set(id, y);
601
+ }
602
+ }
603
+ return boxes.map((box) => {
604
+ const y = resolved.get(box.id) ?? box.y;
605
+ return { id: box.id, dx: y === box.y ? 0 : clearOfDisc(box, y, disc), dy: y - box.y };
606
+ });
607
+ }
608
+
609
+ exports.BEST_FIT_EDGE_TOLERANCE = BEST_FIT_EDGE_TOLERANCE;
610
+ exports.BEST_FIT_OUTSIDE_GAP = BEST_FIT_OUTSIDE_GAP;
611
+ exports.BEST_FIT_RIM_INSET = BEST_FIT_RIM_INSET;
612
+ exports.LABEL_COLLISION_GAP = LABEL_COLLISION_GAP;
383
613
  exports.aggregateByCategory = aggregateByCategory;
384
614
  exports.axisTargetIntervals = axisTargetIntervals;
385
615
  exports.computeBoxStats = computeBoxStats;
386
616
  exports.computeHistogramBins = computeHistogramBins;
387
617
  exports.computeLinearRegression = computeLinearRegression;
388
618
  exports.computeRSquared = computeRSquared;
619
+ exports.computeRadarPoints = computeRadarPoints;
389
620
  exports.computeStackedSeriesPlots = computeStackedSeriesPlots;
390
621
  exports.fitPolynomial = fitPolynomial;
622
+ exports.formatExponentialEquation = formatExponentialEquation;
623
+ exports.formatLinearEquation = formatLinearEquation;
624
+ exports.formatLogarithmicEquation = formatLogarithmicEquation;
625
+ exports.formatPolynomialEquation = formatPolynomialEquation;
626
+ exports.formatPowerEquation = formatPowerEquation;
627
+ exports.formatTrendlineCoefficient = formatTrendlineCoefficient;
391
628
  exports.groupRowsByCategory = groupRowsByCategory;
392
629
  exports.niceAxisStep = niceAxisStep;
393
630
  exports.niceValueAxisBounds = niceValueAxisBounds;
631
+ exports.nudgeOutsideLabels = nudgeOutsideLabels;
632
+ exports.placeBestFitLabel = placeBestFitLabel;
633
+ exports.radarAngle = radarAngle;
634
+ exports.radarRingPoints = radarRingPoints;
394
635
  exports.resolveBlankDisplay = resolveBlankDisplay;
395
636
  exports.scottBinCount = scottBinCount;
637
+ exports.squarify = squarify;
396
638
  exports.visibleRuns = visibleRuns;
@@ -5,4 +5,9 @@ export * from './blank-display.js';
5
5
  export * from './stacked-series.js';
6
6
  export * from './axis-nice.js';
7
7
  export * from './histogram-binning.js';
8
+ export * from './treemap-squarify.js';
9
+ export * from './trendline-equation.js';
10
+ export * from './radar-geometry.js';
11
+ export * from './pie-best-fit.js';
12
+ export * from './pie-label-collision.js';
8
13
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../chart/index.ts"],"names":[],"mappings":"AAAA,+EAA+E;AAC/E,cAAc,iBAAiB,CAAC;AAChC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,oBAAoB,CAAC;AACnC,cAAc,qBAAqB,CAAC;AACpC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,wBAAwB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../chart/index.ts"],"names":[],"mappings":"AAAA,+EAA+E;AAC/E,cAAc,iBAAiB,CAAC;AAChC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,oBAAoB,CAAC;AACnC,cAAc,qBAAqB,CAAC;AACpC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,wBAAwB,CAAC;AACvC,cAAc,uBAAuB,CAAC;AACtC,cAAc,yBAAyB,CAAC;AACxC,cAAc,qBAAqB,CAAC;AACpC,cAAc,mBAAmB,CAAC;AAClC,cAAc,0BAA0B,CAAC"}