@rowsncolumns/charts 27.0.30

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 (281) hide show
  1. package/LICENSE.md +100 -0
  2. package/Readme.md +81 -0
  3. package/dist/cjs/atoms/editing-chart.atom.d.ts +13 -0
  4. package/dist/cjs/atoms/editing-chart.atom.d.ts.map +1 -0
  5. package/dist/cjs/atoms/editing-chart.atom.js +26 -0
  6. package/dist/cjs/basic-chart/basic-chart.d.ts +5 -0
  7. package/dist/cjs/basic-chart/basic-chart.d.ts.map +1 -0
  8. package/dist/cjs/basic-chart/basic-chart.js +2250 -0
  9. package/dist/cjs/basic-chart/helpers.d.ts +12 -0
  10. package/dist/cjs/basic-chart/helpers.d.ts.map +1 -0
  11. package/dist/cjs/basic-chart/helpers.js +105 -0
  12. package/dist/cjs/basic-chart/index.d.ts +5 -0
  13. package/dist/cjs/basic-chart/index.d.ts.map +1 -0
  14. package/dist/cjs/basic-chart/index.js +12 -0
  15. package/dist/cjs/components/dialogs/chart-editor-dialog.d.ts +5 -0
  16. package/dist/cjs/components/dialogs/chart-editor-dialog.d.ts.map +1 -0
  17. package/dist/cjs/components/dialogs/chart-editor-dialog.js +25 -0
  18. package/dist/cjs/components/editors/chart-editor.d.ts +10 -0
  19. package/dist/cjs/components/editors/chart-editor.d.ts.map +1 -0
  20. package/dist/cjs/components/editors/chart-editor.js +631 -0
  21. package/dist/cjs/components/editors/chart-range-input.d.ts +17 -0
  22. package/dist/cjs/components/editors/chart-range-input.d.ts.map +1 -0
  23. package/dist/cjs/components/editors/chart-range-input.js +16 -0
  24. package/dist/cjs/config.d.ts +19 -0
  25. package/dist/cjs/config.d.ts.map +1 -0
  26. package/dist/cjs/config.js +196 -0
  27. package/dist/cjs/hooks/use-chart-editor.d.ts +8 -0
  28. package/dist/cjs/hooks/use-chart-editor.d.ts.map +1 -0
  29. package/dist/cjs/hooks/use-chart-editor.js +29 -0
  30. package/dist/cjs/hooks/use-charts.d.ts +35 -0
  31. package/dist/cjs/hooks/use-charts.d.ts.map +1 -0
  32. package/dist/cjs/hooks/use-charts.js +67 -0
  33. package/dist/cjs/hooks/use-on-create-chart.d.ts +13 -0
  34. package/dist/cjs/hooks/use-on-create-chart.d.ts.map +1 -0
  35. package/dist/cjs/hooks/use-on-create-chart.js +24 -0
  36. package/dist/cjs/hooks/use-on-delete-chart.d.ts +8 -0
  37. package/dist/cjs/hooks/use-on-delete-chart.d.ts.map +1 -0
  38. package/dist/cjs/hooks/use-on-delete-chart.js +19 -0
  39. package/dist/cjs/hooks/use-on-move-chart.d.ts +8 -0
  40. package/dist/cjs/hooks/use-on-move-chart.d.ts.map +1 -0
  41. package/dist/cjs/hooks/use-on-move-chart.js +15 -0
  42. package/dist/cjs/hooks/use-on-resize-chart.d.ts +8 -0
  43. package/dist/cjs/hooks/use-on-resize-chart.d.ts.map +1 -0
  44. package/dist/cjs/hooks/use-on-resize-chart.js +15 -0
  45. package/dist/cjs/hooks/use-on-update-chart.d.ts +9 -0
  46. package/dist/cjs/hooks/use-on-update-chart.d.ts.map +1 -0
  47. package/dist/cjs/hooks/use-on-update-chart.js +15 -0
  48. package/dist/cjs/index.d.ts +11 -0
  49. package/dist/cjs/index.d.ts.map +1 -0
  50. package/dist/cjs/index.js +18 -0
  51. package/dist/cjs/interface/create-chart.d.ts +9 -0
  52. package/dist/cjs/interface/create-chart.d.ts.map +1 -0
  53. package/dist/cjs/interface/create-chart.js +110 -0
  54. package/dist/cjs/interface/delete-chart.d.ts +4 -0
  55. package/dist/cjs/interface/delete-chart.d.ts.map +1 -0
  56. package/dist/cjs/interface/delete-chart.js +26 -0
  57. package/dist/cjs/interface/move-chart.d.ts +5 -0
  58. package/dist/cjs/interface/move-chart.d.ts.map +1 -0
  59. package/dist/cjs/interface/move-chart.js +28 -0
  60. package/dist/cjs/interface/resize-chart.d.ts +5 -0
  61. package/dist/cjs/interface/resize-chart.d.ts.map +1 -0
  62. package/dist/cjs/interface/resize-chart.js +30 -0
  63. package/dist/cjs/interface/update-chart.d.ts +4 -0
  64. package/dist/cjs/interface/update-chart.d.ts.map +1 -0
  65. package/dist/cjs/interface/update-chart.js +29 -0
  66. package/dist/cjs/plotly/basic-chart/basic-chart.d.ts +5 -0
  67. package/dist/cjs/plotly/basic-chart/basic-chart.d.ts.map +1 -0
  68. package/dist/cjs/plotly/basic-chart/basic-chart.js +1586 -0
  69. package/dist/cjs/plotly/basic-chart/helpers.d.ts +20 -0
  70. package/dist/cjs/plotly/basic-chart/helpers.d.ts.map +1 -0
  71. package/dist/cjs/plotly/basic-chart/helpers.js +114 -0
  72. package/dist/cjs/plotly/basic-chart/index.d.ts +5 -0
  73. package/dist/cjs/plotly/basic-chart/index.d.ts.map +1 -0
  74. package/dist/cjs/plotly/basic-chart/index.js +12 -0
  75. package/dist/cjs/tsconfig.cjs.tsbuildinfo +1 -0
  76. package/dist/cjs/types.d.ts +27 -0
  77. package/dist/cjs/types.d.ts.map +1 -0
  78. package/dist/cjs/types.js +2 -0
  79. package/dist/cjs/utils/aggregation.d.ts +6 -0
  80. package/dist/cjs/utils/aggregation.d.ts.map +1 -0
  81. package/dist/cjs/utils/aggregation.js +127 -0
  82. package/dist/cjs/utils/bar-layout.d.ts +57 -0
  83. package/dist/cjs/utils/bar-layout.d.ts.map +1 -0
  84. package/dist/cjs/utils/bar-layout.js +81 -0
  85. package/dist/cjs/utils/boxplot.d.ts +62 -0
  86. package/dist/cjs/utils/boxplot.d.ts.map +1 -0
  87. package/dist/cjs/utils/boxplot.js +105 -0
  88. package/dist/cjs/utils/bubble.d.ts +52 -0
  89. package/dist/cjs/utils/bubble.d.ts.map +1 -0
  90. package/dist/cjs/utils/bubble.js +139 -0
  91. package/dist/cjs/utils/chart-spec-utils.d.ts +67 -0
  92. package/dist/cjs/utils/chart-spec-utils.d.ts.map +1 -0
  93. package/dist/cjs/utils/chart-spec-utils.js +355 -0
  94. package/dist/cjs/utils/echarts-gl-loader.d.ts +11 -0
  95. package/dist/cjs/utils/echarts-gl-loader.d.ts.map +1 -0
  96. package/dist/cjs/utils/echarts-gl-loader.js +59 -0
  97. package/dist/cjs/utils/error-bars.d.ts +58 -0
  98. package/dist/cjs/utils/error-bars.d.ts.map +1 -0
  99. package/dist/cjs/utils/error-bars.js +154 -0
  100. package/dist/cjs/utils/gradient.d.ts +52 -0
  101. package/dist/cjs/utils/gradient.d.ts.map +1 -0
  102. package/dist/cjs/utils/gradient.js +93 -0
  103. package/dist/cjs/utils/hierarchy.d.ts +28 -0
  104. package/dist/cjs/utils/hierarchy.d.ts.map +1 -0
  105. package/dist/cjs/utils/hierarchy.js +186 -0
  106. package/dist/cjs/utils/histogram.d.ts +46 -0
  107. package/dist/cjs/utils/histogram.d.ts.map +1 -0
  108. package/dist/cjs/utils/histogram.js +125 -0
  109. package/dist/cjs/utils/of-pie.d.ts +101 -0
  110. package/dist/cjs/utils/of-pie.d.ts.map +1 -0
  111. package/dist/cjs/utils/of-pie.js +159 -0
  112. package/dist/cjs/utils/overrides.d.ts +12 -0
  113. package/dist/cjs/utils/overrides.d.ts.map +1 -0
  114. package/dist/cjs/utils/overrides.js +171 -0
  115. package/dist/cjs/utils/radar.d.ts +37 -0
  116. package/dist/cjs/utils/radar.d.ts.map +1 -0
  117. package/dist/cjs/utils/radar.js +70 -0
  118. package/dist/cjs/utils/region-map-loader.d.ts +22 -0
  119. package/dist/cjs/utils/region-map-loader.d.ts.map +1 -0
  120. package/dist/cjs/utils/region-map-loader.js +174 -0
  121. package/dist/cjs/utils/region-map.d.ts +100 -0
  122. package/dist/cjs/utils/region-map.d.ts.map +1 -0
  123. package/dist/cjs/utils/region-map.js +327 -0
  124. package/dist/cjs/utils/stock.d.ts +64 -0
  125. package/dist/cjs/utils/stock.d.ts.map +1 -0
  126. package/dist/cjs/utils/stock.js +153 -0
  127. package/dist/cjs/utils/three-d.d.ts +96 -0
  128. package/dist/cjs/utils/three-d.d.ts.map +1 -0
  129. package/dist/cjs/utils/three-d.js +191 -0
  130. package/dist/cjs/utils/trendline.d.ts +41 -0
  131. package/dist/cjs/utils/trendline.d.ts.map +1 -0
  132. package/dist/cjs/utils/trendline.js +303 -0
  133. package/dist/cjs/utils/waterfall.d.ts +24 -0
  134. package/dist/cjs/utils/waterfall.d.ts.map +1 -0
  135. package/dist/cjs/utils/waterfall.js +61 -0
  136. package/dist/cjs/utils/webgl.d.ts +12 -0
  137. package/dist/cjs/utils/webgl.d.ts.map +1 -0
  138. package/dist/cjs/utils/webgl.js +36 -0
  139. package/dist/cjs/vitest.config.d.ts +3 -0
  140. package/dist/cjs/vitest.config.d.ts.map +1 -0
  141. package/dist/cjs/vitest.config.js +9 -0
  142. package/dist/esm/atoms/editing-chart.atom.d.ts +13 -0
  143. package/dist/esm/atoms/editing-chart.atom.d.ts.map +1 -0
  144. package/dist/esm/atoms/editing-chart.atom.js +12 -0
  145. package/dist/esm/basic-chart/basic-chart.d.ts +5 -0
  146. package/dist/esm/basic-chart/basic-chart.d.ts.map +1 -0
  147. package/dist/esm/basic-chart/basic-chart.js +2343 -0
  148. package/dist/esm/basic-chart/helpers.d.ts +12 -0
  149. package/dist/esm/basic-chart/helpers.d.ts.map +1 -0
  150. package/dist/esm/basic-chart/helpers.js +98 -0
  151. package/dist/esm/basic-chart/index.d.ts +5 -0
  152. package/dist/esm/basic-chart/index.d.ts.map +1 -0
  153. package/dist/esm/basic-chart/index.js +7 -0
  154. package/dist/esm/components/dialogs/chart-editor-dialog.d.ts +5 -0
  155. package/dist/esm/components/dialogs/chart-editor-dialog.d.ts.map +1 -0
  156. package/dist/esm/components/dialogs/chart-editor-dialog.js +19 -0
  157. package/dist/esm/components/editors/chart-editor.d.ts +10 -0
  158. package/dist/esm/components/editors/chart-editor.d.ts.map +1 -0
  159. package/dist/esm/components/editors/chart-editor.js +550 -0
  160. package/dist/esm/components/editors/chart-range-input.d.ts +17 -0
  161. package/dist/esm/components/editors/chart-range-input.d.ts.map +1 -0
  162. package/dist/esm/components/editors/chart-range-input.js +10 -0
  163. package/dist/esm/config.d.ts +19 -0
  164. package/dist/esm/config.d.ts.map +1 -0
  165. package/dist/esm/config.js +161 -0
  166. package/dist/esm/hooks/use-chart-editor.d.ts +8 -0
  167. package/dist/esm/hooks/use-chart-editor.d.ts.map +1 -0
  168. package/dist/esm/hooks/use-chart-editor.js +32 -0
  169. package/dist/esm/hooks/use-charts.d.ts +35 -0
  170. package/dist/esm/hooks/use-charts.d.ts.map +1 -0
  171. package/dist/esm/hooks/use-charts.js +62 -0
  172. package/dist/esm/hooks/use-on-create-chart.d.ts +13 -0
  173. package/dist/esm/hooks/use-on-create-chart.d.ts.map +1 -0
  174. package/dist/esm/hooks/use-on-create-chart.js +19 -0
  175. package/dist/esm/hooks/use-on-delete-chart.d.ts +8 -0
  176. package/dist/esm/hooks/use-on-delete-chart.d.ts.map +1 -0
  177. package/dist/esm/hooks/use-on-delete-chart.js +14 -0
  178. package/dist/esm/hooks/use-on-move-chart.d.ts +8 -0
  179. package/dist/esm/hooks/use-on-move-chart.d.ts.map +1 -0
  180. package/dist/esm/hooks/use-on-move-chart.js +10 -0
  181. package/dist/esm/hooks/use-on-resize-chart.d.ts +8 -0
  182. package/dist/esm/hooks/use-on-resize-chart.d.ts.map +1 -0
  183. package/dist/esm/hooks/use-on-resize-chart.js +10 -0
  184. package/dist/esm/hooks/use-on-update-chart.d.ts +9 -0
  185. package/dist/esm/hooks/use-on-update-chart.d.ts.map +1 -0
  186. package/dist/esm/hooks/use-on-update-chart.js +10 -0
  187. package/dist/esm/index.d.ts +11 -0
  188. package/dist/esm/index.d.ts.map +1 -0
  189. package/dist/esm/index.js +13 -0
  190. package/dist/esm/interface/create-chart.d.ts +9 -0
  191. package/dist/esm/interface/create-chart.d.ts.map +1 -0
  192. package/dist/esm/interface/create-chart.js +126 -0
  193. package/dist/esm/interface/delete-chart.d.ts +4 -0
  194. package/dist/esm/interface/delete-chart.d.ts.map +1 -0
  195. package/dist/esm/interface/delete-chart.js +20 -0
  196. package/dist/esm/interface/move-chart.d.ts +5 -0
  197. package/dist/esm/interface/move-chart.d.ts.map +1 -0
  198. package/dist/esm/interface/move-chart.js +22 -0
  199. package/dist/esm/interface/resize-chart.d.ts +5 -0
  200. package/dist/esm/interface/resize-chart.d.ts.map +1 -0
  201. package/dist/esm/interface/resize-chart.js +24 -0
  202. package/dist/esm/interface/update-chart.d.ts +4 -0
  203. package/dist/esm/interface/update-chart.d.ts.map +1 -0
  204. package/dist/esm/interface/update-chart.js +26 -0
  205. package/dist/esm/plotly/basic-chart/basic-chart.d.ts +5 -0
  206. package/dist/esm/plotly/basic-chart/basic-chart.d.ts.map +1 -0
  207. package/dist/esm/plotly/basic-chart/basic-chart.js +1682 -0
  208. package/dist/esm/plotly/basic-chart/helpers.d.ts +20 -0
  209. package/dist/esm/plotly/basic-chart/helpers.d.ts.map +1 -0
  210. package/dist/esm/plotly/basic-chart/helpers.js +107 -0
  211. package/dist/esm/plotly/basic-chart/index.d.ts +5 -0
  212. package/dist/esm/plotly/basic-chart/index.d.ts.map +1 -0
  213. package/dist/esm/plotly/basic-chart/index.js +7 -0
  214. package/dist/esm/tsconfig.esm.tsbuildinfo +1 -0
  215. package/dist/esm/types.d.ts +27 -0
  216. package/dist/esm/types.d.ts.map +1 -0
  217. package/dist/esm/types.js +1 -0
  218. package/dist/esm/utils/aggregation.d.ts +6 -0
  219. package/dist/esm/utils/aggregation.d.ts.map +1 -0
  220. package/dist/esm/utils/aggregation.js +96 -0
  221. package/dist/esm/utils/bar-layout.d.ts +57 -0
  222. package/dist/esm/utils/bar-layout.d.ts.map +1 -0
  223. package/dist/esm/utils/bar-layout.js +70 -0
  224. package/dist/esm/utils/boxplot.d.ts +62 -0
  225. package/dist/esm/utils/boxplot.d.ts.map +1 -0
  226. package/dist/esm/utils/boxplot.js +91 -0
  227. package/dist/esm/utils/bubble.d.ts +52 -0
  228. package/dist/esm/utils/bubble.d.ts.map +1 -0
  229. package/dist/esm/utils/bubble.js +122 -0
  230. package/dist/esm/utils/chart-spec-utils.d.ts +67 -0
  231. package/dist/esm/utils/chart-spec-utils.d.ts.map +1 -0
  232. package/dist/esm/utils/chart-spec-utils.js +270 -0
  233. package/dist/esm/utils/echarts-gl-loader.d.ts +11 -0
  234. package/dist/esm/utils/echarts-gl-loader.d.ts.map +1 -0
  235. package/dist/esm/utils/echarts-gl-loader.js +51 -0
  236. package/dist/esm/utils/error-bars.d.ts +58 -0
  237. package/dist/esm/utils/error-bars.d.ts.map +1 -0
  238. package/dist/esm/utils/error-bars.js +134 -0
  239. package/dist/esm/utils/gradient.d.ts +52 -0
  240. package/dist/esm/utils/gradient.d.ts.map +1 -0
  241. package/dist/esm/utils/gradient.js +84 -0
  242. package/dist/esm/utils/hierarchy.d.ts +28 -0
  243. package/dist/esm/utils/hierarchy.d.ts.map +1 -0
  244. package/dist/esm/utils/hierarchy.js +150 -0
  245. package/dist/esm/utils/histogram.d.ts +46 -0
  246. package/dist/esm/utils/histogram.d.ts.map +1 -0
  247. package/dist/esm/utils/histogram.js +95 -0
  248. package/dist/esm/utils/of-pie.d.ts +101 -0
  249. package/dist/esm/utils/of-pie.d.ts.map +1 -0
  250. package/dist/esm/utils/of-pie.js +141 -0
  251. package/dist/esm/utils/overrides.d.ts +12 -0
  252. package/dist/esm/utils/overrides.d.ts.map +1 -0
  253. package/dist/esm/utils/overrides.js +159 -0
  254. package/dist/esm/utils/radar.d.ts +37 -0
  255. package/dist/esm/utils/radar.d.ts.map +1 -0
  256. package/dist/esm/utils/radar.js +49 -0
  257. package/dist/esm/utils/region-map-loader.d.ts +22 -0
  258. package/dist/esm/utils/region-map-loader.d.ts.map +1 -0
  259. package/dist/esm/utils/region-map-loader.js +141 -0
  260. package/dist/esm/utils/region-map.d.ts +100 -0
  261. package/dist/esm/utils/region-map.d.ts.map +1 -0
  262. package/dist/esm/utils/region-map.js +266 -0
  263. package/dist/esm/utils/stock.d.ts +64 -0
  264. package/dist/esm/utils/stock.d.ts.map +1 -0
  265. package/dist/esm/utils/stock.js +123 -0
  266. package/dist/esm/utils/three-d.d.ts +96 -0
  267. package/dist/esm/utils/three-d.d.ts.map +1 -0
  268. package/dist/esm/utils/three-d.js +162 -0
  269. package/dist/esm/utils/trendline.d.ts +41 -0
  270. package/dist/esm/utils/trendline.d.ts.map +1 -0
  271. package/dist/esm/utils/trendline.js +257 -0
  272. package/dist/esm/utils/waterfall.d.ts +24 -0
  273. package/dist/esm/utils/waterfall.d.ts.map +1 -0
  274. package/dist/esm/utils/waterfall.js +55 -0
  275. package/dist/esm/utils/webgl.d.ts +12 -0
  276. package/dist/esm/utils/webgl.d.ts.map +1 -0
  277. package/dist/esm/utils/webgl.js +30 -0
  278. package/dist/esm/vitest.config.d.ts +3 -0
  279. package/dist/esm/vitest.config.d.ts.map +1 -0
  280. package/dist/esm/vitest.config.js +7 -0
  281. package/package.json +96 -0
@@ -0,0 +1,134 @@
1
+ /**
2
+ * Error-bar magnitudes shared by chart renderers.
3
+ *
4
+ * Mirrors OOXML `<c:errBars>`. Given a series' numeric values and an
5
+ * error-bar spec, this returns the +/- magnitude for each data point so
6
+ * the renderer can draw whiskers. Both echarts (via a custom series) and
7
+ * plotly (via native `error_y`/`error_x`) consume the same arrays.
8
+ *
9
+ * Supported valueTypes: fixedVal, percentage, stdDev, stdErr, custom.
10
+ * `custom` (Excel `errValType="cust"`) reads per-point plus/minus
11
+ * magnitudes from `plusValues` / `minusValues` — resolved from the
12
+ * spec's `plusRange` / `minusRange` worksheet ranges via
13
+ * `resolveCustomErrorBarValues` before this runs.
14
+ */
15
+ const isFiniteNum = (v) => typeof v === "number" && Number.isFinite(v);
16
+ const mean = (nums) => nums.length === 0 ? 0 : nums.reduce((s, n) => s + n, 0) / nums.length;
17
+ /**
18
+ * Population-style standard deviation (divides by N), matching Excel's
19
+ * STDEVP, which is what chart stdDev error bars use.
20
+ */
21
+ const stdDev = (nums) => {
22
+ if (nums.length === 0)
23
+ return 0;
24
+ const m = mean(nums);
25
+ const variance = nums.reduce((s, n) => s + (n - m) ** 2, 0) / nums.length;
26
+ return Math.sqrt(variance);
27
+ };
28
+ /**
29
+ * Resolve a custom error-bar spec's `plusRange` / `minusRange` into the
30
+ * `plusValues` / `minusValues` arrays `computeErrorBars` consumes.
31
+ *
32
+ * Range resolution is injected so this stays renderer-agnostic — both
33
+ * chart renderers pass the same `getSeriesValuesFromRange` extractor
34
+ * they already use for series data. Non-custom specs pass through
35
+ * untouched. Cells that don't hold finite numbers resolve to null
36
+ * (zero magnitude), keeping the arrays index-aligned with the range.
37
+ */
38
+ export const resolveCustomErrorBarValues = (spec, getValuesFromRange) => {
39
+ if (!spec || spec.valueType !== "custom")
40
+ return spec;
41
+ const resolve = (range) => {
42
+ if (!range)
43
+ return undefined;
44
+ return (getValuesFromRange(range) ?? []).map((raw) => {
45
+ const n = typeof raw === "string" ? Number(raw) : raw;
46
+ return isFiniteNum(n) ? n : null;
47
+ });
48
+ };
49
+ return {
50
+ ...spec,
51
+ plusValues: spec.plusValues ?? resolve(spec.plusRange),
52
+ minusValues: spec.minusValues ?? resolve(spec.minusRange),
53
+ };
54
+ };
55
+ /**
56
+ * Compute per-point error-bar magnitudes for a series.
57
+ *
58
+ * @param values Raw series y-values (non-finite entries become 0
59
+ * magnitude so the arrays stay index-aligned).
60
+ * @param spec The parsed error-bar spec.
61
+ * @returns Plus/minus magnitude arrays, or null when the spec is
62
+ * undrawable (zero magnitude, or custom without values).
63
+ */
64
+ export const computeErrorBars = (values, spec) => {
65
+ if (!spec)
66
+ return null;
67
+ const valueType = spec.valueType ?? "fixedVal";
68
+ const barType = spec.barType ?? "both";
69
+ // `custom` — asymmetric per-point magnitudes read from the resolved
70
+ // plus/minus ranges. Arrays are index-aligned with `values`; entries
71
+ // beyond a range's length (or non-finite ones) get 0 magnitude.
72
+ if (valueType === "custom") {
73
+ const plusSource = barType === "minus" ? null : spec.plusValues;
74
+ const minusSource = barType === "plus" ? null : spec.minusValues;
75
+ const plus = values.map((_, i) => {
76
+ const v = plusSource?.[i];
77
+ return isFiniteNum(v) ? Math.abs(v) : 0;
78
+ });
79
+ const minus = values.map((_, i) => {
80
+ const v = minusSource?.[i];
81
+ return isFiniteNum(v) ? Math.abs(v) : 0;
82
+ });
83
+ if (plus.every((v) => v === 0) && minus.every((v) => v === 0)) {
84
+ return null;
85
+ }
86
+ return { plus, minus };
87
+ }
88
+ const numeric = values.filter(isFiniteNum);
89
+ let magnitudeFor;
90
+ switch (valueType) {
91
+ case "fixedVal": {
92
+ const v = isFiniteNum(spec.value) ? Math.abs(spec.value) : 0;
93
+ if (v === 0)
94
+ return null;
95
+ magnitudeFor = () => v;
96
+ break;
97
+ }
98
+ case "percentage": {
99
+ const pct = isFiniteNum(spec.value) ? Math.abs(spec.value) : 0;
100
+ if (pct === 0)
101
+ return null;
102
+ magnitudeFor = (v) => Math.abs(v) * (pct / 100);
103
+ break;
104
+ }
105
+ case "stdDev": {
106
+ const multiplier = isFiniteNum(spec.value) ? Math.abs(spec.value) : 1;
107
+ const sd = stdDev(numeric);
108
+ if (sd === 0)
109
+ return null;
110
+ const mag = sd * multiplier;
111
+ magnitudeFor = () => mag;
112
+ break;
113
+ }
114
+ case "stdErr": {
115
+ const sd = stdDev(numeric);
116
+ if (sd === 0 || numeric.length === 0)
117
+ return null;
118
+ const se = sd / Math.sqrt(numeric.length);
119
+ magnitudeFor = () => se;
120
+ break;
121
+ }
122
+ default:
123
+ return null;
124
+ }
125
+ const plus = [];
126
+ const minus = [];
127
+ for (const raw of values) {
128
+ const v = isFiniteNum(raw) ? raw : 0;
129
+ const mag = magnitudeFor(v);
130
+ plus.push(barType === "minus" ? 0 : mag);
131
+ minus.push(barType === "plus" ? 0 : mag);
132
+ }
133
+ return { plus, minus };
134
+ };
@@ -0,0 +1,52 @@
1
+ /**
2
+ * Series gradient-fill helpers shared by chart renderers.
3
+ *
4
+ * Mirrors OOXML `<a:gradFill>` carried on a series via
5
+ * `ChartData.gradient`. A linear gradient has a `degree` rotation and a
6
+ * list of color stops; a `path` gradient is radial-ish with inset axes.
7
+ *
8
+ * echarts: paints faithfully via `echarts.graphic.LinearGradient`. We
9
+ * produce the constructor args (x, y, x2, y2, colorStops) so the
10
+ * renderer can `new echarts.graphic.LinearGradient(...)`.
11
+ *
12
+ * plotly: has NO faithful per-trace gradient fill for bar/line series.
13
+ * We degrade to a representative solid color (the middle stop) so the
14
+ * series still reads as "the gradient color". Documented as
15
+ * lower-fidelity.
16
+ */
17
+ export type GradientSpec = {
18
+ type: "linear" | "path";
19
+ degree?: number | null;
20
+ left?: number | null;
21
+ right?: number | null;
22
+ top?: number | null;
23
+ bottom?: number | null;
24
+ stops: Array<{
25
+ position: number;
26
+ color: string;
27
+ }>;
28
+ };
29
+ export type EchartsLinearGradientArgs = {
30
+ x: number;
31
+ y: number;
32
+ x2: number;
33
+ y2: number;
34
+ colorStops: Array<{
35
+ offset: number;
36
+ color: string;
37
+ }>;
38
+ };
39
+ /**
40
+ * Build the args for `new echarts.graphic.LinearGradient(x, y, x2, y2,
41
+ * colorStops)`. Returns null when there are no usable stops. A `path`
42
+ * gradient is approximated as a linear gradient (echarts core has no
43
+ * radial graphic for series fills).
44
+ */
45
+ export declare const gradientToEcharts: (gradient: GradientSpec | null | undefined) => EchartsLinearGradientArgs | null;
46
+ /**
47
+ * Plotly fallback: there is no per-trace gradient, so pick a single
48
+ * representative color (the median stop). Returns undefined when no
49
+ * usable stops exist.
50
+ */
51
+ export declare const gradientToPlotly: (gradient: GradientSpec | null | undefined) => string | undefined;
52
+ //# sourceMappingURL=gradient.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gradient.d.ts","sourceRoot":"","sources":["../../../utils/gradient.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,MAAM,MAAM,YAAY,GAAG;IACzB,IAAI,EAAE,QAAQ,GAAG,MAAM,CAAC;IACxB,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,IAAI,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,GAAG,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,KAAK,EAAE,KAAK,CAAC;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CACnD,CAAC;AAEF,MAAM,MAAM,yBAAyB,GAAG;IACtC,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,MAAM,CAAC;IACV,EAAE,EAAE,MAAM,CAAC;IACX,EAAE,EAAE,MAAM,CAAC;IACX,UAAU,EAAE,KAAK,CAAC;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CACtD,CAAC;AA0CF;;;;;GAKG;AACH,eAAO,MAAM,iBAAiB,GAC5B,UAAU,YAAY,GAAG,IAAI,GAAG,SAAS,KACxC,yBAAyB,GAAG,IAY9B,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,gBAAgB,GAC3B,UAAU,YAAY,GAAG,IAAI,GAAG,SAAS,KACxC,MAAM,GAAG,SAMX,CAAC"}
@@ -0,0 +1,84 @@
1
+ /**
2
+ * Series gradient-fill helpers shared by chart renderers.
3
+ *
4
+ * Mirrors OOXML `<a:gradFill>` carried on a series via
5
+ * `ChartData.gradient`. A linear gradient has a `degree` rotation and a
6
+ * list of color stops; a `path` gradient is radial-ish with inset axes.
7
+ *
8
+ * echarts: paints faithfully via `echarts.graphic.LinearGradient`. We
9
+ * produce the constructor args (x, y, x2, y2, colorStops) so the
10
+ * renderer can `new echarts.graphic.LinearGradient(...)`.
11
+ *
12
+ * plotly: has NO faithful per-trace gradient fill for bar/line series.
13
+ * We degrade to a representative solid color (the middle stop) so the
14
+ * series still reads as "the gradient color". Documented as
15
+ * lower-fidelity.
16
+ */
17
+ /**
18
+ * Normalize + sort stops to echarts' `{ offset, color }` shape. Positions
19
+ * are clamped to 0..1; offsets <0 or >1 are not allowed by echarts.
20
+ */
21
+ const toColorStops = (stops) => {
22
+ return (stops ?? [])
23
+ .filter((s) => s && typeof s.color === "string")
24
+ .map((s) => ({
25
+ offset: Math.max(0, Math.min(1, s.position ?? 0)),
26
+ color: s.color,
27
+ }))
28
+ .sort((a, b) => a.offset - b.offset);
29
+ };
30
+ /**
31
+ * Convert a gradient `degree` (clockwise from the +x axis, OOXML uses
32
+ * 60000ths of a degree but the parser already normalizes to plain
33
+ * degrees) into echarts' (x, y) -> (x2, y2) endpoints inside the unit
34
+ * box. 0° = left→right, 90° = top→bottom.
35
+ */
36
+ const degreeToEndpoints = (degree) => {
37
+ const rad = (degree * Math.PI) / 180;
38
+ // Direction vector of the gradient axis.
39
+ const dx = Math.cos(rad);
40
+ const dy = Math.sin(rad);
41
+ // Project the direction through the centre of the unit box so the ramp
42
+ // is axis-aligned for the cardinal angles (0° = left→right, 90° =
43
+ // top→bottom) and points sensibly for everything in between.
44
+ return {
45
+ x: 0.5 - dx / 2,
46
+ y: 0.5 - dy / 2,
47
+ x2: 0.5 + dx / 2,
48
+ y2: 0.5 + dy / 2,
49
+ };
50
+ };
51
+ /**
52
+ * Build the args for `new echarts.graphic.LinearGradient(x, y, x2, y2,
53
+ * colorStops)`. Returns null when there are no usable stops. A `path`
54
+ * gradient is approximated as a linear gradient (echarts core has no
55
+ * radial graphic for series fills).
56
+ */
57
+ export const gradientToEcharts = (gradient) => {
58
+ if (!gradient)
59
+ return null;
60
+ const colorStops = toColorStops(gradient.stops);
61
+ if (colorStops.length < 1)
62
+ return null;
63
+ // A single stop is a solid color; still emit a valid gradient (two
64
+ // identical stops) so callers have a uniform return type.
65
+ const stops = colorStops.length === 1
66
+ ? [colorStops[0], { offset: 1, color: colorStops[0].color }]
67
+ : colorStops;
68
+ const { x, y, x2, y2 } = degreeToEndpoints(gradient.degree ?? 90);
69
+ return { x, y, x2, y2, colorStops: stops };
70
+ };
71
+ /**
72
+ * Plotly fallback: there is no per-trace gradient, so pick a single
73
+ * representative color (the median stop). Returns undefined when no
74
+ * usable stops exist.
75
+ */
76
+ export const gradientToPlotly = (gradient) => {
77
+ if (!gradient)
78
+ return undefined;
79
+ const stops = toColorStops(gradient.stops);
80
+ if (stops.length === 0)
81
+ return undefined;
82
+ // Median stop is the most "representative" of the ramp.
83
+ return stops[Math.floor(stops.length / 2)].color;
84
+ };
@@ -0,0 +1,28 @@
1
+ import type { ChartSpec, SheetRange, TreemapChart } from "@rowsncolumns/spreadsheet";
2
+ type Extractor = (range: SheetRange, hiddenStrategy?: ChartSpec["hiddenDimensionStrategy"]) => (string | number | boolean | null | undefined)[];
3
+ export type HierarchyRow = {
4
+ label: string;
5
+ parent: string | null;
6
+ value: number | null;
7
+ };
8
+ export type HierarchyNode = {
9
+ name: string;
10
+ id?: string;
11
+ value?: number | null;
12
+ children?: HierarchyNode[];
13
+ };
14
+ type ExtractOptions = {
15
+ getDomainValuesFromRange: Extractor;
16
+ getSeriesValuesFromRange: Extractor;
17
+ hiddenDimensionStrategy?: ChartSpec["hiddenDimensionStrategy"];
18
+ };
19
+ export declare const getHierarchyHeaderTitle: (spec: TreemapChart, options: ExtractOptions) => string | undefined;
20
+ export declare const extractHierarchyRows: (spec: TreemapChart, options: ExtractOptions) => HierarchyRow[];
21
+ export declare const buildHierarchyTree: (rows: HierarchyRow[]) => HierarchyNode[];
22
+ export declare const toPlotlyHierarchy: (rows: HierarchyRow[]) => {
23
+ labels: string[];
24
+ parents: string[];
25
+ values: number[];
26
+ };
27
+ export {};
28
+ //# sourceMappingURL=hierarchy.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hierarchy.d.ts","sourceRoot":"","sources":["../../../utils/hierarchy.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAEV,SAAS,EACT,UAAU,EACV,YAAY,EACb,MAAM,2BAA2B,CAAC;AAEnC,KAAK,SAAS,GAAG,CACf,KAAK,EAAE,UAAU,EACjB,cAAc,CAAC,EAAE,SAAS,CAAC,yBAAyB,CAAC,KAClD,CAAC,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,IAAI,GAAG,SAAS,CAAC,EAAE,CAAC;AAEtD,MAAM,MAAM,YAAY,GAAG;IACzB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;CACtB,CAAC;AAEF,MAAM,MAAM,aAAa,GAAG;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,QAAQ,CAAC,EAAE,aAAa,EAAE,CAAC;CAC5B,CAAC;AAEF,KAAK,cAAc,GAAG;IACpB,wBAAwB,EAAE,SAAS,CAAC;IACpC,wBAAwB,EAAE,SAAS,CAAC;IACpC,uBAAuB,CAAC,EAAE,SAAS,CAAC,yBAAyB,CAAC,CAAC;CAChE,CAAC;AA+CF,eAAO,MAAM,uBAAuB,GAClC,MAAM,YAAY,EAClB,SAAS,cAAc,KACtB,MAAM,GAAG,SAgBX,CAAC;AAEF,eAAO,MAAM,oBAAoB,GAC/B,MAAM,YAAY,EAClB,SAAS,cAAc,KACtB,YAAY,EAyCd,CAAC;AAyBF,eAAO,MAAM,kBAAkB,GAAI,MAAM,YAAY,EAAE,KAAG,aAAa,EAoDtE,CAAC;AAEF,eAAO,MAAM,iBAAiB,GAAI,MAAM,YAAY,EAAE;;;;CAoBrD,CAAC"}
@@ -0,0 +1,150 @@
1
+ const flattenChartData = (chartData, extractor, hiddenStrategy, skipFirstRow = false) => {
2
+ if (!chartData || chartData.length === 0)
3
+ return [];
4
+ const values = [];
5
+ chartData.forEach((entry) => {
6
+ entry.sources.forEach((source) => {
7
+ const extracted = extractor(source, hiddenStrategy) ?? [];
8
+ const trimmed = skipFirstRow && extracted.length > 0 ? extracted.slice(1) : extracted;
9
+ values.push(...trimmed);
10
+ });
11
+ });
12
+ return values;
13
+ };
14
+ const toLabelString = (value) => {
15
+ if (value === null || value === undefined)
16
+ return "";
17
+ return `${value}`.trim();
18
+ };
19
+ const toParentLabel = (value) => {
20
+ const label = toLabelString(value);
21
+ return label === "" ? null : label;
22
+ };
23
+ const toNumericValue = (value) => {
24
+ if (typeof value === "number" && Number.isFinite(value)) {
25
+ return value;
26
+ }
27
+ if (typeof value === "string") {
28
+ const parsed = Number(value);
29
+ return Number.isFinite(parsed) ? parsed : null;
30
+ }
31
+ if (typeof value === "boolean") {
32
+ return value ? 1 : 0;
33
+ }
34
+ return null;
35
+ };
36
+ export const getHierarchyHeaderTitle = (spec, options) => {
37
+ if (spec.useFirstRowAsHeaders === false)
38
+ return undefined;
39
+ const { getDomainValuesFromRange } = options;
40
+ for (const entry of spec.parentLabels ?? []) {
41
+ for (const source of entry.sources ?? []) {
42
+ const values = getDomainValuesFromRange(source, options.hiddenDimensionStrategy) ?? [];
43
+ if (values.length > 0) {
44
+ const header = toLabelString(values[0]);
45
+ if (header) {
46
+ return header;
47
+ }
48
+ }
49
+ }
50
+ }
51
+ return undefined;
52
+ };
53
+ export const extractHierarchyRows = (spec, options) => {
54
+ const { getDomainValuesFromRange, getSeriesValuesFromRange } = options;
55
+ const hiddenStrategy = options.hiddenDimensionStrategy;
56
+ const skipHeaders = spec.useFirstRowAsHeaders !== false;
57
+ const labels = flattenChartData(spec.labels, getDomainValuesFromRange, hiddenStrategy, skipHeaders).map(toLabelString);
58
+ const parents = flattenChartData(spec.parentLabels, getDomainValuesFromRange, hiddenStrategy, skipHeaders);
59
+ const sizes = flattenChartData(spec.sizeData, getSeriesValuesFromRange, hiddenStrategy, skipHeaders);
60
+ const rows = [];
61
+ const seenLabels = new Set();
62
+ for (let i = 0; i < labels.length; i++) {
63
+ const label = labels[i];
64
+ if (!label)
65
+ continue;
66
+ if (seenLabels.has(label)) {
67
+ throw new Error(`Duplicate hierarchy label detected: ${label}`);
68
+ }
69
+ seenLabels.add(label);
70
+ rows.push({
71
+ label,
72
+ parent: toParentLabel(parents[i]),
73
+ value: toNumericValue(sizes[i]),
74
+ });
75
+ }
76
+ return rows;
77
+ };
78
+ const getOrCreateNode = (nodes, name) => {
79
+ const existing = nodes.get(name);
80
+ if (existing)
81
+ return existing;
82
+ const node = {
83
+ name,
84
+ value: 0,
85
+ parent: null,
86
+ children: new Set(),
87
+ };
88
+ nodes.set(name, node);
89
+ return node;
90
+ };
91
+ export const buildHierarchyTree = (rows) => {
92
+ const nodes = new Map();
93
+ rows.forEach((row) => {
94
+ if (!row.label)
95
+ return;
96
+ const labelNode = getOrCreateNode(nodes, row.label);
97
+ const numericValue = row.value ?? 0;
98
+ labelNode.value += numericValue;
99
+ const parentName = row.parent && row.parent !== row.label ? row.parent : null;
100
+ if (parentName) {
101
+ const parentNode = getOrCreateNode(nodes, parentName);
102
+ parentNode.children.add(row.label);
103
+ if (labelNode.parent == null) {
104
+ labelNode.parent = parentName;
105
+ }
106
+ }
107
+ });
108
+ const buildNode = (internal) => {
109
+ const children = internal.children.size > 0
110
+ ? Array.from(internal.children).map((childName) => buildNode(getOrCreateNode(nodes, childName)))
111
+ : undefined;
112
+ const value = children && children.length > 0
113
+ ? children.reduce((sum, child) => sum + (child.value ?? 0), 0)
114
+ : internal.value;
115
+ const node = {
116
+ name: internal.name,
117
+ id: internal.name,
118
+ value,
119
+ };
120
+ if (children && children.length > 0) {
121
+ node.children = children;
122
+ }
123
+ return node;
124
+ };
125
+ const roots = [];
126
+ nodes.forEach((internal) => {
127
+ if (internal.parent == null) {
128
+ roots.push(buildNode(internal));
129
+ }
130
+ });
131
+ return roots;
132
+ };
133
+ export const toPlotlyHierarchy = (rows) => {
134
+ const tree = buildHierarchyTree(rows);
135
+ const labels = [];
136
+ const parents = [];
137
+ const values = [];
138
+ const traverse = (nodes, parentName) => {
139
+ nodes.forEach((node) => {
140
+ labels.push(node.name);
141
+ parents.push(parentName ?? "");
142
+ values.push(node.value ?? 0);
143
+ if (node.children) {
144
+ traverse(node.children, node.name);
145
+ }
146
+ });
147
+ };
148
+ traverse(tree, null);
149
+ return { labels, parents, values };
150
+ };
@@ -0,0 +1,46 @@
1
+ /**
2
+ * Histogram binning utility shared by chart renderers.
3
+ *
4
+ * Plotly has native histogram trace support, so it does its own binning.
5
+ * Echarts has no native histogram series, so the renderer pre-bins values
6
+ * here and emits a bar series with the bin labels as the x-axis category.
7
+ */
8
+ export type HistogramBins = {
9
+ labels: string[];
10
+ counts: number[];
11
+ };
12
+ export type ParetoData = {
13
+ /** Bin labels reordered by descending count. */
14
+ labels: string[];
15
+ /** Bin counts sorted descending (stable — ties keep bin order). */
16
+ counts: number[];
17
+ /**
18
+ * Running cumulative percentage of the total count per sorted bin.
19
+ * The final entry is exactly 100 (avoids float drift), so the line
20
+ * always meets the top of a 0-100 secondary axis.
21
+ */
22
+ cumulativePercents: number[];
23
+ };
24
+ /**
25
+ * True when a chart spec should render as a Pareto chart: either a
26
+ * native `pareto` chartType token, or a histogram payload that was
27
+ * degraded from an authored Pareto chart (`sourceChartType: "PARETO"`,
28
+ * preserved verbatim through the document).
29
+ */
30
+ export declare const isParetoSpec: (spec: {
31
+ chartType?: string;
32
+ sourceChartType?: string | null;
33
+ }) => boolean;
34
+ /**
35
+ * Transform histogram bins into Pareto form: bins sorted by count
36
+ * descending (stable sort — equal counts keep their original bin
37
+ * order) plus the cumulative percentage of the running total for the
38
+ * secondary-axis overlay line.
39
+ */
40
+ export declare const computePareto: (bins: HistogramBins) => ParetoData;
41
+ /**
42
+ * Bin a set of numeric values into `binCount` equal-width bins.
43
+ * When `binCount` is omitted, uses Sturges' rule: ceil(log2(n) + 1).
44
+ */
45
+ export declare const binHistogram: (values: (number | null | undefined)[], binCount?: number | null) => HistogramBins;
46
+ //# sourceMappingURL=histogram.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"histogram.d.ts","sourceRoot":"","sources":["../../../utils/histogram.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,MAAM,MAAM,aAAa,GAAG;IAC1B,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,MAAM,EAAE,MAAM,EAAE,CAAC;CAClB,CAAC;AAEF,MAAM,MAAM,UAAU,GAAG;IACvB,gDAAgD;IAChD,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,mEAAmE;IACnE,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB;;;;OAIG;IACH,kBAAkB,EAAE,MAAM,EAAE,CAAC;CAC9B,CAAC;AAEF;;;;;GAKG;AACH,eAAO,MAAM,YAAY,GAAI,MAAM;IACjC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,eAAe,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CACjC,KAAG,OAGgD,CAAC;AAErD;;;;;GAKG;AACH,eAAO,MAAM,aAAa,GAAI,MAAM,aAAa,KAAG,UAmBnD,CAAC;AASF;;;GAGG;AACH,eAAO,MAAM,YAAY,GACvB,QAAQ,CAAC,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC,EAAE,EACrC,WAAW,MAAM,GAAG,IAAI,KACvB,aA0CF,CAAC"}
@@ -0,0 +1,95 @@
1
+ /**
2
+ * Histogram binning utility shared by chart renderers.
3
+ *
4
+ * Plotly has native histogram trace support, so it does its own binning.
5
+ * Echarts has no native histogram series, so the renderer pre-bins values
6
+ * here and emits a bar series with the bin labels as the x-axis category.
7
+ */
8
+ /**
9
+ * True when a chart spec should render as a Pareto chart: either a
10
+ * native `pareto` chartType token, or a histogram payload that was
11
+ * degraded from an authored Pareto chart (`sourceChartType: "PARETO"`,
12
+ * preserved verbatim through the document).
13
+ */
14
+ export const isParetoSpec = (spec) => spec.chartType === "pareto" ||
15
+ (typeof spec.sourceChartType === "string" &&
16
+ spec.sourceChartType.toUpperCase() === "PARETO");
17
+ /**
18
+ * Transform histogram bins into Pareto form: bins sorted by count
19
+ * descending (stable sort — equal counts keep their original bin
20
+ * order) plus the cumulative percentage of the running total for the
21
+ * secondary-axis overlay line.
22
+ */
23
+ export const computePareto = (bins) => {
24
+ const order = bins.counts.map((_, i) => i);
25
+ order.sort((a, b) => bins.counts[b] - bins.counts[a] || a - b);
26
+ const counts = order.map((i) => bins.counts[i]);
27
+ const labels = order.map((i) => bins.labels[i] ?? "");
28
+ const total = counts.reduce((sum, c) => sum + c, 0);
29
+ const cumulativePercents = new Array(counts.length);
30
+ let running = 0;
31
+ for (let i = 0; i < counts.length; i++) {
32
+ running += counts[i];
33
+ if (total <= 0) {
34
+ cumulativePercents[i] = 0;
35
+ }
36
+ else if (i === counts.length - 1) {
37
+ cumulativePercents[i] = 100;
38
+ }
39
+ else {
40
+ cumulativePercents[i] = (running / total) * 100;
41
+ }
42
+ }
43
+ return { labels, counts, cumulativePercents };
44
+ };
45
+ const formatBinBound = (value) => {
46
+ if (!Number.isFinite(value))
47
+ return String(value);
48
+ const abs = Math.abs(value);
49
+ if (abs >= 1000 || abs < 0.01)
50
+ return value.toExponential(2);
51
+ return Number.parseFloat(value.toFixed(2)).toString();
52
+ };
53
+ /**
54
+ * Bin a set of numeric values into `binCount` equal-width bins.
55
+ * When `binCount` is omitted, uses Sturges' rule: ceil(log2(n) + 1).
56
+ */
57
+ export const binHistogram = (values, binCount) => {
58
+ const numeric = values.filter((v) => typeof v === "number" && Number.isFinite(v));
59
+ if (numeric.length === 0) {
60
+ return { labels: [], counts: [] };
61
+ }
62
+ let min = numeric[0];
63
+ let max = numeric[0];
64
+ for (const v of numeric) {
65
+ if (v < min)
66
+ min = v;
67
+ if (v > max)
68
+ max = v;
69
+ }
70
+ const sturges = Math.max(1, Math.ceil(Math.log2(numeric.length) + 1));
71
+ const bins = typeof binCount === "number" && binCount > 0
72
+ ? Math.floor(binCount)
73
+ : sturges;
74
+ if (min === max) {
75
+ return { labels: [formatBinBound(min)], counts: [numeric.length] };
76
+ }
77
+ const width = (max - min) / bins;
78
+ const counts = new Array(bins).fill(0);
79
+ for (const v of numeric) {
80
+ let idx = Math.floor((v - min) / width);
81
+ if (idx >= bins)
82
+ idx = bins - 1;
83
+ if (idx < 0)
84
+ idx = 0;
85
+ counts[idx]++;
86
+ }
87
+ const labels = new Array(bins);
88
+ for (let i = 0; i < bins; i++) {
89
+ const lo = min + width * i;
90
+ const hi = min + width * (i + 1);
91
+ const closing = i === bins - 1 ? "]" : ")";
92
+ labels[i] = `[${formatBinBound(lo)}, ${formatBinBound(hi)}${closing}`;
93
+ }
94
+ return { labels, counts };
95
+ };