@diagrammo/dgmo 0.46.0 → 0.47.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 (232) hide show
  1. package/dist/advanced.d.cts +3 -855
  2. package/dist/advanced.d.ts +3 -855
  3. package/dist/advanced.js +541 -70107
  4. package/dist/analyzer-UYKBXAKR.js +15 -0
  5. package/dist/auto.cjs +1 -1
  6. package/dist/auto.js +1 -1
  7. package/dist/auto.mjs +1 -1
  8. package/dist/block.js +83 -69417
  9. package/dist/calculator-LDYQCDDY.js +11 -0
  10. package/dist/charts-d3-QOZHKTCT.js +25 -0
  11. package/dist/chunk-2DXMZRWJ.js +800 -0
  12. package/dist/chunk-2LACINWN.js +786 -0
  13. package/dist/chunk-33LCUH4H.js +893 -0
  14. package/dist/chunk-3CIOJ5YJ.js +35 -0
  15. package/dist/chunk-3HDBAXF5.js +86 -0
  16. package/dist/chunk-3JLKYGBQ.js +290 -0
  17. package/dist/chunk-3MQQNXPD.js +415 -0
  18. package/dist/chunk-3R5POCTZ.js +967 -0
  19. package/dist/chunk-3SMGIJDD.js +112 -0
  20. package/dist/chunk-44CM72SQ.js +409 -0
  21. package/dist/chunk-4GRI7MEB.js +1604 -0
  22. package/dist/chunk-4OHACBGX.js +606 -0
  23. package/dist/chunk-5DH3KYFH.js +21 -0
  24. package/dist/chunk-5MLYVTYM.js +76 -0
  25. package/dist/chunk-5QXL4NYS.js +553 -0
  26. package/dist/chunk-5WRI5ZAA.js +31 -0
  27. package/dist/chunk-6EK55ART.js +117 -0
  28. package/dist/chunk-6FZYCKXB.js +2065 -0
  29. package/dist/chunk-6HO5ZTXK.js +277 -0
  30. package/dist/chunk-6KFWC4CO.js +7 -0
  31. package/dist/chunk-6MTWIQVO.js +121 -0
  32. package/dist/chunk-6NYWYNBI.js +329 -0
  33. package/dist/chunk-6SM5NJQH.js +620 -0
  34. package/dist/chunk-725LDASD.js +422 -0
  35. package/dist/chunk-76OWZ3BA.js +130 -0
  36. package/dist/chunk-7JEGTRLD.js +338 -0
  37. package/dist/chunk-A44USUV7.js +923 -0
  38. package/dist/chunk-A4GOY3PE.js +25 -0
  39. package/dist/chunk-A4VFQH5G.js +251 -0
  40. package/dist/chunk-AELOBFW5.js +375 -0
  41. package/dist/chunk-AN2MOO5T.js +522 -0
  42. package/dist/chunk-BMR4CIEO.js +41 -0
  43. package/dist/chunk-BS7SV7C3.js +51 -0
  44. package/dist/chunk-BUPDIHML.js +613 -0
  45. package/dist/chunk-BUVJFRPY.js +887 -0
  46. package/dist/chunk-C3Z4DX3V.js +112 -0
  47. package/dist/chunk-CI2G3OEB.js +1818 -0
  48. package/dist/chunk-CKNVO24D.js +54 -0
  49. package/dist/chunk-CUVHQHZ7.js +391 -0
  50. package/dist/chunk-CXK4DZH3.js +114 -0
  51. package/dist/chunk-DKEPS5LH.js +315 -0
  52. package/dist/chunk-DU7W62N5.js +487 -0
  53. package/dist/chunk-DZAQBW35.js +751 -0
  54. package/dist/chunk-EB6FPQUP.js +316 -0
  55. package/dist/chunk-EMS2SK26.js +376 -0
  56. package/dist/chunk-ERLBPDGU.js +510 -0
  57. package/dist/chunk-EVCVDTXE.js +732 -0
  58. package/dist/chunk-FCIIHT24.js +384 -0
  59. package/dist/chunk-FFUN5D66.js +97 -0
  60. package/dist/chunk-FITOLXSX.js +714 -0
  61. package/dist/chunk-FMDSF3OV.js +68 -0
  62. package/dist/chunk-FNB3PNGF.js +410 -0
  63. package/dist/chunk-G2H74XJZ.js +2104 -0
  64. package/dist/chunk-GMR5MFHJ.js +339 -0
  65. package/dist/chunk-H6PI3KJT.js +305 -0
  66. package/dist/chunk-I46X5VUA.js +59 -0
  67. package/dist/chunk-I5IGB56C.js +255 -0
  68. package/dist/chunk-IH6ZMVQ5.js +3950 -0
  69. package/dist/chunk-IJZFN3HM.js +463 -0
  70. package/dist/chunk-IPWPWM6R.js +3079 -0
  71. package/dist/chunk-IYULMRBE.js +23 -0
  72. package/dist/chunk-IZMRON6C.js +167 -0
  73. package/dist/chunk-J5FIXQRT.js +813 -0
  74. package/dist/chunk-JED6ZDEE.js +73 -0
  75. package/dist/chunk-JEUJOX5X.js +136 -0
  76. package/dist/chunk-JM4LEPY3.js +436 -0
  77. package/dist/chunk-L3UHP6TD.js +101 -0
  78. package/dist/chunk-L66GEFB6.js +524 -0
  79. package/dist/chunk-LIOHW36Z.js +469 -0
  80. package/dist/chunk-LP7EC4MI.js +16 -0
  81. package/dist/chunk-MITI6NOL.js +548 -0
  82. package/dist/chunk-MO5YHHMQ.js +168 -0
  83. package/dist/chunk-MZSBFAPJ.js +885 -0
  84. package/dist/chunk-NNAAJVBU.js +9 -0
  85. package/dist/chunk-NOTA6TEI.js +47 -0
  86. package/dist/chunk-NURGB5Z4.js +335 -0
  87. package/dist/chunk-NX4EQS3U.js +1062 -0
  88. package/dist/chunk-NXMESY4J.js +1 -0
  89. package/dist/chunk-OJR3MQYV.js +1458 -0
  90. package/dist/chunk-OURHMODR.js +454 -0
  91. package/dist/chunk-P235SVAH.js +1057 -0
  92. package/dist/chunk-P4RH4OSP.js +1038 -0
  93. package/dist/chunk-P6T66CCZ.js +226 -0
  94. package/dist/chunk-PBAHEPLQ.js +223 -0
  95. package/dist/chunk-PBHE5UG6.js +1530 -0
  96. package/dist/chunk-PDKUANDZ.js +35 -0
  97. package/dist/chunk-PWIHZ6NJ.js +296 -0
  98. package/dist/chunk-QEBUY5NY.js +1685 -0
  99. package/dist/chunk-QLCCJEJI.js +253 -0
  100. package/dist/chunk-QOOUC2J5.js +52 -0
  101. package/dist/chunk-QYTPPDAO.js +402 -0
  102. package/dist/chunk-R3XI2VC7.js +801 -0
  103. package/dist/chunk-RCERWOJR.js +154 -0
  104. package/dist/chunk-RJE5JTVK.js +362 -0
  105. package/dist/chunk-RTLBSQ3G.js +1155 -0
  106. package/dist/chunk-RUNNI6VE.js +448 -0
  107. package/dist/chunk-RVMRZAPB.js +354 -0
  108. package/dist/chunk-RYEGFMUO.js +955 -0
  109. package/dist/chunk-RZCF2VVM.js +528 -0
  110. package/dist/chunk-SML67VPV.js +1025 -0
  111. package/dist/chunk-SQ7IY53Q.js +44 -0
  112. package/dist/chunk-SQAG3UXH.js +161 -0
  113. package/dist/chunk-SRTU6HMQ.js +1851 -0
  114. package/dist/chunk-T2OE3YQB.js +550 -0
  115. package/dist/chunk-T3UBLACL.js +299 -0
  116. package/dist/chunk-TLVIHFRO.js +296 -0
  117. package/dist/chunk-TYS6CSS5.js +52 -0
  118. package/dist/chunk-U6EQI3U6.js +130 -0
  119. package/dist/chunk-UJ4FJLBF.js +86 -0
  120. package/dist/chunk-ULOEATBZ.js +347 -0
  121. package/dist/chunk-UNCCZ3NU.js +127 -0
  122. package/dist/chunk-USZW2HHU.js +197 -0
  123. package/dist/chunk-UWN33VNX.js +1610 -0
  124. package/dist/chunk-UZ7EZW3B.js +40 -0
  125. package/dist/chunk-VOKGH627.js +37 -0
  126. package/dist/chunk-WBRZJOEC.js +204 -0
  127. package/dist/chunk-WCSOJFU3.js +380 -0
  128. package/dist/chunk-WCT3NYCV.js +160 -0
  129. package/dist/chunk-WVAJFAIA.js +529 -0
  130. package/dist/chunk-X5LCYCL5.js +394 -0
  131. package/dist/chunk-XAKEUMEJ.js +1256 -0
  132. package/dist/chunk-Y2ZE3MSG.js +590 -0
  133. package/dist/chunk-Y3J7VR52.js +646 -0
  134. package/dist/chunk-Y5QGVTVC.js +33 -0
  135. package/dist/chunk-YAA6IID3.js +548 -0
  136. package/dist/chunk-YGJTDXVR.js +692 -0
  137. package/dist/chunk-YRYKTYMQ.js +186 -0
  138. package/dist/chunk-YYVTBZZC.js +61 -0
  139. package/dist/chunk-Z5CGSFJB.js +647 -0
  140. package/dist/chunk-Z72XRXD4.js +787 -0
  141. package/dist/chunk-ZGR7ULFJ.js +978 -0
  142. package/dist/chunk-ZJEL4H2R.js +851 -0
  143. package/dist/chunk-ZO27FGJS.js +554 -0
  144. package/dist/collapse-CLPJD2RL.js +11 -0
  145. package/dist/collapse-SLINJQW6.js +9 -0
  146. package/dist/collapse-ZWUFFCK2.js +9 -0
  147. package/dist/compute-G55RG53N.js +8 -0
  148. package/dist/dgmo-router-DZCIDUO5.js +73 -0
  149. package/dist/dimensions-J3FHIMJ6.js +22 -0
  150. package/dist/element.cjs +1 -1
  151. package/dist/element.js +1 -1
  152. package/dist/element.mjs +1 -1
  153. package/dist/flowchart-parser-ZNSKIBVR.js +21 -0
  154. package/dist/flowchart-renderer-ZAEOL4DX.js +35 -0
  155. package/dist/index.cjs +3 -3
  156. package/dist/index.d.cts +3 -469
  157. package/dist/index.d.ts +3 -469
  158. package/dist/index.js +137 -69685
  159. package/dist/layout-46N4JJYK.js +17 -0
  160. package/dist/layout-5RA6QWR6.js +26 -0
  161. package/dist/layout-5T6RH5M6.js +14 -0
  162. package/dist/layout-7QXCNOVS.js +13 -0
  163. package/dist/layout-EE6GE46Y.js +17 -0
  164. package/dist/layout-G23F7LDH.js +9 -0
  165. package/dist/layout-HRBZPFCN.js +23 -0
  166. package/dist/layout-HWDSRX5E.js +20 -0
  167. package/dist/layout-HYZPWAK5.js +18 -0
  168. package/dist/layout-N76NR4ZO.js +19 -0
  169. package/dist/layout-OB3RI63S.js +14 -0
  170. package/dist/layout-S2QBYSK2.js +15 -0
  171. package/dist/layout-UM5F4DJB.js +15 -0
  172. package/dist/layout-search-OYV77HHS.js +1655 -0
  173. package/dist/load-data-EHDDZ5ZS.js +8 -0
  174. package/dist/palettes-PRJ2RI6D.js +56 -0
  175. package/dist/parser-2MFP2H2E.js +11 -0
  176. package/dist/parser-3STE62GB.js +21 -0
  177. package/dist/parser-4FL2CMDG.js +18 -0
  178. package/dist/parser-4OGOBJJ4.js +13 -0
  179. package/dist/parser-6BTRLKRZ.js +15 -0
  180. package/dist/parser-AQETEVRW.js +21 -0
  181. package/dist/parser-CFZD5IBL.js +12 -0
  182. package/dist/parser-FUSKFGZN.js +16 -0
  183. package/dist/parser-I6KXEYFK.js +13 -0
  184. package/dist/parser-IXSZZQHY.js +18 -0
  185. package/dist/parser-JAL2HD4I.js +13 -0
  186. package/dist/parser-KNSQSKQO.js +20 -0
  187. package/dist/parser-N5O4IHUN.js +13 -0
  188. package/dist/parser-NGU3ETO6.js +16 -0
  189. package/dist/parser-NRAI6O25.js +19 -0
  190. package/dist/parser-O5J4VBII.js +11 -0
  191. package/dist/parser-OV7NOBW6.js +21 -0
  192. package/dist/parser-PQ3POX45.js +23 -0
  193. package/dist/parser-TFZFBVWN.js +11 -0
  194. package/dist/parser-TRTHDFPP.js +18 -0
  195. package/dist/parser-VQP2UJ6W.js +11 -0
  196. package/dist/parser-XDVELHRK.js +13 -0
  197. package/dist/parser-XKRP7QKP.js +11 -0
  198. package/dist/parser-Y6WTBU76.js +14 -0
  199. package/dist/parser-ZSDPZYQP.js +12 -0
  200. package/dist/renderer-2XQQQ6QR.js +28 -0
  201. package/dist/renderer-5NDVNDAZ.js +26 -0
  202. package/dist/renderer-6EEZHMH4.js +26 -0
  203. package/dist/renderer-6W5462RB.js +43 -0
  204. package/dist/renderer-7MVLWWQD.js +22 -0
  205. package/dist/renderer-AWWAF6XD.js +33 -0
  206. package/dist/renderer-BYCNMBF7.js +34 -0
  207. package/dist/renderer-CLJHQPBJ.js +29 -0
  208. package/dist/renderer-GHPZMFU2.js +15 -0
  209. package/dist/renderer-JGBKUCZD.js +16 -0
  210. package/dist/renderer-KQB7UGDS.js +19 -0
  211. package/dist/renderer-PGX2TKVS.js +24 -0
  212. package/dist/renderer-QIWVHWZO.js +15 -0
  213. package/dist/renderer-RPOA5YPZ.js +35 -0
  214. package/dist/renderer-SMIBCRVB.js +24 -0
  215. package/dist/renderer-T7YKC5VP.js +28 -0
  216. package/dist/renderer-TOCOVLHS.js +37 -0
  217. package/dist/renderer-UWA5XS74.js +39 -0
  218. package/dist/renderer-VACVQYML.js +25 -0
  219. package/dist/renderer-W4M6QLIJ.js +37 -0
  220. package/dist/renderer-WZRREQ2P.js +23 -0
  221. package/dist/renderer-YY2OSF6E.js +27 -0
  222. package/dist/renderer-Z7I3W2PP.js +24 -0
  223. package/dist/renderer-ZGNTUWC4.js +22 -0
  224. package/dist/renderer-ZTR3DER3.js +19 -0
  225. package/dist/renderer-radial-C32GPJQ7.js +21 -0
  226. package/dist/resolver-NOAA7ID7.js +13 -0
  227. package/dist/state-collapse-HCGD6IVJ.js +8 -0
  228. package/dist/state-parser-Z2V23ATR.js +18 -0
  229. package/dist/state-renderer-RFYHSP4K.js +34 -0
  230. package/dist/themes-1-CuKpeH.d.cts +908 -0
  231. package/dist/themes-1-CuKpeH.d.ts +908 -0
  232. package/package.json +1 -1
@@ -0,0 +1,1458 @@
1
+ import {
2
+ EXPORT_HEIGHT,
3
+ EXPORT_WIDTH,
4
+ createExportContainer,
5
+ finalizeSvgExport,
6
+ initD3Chart,
7
+ renderChartTitle,
8
+ resolveExportPalette
9
+ } from "./chunk-U6EQI3U6.js";
10
+ import {
11
+ computeLegendLayout
12
+ } from "./chunk-YAA6IID3.js";
13
+ import {
14
+ FONT_FAMILY
15
+ } from "./chunk-6KFWC4CO.js";
16
+ import {
17
+ LEGEND_DOT_R,
18
+ LEGEND_ENTRY_FONT_SIZE,
19
+ LEGEND_HEIGHT,
20
+ LEGEND_PILL_FONT_SIZE
21
+ } from "./chunk-QOOUC2J5.js";
22
+ import {
23
+ measureText
24
+ } from "./chunk-WCT3NYCV.js";
25
+ import {
26
+ getExtendedChartLegendGroups,
27
+ getSimpleChartLegendGroups,
28
+ parseChart,
29
+ parseExtendedChart
30
+ } from "./chunk-P235SVAH.js";
31
+ import {
32
+ getSegmentColors,
33
+ getSeriesColors,
34
+ hexToHSL,
35
+ hslToHex,
36
+ mix,
37
+ shapeFill
38
+ } from "./chunk-P6T66CCZ.js";
39
+ import {
40
+ tagAttrKey
41
+ } from "./chunk-QLCCJEJI.js";
42
+
43
+ // src/utils/legend-svg.ts
44
+ function esc(s) {
45
+ return s.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
46
+ }
47
+ function emitCapsule(capsule, palette, groupBg, pillBorder) {
48
+ const inner = [];
49
+ const pill = capsule.pill;
50
+ inner.push(
51
+ `<rect width="${capsule.width}" height="${capsule.height}" rx="${LEGEND_HEIGHT / 2}" fill="${esc(groupBg)}"/>`
52
+ );
53
+ inner.push(
54
+ `<rect x="${pill.x}" y="${pill.y}" width="${pill.width}" height="${pill.height}" rx="${pill.height / 2}" fill="${esc(palette.bg)}"/>`,
55
+ `<rect x="${pill.x}" y="${pill.y}" width="${pill.width}" height="${pill.height}" rx="${pill.height / 2}" fill="none" stroke="${esc(pillBorder)}" stroke-width="0.75"/>`
56
+ );
57
+ inner.push(
58
+ `<text x="${pill.x + pill.width / 2}" y="${LEGEND_HEIGHT / 2 + LEGEND_PILL_FONT_SIZE / 2 - 2}" font-size="${LEGEND_PILL_FONT_SIZE}" font-weight="500" fill="${esc(palette.text)}" text-anchor="middle" font-family="${esc(FONT_FAMILY)}">${esc(capsule.groupName)}</text>`
59
+ );
60
+ for (const entry of capsule.entries) {
61
+ const label = entry.displayValue ?? entry.value;
62
+ const hit = entry.width != null ? `<rect x="${entry.x}" y="${entry.y}" width="${entry.width}" height="${LEGEND_HEIGHT}" fill="transparent"/>` : "";
63
+ inner.push(
64
+ `<g data-legend-entry="${esc(entry.value.toLowerCase())}" data-series-name="${esc(entry.value)}" style="cursor:pointer">` + hit + `<circle cx="${entry.dotCx}" cy="${entry.dotCy}" r="${LEGEND_DOT_R}" fill="${esc(entry.color)}"/><text x="${entry.textX}" y="${entry.dotCy + LEGEND_ENTRY_FONT_SIZE / 2 - 1}" font-size="${LEGEND_ENTRY_FONT_SIZE}" fill="${esc(palette.textMuted)}" font-family="${esc(FONT_FAMILY)}">${esc(label)}</text></g>`
65
+ );
66
+ }
67
+ return `<g transform="translate(${capsule.x},${capsule.y})" data-legend-group="${esc(tagAttrKey(capsule.groupName))}" style="cursor:pointer">${inner.join("")}</g>`;
68
+ }
69
+ function emitPill(pill, palette, groupBg) {
70
+ return `<g transform="translate(${pill.x},${pill.y})" data-legend-group="${esc(tagAttrKey(pill.groupName))}" style="cursor:pointer"><rect width="${pill.width}" height="${pill.height}" rx="${pill.height / 2}" fill="${esc(groupBg)}"/><text x="${pill.width / 2}" y="${pill.height / 2 + LEGEND_PILL_FONT_SIZE / 2 - 2}" font-size="${LEGEND_PILL_FONT_SIZE}" font-weight="500" fill="${esc(palette.textMuted)}" text-anchor="middle" font-family="${esc(FONT_FAMILY)}">${esc(pill.groupName)}</text></g>`;
71
+ }
72
+ function renderLegendSvg(groups, options) {
73
+ if (groups.length === 0) return { svg: "", height: 0, width: 0 };
74
+ const { palette, isDark, containerWidth, activeGroup, className } = options;
75
+ const wrapWidth = containerWidth > 0 ? containerWidth : 1200;
76
+ const config = {
77
+ groups,
78
+ position: { placement: "top-center", titleRelation: "below-title" },
79
+ mode: "preview"
80
+ };
81
+ const state = { activeGroup: activeGroup ?? null };
82
+ const layout = computeLegendLayout(config, state, wrapWidth);
83
+ if (!layout.activeCapsule && layout.pills.length === 0) {
84
+ return { svg: "", height: 0, width: 0 };
85
+ }
86
+ const groupBg = isDark ? mix(palette.surface, palette.bg, 50) : mix(palette.surface, palette.bg, 30);
87
+ const pillBorder = mix(palette.textMuted, palette.bg, 50);
88
+ const parts = [];
89
+ let bottom = 0;
90
+ let left = Infinity;
91
+ let right = 0;
92
+ const track = (x, y, w, h) => {
93
+ left = Math.min(left, x);
94
+ right = Math.max(right, x + w);
95
+ bottom = Math.max(bottom, y + h);
96
+ };
97
+ if (layout.activeCapsule) {
98
+ const c = layout.activeCapsule;
99
+ parts.push(emitCapsule(c, palette, groupBg, pillBorder));
100
+ track(c.x, c.y, c.width, c.height);
101
+ }
102
+ for (const pill of layout.pills) {
103
+ parts.push(emitPill(pill, palette, groupBg));
104
+ track(pill.x, pill.y, pill.width, pill.height);
105
+ }
106
+ const classAttr = className ? ` class="${esc(className)}"` : "";
107
+ const activeAttr = activeGroup ? ` data-legend-active="${esc(tagAttrKey(activeGroup))}"` : "";
108
+ const svg = `<g${classAttr}${activeAttr}>${parts.join("")}</g>`;
109
+ return {
110
+ svg,
111
+ height: bottom,
112
+ width: right - (left === Infinity ? 0 : left)
113
+ };
114
+ }
115
+ function renderLegendSvgFromConfig(config, state, palette, containerWidth) {
116
+ return renderLegendSvg(config.groups, {
117
+ palette: {
118
+ bg: palette.bg,
119
+ surface: palette.surface,
120
+ text: palette.text,
121
+ textMuted: palette.textMuted
122
+ },
123
+ isDark: palette.isDark,
124
+ containerWidth,
125
+ activeGroup: state.activeGroup
126
+ });
127
+ }
128
+
129
+ // src/charts-d3/shared.ts
130
+ var AXIS_LABEL_FONT = 13;
131
+ var TICK_FONT = 12;
132
+ var VALUE_FONT = 12;
133
+ function fmtNum(n) {
134
+ if (!isFinite(n)) return "";
135
+ if (Math.abs(n) >= 1e3) return n.toLocaleString("en-US");
136
+ return String(Math.round(n * 100) / 100);
137
+ }
138
+ function reserveHeader(svg, chart, colors, palette, isDark, hasTitle, width) {
139
+ return injectLegendGroups(
140
+ svg,
141
+ getSimpleChartLegendGroups(chart, colors),
142
+ palette,
143
+ isDark,
144
+ hasTitle,
145
+ width
146
+ );
147
+ }
148
+ function injectLegendGroups(svg, groups, palette, isDark, hasTitle, width) {
149
+ const titleH = hasTitle ? 40 : 0;
150
+ if (groups.length === 0) return hasTitle ? titleH + 12 : 24;
151
+ const legendY = 8 + titleH;
152
+ const { svg: legendSvg, height: legendH } = renderLegendSvg(groups, {
153
+ palette,
154
+ isDark,
155
+ containerWidth: width,
156
+ activeGroup: groups[0].name,
157
+ className: "chart-legend"
158
+ });
159
+ const node = svg.node();
160
+ if (node && legendSvg) {
161
+ node.insertAdjacentHTML(
162
+ "beforeend",
163
+ `<g transform="translate(0,${legendY})">${legendSvg}</g>`
164
+ );
165
+ }
166
+ return legendY + legendH + 14;
167
+ }
168
+ function computeLeftMargin(yLabel, leftLabels) {
169
+ const pad = 16;
170
+ const titleBand = yLabel ? 22 : 0;
171
+ const labelW = leftLabels.length ? Math.max(...leftLabels.map((t) => measureText(t, TICK_FONT))) : 0;
172
+ return Math.max(56, pad + titleBand + labelW + 14);
173
+ }
174
+ function drawXAxisTitle(svg, label, cx, y, textColor) {
175
+ if (!label) return;
176
+ svg.append("text").attr("x", cx).attr("y", y).attr("text-anchor", "middle").attr("fill", textColor).attr("font-size", AXIS_LABEL_FONT).attr("font-family", FONT_FAMILY).attr("font-weight", 600).text(label);
177
+ }
178
+ function drawYAxisTitle(svg, label, cy, x, textColor) {
179
+ if (!label) return;
180
+ svg.append("text").attr("transform", `translate(${x},${cy}) rotate(-90)`).attr("text-anchor", "middle").attr("fill", textColor).attr("font-size", AXIS_LABEL_FONT).attr("font-family", FONT_FAMILY).attr("font-weight", 600).text(label);
181
+ }
182
+ function tagDatum(sel, o) {
183
+ const cls = (sel.attr("class") ?? "").split(/\s+/).filter(Boolean);
184
+ if (!cls.includes("dgmo-datum")) cls.push("dgmo-datum");
185
+ sel.attr("class", cls.join(" "));
186
+ if (o.line != null) sel.attr("data-line-number", o.line);
187
+ if (o.key != null) sel.attr("data-emph-key", o.key);
188
+ if (o.name != null) sel.attr("data-name", o.name);
189
+ if (o.value != null) sel.attr("data-value", String(o.value));
190
+ if (o.color != null) sel.attr("data-color", o.color);
191
+ return sel;
192
+ }
193
+ function drawValueLabel(svg, text, x, y, color, anchor = "middle") {
194
+ svg.append("text").attr("x", x).attr("y", y).attr("text-anchor", anchor).attr("fill", color).attr("font-size", VALUE_FONT).attr("font-family", FONT_FAMILY).text(text);
195
+ }
196
+
197
+ // src/charts-d3/bar.ts
198
+ import { scaleBand, scaleLinear } from "d3-scale";
199
+ import { max as d3max } from "d3-array";
200
+ function seriesValues(pt, seriesCount) {
201
+ const out = [pt.value];
202
+ for (let s = 1; s < seriesCount; s++) out.push(pt.extraValues?.[s - 1] ?? 0);
203
+ return out;
204
+ }
205
+ function renderBar(svg, chart, width, height, colors, palette, isDark, textColor, mutedColor, hasTitle) {
206
+ const data = chart.data;
207
+ const seriesNames = chart.seriesNames?.length ? chart.seriesNames : [chart.series ?? ""];
208
+ const stacked = chart.barLayout === "stack";
209
+ const grouped = chart.barLayout === "group";
210
+ const seriesCount = stacked || grouped ? Math.max(
211
+ 1,
212
+ seriesNames.length,
213
+ 1 + Math.max(0, ...data.map((d) => d.extraValues?.length ?? 0))
214
+ ) : 1;
215
+ const horizontal = chart.orientation === "horizontal";
216
+ const multiSeries = seriesCount > 1;
217
+ const solid = chart.solidFill === true;
218
+ const colorFor = (seriesIdx, catIdx, override) => {
219
+ if (override) return override;
220
+ return multiSeries ? colors[seriesIdx % colors.length] : colors[catIdx % colors.length];
221
+ };
222
+ const fillOf = (c) => solid ? c : shapeFill(palette, c, isDark);
223
+ const top = reserveHeader(
224
+ svg,
225
+ chart,
226
+ colors,
227
+ palette,
228
+ isDark,
229
+ hasTitle,
230
+ width
231
+ );
232
+ const perCat = data.map((d) => seriesValues(d, seriesCount));
233
+ const maxVal = stacked ? d3max(perCat, (vals) => vals.reduce((a, b) => a + b, 0)) ?? 0 : d3max(perCat, (vals) => d3max(vals) ?? 0) ?? 0;
234
+ const niceMax = maxVal === 0 ? 1 : maxVal;
235
+ const leftLabels = horizontal ? data.map((d) => d.label) : [fmtNum(niceMax), fmtNum(niceMax / 2)];
236
+ const m = {
237
+ top: top + 8,
238
+ right: 32,
239
+ bottom: 64,
240
+ left: computeLeftMargin(chart.ylabel, leftLabels)
241
+ };
242
+ const plotW = width - m.left - m.right;
243
+ const plotH = height - m.top - m.bottom;
244
+ const horiz = horizontal;
245
+ const catRange = horiz ? [m.top, m.top + plotH] : [m.left, m.left + plotW];
246
+ const cat = scaleBand().domain(data.map((d) => d.label)).range(catRange).padding(0.3);
247
+ const valRange = horiz ? [m.left, m.left + plotW] : [m.top + plotH, m.top];
248
+ const val = scaleLinear().domain([0, niceMax]).nice().range(valRange);
249
+ for (const t of val.ticks(6)) {
250
+ if (horiz) {
251
+ const x = val(t);
252
+ svg.append("line").attr("x1", x).attr("x2", x).attr("y1", m.top).attr("y2", m.top + plotH).attr("stroke", mutedColor).attr("stroke-opacity", t === 0 ? 0.6 : 0.25);
253
+ svg.append("text").attr("x", x).attr("y", m.top + plotH + 18).attr("text-anchor", "middle").attr("fill", textColor).attr("font-size", TICK_FONT).attr("font-family", FONT_FAMILY).text(fmtNum(t));
254
+ } else {
255
+ const y = val(t);
256
+ svg.append("line").attr("x1", m.left).attr("x2", m.left + plotW).attr("y1", y).attr("y2", y).attr("stroke", mutedColor).attr("stroke-opacity", t === 0 ? 0.6 : 0.25);
257
+ svg.append("text").attr("x", m.left - 10).attr("y", y + 4).attr("text-anchor", "end").attr("fill", textColor).attr("font-size", TICK_FONT).attr("font-family", FONT_FAMILY).text(fmtNum(t));
258
+ }
259
+ }
260
+ for (const d of data) {
261
+ const c = (cat(d.label) ?? 0) + cat.bandwidth() / 2;
262
+ if (horiz) {
263
+ svg.append("text").attr("x", m.left - 10).attr("y", c + 4).attr("text-anchor", "end").attr("fill", textColor).attr("font-size", TICK_FONT).attr("font-family", FONT_FAMILY).text(d.label);
264
+ } else {
265
+ svg.append("text").attr("x", c).attr("y", m.top + plotH + 18).attr("text-anchor", "middle").attr("fill", textColor).attr("font-size", TICK_FONT).attr("font-family", FONT_FAMILY).text(d.label);
266
+ }
267
+ }
268
+ const zero = val(0);
269
+ const inner = stacked ? null : scaleBand().domain(seriesNames.map((_, i) => i)).range([0, cat.bandwidth()]).padding(0.12);
270
+ data.forEach((d, ci) => {
271
+ const base = cat(d.label) ?? 0;
272
+ const vals = seriesValues(d, seriesCount);
273
+ let acc = 0;
274
+ vals.forEach((v, s) => {
275
+ const stroke = colorFor(s, ci, seriesCount === 1 ? d.color : void 0);
276
+ const fill = fillOf(stroke);
277
+ const sName = seriesNames[s] ?? "";
278
+ const tag = {
279
+ line: d.lineNumber,
280
+ key: multiSeries ? sName : d.label,
281
+ name: multiSeries ? `${d.label} \xB7 ${sName}` : d.label,
282
+ value: v,
283
+ color: stroke
284
+ };
285
+ if (stacked) {
286
+ if (horiz) {
287
+ const x0 = val(acc);
288
+ const x1 = val(acc + v);
289
+ rect(
290
+ svg,
291
+ Math.min(x0, x1),
292
+ base,
293
+ Math.abs(x1 - x0),
294
+ cat.bandwidth(),
295
+ fill,
296
+ stroke,
297
+ tag
298
+ );
299
+ if (Math.abs(x1 - x0) > 26)
300
+ drawValueLabel(
301
+ svg,
302
+ fmtNum(v),
303
+ (x0 + x1) / 2,
304
+ base + cat.bandwidth() / 2 + 4,
305
+ textColor
306
+ );
307
+ } else {
308
+ const y0 = val(acc);
309
+ const y1 = val(acc + v);
310
+ rect(
311
+ svg,
312
+ base,
313
+ Math.min(y0, y1),
314
+ cat.bandwidth(),
315
+ Math.abs(y1 - y0),
316
+ fill,
317
+ stroke,
318
+ tag
319
+ );
320
+ if (Math.abs(y1 - y0) > 18)
321
+ drawValueLabel(
322
+ svg,
323
+ fmtNum(v),
324
+ base + cat.bandwidth() / 2,
325
+ (y0 + y1) / 2 + 4,
326
+ textColor
327
+ );
328
+ }
329
+ acc += v;
330
+ } else {
331
+ const off = inner(s) ?? 0;
332
+ const bw = inner.bandwidth();
333
+ if (horiz) {
334
+ const x1 = val(v);
335
+ rect(
336
+ svg,
337
+ Math.min(zero, x1),
338
+ base + off,
339
+ Math.abs(x1 - zero),
340
+ bw,
341
+ fill,
342
+ stroke,
343
+ tag
344
+ );
345
+ drawValueLabel(
346
+ svg,
347
+ fmtNum(v),
348
+ x1 + 6,
349
+ base + off + bw / 2 + 4,
350
+ textColor,
351
+ "start"
352
+ );
353
+ } else {
354
+ const y1 = val(v);
355
+ rect(
356
+ svg,
357
+ base + off,
358
+ Math.min(zero, y1),
359
+ bw,
360
+ Math.abs(zero - y1),
361
+ fill,
362
+ stroke,
363
+ tag
364
+ );
365
+ drawValueLabel(
366
+ svg,
367
+ fmtNum(v),
368
+ base + off + bw / 2,
369
+ y1 - 6,
370
+ textColor
371
+ );
372
+ }
373
+ }
374
+ });
375
+ });
376
+ drawXAxisTitle(
377
+ svg,
378
+ chart.xlabel,
379
+ m.left + plotW / 2,
380
+ m.top + plotH + 46,
381
+ textColor
382
+ );
383
+ drawYAxisTitle(svg, chart.ylabel, m.top + plotH / 2, 20, textColor);
384
+ }
385
+ function rect(svg, x, y, w, h, fill, stroke, tag) {
386
+ const r = svg.append("rect").attr("x", x).attr("y", y).attr("width", w).attr("height", h).attr("fill", fill).attr("stroke", stroke).attr("stroke-width", 1.5);
387
+ tagDatum(r, tag);
388
+ }
389
+
390
+ // src/charts-d3/line.ts
391
+ import { scalePoint, scaleLinear as scaleLinear2 } from "d3-scale";
392
+ import { line as d3line, area as d3area } from "d3-shape";
393
+ function seriesValue(pt, s) {
394
+ return s === 0 ? pt.value : pt.extraValues?.[s - 1] ?? 0;
395
+ }
396
+ function renderLine(svg, chart, width, height, colors, palette, isDark, textColor, mutedColor, bgColor, hasTitle) {
397
+ const data = chart.data;
398
+ const isArea = chart.fill === true;
399
+ const seriesNames = chart.seriesNames?.length ? chart.seriesNames : [chart.series ?? ""];
400
+ const seriesCount = Math.max(
401
+ 1,
402
+ seriesNames.length,
403
+ 1 + Math.max(0, ...data.map((d) => d.extraValues?.length ?? 0))
404
+ );
405
+ const seriesColor = (s) => chart.seriesNameColors?.[s] ?? colors[s % colors.length];
406
+ const axisOf = (s) => chart.seriesAxes?.[s] ?? "left";
407
+ const dual = (chart.seriesAxes ?? []).includes("right");
408
+ const soleAxisColor = (side) => {
409
+ const owners = [];
410
+ for (let s = 0; s < seriesCount; s++)
411
+ if (axisOf(s) === side) owners.push(s);
412
+ return owners.length === 1 ? seriesColor(owners[0]) : void 0;
413
+ };
414
+ const top = reserveHeader(
415
+ svg,
416
+ chart,
417
+ colors,
418
+ palette,
419
+ isDark,
420
+ hasTitle,
421
+ width
422
+ );
423
+ const extentFor = (side) => {
424
+ let lo = Infinity;
425
+ let hi = -Infinity;
426
+ for (const d of data)
427
+ for (let s = 0; s < seriesCount; s++)
428
+ if (axisOf(s) === side) {
429
+ const v = seriesValue(d, s);
430
+ lo = Math.min(lo, v);
431
+ hi = Math.max(hi, v);
432
+ }
433
+ if (!isFinite(lo)) return [0, 1];
434
+ if (lo > 0) lo = 0;
435
+ if (lo === hi) hi = lo + 1;
436
+ return [lo, hi];
437
+ };
438
+ const [loL, hiL] = extentFor("left");
439
+ const [loR, hiR] = dual ? extentFor("right") : [0, 1];
440
+ const m = {
441
+ top: top + 8,
442
+ right: dual ? computeLeftMargin(chart.yrlabel, [fmtNum(hiR), fmtNum(loR)]) : 32,
443
+ bottom: 64,
444
+ left: computeLeftMargin(chart.ylabel, [fmtNum(hiL), fmtNum(loL)])
445
+ };
446
+ const plotW = width - m.left - m.right;
447
+ const plotH = height - m.top - m.bottom;
448
+ const x = scalePoint().domain(data.map((d) => d.label)).range([m.left, m.left + plotW]).padding(0.5);
449
+ const yL = scaleLinear2().domain([loL, hiL]).nice().range([m.top + plotH, m.top]);
450
+ const yR = dual ? scaleLinear2().domain([loR, hiR]).nice().range([m.top + plotH, m.top]) : null;
451
+ const yFor = (s) => dual && axisOf(s) === "right" && yR ? yR : yL;
452
+ const leftTint = dual ? soleAxisColor("left") : void 0;
453
+ const rightTint = soleAxisColor("right");
454
+ if (chart.eras?.length) {
455
+ chart.eras.forEach((era, i) => {
456
+ const xs = x(era.start);
457
+ const xe = x(era.end);
458
+ if (xs == null || xe == null) return;
459
+ const fill = era.color ?? colors[i % colors.length];
460
+ svg.append("rect").attr("x", Math.min(xs, xe)).attr("y", m.top).attr("width", Math.abs(xe - xs)).attr("height", plotH).attr("fill", mix(fill, bgColor, 12));
461
+ svg.append("text").attr("x", (xs + xe) / 2).attr("y", m.top + 14).attr("text-anchor", "middle").attr("fill", textColor).attr("font-size", TICK_FONT).attr("font-family", FONT_FAMILY).attr("opacity", 0.8).text(era.label);
462
+ });
463
+ }
464
+ for (const t of yL.ticks(6)) {
465
+ const yy = yL(t);
466
+ svg.append("line").attr("x1", m.left).attr("x2", m.left + plotW).attr("y1", yy).attr("y2", yy).attr("stroke", mutedColor).attr("stroke-opacity", t === 0 ? 0.6 : 0.25);
467
+ svg.append("text").attr("class", "dgmo-tick").attr("x", m.left - 10).attr("y", yy + 4).attr("text-anchor", "end").attr("fill", leftTint ?? textColor).attr("font-size", TICK_FONT).attr("font-family", FONT_FAMILY).text(fmtNum(t));
468
+ }
469
+ if (dual && yR) {
470
+ for (const t of yR.ticks(6)) {
471
+ svg.append("text").attr("class", "dgmo-tick").attr("x", m.left + plotW + 10).attr("y", yR(t) + 4).attr("text-anchor", "start").attr("fill", rightTint ?? textColor).attr("font-size", TICK_FONT).attr("font-family", FONT_FAMILY).text(fmtNum(t));
472
+ }
473
+ }
474
+ for (const d of data) {
475
+ svg.append("text").attr("class", "dgmo-tick").attr("x", x(d.label) ?? 0).attr("y", m.top + plotH + 18).attr("text-anchor", "middle").attr("fill", textColor).attr("font-size", TICK_FONT).attr("font-family", FONT_FAMILY).text(d.label);
476
+ }
477
+ svg.append("rect").attr("class", "dgmo-plot-rect").attr("x", m.left).attr("y", m.top).attr("width", plotW).attr("height", plotH).attr("fill", "transparent");
478
+ for (let s = 0; s < seriesCount; s++) {
479
+ const color = seriesColor(s);
480
+ const yy = yFor(s);
481
+ const name = seriesNames[s] ?? `Series ${s + 1}`;
482
+ const pts = data.map((d, i) => ({
483
+ label: d.label,
484
+ v: seriesValue(d, s),
485
+ xIndex: i,
486
+ lineNumber: d.lineNumber
487
+ }));
488
+ const g = svg.append("g").attr("class", "dgmo-series").attr("data-series-index", s).attr("data-series-name", name).attr("data-axis", axisOf(s)).attr("data-color", color);
489
+ const seriesLine = chart.seriesNameLineNumbers?.[s];
490
+ if (seriesLine !== void 0) g.attr("data-line-number", seriesLine);
491
+ if (isArea) {
492
+ const areaGen = d3area().x((p) => x(p.label) ?? 0).y0(yy(Math.max(yy.domain()[0], 0))).y1((p) => yy(p.v));
493
+ g.append("path").attr("class", "dgmo-series-area").attr("d", areaGen(pts) ?? "").attr("fill", shapeFill(palette, color, isDark)).attr("stroke", "none");
494
+ }
495
+ const lineGen = d3line().x((p) => x(p.label) ?? 0).y((p) => yy(p.v));
496
+ g.append("path").attr("class", "dgmo-series-line").attr("d", lineGen(pts) ?? "").attr("fill", "none").attr("stroke", color).attr("stroke-width", 2.5).attr("stroke-linejoin", "round").attr("stroke-linecap", "round");
497
+ const labelColor = mix(color, textColor, 60);
498
+ for (const p of pts) {
499
+ g.append("circle").attr("class", "dgmo-pt").attr("data-series-index", s).attr("data-series-name", name).attr("data-color", color).attr("data-x-index", p.xIndex).attr("data-x-label", p.label).attr("data-value", p.v).attr("data-line-number", p.lineNumber).attr("cx", x(p.label) ?? 0).attr("cy", yy(p.v)).attr("r", 3.5).attr("fill", bgColor).attr("stroke", color).attr("stroke-width", 2);
500
+ if (!chart.noValue) {
501
+ drawValueLabel(
502
+ svg,
503
+ fmtNum(p.v),
504
+ x(p.label) ?? 0,
505
+ yy(p.v) - 10,
506
+ labelColor
507
+ );
508
+ }
509
+ }
510
+ }
511
+ drawXAxisTitle(
512
+ svg,
513
+ chart.xlabel,
514
+ m.left + plotW / 2,
515
+ m.top + plotH + 46,
516
+ textColor
517
+ );
518
+ drawYAxisTitle(
519
+ svg,
520
+ chart.ylabel,
521
+ m.top + plotH / 2,
522
+ 20,
523
+ leftTint ?? textColor
524
+ );
525
+ if (dual) {
526
+ drawYAxisTitle(
527
+ svg,
528
+ chart.yrlabel,
529
+ m.top + plotH / 2,
530
+ width - 18,
531
+ rightTint ?? textColor
532
+ );
533
+ svg.append("rect").attr("class", "dgmo-axis-legend").attr("data-axis-legend", "left").attr("x", 0).attr("y", m.top).attr("width", m.left).attr("height", plotH).attr("fill", "transparent").attr("style", "cursor:pointer");
534
+ svg.append("rect").attr("class", "dgmo-axis-legend").attr("data-axis-legend", "right").attr("x", m.left + plotW).attr("y", m.top).attr("width", m.right).attr("height", plotH).attr("fill", "transparent").attr("style", "cursor:pointer");
535
+ }
536
+ }
537
+
538
+ // src/charts-d3/pie.ts
539
+ import { pie as d3pie, arc as d3arc } from "d3-shape";
540
+ var LABEL_FONT = 14;
541
+ function renderPie(svg, chart, width, height, palette, isDark, textColor, topInset) {
542
+ const data = chart.data.filter((d) => d.value > 0);
543
+ if (data.length === 0) return;
544
+ const holeRatio = chart.hole;
545
+ const hasHole = holeRatio !== void 0;
546
+ const solid = chart.solidFill === true;
547
+ const total = data.reduce((a, d) => a + d.value, 0);
548
+ const cx = width / 2;
549
+ const top = topInset + 12;
550
+ const cy = top + (height - top) / 2;
551
+ const radius = Math.min(width / 2 - 220, (height - top) / 2 - 40);
552
+ const segColors = getSegmentColors(palette, data.length);
553
+ const strokeFor = (i, override) => override ?? segColors[i % segColors.length];
554
+ const arcs = d3pie().sort(null).value((d) => d.value)(data.map((d) => ({ value: d.value })));
555
+ const arcGen = d3arc().innerRadius(hasHole ? radius * holeRatio : 0).outerRadius(radius);
556
+ const g = svg.append("g").attr("transform", `translate(${cx},${cy})`);
557
+ arcs.forEach((a, i) => {
558
+ const stroke = strokeFor(i, data[i].color);
559
+ const fill = solid ? stroke : shapeFill(palette, stroke, isDark);
560
+ const pct = Math.round(data[i].value / total * 100);
561
+ const slice = g.append("path").attr("d", arcGen(a) ?? "").attr("fill", fill).attr("stroke", stroke).attr("stroke-width", 1.5);
562
+ tagDatum(slice, {
563
+ line: data[i].lineNumber,
564
+ key: data[i].label,
565
+ name: data[i].label,
566
+ value: `${fmtNum(data[i].value)} (${pct}%)`,
567
+ color: stroke
568
+ });
569
+ const mid = (a.startAngle + a.endAngle) / 2 - Math.PI / 2;
570
+ const rightSide = Math.cos(mid) >= 0;
571
+ const x0 = Math.cos(mid) * radius;
572
+ const y0 = Math.sin(mid) * radius;
573
+ const x1 = Math.cos(mid) * (radius + 16);
574
+ const y1 = Math.sin(mid) * (radius + 16);
575
+ const x2 = x1 + (rightSide ? 28 : -28);
576
+ const nm = chart.noName ? "" : data[i].label;
577
+ const tail = [
578
+ chart.noValue ? "" : fmtNum(data[i].value),
579
+ chart.noPercent ? "" : `(${pct}%)`
580
+ ].filter(Boolean).join(" ");
581
+ const label = [nm, tail].filter(Boolean).join(" \u2014 ");
582
+ if (label) {
583
+ g.append("polyline").attr("points", `${x0},${y0} ${x1},${y1} ${x2},${y1}`).attr("fill", "none").attr("stroke", stroke).attr("stroke-width", 1);
584
+ g.append("text").attr("x", x2 + (rightSide ? 4 : -4)).attr("y", y1 + 4).attr("text-anchor", rightSide ? "start" : "end").attr("fill", textColor).attr("font-size", LABEL_FONT).attr("font-family", FONT_FAMILY).text(label);
585
+ }
586
+ });
587
+ if (hasHole && !chart.noCenterTotal) {
588
+ g.append("text").attr("text-anchor", "middle").attr("y", 8).attr("fill", textColor).attr("font-size", 26).attr("font-weight", 700).attr("font-family", FONT_FAMILY).text(fmtNum(total));
589
+ }
590
+ }
591
+
592
+ // src/charts-d3/radar.ts
593
+ var RINGS = 5;
594
+ function seriesValue2(d, s) {
595
+ return s === 0 ? d.value : d.extraValues?.[s - 1] ?? 0;
596
+ }
597
+ function renderRadar(svg, chart, width, height, colors, palette, isDark, textColor, mutedColor, topInset) {
598
+ const data = chart.data;
599
+ const n = data.length;
600
+ if (n < 3) return;
601
+ const solid = chart.solidFill === true;
602
+ const seriesNames = chart.seriesNames?.length ? chart.seriesNames : [chart.series ?? ""];
603
+ const seriesCount = Math.max(
604
+ 1,
605
+ seriesNames.length,
606
+ 1 + Math.max(0, ...data.map((d) => d.extraValues?.length ?? 0))
607
+ );
608
+ const multi = seriesCount > 1;
609
+ const seriesColor = (s) => multi ? chart.seriesNameColors?.[s] ?? colors[s % colors.length] : chart.color ?? chart.seriesNameColors?.[0] ?? palette.primary;
610
+ const allValues = [];
611
+ for (const d of data)
612
+ for (let s = 0; s < seriesCount; s++) allValues.push(seriesValue2(d, s));
613
+ const maxValue = Math.max(...allValues) * 1.15 || 1;
614
+ const cx = width / 2;
615
+ const top = topInset + 8;
616
+ const cy = top + (height - top) / 2;
617
+ const radius = Math.min(width / 2 - 130, (height - top) / 2 - 50);
618
+ const angle = (i) => -Math.PI / 2 + i * 2 * Math.PI / n;
619
+ const pt = (i, r) => [
620
+ cx + Math.cos(angle(i)) * r,
621
+ cy + Math.sin(angle(i)) * r
622
+ ];
623
+ for (let l = 1; l <= RINGS; l++) {
624
+ const r = radius * l / RINGS;
625
+ const poly = data.map((_, i) => pt(i, r).join(",")).join(" ");
626
+ svg.append("polygon").attr("points", poly).attr("fill", "none").attr("stroke", mutedColor).attr("stroke-opacity", 0.4);
627
+ }
628
+ data.forEach((_, i) => {
629
+ const [x, y] = pt(i, radius);
630
+ svg.append("line").attr("x1", cx).attr("y1", cy).attr("x2", x).attr("y2", y).attr("stroke", mutedColor).attr("stroke-opacity", 0.4);
631
+ const [lx, ly] = pt(i, radius + 18);
632
+ const c = Math.cos(angle(i));
633
+ svg.append("text").attr("x", lx).attr("y", ly + 4).attr(
634
+ "text-anchor",
635
+ Math.abs(c) < 0.3 ? "middle" : c > 0 ? "start" : "end"
636
+ ).attr("fill", textColor).attr("font-size", 14).attr("font-family", FONT_FAMILY).text(data[i].label);
637
+ });
638
+ for (let s = 0; s < seriesCount; s++) {
639
+ const color = seriesColor(s);
640
+ const name = seriesNames[s] ?? `Series ${s + 1}`;
641
+ const g = svg.append("g").attr("class", "dgmo-series").attr("data-series-index", s).attr("data-series-name", name).attr("data-color", color);
642
+ const seriesLine = chart.seriesNameLineNumbers?.[s];
643
+ if (seriesLine !== void 0) g.attr("data-line-number", seriesLine);
644
+ const poly = data.map((d, i) => pt(i, seriesValue2(d, s) / maxValue * radius).join(",")).join(" ");
645
+ g.append("polygon").attr("points", poly).attr("fill", solid || multi ? color : shapeFill(palette, color, isDark)).attr("fill-opacity", solid ? 0.6 : multi ? 0.2 : 1).attr("stroke", color).attr("stroke-width", 2);
646
+ data.forEach((d, i) => {
647
+ const v = seriesValue2(d, s);
648
+ const [x, y] = pt(i, v / maxValue * radius);
649
+ const vtx = g.append("circle").attr("cx", x).attr("cy", y).attr("r", 4).attr("fill", color);
650
+ tagDatum(vtx, {
651
+ line: d.lineNumber,
652
+ key: multi ? `${name}\0${d.label}` : d.label,
653
+ name: multi ? `${name} \u2014 ${d.label}` : d.label,
654
+ value: fmtNum(v),
655
+ color
656
+ });
657
+ if (!chart.noValue) {
658
+ g.append("text").attr("x", x).attr("y", y - 8).attr("text-anchor", "middle").attr("fill", textColor).attr("font-size", 12).attr("font-family", FONT_FAMILY).text(fmtNum(v));
659
+ }
660
+ });
661
+ }
662
+ }
663
+
664
+ // src/charts-d3/polar.ts
665
+ import { arc as d3arc2 } from "d3-shape";
666
+ function renderPolarArea(svg, chart, width, height, palette, isDark, textColor, topInset) {
667
+ const data = chart.data.filter((d) => d.value > 0);
668
+ const n = data.length;
669
+ if (n === 0) return;
670
+ const solid = chart.solidFill === true;
671
+ const total = data.reduce((a, d) => a + d.value, 0);
672
+ const maxValue = Math.max(...data.map((d) => d.value)) || 1;
673
+ const cx = width / 2;
674
+ const top = topInset + 8;
675
+ const cy = top + (height - top) / 2;
676
+ const outerR = Math.min(width / 2 - 200, (height - top) / 2 - 40);
677
+ const innerR = outerR * 0.14;
678
+ const segColors = getSegmentColors(palette, n);
679
+ const g = svg.append("g").attr("transform", `translate(${cx},${cy})`);
680
+ const seg = 2 * Math.PI / n;
681
+ data.forEach((d, i) => {
682
+ const stroke = d.color ?? segColors[i % segColors.length];
683
+ const r = innerR + d.value / maxValue * (outerR - innerR);
684
+ const a0 = i * seg;
685
+ const a1 = a0 + seg;
686
+ const arcGen = d3arc2().innerRadius(innerR).outerRadius(r).startAngle(a0).endAngle(a1);
687
+ const pct = Math.round(d.value / total * 100);
688
+ const sector = g.append("path").attr("d", arcGen({}) ?? "").attr("fill", solid ? stroke : shapeFill(palette, stroke, isDark)).attr("stroke", stroke).attr("stroke-width", 2);
689
+ tagDatum(sector, {
690
+ line: d.lineNumber,
691
+ key: d.label,
692
+ name: d.label,
693
+ value: `${fmtNum(d.value)} (${pct}%)`,
694
+ color: stroke
695
+ });
696
+ const mid = a0 + seg / 2 - Math.PI / 2;
697
+ const rightSide = Math.cos(mid) >= 0;
698
+ const lx = Math.cos(mid) * (outerR + 14);
699
+ const ly = Math.sin(mid) * (outerR + 14);
700
+ const nm = chart.noName ? "" : d.label;
701
+ const tail = [
702
+ chart.noValue ? "" : fmtNum(d.value),
703
+ chart.noPercent ? "" : `(${pct}%)`
704
+ ].filter(Boolean).join(" ");
705
+ const label = [nm, tail].filter(Boolean).join(" \u2014 ");
706
+ if (label) {
707
+ g.append("text").attr("x", lx + (rightSide ? 4 : -4)).attr("y", ly + 4).attr("text-anchor", rightSide ? "start" : "end").attr("fill", textColor).attr("font-size", 13).attr("font-family", FONT_FAMILY).text(label);
708
+ }
709
+ });
710
+ }
711
+
712
+ // src/charts-d3/funnel.ts
713
+ function renderFunnel(svg, chart, width, height, colors, palette, isDark, textColor, topInset) {
714
+ const sorted = [...chart.data].sort((a, b) => b.value - a.value);
715
+ const n = sorted.length;
716
+ if (n === 0) return;
717
+ const solid = chart.solidFill === true;
718
+ const plotLeft = width * 0.2;
719
+ const plotW = width * 0.6;
720
+ const cx = width / 2;
721
+ const top = topInset + 8;
722
+ const bottom = height - 30;
723
+ const H = bottom - top;
724
+ const bandH = H / n;
725
+ const maxValue = sorted[0].value || 1;
726
+ const minW = plotW * 0.08;
727
+ const scaleW = (v) => Math.max(minW, v / maxValue * plotW);
728
+ sorted.forEach((d, i) => {
729
+ const stroke = d.color ?? colors[chart.data.indexOf(d) % colors.length];
730
+ const fill = solid ? stroke : shapeFill(palette, stroke, isDark);
731
+ const topW = scaleW(d.value);
732
+ const botW = i < n - 1 ? scaleW(sorted[i + 1].value) : minW;
733
+ const y0 = top + i * bandH;
734
+ const y1 = y0 + bandH;
735
+ const seg = svg.append("polygon").attr(
736
+ "points",
737
+ `${cx - topW / 2},${y0} ${cx + topW / 2},${y0} ${cx + botW / 2},${y1} ${cx - botW / 2},${y1}`
738
+ ).attr("fill", fill).attr("stroke", stroke).attr("stroke-width", 2);
739
+ tagDatum(seg, {
740
+ line: d.lineNumber,
741
+ key: d.label,
742
+ name: d.label,
743
+ value: fmtNum(d.value),
744
+ color: stroke
745
+ });
746
+ const yMid = (y0 + y1) / 2;
747
+ if (!chart.noName) {
748
+ svg.append("line").attr("x1", plotLeft - 8).attr("y1", yMid).attr("x2", cx - topW / 2).attr("y2", yMid).attr("stroke", textColor).attr("stroke-opacity", 0.3);
749
+ svg.append("text").attr("x", plotLeft - 12).attr("y", yMid + 4).attr("text-anchor", "end").attr("fill", textColor).attr("font-size", 13).attr("font-family", FONT_FAMILY).text(d.label);
750
+ }
751
+ if (!chart.noValue) {
752
+ svg.append("line").attr("x1", cx + topW / 2).attr("y1", yMid).attr("x2", plotLeft + plotW + 8).attr("y2", yMid).attr("stroke", textColor).attr("stroke-opacity", 0.3);
753
+ svg.append("text").attr("x", plotLeft + plotW + 12).attr("y", yMid + 4).attr("text-anchor", "start").attr("fill", textColor).attr("font-size", 13).attr("font-family", FONT_FAMILY).text(fmtNum(d.value));
754
+ }
755
+ });
756
+ }
757
+
758
+ // src/charts-d3/heatmap.ts
759
+ function renderHeatmap(svg, chart, width, height, palette, isDark, textColor, bgColor, topInset) {
760
+ const rows = chart.heatmapRows ?? [];
761
+ if (rows.length === 0) return;
762
+ const ncols = Math.max(
763
+ chart.columns?.length ?? 0,
764
+ ...rows.map((r) => r.values.length)
765
+ );
766
+ const nrows = rows.length;
767
+ const cols = chart.columns ?? [];
768
+ let minValue = Infinity;
769
+ let maxValue = -Infinity;
770
+ for (const r of rows)
771
+ for (const v of r.values) {
772
+ minValue = Math.min(minValue, v);
773
+ maxValue = Math.max(maxValue, v);
774
+ }
775
+ const solid = chart.solidFill === true;
776
+ const gradientStops = [
777
+ shapeFill(palette, palette.primary, isDark, { solid }),
778
+ shapeFill(palette, palette.colors.cyan, isDark, { solid }),
779
+ shapeFill(palette, palette.colors.yellow, isDark, { solid }),
780
+ shapeFill(palette, palette.colors.orange, isDark, { solid })
781
+ ];
782
+ const gradientAt = (t) => {
783
+ const tt = Math.max(0, Math.min(1, t));
784
+ const seg = 1 / (gradientStops.length - 1);
785
+ const idx = Math.min(gradientStops.length - 2, Math.floor(tt / seg));
786
+ const localT = (tt - idx * seg) / seg;
787
+ return mix(gradientStops[idx + 1], gradientStops[idx], localT * 100);
788
+ };
789
+ const labelTint = (cellColor) => {
790
+ const { h, s } = hexToHSL(cellColor);
791
+ const targetS = Math.min(75, Math.max(s + 25, 50));
792
+ const targetL = isDark ? 72 : 30;
793
+ return hslToHex(h, targetS, targetL);
794
+ };
795
+ const rowLabelW = Math.max(0, ...rows.map((r) => measureText(r.label, 13))) + 16;
796
+ const left = 20 + rowLabelW;
797
+ const top = topInset + 28;
798
+ const right = 24;
799
+ const bottom = 24;
800
+ const plotW = width - left - right;
801
+ const plotH = height - top - bottom;
802
+ const cw = plotW / ncols;
803
+ const ch = plotH / nrows;
804
+ const cellFont = Math.max(11, Math.min(18, Math.min(cw, ch) * 0.28));
805
+ const rotate = cols.some((c) => measureText(c, 12) > cw * 0.85);
806
+ cols.forEach((c, ci) => {
807
+ const x = left + ci * cw + cw / 2;
808
+ const t = svg.append("text").attr("fill", textColor).attr("font-size", 12).attr("font-family", FONT_FAMILY).text(c);
809
+ if (rotate) {
810
+ t.attr("transform", `translate(${x},${top - 8}) rotate(-40)`).attr(
811
+ "text-anchor",
812
+ "start"
813
+ );
814
+ } else {
815
+ t.attr("x", x).attr("y", top - 8).attr("text-anchor", "middle");
816
+ }
817
+ });
818
+ rows.forEach((row, ri) => {
819
+ svg.append("text").attr("x", left - 10).attr("y", top + ri * ch + ch / 2 + 4).attr("text-anchor", "end").attr("fill", textColor).attr("font-size", 13).attr("font-family", FONT_FAMILY).text(row.label);
820
+ row.values.forEach((v, ci) => {
821
+ const t = maxValue === minValue ? 0.5 : (v - minValue) / (maxValue - minValue);
822
+ const cell = gradientAt(t);
823
+ const colLabel = cols[ci] ?? `Col ${ci + 1}`;
824
+ const r = svg.append("rect").attr("x", left + ci * cw).attr("y", top + ri * ch).attr("width", cw).attr("height", ch).attr("fill", cell).attr("stroke", bgColor).attr("stroke-width", 2);
825
+ tagDatum(r, {
826
+ line: row.lineNumber,
827
+ key: row.label,
828
+ name: `${row.label} \xB7 ${colLabel}`,
829
+ value: fmtNum(v),
830
+ color: cell
831
+ });
832
+ if (!chart.noValue) {
833
+ svg.append("text").attr("x", left + ci * cw + cw / 2).attr("y", top + ri * ch + ch / 2 + cellFont / 3).attr("text-anchor", "middle").attr("fill", labelTint(cell)).attr("font-size", cellFont).attr("font-weight", 600).attr("font-family", FONT_FAMILY).text(fmtNum(v));
834
+ }
835
+ });
836
+ });
837
+ }
838
+
839
+ // src/charts-d3/scatter.ts
840
+ import { scaleLinear as scaleLinear3 } from "d3-scale";
841
+ var FILL_OPACITY = 0.65;
842
+ var DEFAULT_SIZE = 15;
843
+ function renderScatter(svg, chart, width, height, colors, palette, isDark, textColor, mutedColor, topInset) {
844
+ const points = chart.scatterPoints ?? [];
845
+ if (points.length === 0) return;
846
+ const solid = chart.solidFill === true;
847
+ const hasSize = points.some((p) => p.size !== void 0);
848
+ const categories = [
849
+ ...new Set(points.map((p) => p.category).filter(Boolean))
850
+ ];
851
+ const catColor = (cat, i) => chart.categoryColors?.[cat] ?? colors[i % colors.length];
852
+ const catIndex = new Map(categories.map((c, i) => [c, i]));
853
+ const xs = points.map((p) => p.x);
854
+ const ys = points.map((p) => p.y);
855
+ const xMin = Math.min(...xs);
856
+ const xMax = Math.max(...xs);
857
+ const yMin = Math.min(...ys);
858
+ const yMax = Math.max(...ys);
859
+ const xPad = (xMax - xMin) * 0.1 || 1;
860
+ const yPad = (yMax - yMin) * 0.1 || 1;
861
+ const m = {
862
+ top: topInset + 8,
863
+ right: 32,
864
+ bottom: 64,
865
+ left: computeLeftMargin(chart.ylabel, [
866
+ fmtNum(Math.ceil(yMax + yPad)),
867
+ fmtNum(Math.floor(yMin - yPad))
868
+ ])
869
+ };
870
+ const plotW = width - m.left - m.right;
871
+ const plotH = height - m.top - m.bottom;
872
+ const x = scaleLinear3().domain([Math.floor(xMin - xPad), Math.ceil(xMax + xPad)]).range([m.left, m.left + plotW]);
873
+ const y = scaleLinear3().domain([Math.floor(yMin - yPad), Math.ceil(yMax + yPad)]).range([m.top + plotH, m.top]);
874
+ svg.append("rect").attr("class", "dgmo-plot-rect").attr("x", m.left).attr("y", m.top).attr("width", plotW).attr("height", plotH).attr("fill", "transparent").attr("pointer-events", "none");
875
+ for (const t of y.ticks(6)) {
876
+ const yy = y(t);
877
+ svg.append("line").attr("x1", m.left).attr("x2", m.left + plotW).attr("y1", yy).attr("y2", yy).attr("stroke", mutedColor).attr("stroke-opacity", 0.25);
878
+ svg.append("text").attr("class", "dgmo-tick").attr("x", m.left - 10).attr("y", yy + 4).attr("text-anchor", "end").attr("fill", textColor).attr("font-size", TICK_FONT).attr("font-family", FONT_FAMILY).text(fmtNum(t));
879
+ }
880
+ for (const t of x.ticks(6)) {
881
+ const xx = x(t);
882
+ svg.append("line").attr("x1", xx).attr("x2", xx).attr("y1", m.top).attr("y2", m.top + plotH).attr("stroke", mutedColor).attr("stroke-opacity", 0.25);
883
+ svg.append("text").attr("class", "dgmo-tick").attr("x", xx).attr("y", m.top + plotH + 18).attr("text-anchor", "middle").attr("fill", textColor).attr("font-size", TICK_FONT).attr("font-family", FONT_FAMILY).text(fmtNum(t));
884
+ }
885
+ points.forEach((p, i) => {
886
+ const stroke = p.color ?? (p.category ? catColor(p.category, catIndex.get(p.category) ?? 0) : colors[i % colors.length]);
887
+ const r = (hasSize ? p.size ?? DEFAULT_SIZE : DEFAULT_SIZE) / 2;
888
+ const dot = svg.append("circle").attr("cx", x(p.x)).attr("cy", y(p.y)).attr("r", r).attr("fill", solid ? stroke : shapeFill(palette, stroke, isDark)).attr("fill-opacity", FILL_OPACITY).attr("stroke", stroke).attr("stroke-width", 2);
889
+ tagDatum(dot, {
890
+ line: p.lineNumber,
891
+ key: p.name,
892
+ name: p.category ? `${p.name} \xB7 ${p.category}` : p.name,
893
+ value: `(${fmtNum(p.x)}, ${fmtNum(p.y)})`,
894
+ color: stroke
895
+ });
896
+ dot.attr("data-axval-x", fmtNum(p.x)).attr("data-axval-y", fmtNum(p.y));
897
+ if (p.category) dot.attr("data-category", p.category);
898
+ if (!chart.noName) {
899
+ svg.append("text").attr("x", x(p.x)).attr("y", y(p.y) - r - 5).attr("text-anchor", "middle").attr("fill", textColor).attr("font-size", 11).attr("font-family", FONT_FAMILY).text(p.name);
900
+ }
901
+ });
902
+ drawXAxisTitle(
903
+ svg,
904
+ chart.xlabel,
905
+ m.left + plotW / 2,
906
+ m.top + plotH + 46,
907
+ textColor
908
+ );
909
+ drawYAxisTitle(svg, chart.ylabel, m.top + plotH / 2, 20, textColor);
910
+ }
911
+
912
+ // src/charts-d3/sankey.ts
913
+ var NODE_W = 20;
914
+ var NODE_GAP = 14;
915
+ function renderSankey(svg, chart, width, height, colors, bgColor, textColor, topInset) {
916
+ const links = chart.links ?? [];
917
+ if (links.length === 0) return;
918
+ const solid = chart.solidFill === true;
919
+ const names = Array.from(new Set(links.flatMap((l) => [l.source, l.target])));
920
+ const node = /* @__PURE__ */ new Map();
921
+ names.forEach(
922
+ (name, i) => node.set(name, {
923
+ name,
924
+ rank: 0,
925
+ value: 0,
926
+ order: i,
927
+ x: 0,
928
+ y: 0,
929
+ h: 0,
930
+ raw: chart.nodeColors?.[name] ?? colors[i % colors.length]
931
+ })
932
+ );
933
+ for (let pass = 0; pass < names.length; pass++) {
934
+ let changed = false;
935
+ for (const l of links) {
936
+ const s = node.get(l.source);
937
+ const t = node.get(l.target);
938
+ if (t.rank < s.rank + 1) {
939
+ t.rank = s.rank + 1;
940
+ changed = true;
941
+ }
942
+ }
943
+ if (!changed) break;
944
+ }
945
+ for (const n of node.values()) {
946
+ const out = links.filter((l) => l.source === n.name).reduce((a, l) => a + l.value, 0);
947
+ const inc = links.filter((l) => l.target === n.name).reduce((a, l) => a + l.value, 0);
948
+ n.value = Math.max(out, inc, 1);
949
+ }
950
+ const maxRank = Math.max(...[...node.values()].map((n) => n.rank));
951
+ const layers = Array.from({ length: maxRank + 1 }, () => []);
952
+ for (const n of node.values()) layers[n.rank].push(n);
953
+ const plotLeft = 16;
954
+ const top = topInset + 8;
955
+ const plotW = width - plotLeft - 16 - measureText("M", 14);
956
+ const plotH = height - top - 24;
957
+ let valueScale = Infinity;
958
+ for (const layer of layers) {
959
+ const tot = layer.reduce((a, n) => a + n.value, 0);
960
+ const avail = plotH - (layer.length - 1) * NODE_GAP;
961
+ if (tot > 0) valueScale = Math.min(valueScale, avail / tot);
962
+ }
963
+ if (!isFinite(valueScale)) valueScale = 1;
964
+ const layerX = (r) => plotLeft + (maxRank === 0 ? 0 : r * (plotW - NODE_W) / maxRank);
965
+ const placeLayer = (layer) => {
966
+ layer.sort((a, b) => a.order - b.order);
967
+ const totH = layer.reduce((a, n) => a + n.value * valueScale, 0) + (layer.length - 1) * NODE_GAP;
968
+ let y = top + (plotH - totH) / 2;
969
+ for (const n of layer) {
970
+ n.h = n.value * valueScale;
971
+ n.x = layerX(n.rank);
972
+ n.y = y;
973
+ y += n.h + NODE_GAP;
974
+ }
975
+ };
976
+ layers.forEach(placeLayer);
977
+ const center = (n) => n.y + n.h / 2;
978
+ for (let iter = 0; iter < 6; iter++) {
979
+ const forward = iter % 2 === 0;
980
+ const order = forward ? [...layers.keys()] : [...layers.keys()].reverse();
981
+ for (const r of order) {
982
+ for (const n of layers[r]) {
983
+ const neigh = forward ? links.filter((l) => l.target === n.name).map((l) => node.get(l.source)) : links.filter((l) => l.source === n.name).map((l) => node.get(l.target));
984
+ n.order = neigh.length ? neigh.reduce((a, m) => a + center(m), 0) / neigh.length : center(n);
985
+ }
986
+ placeLayer(layers[r]);
987
+ }
988
+ }
989
+ const outOff = /* @__PURE__ */ new Map();
990
+ const inOff = /* @__PURE__ */ new Map();
991
+ for (const n of node.values()) {
992
+ outOff.set(n.name, n.y);
993
+ inOff.set(n.name, n.y);
994
+ }
995
+ const ordered = [...links].sort((a, b) => {
996
+ const sa = node.get(a.source);
997
+ const sb = node.get(b.source);
998
+ if (sa.y !== sb.y) return sa.y - sb.y;
999
+ return node.get(a.target).y - node.get(b.target).y;
1000
+ });
1001
+ for (const l of ordered) {
1002
+ const s = node.get(l.source);
1003
+ const t = node.get(l.target);
1004
+ const th = l.value * valueScale;
1005
+ const sy = outOff.get(s.name);
1006
+ const ty = inOff.get(t.name);
1007
+ outOff.set(s.name, sy + th);
1008
+ inOff.set(t.name, ty + th);
1009
+ const sx = s.x + NODE_W;
1010
+ const tx = t.x;
1011
+ const cx0 = sx + (tx - sx) * 0.5;
1012
+ const raw = l.color ?? s.raw;
1013
+ const color = solid ? raw : mix(raw, bgColor, 45);
1014
+ const ribbon = svg.append("path").attr(
1015
+ "d",
1016
+ `M${sx},${sy} C${cx0},${sy} ${cx0},${ty} ${tx},${ty} L${tx},${ty + th} C${cx0},${ty + th} ${cx0},${sy + th} ${sx},${sy + th} Z`
1017
+ ).attr("fill", color).attr("fill-opacity", 0.6);
1018
+ tagDatum(ribbon, {
1019
+ line: l.lineNumber,
1020
+ key: `link:${s.name}\0${t.name}`,
1021
+ name: `${s.name} \u2192 ${t.name}`,
1022
+ value: String(l.value),
1023
+ color: s.raw
1024
+ });
1025
+ }
1026
+ for (const n of node.values()) {
1027
+ const nodeLine = links.find(
1028
+ (l) => l.source === n.name || l.target === n.name
1029
+ )?.lineNumber;
1030
+ const nr = svg.append("rect").attr("x", n.x).attr("y", n.y).attr("width", NODE_W).attr("height", Math.max(1, n.h)).attr("fill", solid ? n.raw : mix(n.raw, bgColor, 75)).attr("stroke", n.raw).attr("stroke-width", 1);
1031
+ tagDatum(nr, {
1032
+ ...nodeLine !== void 0 && { line: nodeLine },
1033
+ key: `node:${n.name}`,
1034
+ name: n.name,
1035
+ value: String(n.value),
1036
+ color: n.raw
1037
+ });
1038
+ const lastLayer = n.rank === maxRank;
1039
+ svg.append("text").attr("x", lastLayer ? n.x - 6 : n.x + NODE_W + 6).attr("y", n.y + n.h / 2 + 4).attr("text-anchor", lastLayer ? "end" : "start").attr("fill", textColor).attr("font-size", 13).attr("font-family", FONT_FAMILY).text(n.name);
1040
+ }
1041
+ }
1042
+
1043
+ // src/charts-d3/chord.ts
1044
+ function renderChord(svg, chart, width, height, colors, palette, isDark, textColor, topInset) {
1045
+ const links = chart.links ?? [];
1046
+ if (links.length === 0) return;
1047
+ const solid = chart.solidFill === true;
1048
+ const names = Array.from(new Set(links.flatMap((l) => [l.source, l.target])));
1049
+ const n = names.length;
1050
+ const idx = new Map(names.map((nm, i) => [nm, i]));
1051
+ const colorOf = (i) => colors[i % colors.length];
1052
+ const top = topInset + 8;
1053
+ const cx = width / 2;
1054
+ const cy = top + (height - top) / 2;
1055
+ const maxLabel = Math.max(0, ...names.map((nm) => measureText(nm, 14)));
1056
+ const radius = Math.min(width / 2 - maxLabel - 60, (height - top) / 2 - 50);
1057
+ const angle = (i) => -Math.PI / 2 + i * 2 * Math.PI / n;
1058
+ const pos = (i) => [
1059
+ cx + Math.cos(angle(i)) * radius,
1060
+ cy + Math.sin(angle(i)) * radius
1061
+ ];
1062
+ const pairKeys = /* @__PURE__ */ new Set();
1063
+ for (const l of links) {
1064
+ if (links.some(
1065
+ (r) => r.source === l.target && r.target === l.source && r !== l
1066
+ ))
1067
+ pairKeys.add(`${l.source}\0${l.target}`);
1068
+ }
1069
+ for (const link of links) {
1070
+ const si = idx.get(link.source);
1071
+ const ti = idx.get(link.target);
1072
+ const [x0, y0] = pos(si);
1073
+ const [x1, y1] = pos(ti);
1074
+ const hasOpp = pairKeys.has(`${link.source}\0${link.target}`);
1075
+ const base = 0.3;
1076
+ const curve = hasOpp ? link.source < link.target ? base + 0.15 : base - 0.15 : base;
1077
+ const mx = (x0 + x1) / 2;
1078
+ const my = (y0 + y1) / 2;
1079
+ const ctrlX = mx + (cx - mx) * curve * 2;
1080
+ const ctrlY = my + (cy - my) * curve * 2;
1081
+ const w = Math.max(1, Math.min(link.value / 20, 10));
1082
+ const stroke = colorOf(si);
1083
+ const edge = svg.append("path").attr("d", `M${x0},${y0} Q${ctrlX},${ctrlY} ${x1},${y1}`).attr("fill", "none").attr("stroke", stroke).attr("stroke-width", w).attr("stroke-opacity", 0.6);
1084
+ tagDatum(edge, {
1085
+ line: link.lineNumber,
1086
+ key: `edge:${link.source} ${link.target}`,
1087
+ name: `${link.source} \u2192 ${link.target}`,
1088
+ value: String(link.value),
1089
+ color: stroke
1090
+ });
1091
+ if (link.directed) {
1092
+ const ang = Math.atan2(y1 - ctrlY, x1 - ctrlX);
1093
+ const ah = 11;
1094
+ const tipX = x1 - Math.cos(ang) * 10;
1095
+ const tipY = y1 - Math.sin(ang) * 10;
1096
+ const a1 = ang + Math.PI - 0.4;
1097
+ const a2 = ang + Math.PI + 0.4;
1098
+ svg.append("polygon").attr(
1099
+ "points",
1100
+ `${tipX},${tipY} ${tipX + Math.cos(a1) * ah},${tipY + Math.sin(a1) * ah} ${tipX + Math.cos(a2) * ah},${tipY + Math.sin(a2) * ah}`
1101
+ ).attr("fill", stroke).attr("fill-opacity", 0.7);
1102
+ }
1103
+ }
1104
+ names.forEach((nm, i) => {
1105
+ const [px, py] = pos(i);
1106
+ const stroke = colorOf(i);
1107
+ const dot = svg.append("circle").attr("cx", px).attr("cy", py).attr("r", 10).attr("fill", solid ? stroke : shapeFill(palette, stroke, isDark)).attr("stroke", stroke).attr("stroke-width", 2);
1108
+ tagDatum(dot, { key: `node:${nm}`, name: nm, color: stroke });
1109
+ const c = Math.cos(angle(i));
1110
+ const lx = px + c * 16;
1111
+ const ly = py + Math.sin(angle(i)) * 16;
1112
+ svg.append("text").attr("x", lx).attr("y", ly + 4).attr(
1113
+ "text-anchor",
1114
+ Math.abs(c) < 0.3 ? "middle" : c > 0 ? "start" : "end"
1115
+ ).attr("fill", textColor).attr("font-size", 14).attr("font-family", FONT_FAMILY).text(nm);
1116
+ });
1117
+ }
1118
+
1119
+ // src/charts-d3/function.ts
1120
+ import { scaleLinear as scaleLinear4 } from "d3-scale";
1121
+ import { line as d3line2 } from "d3-shape";
1122
+
1123
+ // src/utils/expr-eval.ts
1124
+ function evaluateExpression(expr, x) {
1125
+ try {
1126
+ const processed = expr.replace(/\bpi\b/gi, String(Math.PI)).replace(/\be\b/g, String(Math.E)).replace(/\bsin\s*\(/gi, "Math.sin(").replace(/\bcos\s*\(/gi, "Math.cos(").replace(/\btan\s*\(/gi, "Math.tan(").replace(/\bln\s*\(/gi, "Math.log(").replace(/\blog\s*\(/gi, "Math.log10(").replace(/\bexp\s*\(/gi, "Math.exp(").replace(/\bsqrt\s*\(/gi, "Math.sqrt(").replace(/\babs\s*\(/gi, "Math.abs(").replace(/\bx\b/gi, `(${x})`).replace(/\^/g, "**");
1127
+ const result = new Function(`return ${processed}`)();
1128
+ return typeof result === "number" && isFinite(result) ? result : NaN;
1129
+ } catch {
1130
+ return NaN;
1131
+ }
1132
+ }
1133
+
1134
+ // src/charts-d3/function.ts
1135
+ var SAMPLES = 200;
1136
+ function renderFunction(svg, chart, width, height, colors, textColor, mutedColor, topInset) {
1137
+ const fns = chart.functions ?? [];
1138
+ if (fns.length === 0) return;
1139
+ const xr = chart.xRange ?? { min: -10, max: 10 };
1140
+ const step = (xr.max - xr.min) / SAMPLES;
1141
+ const xs = [];
1142
+ for (let i = 0; i <= SAMPLES; i++) xs.push(xr.min + i * step);
1143
+ const curves = fns.map((fn, i) => ({
1144
+ color: fn.color ?? colors[i % colors.length],
1145
+ name: fn.name,
1146
+ line: fn.lineNumber,
1147
+ pts: xs.map(
1148
+ (x2) => [x2, evaluateExpression(fn.expression, x2)]
1149
+ )
1150
+ }));
1151
+ let yLo = Infinity;
1152
+ let yHi = -Infinity;
1153
+ for (const c of curves)
1154
+ for (const [, y2] of c.pts)
1155
+ if (isFinite(y2)) {
1156
+ yLo = Math.min(yLo, y2);
1157
+ yHi = Math.max(yHi, y2);
1158
+ }
1159
+ if (!isFinite(yLo)) {
1160
+ yLo = -1;
1161
+ yHi = 1;
1162
+ }
1163
+ if (yLo === yHi) yHi = yLo + 1;
1164
+ const m = {
1165
+ top: topInset + 8,
1166
+ right: 32,
1167
+ bottom: 64,
1168
+ left: computeLeftMargin(chart.ylabel, [fmtNum(yHi), fmtNum(yLo)])
1169
+ };
1170
+ const plotW = width - m.left - m.right;
1171
+ const plotH = height - m.top - m.bottom;
1172
+ const x = scaleLinear4().domain([xr.min, xr.max]).range([m.left, m.left + plotW]);
1173
+ const y = scaleLinear4().domain([yLo, yHi]).nice().range([m.top + plotH, m.top]);
1174
+ for (const t of y.ticks(8)) {
1175
+ const yy = y(t);
1176
+ svg.append("line").attr("x1", m.left).attr("x2", m.left + plotW).attr("y1", yy).attr("y2", yy).attr("stroke", mutedColor).attr("stroke-opacity", t === 0 ? 0.6 : 0.2);
1177
+ svg.append("text").attr("class", "dgmo-tick").attr("x", m.left - 10).attr("y", yy + 4).attr("text-anchor", "end").attr("fill", textColor).attr("font-size", TICK_FONT).attr("font-family", FONT_FAMILY).text(fmtNum(t));
1178
+ }
1179
+ for (const t of x.ticks(10)) {
1180
+ const xx = x(t);
1181
+ svg.append("line").attr("x1", xx).attr("x2", xx).attr("y1", m.top).attr("y2", m.top + plotH).attr("stroke", mutedColor).attr("stroke-opacity", t === 0 ? 0.6 : 0.2);
1182
+ svg.append("text").attr("class", "dgmo-tick").attr("x", xx).attr("y", m.top + plotH + 18).attr("text-anchor", "middle").attr("fill", textColor).attr("font-size", TICK_FONT).attr("font-family", FONT_FAMILY).text(fmtNum(t));
1183
+ }
1184
+ const gen = d3line2().defined((p) => isFinite(p[1]) && p[1] >= yLo - 1 && p[1] <= yHi + 1).x((p) => x(p[0])).y((p) => y(p[1]));
1185
+ for (const c of curves) {
1186
+ const path = svg.append("path").attr("d", gen(c.pts) ?? "").attr("fill", "none").attr("stroke", c.color).attr("stroke-width", 2.5).attr("stroke-linejoin", "round");
1187
+ tagDatum(path, { line: c.line, key: c.name, name: c.name, color: c.color });
1188
+ }
1189
+ drawXAxisTitle(
1190
+ svg,
1191
+ chart.xlabel,
1192
+ m.left + plotW / 2,
1193
+ m.top + plotH + 46,
1194
+ textColor
1195
+ );
1196
+ drawYAxisTitle(svg, chart.ylabel, m.top + plotH / 2, 20, textColor);
1197
+ }
1198
+
1199
+ // src/charts-d3/index.ts
1200
+ var STANDARD = /* @__PURE__ */ new Set(["bar", "line", "pie", "radar", "polar-area"]);
1201
+ var EXTENDED = /* @__PURE__ */ new Set([
1202
+ "funnel",
1203
+ "heatmap",
1204
+ "scatter",
1205
+ "sankey",
1206
+ "chord",
1207
+ "function"
1208
+ ]);
1209
+ var D3_DATA_CHART_TYPES = /* @__PURE__ */ new Set([...STANDARD, ...EXTENDED]);
1210
+ function supportsD3DataChart(type) {
1211
+ return D3_DATA_CHART_TYPES.has(type);
1212
+ }
1213
+ function firstNonCommentToken(content) {
1214
+ for (const raw of content.split("\n")) {
1215
+ const l = raw.trim();
1216
+ if (!l || l.startsWith("//")) continue;
1217
+ return l.split(/\s+/)[0].toLowerCase();
1218
+ }
1219
+ return "";
1220
+ }
1221
+ async function renderDataChartD3(content, theme, palette, dims) {
1222
+ const effectivePalette = await resolveExportPalette(theme, palette);
1223
+ const isDark = theme === "dark";
1224
+ const w = dims?.width && dims.width > 0 ? Math.round(dims.width) : EXPORT_WIDTH;
1225
+ const h = dims?.height && dims.height > 0 ? Math.round(dims.height) : EXPORT_HEIGHT;
1226
+ const container = createExportContainer(w, h);
1227
+ const init = initD3Chart(container, effectivePalette, {
1228
+ width: w,
1229
+ height: h
1230
+ });
1231
+ if (!init) {
1232
+ document.body.removeChild(container);
1233
+ return "";
1234
+ }
1235
+ const { svg, width, height, textColor, mutedColor, bgColor, colors } = init;
1236
+ const ok = renderInto(
1237
+ svg,
1238
+ content,
1239
+ effectivePalette,
1240
+ isDark,
1241
+ width,
1242
+ height,
1243
+ textColor,
1244
+ mutedColor,
1245
+ bgColor,
1246
+ colors
1247
+ );
1248
+ if (!ok) {
1249
+ document.body.removeChild(container);
1250
+ return "";
1251
+ }
1252
+ return finalizeSvgExport(container, theme, effectivePalette);
1253
+ }
1254
+ function renderInto(s, content, palette, isDark, width, height, textColor, mutedColor, bgColor, colors) {
1255
+ const firstTok = firstNonCommentToken(content);
1256
+ const forceExtended = EXTENDED.has(firstTok);
1257
+ const std = forceExtended ? null : parseChart(content, palette);
1258
+ if (std && !std.error && std.data.length > 0 && STANDARD.has(std.type)) {
1259
+ const hasTitle2 = !std.noTitle && !!std.title;
1260
+ if (hasTitle2)
1261
+ renderChartTitle(s, std.title, std.titleLineNumber, width, textColor);
1262
+ switch (std.type) {
1263
+ case "bar":
1264
+ renderBar(
1265
+ s,
1266
+ std,
1267
+ width,
1268
+ height,
1269
+ colors,
1270
+ palette,
1271
+ isDark,
1272
+ textColor,
1273
+ mutedColor,
1274
+ hasTitle2
1275
+ );
1276
+ return true;
1277
+ case "line":
1278
+ renderLine(
1279
+ s,
1280
+ std,
1281
+ width,
1282
+ height,
1283
+ colors,
1284
+ palette,
1285
+ isDark,
1286
+ textColor,
1287
+ mutedColor,
1288
+ bgColor,
1289
+ hasTitle2
1290
+ );
1291
+ return true;
1292
+ case "pie":
1293
+ renderPie(
1294
+ s,
1295
+ std,
1296
+ width,
1297
+ height,
1298
+ palette,
1299
+ isDark,
1300
+ textColor,
1301
+ hasTitle2 ? 52 : 24
1302
+ );
1303
+ return true;
1304
+ case "radar": {
1305
+ const groups = getSimpleChartLegendGroups(std, colors);
1306
+ const top = injectLegendGroups(
1307
+ s,
1308
+ groups,
1309
+ palette,
1310
+ isDark,
1311
+ hasTitle2,
1312
+ width
1313
+ );
1314
+ renderRadar(
1315
+ s,
1316
+ std,
1317
+ width,
1318
+ height,
1319
+ colors,
1320
+ palette,
1321
+ isDark,
1322
+ textColor,
1323
+ mutedColor,
1324
+ top
1325
+ );
1326
+ return true;
1327
+ }
1328
+ case "polar-area":
1329
+ renderPolarArea(
1330
+ s,
1331
+ std,
1332
+ width,
1333
+ height,
1334
+ palette,
1335
+ isDark,
1336
+ textColor,
1337
+ hasTitle2 ? 52 : 24
1338
+ );
1339
+ return true;
1340
+ }
1341
+ }
1342
+ const ext = parseExtendedChart(content, palette);
1343
+ if (ext.error || !EXTENDED.has(ext.type)) return false;
1344
+ const seriesColors = getSeriesColors(palette);
1345
+ const hasTitle = !ext.noTitle && !!ext.title;
1346
+ if (hasTitle)
1347
+ renderChartTitle(s, ext.title, ext.titleLineNumber, width, textColor);
1348
+ switch (ext.type) {
1349
+ case "funnel":
1350
+ renderFunnel(
1351
+ s,
1352
+ ext,
1353
+ width,
1354
+ height,
1355
+ seriesColors,
1356
+ palette,
1357
+ isDark,
1358
+ textColor,
1359
+ hasTitle ? 52 : 24
1360
+ );
1361
+ return true;
1362
+ case "heatmap":
1363
+ renderHeatmap(
1364
+ s,
1365
+ ext,
1366
+ width,
1367
+ height,
1368
+ palette,
1369
+ isDark,
1370
+ textColor,
1371
+ bgColor,
1372
+ hasTitle ? 52 : 24
1373
+ );
1374
+ return true;
1375
+ case "scatter": {
1376
+ const groups = getExtendedChartLegendGroups(ext, seriesColors);
1377
+ const top = injectLegendGroups(
1378
+ s,
1379
+ groups,
1380
+ palette,
1381
+ isDark,
1382
+ hasTitle,
1383
+ width
1384
+ );
1385
+ renderScatter(
1386
+ s,
1387
+ ext,
1388
+ width,
1389
+ height,
1390
+ seriesColors,
1391
+ palette,
1392
+ isDark,
1393
+ textColor,
1394
+ mutedColor,
1395
+ top
1396
+ );
1397
+ return true;
1398
+ }
1399
+ case "sankey":
1400
+ renderSankey(
1401
+ s,
1402
+ ext,
1403
+ width,
1404
+ height,
1405
+ seriesColors,
1406
+ bgColor,
1407
+ textColor,
1408
+ hasTitle ? 52 : 24
1409
+ );
1410
+ return true;
1411
+ case "chord":
1412
+ renderChord(
1413
+ s,
1414
+ ext,
1415
+ width,
1416
+ height,
1417
+ seriesColors,
1418
+ palette,
1419
+ isDark,
1420
+ textColor,
1421
+ hasTitle ? 52 : 24
1422
+ );
1423
+ return true;
1424
+ case "function": {
1425
+ const groups = getExtendedChartLegendGroups(ext, seriesColors);
1426
+ const top = injectLegendGroups(
1427
+ s,
1428
+ groups,
1429
+ palette,
1430
+ isDark,
1431
+ hasTitle,
1432
+ width
1433
+ );
1434
+ renderFunction(
1435
+ s,
1436
+ ext,
1437
+ width,
1438
+ height,
1439
+ seriesColors,
1440
+ textColor,
1441
+ mutedColor,
1442
+ top
1443
+ );
1444
+ return true;
1445
+ }
1446
+ default:
1447
+ return false;
1448
+ }
1449
+ }
1450
+
1451
+ export {
1452
+ renderLegendSvg,
1453
+ renderLegendSvgFromConfig,
1454
+ D3_DATA_CHART_TYPES,
1455
+ supportsD3DataChart,
1456
+ renderDataChartD3
1457
+ };
1458
+ //# sourceMappingURL=chunk-OJR3MQYV.js.map