@svgrid/grid 3.0.2 → 3.0.4

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/README.md +2 -1
  2. package/dist/GridMenus.svelte +44 -2
  3. package/dist/SvChartMenu.svelte +69 -0
  4. package/dist/SvChartMenu.svelte.d.ts +11 -0
  5. package/dist/SvChartPanes.svelte +113 -0
  6. package/dist/SvChartPanes.svelte.d.ts +20 -0
  7. package/dist/SvGrid.controller.svelte.d.ts +110 -39
  8. package/dist/SvGrid.controller.svelte.js +655 -200
  9. package/dist/SvGrid.css +105 -2
  10. package/dist/SvGrid.svelte +386 -514
  11. package/dist/SvGrid.types.d.ts +210 -9
  12. package/dist/SvGridCellEditor.svelte +17 -0
  13. package/dist/SvGridChart.svelte +2257 -329
  14. package/dist/SvGridChart.svelte.d.ts +6 -106
  15. package/dist/SvGridChart.types.d.ts +338 -0
  16. package/dist/SvGridChart.types.js +1 -0
  17. package/dist/SvGridChartBuilder.svelte +323 -0
  18. package/dist/SvGridChartBuilder.svelte.d.ts +34 -0
  19. package/dist/SvGridChartPanel.svelte +221 -275
  20. package/dist/SvGridChartPickers.svelte +417 -0
  21. package/dist/SvGridChartPickers.svelte.d.ts +70 -0
  22. package/dist/SvGridChartView.svelte +13 -14
  23. package/dist/SvModal.svelte +7 -1
  24. package/dist/SvModal.svelte.d.ts +5 -0
  25. package/dist/ai.d.ts +40 -8
  26. package/dist/ai.js +135 -24
  27. package/dist/aria.d.ts +11 -0
  28. package/dist/build-api.js +108 -22
  29. package/dist/cdn/GridMenus-7kbpnnBW.js +635 -0
  30. package/dist/cdn/GridMenus-BL7ZgQvU.js +636 -0
  31. package/dist/cdn/SvChartMenu-Bl6PBbkT.js +58 -0
  32. package/dist/cdn/SvChartMenu-FBSMINA6.js +59 -0
  33. package/dist/cdn/{SvDateRangeInput-DMLKmGEc.js → SvDateRangeInput-BeU_TkbQ.js} +15 -14
  34. package/dist/cdn/{SvDateRangeInput-CaOuMs8O.js → SvDateRangeInput-yX8vzleW.js} +38 -37
  35. package/dist/cdn/{SvDateTimePicker-sonaH0oh.js → SvDateTimePicker-DQwt4UAs.js} +139 -180
  36. package/dist/cdn/SvDateTimePicker-vNU6bZ-q.js +775 -0
  37. package/dist/cdn/{SvGridCellEditor-CjEJMJKc.js → SvGridCellEditor-B1p-vCK5.js} +188 -178
  38. package/dist/cdn/{SvGridCellEditor-BWnTi2N7.js → SvGridCellEditor-D_0q4xAu.js} +98 -88
  39. package/dist/cdn/SvGridChart-C3EWAZaM.js +3574 -0
  40. package/dist/cdn/SvGridChart-DzLkSwCH.js +3572 -0
  41. package/dist/cdn/SvGridChartBuilder-BE2T1ykB.js +783 -0
  42. package/dist/cdn/SvGridChartBuilder-CfII62sZ.js +784 -0
  43. package/dist/cdn/SvGridChartPanel-CCX5_Wgd.js +675 -0
  44. package/dist/cdn/SvGridChartPanel-xf4sWVTo.js +699 -0
  45. package/dist/cdn/SvGridChartView-CfuXmY5I.js +48 -0
  46. package/dist/cdn/SvGridChartView-DYwabQWj.js +47 -0
  47. package/dist/cdn/SvMenuList-CLN8OuIK.js +452 -0
  48. package/dist/cdn/SvMenuList-DkGhNKjf.js +451 -0
  49. package/dist/cdn/SvModal-BAE-pjZX.js +396 -0
  50. package/dist/cdn/SvModal-CIZWCcad.js +397 -0
  51. package/dist/cdn/chart-Dz7SqMXH.js +4341 -0
  52. package/dist/cdn/chart-export-BXqueeWJ.js +135 -0
  53. package/dist/cdn/chart-export-pdf-DONFUnNY.js +110 -0
  54. package/dist/cdn/chart-panel-messages-CUbf2R4i.js +1052 -0
  55. package/dist/cdn/chart-panel-messages-CmNrMdsr.js +1053 -0
  56. package/dist/cdn/chart-summary-BJW_tg_X.js +138 -0
  57. package/dist/cdn/chart-trend-CaN9mDEV.js +50 -0
  58. package/dist/cdn/chart-validate-HcOVFAfJ.js +188 -0
  59. package/dist/cdn/{column-resize-DsfNXMom.js → column-resize-DpmOLfRp.js} +3 -3
  60. package/dist/cdn/{date-format-BnnHlqGw.js → date-format-CtquV-p3.js} +583 -603
  61. package/dist/cdn/{date-format-BNii4zeD.js → date-format-D6KFzU_W.js} +347 -367
  62. package/dist/cdn/dismissable-DAHetSNk.js +44 -0
  63. package/dist/cdn/editor-contract-LXgQJAgd.js +22 -0
  64. package/dist/cdn/focus-trap-BBOQYBma.js +65 -0
  65. package/dist/cdn/{row-resize-BRcimkUT.js → row-resize-niQCp040.js} +44 -36
  66. package/dist/cdn/{src-DNbz94Ct.js → src-BKhZ6eXd.js} +10227 -9787
  67. package/dist/cdn/{src-BJ2opd0j.js → src-V1uu8iE9.js} +6568 -6128
  68. package/dist/cdn/svgrid.js +26 -15
  69. package/dist/cdn/svgrid.svelte-external.js +26 -15
  70. package/dist/chart-axes.d.ts +53 -0
  71. package/dist/chart-axes.js +351 -0
  72. package/dist/chart-cartesian.d.ts +88 -0
  73. package/dist/chart-cartesian.js +1862 -0
  74. package/dist/chart-decimate.d.ts +51 -0
  75. package/dist/chart-decimate.js +199 -0
  76. package/dist/chart-export-pdf.d.ts +47 -0
  77. package/dist/chart-export-pdf.js +187 -0
  78. package/dist/chart-export.d.ts +2 -1
  79. package/dist/chart-export.js +18 -3
  80. package/dist/chart-financial.d.ts +91 -0
  81. package/dist/chart-financial.js +175 -0
  82. package/dist/chart-flow.d.ts +10 -0
  83. package/dist/chart-flow.js +298 -0
  84. package/dist/chart-format.d.ts +18 -0
  85. package/dist/chart-format.js +65 -0
  86. package/dist/chart-grid.d.ts +8 -0
  87. package/dist/chart-grid.js +190 -0
  88. package/dist/chart-hierarchy.d.ts +17 -0
  89. package/dist/chart-hierarchy.js +146 -0
  90. package/dist/chart-indicators.d.ts +115 -0
  91. package/dist/chart-indicators.js +389 -0
  92. package/dist/chart-messages.d.ts +95 -0
  93. package/dist/chart-messages.js +92 -0
  94. package/dist/chart-motion.d.ts +16 -0
  95. package/dist/chart-motion.js +141 -0
  96. package/dist/chart-panel-messages.d.ts +265 -0
  97. package/dist/chart-panel-messages.js +285 -0
  98. package/dist/chart-pivot.d.ts +87 -0
  99. package/dist/chart-pivot.js +156 -0
  100. package/dist/chart-polar.d.ts +25 -0
  101. package/dist/chart-polar.js +700 -0
  102. package/dist/chart-samples.d.ts +16 -0
  103. package/dist/chart-samples.js +153 -0
  104. package/dist/chart-scale.d.ts +113 -0
  105. package/dist/chart-scale.js +405 -0
  106. package/dist/chart-stats.d.ts +111 -0
  107. package/dist/chart-stats.js +419 -0
  108. package/dist/chart-stream.d.ts +45 -0
  109. package/dist/chart-stream.js +91 -0
  110. package/dist/chart-summary.d.ts +27 -0
  111. package/dist/chart-summary.js +205 -0
  112. package/dist/chart-sync.svelte.d.ts +32 -0
  113. package/dist/chart-sync.svelte.js +23 -0
  114. package/dist/chart-table.d.ts +35 -0
  115. package/dist/chart-table.js +165 -0
  116. package/dist/chart-trend.d.ts +19 -0
  117. package/dist/chart-trend.js +61 -0
  118. package/dist/chart-types.d.ts +1456 -0
  119. package/dist/chart-types.js +6 -0
  120. package/dist/chart-validate.d.ts +43 -0
  121. package/dist/chart-validate.js +272 -0
  122. package/dist/chart-zoom.d.ts +40 -0
  123. package/dist/chart-zoom.js +145 -0
  124. package/dist/chart.d.ts +186 -865
  125. package/dist/chart.js +460 -2204
  126. package/dist/clipboard.d.ts +1 -0
  127. package/dist/clipboard.js +137 -18
  128. package/dist/column-resize.d.ts +3 -0
  129. package/dist/column-resize.js +4 -1
  130. package/dist/columns.js +3 -0
  131. package/dist/command-context.d.ts +19 -0
  132. package/dist/command-context.js +116 -0
  133. package/dist/core.d.ts +7 -0
  134. package/dist/createPopoverSelect.svelte.d.ts +2 -2
  135. package/dist/editing.d.ts +2 -8
  136. package/dist/editing.js +262 -33
  137. package/dist/fill-patterns.d.ts +0 -5
  138. package/dist/fill-patterns.js +56 -2
  139. package/dist/grid-messages.d.ts +10 -3
  140. package/dist/grid-messages.js +5 -0
  141. package/dist/history.d.ts +83 -0
  142. package/dist/history.js +133 -0
  143. package/dist/index.d.ts +24 -6
  144. package/dist/index.js +20 -2
  145. package/dist/keyboard-handlers.js +88 -31
  146. package/dist/keyboard.d.ts +55 -0
  147. package/dist/keyboard.js +132 -0
  148. package/dist/menus.d.ts +3 -1
  149. package/dist/menus.js +7 -2
  150. package/dist/merges.d.ts +97 -0
  151. package/dist/merges.js +147 -0
  152. package/dist/row-resize.d.ts +3 -0
  153. package/dist/row-resize.js +14 -0
  154. package/dist/scroll-sync.d.ts +1 -0
  155. package/dist/scroll-sync.js +24 -0
  156. package/dist/selection.d.ts +16 -0
  157. package/dist/selection.js +108 -16
  158. package/dist/shortcut-registry.d.ts +145 -0
  159. package/dist/shortcut-registry.js +44 -0
  160. package/dist/svgrid-wrapper.types.d.ts +213 -22
  161. package/dist/virtualization/virtualizer.js +38 -17
  162. package/package.json +6 -1
  163. package/src/GridMenus.svelte +44 -2
  164. package/src/SvChartMenu.svelte +69 -0
  165. package/src/SvChartPanes.svelte +113 -0
  166. package/src/SvChartPanes.test.ts +66 -0
  167. package/src/SvGrid.controller.svelte.ts +629 -172
  168. package/src/SvGrid.css +105 -2
  169. package/src/SvGrid.svelte +386 -514
  170. package/src/SvGrid.types.ts +188 -8
  171. package/src/SvGridCellEditor.svelte +17 -0
  172. package/src/SvGridChart.svelte +2257 -329
  173. package/src/SvGridChart.test.ts +1419 -1
  174. package/src/SvGridChart.types.ts +336 -0
  175. package/src/SvGridChartBuilder.svelte +323 -0
  176. package/src/SvGridChartPanel.svelte +221 -275
  177. package/src/SvGridChartPickers.svelte +417 -0
  178. package/src/SvGridChartView.svelte +13 -14
  179. package/src/SvModal.svelte +7 -1
  180. package/src/SvModal.test.ts +9 -0
  181. package/src/ai.test.ts +57 -2
  182. package/src/ai.ts +166 -22
  183. package/src/aria.d.ts +11 -0
  184. package/src/build-api.coverage.test.ts +184 -0
  185. package/src/build-api.ts +107 -18
  186. package/src/chart-axes.test.ts +573 -0
  187. package/src/chart-axes.ts +342 -0
  188. package/src/chart-cartesian.ts +1863 -0
  189. package/src/chart-decimate.test.ts +249 -0
  190. package/src/chart-decimate.ts +186 -0
  191. package/src/chart-export-pdf.test.ts +91 -0
  192. package/src/chart-export-pdf.ts +207 -0
  193. package/src/chart-export.test.ts +14 -0
  194. package/src/chart-export.ts +13 -3
  195. package/src/chart-financial.ts +208 -0
  196. package/src/chart-flow.ts +286 -0
  197. package/src/chart-format.ts +67 -0
  198. package/src/chart-grid.ts +179 -0
  199. package/src/chart-hierarchy.ts +135 -0
  200. package/src/chart-indicators.test.ts +443 -0
  201. package/src/chart-indicators.ts +384 -0
  202. package/src/chart-messages.test.ts +21 -0
  203. package/src/chart-messages.ts +184 -0
  204. package/src/chart-motion.test.ts +71 -0
  205. package/src/chart-motion.ts +148 -0
  206. package/src/chart-panel-messages.test.ts +29 -0
  207. package/src/chart-panel-messages.ts +550 -0
  208. package/src/chart-pivot.test.ts +54 -0
  209. package/src/chart-pivot.ts +225 -0
  210. package/src/chart-polar.ts +674 -0
  211. package/src/chart-samples.ts +106 -0
  212. package/src/chart-scale.ts +394 -0
  213. package/src/chart-series-types.test.ts +714 -0
  214. package/src/chart-stats.ts +409 -0
  215. package/src/chart-stream.test.ts +75 -0
  216. package/src/chart-stream.ts +114 -0
  217. package/src/chart-summary.test.ts +90 -0
  218. package/src/chart-summary.ts +197 -0
  219. package/src/chart-sync.svelte.ts +41 -0
  220. package/src/chart-table.test.ts +76 -0
  221. package/src/chart-table.ts +178 -0
  222. package/src/chart-trend.ts +47 -0
  223. package/src/chart-types.ts +1309 -0
  224. package/src/chart-validate.test.ts +100 -0
  225. package/src/chart-validate.ts +247 -0
  226. package/src/chart-zoom.test.ts +137 -0
  227. package/src/chart-zoom.ts +124 -0
  228. package/src/chart.coverage.test.ts +81 -0
  229. package/src/chart.test.ts +56 -3
  230. package/src/chart.ts +557 -2784
  231. package/src/clipboard.test.ts +151 -0
  232. package/src/clipboard.ts +145 -16
  233. package/src/column-resize.ts +7 -1
  234. package/src/columns.ts +2 -0
  235. package/src/command-context.test.ts +200 -0
  236. package/src/command-context.ts +137 -0
  237. package/src/core.ts +7 -0
  238. package/src/editing.test.ts +281 -3
  239. package/src/editing.ts +256 -38
  240. package/src/fill-patterns.test.ts +35 -0
  241. package/src/fill-patterns.ts +56 -2
  242. package/src/grid-messages.ts +16 -3
  243. package/src/history.test.ts +196 -0
  244. package/src/history.ts +162 -0
  245. package/src/icon-seam.test.ts +3 -2
  246. package/src/index.ts +150 -2
  247. package/src/keyboard-handlers.coverage.test.ts +126 -6
  248. package/src/keyboard-handlers.ts +91 -30
  249. package/src/keyboard-shortcuts.seam.test.ts +200 -0
  250. package/src/keyboard.test.ts +124 -1
  251. package/src/keyboard.ts +142 -0
  252. package/src/menus.test.ts +26 -0
  253. package/src/menus.ts +10 -3
  254. package/src/merges.test.ts +112 -0
  255. package/src/merges.ts +168 -0
  256. package/src/pivot.grid.test.ts +40 -5
  257. package/src/row-resize.test.ts +44 -0
  258. package/src/row-resize.ts +17 -0
  259. package/src/scroll-sync.test.ts +42 -1
  260. package/src/scroll-sync.ts +25 -0
  261. package/src/selection.multi-range.test.ts +34 -0
  262. package/src/selection.test.ts +30 -2
  263. package/src/selection.ts +111 -19
  264. package/src/shortcut-registry.test.ts +97 -0
  265. package/src/shortcut-registry.ts +181 -0
  266. package/src/svgrid-wrapper.types.ts +184 -13
  267. package/src/svgrid.behavior.test.ts +135 -0
  268. package/src/svgrid.charting.test.ts +513 -3
  269. package/src/svgrid.context-menu.test.ts +31 -0
  270. package/src/svgrid.new-features.wrapper.test.ts +10 -3
  271. package/src/virtualization/virtualizer.ts +39 -17
  272. package/dist/cdn/GridMenus-DyFA-QxA.js +0 -602
  273. package/dist/cdn/GridMenus-M2XP9t0_.js +0 -603
  274. package/dist/cdn/SvDateTimePicker-CCbDZNZB.js +0 -816
  275. package/dist/cdn/SvGridChart-DAb9RbRs.js +0 -1480
  276. package/dist/cdn/SvGridChart-Yl-IXZFO.js +0 -1481
  277. package/dist/cdn/SvGridChartPanel-DayQpDsR.js +0 -834
  278. package/dist/cdn/SvGridChartPanel-aqf8n-vJ.js +0 -810
  279. package/dist/cdn/SvGridChartView-CxfakxR0.js +0 -56
  280. package/dist/cdn/SvGridChartView-Dm_YEx9L.js +0 -55
  281. package/dist/cdn/chart-CKJRCPOk.js +0 -1652
@@ -0,0 +1,351 @@
1
+ import { axisScale, niceLogScale, project, errorSpan } from './chart-scale.js';
2
+ /**
3
+ * @internal Which mark a series draws.
4
+ *
5
+ * A series counts as a candle when it says so OR when it carries `ohlc` data,
6
+ * so a spec typed `'candlestick'` can still hold a plain volume bar series
7
+ * beside the prices. Boxes work the same way. Anything unrecognised falls back
8
+ * to `'bar'`, which is what pie and scatter have always relied on.
9
+ */
10
+ export function kindOf(s, specType) {
11
+ const t = s.type ?? specType;
12
+ if (t === 'candlestick' || t === 'ohlc' || s.ohlc)
13
+ return 'candle';
14
+ if (t === 'boxplot' || s.boxes)
15
+ return 'box';
16
+ if (t === 'line' || t === 'area')
17
+ return t;
18
+ // A stream is a stacked area on a wiggle baseline; the offset is the spec's.
19
+ if (t === 'stream')
20
+ return 'area';
21
+ if (t === 'range-bar' || t === 'range-area' || t === 'lollipop' || t === 'dumbbell')
22
+ return t;
23
+ // A per-series scatter on a category axis: markers with no line.
24
+ if (t === 'scatter' && s.type === 'scatter')
25
+ return 'scatter';
26
+ return 'bar';
27
+ }
28
+ /** @internal Data domain for one axis, honoring stacking of its bar/area series.
29
+ * When `isLog` is true, non-positive values are discarded (log undefined)
30
+ * and the domain is rounded to decade boundaries instead of nice steps. */
31
+ export function axisDomain(list, categories, stacked, extra = [], isLog = false, axis = {}) {
32
+ let dMin = Infinity;
33
+ let dMax = -Infinity;
34
+ const note = (v) => {
35
+ if (!Number.isFinite(v))
36
+ return;
37
+ if (isLog && v <= 0)
38
+ return;
39
+ if (v < dMin)
40
+ dMin = v;
41
+ if (v > dMax)
42
+ dMax = v;
43
+ };
44
+ for (const v of extra)
45
+ note(v);
46
+ const stackable = list.filter((s) => s.kind === 'bar' || s.kind === 'area');
47
+ const lines = list.filter((s) => s.kind === 'line' || s.kind === 'scatter' || s.kind === 'lollipop');
48
+ // Range kinds have no zero baseline: a floating bar from 40 to 60 wants an
49
+ // axis that starts near 40, not at 0, so they are noted but never stacked.
50
+ for (const s of list) {
51
+ if (s.kind !== 'range-bar' && s.kind !== 'range-area' && s.kind !== 'dumbbell')
52
+ continue;
53
+ for (const v of s.values)
54
+ note(v);
55
+ for (const v of s.lowValues ?? [])
56
+ note(v);
57
+ }
58
+ // Totals per stack. Everything shares one stack when `stacked`; otherwise a
59
+ // bar or area series with a `stack` name joins that stack and every other
60
+ // series stands alone, so a lone series contributes its own values. Bars
61
+ // and areas never share a pile even under one name: they are drawn apart.
62
+ const stacks = new Map();
63
+ stackable.forEach((s, k) => {
64
+ const key = s.stack ? `${s.kind}:${s.stack}` : stacked ? s.kind : `#${k}`;
65
+ const list = stacks.get(key);
66
+ if (list)
67
+ list.push(s);
68
+ else
69
+ stacks.set(key, [s]);
70
+ });
71
+ for (const members of stacks.values()) {
72
+ if (members.length === 1) {
73
+ for (const v of members[0].values)
74
+ note(v);
75
+ continue;
76
+ }
77
+ for (let i = 0; i < categories.length; i += 1) {
78
+ let pos = 0;
79
+ let neg = 0;
80
+ for (const s of members) {
81
+ const v = s.values[i] ?? 0;
82
+ if (v >= 0)
83
+ pos += v;
84
+ else
85
+ neg += v;
86
+ }
87
+ note(pos);
88
+ note(neg);
89
+ }
90
+ }
91
+ for (const s of lines)
92
+ for (const v of s.values)
93
+ note(v);
94
+ // Candles: note the HIGH and the LOW, not `values` (the closes), or every
95
+ // wick clips at the body. Note also that candles are deliberately absent
96
+ // from `stackable`, so the zero-baseline rule below does not fire for them:
97
+ // a price series running 180 to 195 keeps a readable domain instead of being
98
+ // flattened against zero.
99
+ for (const s of list) {
100
+ if (s.kind !== 'candle')
101
+ continue;
102
+ for (const k of s.ohlc ?? []) {
103
+ if (!k)
104
+ continue;
105
+ note(k.h);
106
+ note(k.l);
107
+ }
108
+ }
109
+ // Boxes: the whisker ends and any outlier, for the same reason - `values`
110
+ // holds the medians, so a domain built from those alone would clip half of
111
+ // every box. Boxes are also absent from `stackable`, so a sample that never
112
+ // goes near zero keeps a readable domain.
113
+ for (const s of list) {
114
+ if (s.kind !== 'box')
115
+ continue;
116
+ for (const b of s.boxes ?? []) {
117
+ if (!b)
118
+ continue;
119
+ note(b.min);
120
+ note(b.max);
121
+ for (const o of b.outliers ?? [])
122
+ note(o);
123
+ }
124
+ }
125
+ // Error bars extend past their own mark, so a whisker that leaves the plot is
126
+ // the same defect as a clipped candle wick.
127
+ for (const s of list) {
128
+ if (!s.errors)
129
+ continue;
130
+ s.errors.forEach((e, i) => {
131
+ const span = errorSpan(e, s.values[i] ?? 0);
132
+ if (!span)
133
+ return;
134
+ note(span.lo);
135
+ note(span.hi);
136
+ });
137
+ }
138
+ if (dMin === Infinity) {
139
+ dMin = isLog ? 1 : 0;
140
+ dMax = isLog ? 10 : 1;
141
+ }
142
+ // Bar / area charts read against a zero baseline, so always include 0
143
+ // - but only on linear axes (0 is invalid in log). Lollipops grow from the
144
+ // axis the way bars do.
145
+ if ((stackable.length || list.some((s) => s.kind === 'lollipop')) && !isLog) {
146
+ dMin = Math.min(dMin, 0);
147
+ dMax = Math.max(dMax, 0);
148
+ }
149
+ if (isLog) {
150
+ const ls = niceLogScale(dMin, dMax);
151
+ // A pinned end on a log axis has to be positive to mean anything.
152
+ const lo = axis.min != null && axis.min > 0 ? axis.min : ls.min;
153
+ const hi = axis.max != null && axis.max > lo ? axis.max : ls.max;
154
+ if (lo === ls.min && hi === ls.max)
155
+ return ls;
156
+ return { min: lo, max: hi, step: ls.step, ticks: ls.ticks.filter((t) => t >= lo && t <= hi) };
157
+ }
158
+ return axisScale(dMin, dMax, axis);
159
+ }
160
+ /**
161
+ * Turn a laid-out chart's axes into functions, so a caller can draw its own
162
+ * marks in the same coordinates the built-in ones use.
163
+ *
164
+ * This is the custom-series seam. Rather than a registry of mark types, the
165
+ * chart hands over its geometry and its scales and lets the caller render
166
+ * whatever SVG it likes into the plot - which is the Svelte-shaped answer, and
167
+ * means a custom mark is ordinary markup rather than a plugin.
168
+ *
169
+ * Deriving the scale from `geo.axes` matters: the domain a chart drew against
170
+ * is the NICE-ROUNDED one, stretched to include zero for bar charts and any
171
+ * reference lines. Recomputing it from the data outside would land custom marks
172
+ * a few pixels off the built-in ones, in a way that looks like a rendering bug.
173
+ *
174
+ * Returns `null` for a chart with no cartesian axes (pie, gauge, treemap,
175
+ * sankey, calendar, radar, funnel), where plot coordinates mean nothing.
176
+ */
177
+ export function chartScales(geo) {
178
+ const a = geo.axes;
179
+ if (!a)
180
+ return null;
181
+ const { x: px, y: py, w: pw, h: ph } = geo.plot;
182
+ const yFor = (dom) => (value) => {
183
+ let t = project(value, dom.min, dom.max, dom.log);
184
+ if (t === null)
185
+ return Number.NaN;
186
+ if (dom.reversed)
187
+ t = 1 - t;
188
+ return py + ph - t * ph;
189
+ };
190
+ const left = yFor(a.y);
191
+ const right = a.y2 ? yFor(a.y2) : left;
192
+ const invFor = (dom) => (y) => {
193
+ let t = ph === 0 ? 0 : (py + ph - y) / ph;
194
+ if (dom.reversed)
195
+ t = 1 - t;
196
+ if (!dom.log)
197
+ return dom.min + t * (dom.max - dom.min);
198
+ const lo = Math.log10(dom.min);
199
+ return 10 ** (lo + t * (Math.log10(dom.max) - lo));
200
+ };
201
+ // A continuous x axis (time / number) places by value; a category axis by
202
+ // index. `xOf(i)` on a continuous axis goes through the stored per-category
203
+ // values, so a custom mark at "category 3" lands where the built-in one did
204
+ // even when the categories are unevenly spaced in time.
205
+ const cx = a.x;
206
+ const span = cx ? cx.max - cx.min || 1 : 1;
207
+ const logSpan = cx?.log ? Math.log10(cx.max) - Math.log10(cx.min) || 1 : 1;
208
+ const xOfNum = (v) => {
209
+ if (!cx || !Number.isFinite(v))
210
+ return Number.NaN;
211
+ if (cx.log && v <= 0)
212
+ return Number.NaN;
213
+ let t = cx.log ? (Math.log10(v) - Math.log10(cx.min)) / logSpan : (v - cx.min) / span;
214
+ if (cx.reversed)
215
+ t = 1 - t;
216
+ return px + t * pw;
217
+ };
218
+ const xOfIndex = (i) => {
219
+ if (cx?.values) {
220
+ const v = cx.values[Math.round(i)];
221
+ if (v != null && Number.isFinite(v))
222
+ return xOfNum(v);
223
+ }
224
+ const t = px + a.slot * i + a.slot / 2;
225
+ return a.xReversed ? px + pw - (t - px) : t;
226
+ };
227
+ const xOfValue = (value) => {
228
+ if (cx) {
229
+ if (value instanceof Date)
230
+ return xOfNum(value.getTime());
231
+ if (typeof value === 'number')
232
+ return xOfNum(value);
233
+ const n = cx.type === 'time' ? Date.parse(value) : Number(value);
234
+ if (Number.isFinite(n))
235
+ return xOfNum(n);
236
+ // A label that did not parse can still be one of the categories.
237
+ const i = geo.xTicks.findIndex((t) => t.label === value);
238
+ return i >= 0 ? geo.xTicks[i].x : Number.NaN;
239
+ }
240
+ if (typeof value === 'number')
241
+ return xOfIndex(value);
242
+ const label = value instanceof Date ? value.toISOString() : value;
243
+ const i = a.labels ? a.labels.indexOf(label) : -1;
244
+ return i >= 0 ? xOfIndex(i) : Number.NaN;
245
+ };
246
+ const xInvertValue = (x) => {
247
+ let t = pw === 0 ? 0 : (x - px) / pw;
248
+ if (cx ? cx.reversed : a.xReversed)
249
+ t = 1 - t;
250
+ if (cx?.log)
251
+ return Math.pow(10, Math.log10(cx.min) + t * logSpan);
252
+ if (cx)
253
+ return cx.min + t * span;
254
+ return Math.max(0, Math.min(a.count - 1, t * a.count - 0.5));
255
+ };
256
+ return {
257
+ xOf: xOfIndex,
258
+ yOf: (value, axis) => (axis === 'right' ? right(value) : left(value)),
259
+ xInvert: (x) => {
260
+ if (cx?.values) {
261
+ // Nearest category by value, which is what a hover on a time axis means.
262
+ const v = xInvertValue(x);
263
+ let best = 0;
264
+ let bestD = Infinity;
265
+ cx.values.forEach((cv, i) => {
266
+ const d = Math.abs(cv - v);
267
+ if (d < bestD) {
268
+ bestD = d;
269
+ best = i;
270
+ }
271
+ });
272
+ return best;
273
+ }
274
+ const raw = Math.floor((x - px) / (a.slot || 1));
275
+ const i = a.xReversed ? a.count - 1 - raw : raw;
276
+ return Math.max(0, Math.min(a.count - 1, i));
277
+ },
278
+ yInvert: (y, axis) => (axis === 'right' && a.y2 ? invFor(a.y2)(y) : invFor(a.y)(y)),
279
+ xOfValue,
280
+ xInvertValue,
281
+ };
282
+ }
283
+ /**
284
+ * @internal Fold the flat spec shortcuts (`xType`, `yScale`, `xAxisTitle`, ...)
285
+ * into three axis objects with every default filled in, so a layout reads one
286
+ * shape and never has to know which spelling the caller used.
287
+ */
288
+ export function resolveAxes(spec) {
289
+ const x = spec.xAxis ?? {};
290
+ const y = spec.yAxis ?? {};
291
+ const y2 = spec.y2Axis ?? {};
292
+ const fill = (a, defaults) => ({
293
+ ...a,
294
+ type: a.type ?? defaults.type,
295
+ scale: a.scale ?? defaults.scale,
296
+ title: a.title ?? defaults.title,
297
+ gridLines: a.gridLines ?? defaults.gridLines,
298
+ labels: a.labels !== false,
299
+ nice: a.nice !== false,
300
+ reversed: a.reversed === true,
301
+ });
302
+ return {
303
+ x: fill(x, { type: spec.xType ?? 'category', scale: 'linear', title: spec.xAxisTitle, gridLines: false }),
304
+ y: fill(y, { type: 'category', scale: spec.yScale ?? 'linear', title: spec.yAxisTitle, gridLines: true }),
305
+ y2: fill(y2, { type: 'category', scale: spec.y2Scale ?? 'linear', title: spec.y2AxisTitle, gridLines: false }),
306
+ };
307
+ }
308
+ /** Vertical room the frame text takes, per line. */
309
+ const TITLE_H = 22;
310
+ const SUBTITLE_H = 16;
311
+ const CAPTION_H = 16;
312
+ /**
313
+ * @internal Place the title, subtitle and caption and measure the room they
314
+ * take. The title and subtitle sit centred above the plot; the caption sits
315
+ * left-aligned under it. A family adds `top` to its top pad and `bottom` to its
316
+ * bottom pad, and the text positions are already absolute.
317
+ */
318
+ export function chartFrame(spec, width, height) {
319
+ const title = (spec.title ?? '').trim();
320
+ const subtitle = (spec.subtitle ?? '').trim();
321
+ const caption = (spec.caption ?? '').trim();
322
+ let top = 0;
323
+ let titlePos = null;
324
+ let subtitlePos = null;
325
+ if (title) {
326
+ top += TITLE_H;
327
+ titlePos = { x: width / 2, y: top - 6, text: title };
328
+ }
329
+ if (subtitle) {
330
+ top += SUBTITLE_H;
331
+ subtitlePos = { x: width / 2, y: top - 4, text: subtitle };
332
+ }
333
+ if (top)
334
+ top += 4;
335
+ let bottom = 0;
336
+ let captionPos = null;
337
+ if (caption) {
338
+ bottom = CAPTION_H;
339
+ captionPos = { x: 8, y: height - 5, text: caption };
340
+ }
341
+ return { top, bottom, title: titlePos, subtitle: subtitlePos, caption: captionPos };
342
+ }
343
+ /**
344
+ * @internal Format one value-axis tick through the axis' own `formatter` or
345
+ * `format`, falling back to the spec's `valueFormat`.
346
+ */
347
+ export function axisTickLabel(value, index, axis, spec, fmt) {
348
+ if (axis.formatter)
349
+ return axis.formatter(value, index);
350
+ return fmt(value, axis.format ?? spec.valueFormat, spec);
351
+ }
@@ -0,0 +1,88 @@
1
+ /**
2
+ * Cartesian layouts: bar / line / area (combo, stacked, dual axis), horizontal
3
+ * bars and scatter. Every mark is positioned in SVG coordinates against the
4
+ * axes chart-axes.ts resolves.
5
+ */
6
+ import type { ChartDataLabelConfig, ChartGeometry, ChartMarkerShape, ChartSeries, LayoutCtx } from './chart-types.js';
7
+ type Step = NonNullable<ChartSeries['step']>;
8
+ /**
9
+ * Build an SVG path from a list of (x,y) pairs, optionally smoothed via
10
+ * monotone cubic interpolation (preserves local extrema - no overshoots).
11
+ * Breaks the path at `defined === false` gaps unless `connectNulls` is set,
12
+ * and draws steps instead of straight segments when `step` is set.
13
+ */
14
+ export declare function buildLinePath(pts: Array<{
15
+ x: number;
16
+ y: number;
17
+ defined: boolean;
18
+ }>, smooth: boolean, opts?: {
19
+ connectNulls?: boolean;
20
+ step?: Step;
21
+ }): string;
22
+ /**
23
+ * The baseline a stacked area sits on per category. `'zero'` is the axis;
24
+ * `'silhouette'` centres the total on zero; `'wiggle'` picks the baseline
25
+ * that minimises the weighted change of slope across the layers (Byron and
26
+ * Wattenberg's stream graph), which is what makes a stream read as flowing
27
+ * rather than as a stack. Gaps count as 0 so the stack stays continuous.
28
+ */
29
+ export declare function streamBaseline(layers: ReadonlyArray<ReadonlyArray<number>>, offset: 'zero' | 'wiggle' | 'silhouette'): number[];
30
+ /**
31
+ * The SVG path for a point marker of `shape` centred on (cx, cy) with
32
+ * half-size `size`. `'circle'` and `'none'` return `''`: circles draw as
33
+ * `<circle>` (which keeps every existing selector working), and `'none'` draws
34
+ * nothing. Exposed so a legend swatch or a custom mark can match the series.
35
+ */
36
+ export declare function markerPath(shape: ChartMarkerShape, cx: number, cy: number, size: number): string;
37
+ /** One positioned data label. `onBar` means it sits on a mark and should be
38
+ * painted in the contrast colour. */
39
+ export type ChartDataLabel = {
40
+ x: number;
41
+ y: number;
42
+ text: string;
43
+ anchor: 'start' | 'middle' | 'end';
44
+ onBar: boolean;
45
+ series: string;
46
+ /** Rotation in degrees about (x, y), from `dataLabels.rotation`. */
47
+ angle?: number;
48
+ /** A leader from the mark to a label that was pushed away from it. */
49
+ leader?: {
50
+ x1: number;
51
+ y1: number;
52
+ x2: number;
53
+ y2: number;
54
+ };
55
+ };
56
+ /**
57
+ * Place the data labels for a laid-out cartesian chart: one per bar and one
58
+ * per defined line point, positioned by `placement`, formatted by `formatter`
59
+ * (or `fmt`), and thinned so no label overlaps an earlier one when
60
+ * `hideOverlap` is on (the default).
61
+ *
62
+ * Pure, so the renderer derives it and a test can assert on it. Pie slices
63
+ * label themselves (a percentage at the centroid) and are not handled here.
64
+ */
65
+ export declare function layoutDataLabels(geo: ChartGeometry, cfg: ChartDataLabelConfig, fmt: (v: number, series?: string) => string, opts?: {
66
+ stacked?: boolean;
67
+ dense?: boolean;
68
+ share?: boolean;
69
+ }): ChartDataLabel[];
70
+ /** @internal Lay out the scatter family. Called by buildChart. */
71
+ export declare function layoutScatter(ctx: LayoutCtx): ChartGeometry;
72
+ /**
73
+ * @internal Lay out the horizontal bars family. Called by buildChart.
74
+ * Categories run down the left, bars grow rightward. Bars-only (no combo).
75
+ */
76
+ export declare function layoutHorizontalBars(ctx: LayoutCtx): ChartGeometry;
77
+ /** @internal Lay out the cartesian family. Called by buildChart. */
78
+ export declare function layoutCartesian(ctx: LayoutCtx): ChartGeometry;
79
+ /**
80
+ * @internal Lay out the bullet family. Called by buildChart.
81
+ *
82
+ * One row per category: qualitative ranges (poor / ok / good) as full-height
83
+ * bands, the measure as a bar at half height, the target as a tick. The
84
+ * first series is the measure; `targets` on it (or a second series) sets the
85
+ * ticks; `bulletRanges` (or `gaugeRanges`) draws the bands behind every row.
86
+ */
87
+ export declare function layoutBullet(ctx: LayoutCtx): ChartGeometry;
88
+ export {};