@k2b/ui 0.51.0 → 0.52.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 (206) hide show
  1. package/dist/base.css +3 -0
  2. package/dist/browser/actions/Toolbar.js +8 -2
  3. package/dist/browser/actions/Toolbar.js.map +1 -1
  4. package/dist/browser/charts/core/bar-labels.js +5 -0
  5. package/dist/browser/charts/core/bar-labels.js.map +1 -0
  6. package/dist/browser/charts/core/bubble.js +166 -0
  7. package/dist/browser/charts/core/bubble.js.map +1 -0
  8. package/dist/browser/charts/core/default-styles.js +83 -0
  9. package/dist/browser/charts/core/default-styles.js.map +1 -0
  10. package/dist/browser/charts/core/funnel-layout.js +79 -0
  11. package/dist/browser/charts/core/funnel-layout.js.map +1 -0
  12. package/dist/browser/charts/core/funnel.js +27 -0
  13. package/dist/browser/charts/core/funnel.js.map +1 -0
  14. package/dist/browser/charts/core/index.js +6 -1
  15. package/dist/browser/charts/core/index.js.map +1 -1
  16. package/dist/browser/charts/core/pareto.js +120 -0
  17. package/dist/browser/charts/core/pareto.js.map +1 -0
  18. package/dist/browser/charts/core/radar.js +101 -0
  19. package/dist/browser/charts/core/radar.js.map +1 -0
  20. package/dist/browser/charts/core/registry.js +11 -1
  21. package/dist/browser/charts/core/registry.js.map +1 -1
  22. package/dist/browser/charts/core/render-size.js +3 -1
  23. package/dist/browser/charts/core/render-size.js.map +1 -1
  24. package/dist/browser/charts/core/render.js +19 -0
  25. package/dist/browser/charts/core/render.js.map +1 -1
  26. package/dist/browser/charts/core/snapshot.js +1 -1
  27. package/dist/browser/charts/core/snapshot.js.map +1 -1
  28. package/dist/browser/charts/core/svg-barGauge.js +67 -0
  29. package/dist/browser/charts/core/svg-barGauge.js.map +1 -0
  30. package/dist/browser/charts/core/svg-boxplot.js +127 -0
  31. package/dist/browser/charts/core/svg-boxplot.js.map +1 -0
  32. package/dist/browser/charts/core/svg-common.js +1065 -0
  33. package/dist/browser/charts/core/svg-common.js.map +1 -0
  34. package/dist/browser/charts/core/svg-gauge.js +185 -0
  35. package/dist/browser/charts/core/svg-gauge.js.map +1 -0
  36. package/dist/browser/charts/core/svg-heatmap.js +80 -0
  37. package/dist/browser/charts/core/svg-heatmap.js.map +1 -0
  38. package/dist/browser/charts/core/svg-histogram.js +73 -0
  39. package/dist/browser/charts/core/svg-histogram.js.map +1 -0
  40. package/dist/browser/charts/core/svg-renderers.js +9 -1777
  41. package/dist/browser/charts/core/svg-renderers.js.map +1 -1
  42. package/dist/browser/charts/core/svg-scatter.js +114 -0
  43. package/dist/browser/charts/core/svg-scatter.js.map +1 -0
  44. package/dist/browser/charts/core/treemap.js +138 -0
  45. package/dist/browser/charts/core/treemap.js.map +1 -0
  46. package/dist/browser/charts/description.js.map +1 -1
  47. package/dist/browser/content/ChartRoot.js +21 -7
  48. package/dist/browser/content/ChartRoot.js.map +1 -1
  49. package/dist/browser/content/ChartSnapshotView.js +4 -1
  50. package/dist/browser/content/ChartSnapshotView.js.map +1 -1
  51. package/dist/browser/content/DataTable.js +186 -193
  52. package/dist/browser/content/DataTable.js.map +1 -1
  53. package/dist/browser/content/Lightbox.js +55 -23
  54. package/dist/browser/content/Lightbox.js.map +1 -1
  55. package/dist/browser/content/ThumbnailStrip.js +174 -0
  56. package/dist/browser/content/ThumbnailStrip.js.map +1 -0
  57. package/dist/browser/content/chart-bar.js +98 -0
  58. package/dist/browser/content/chart-bar.js.map +1 -1
  59. package/dist/browser/content/chart-inspection-core.js +28 -8
  60. package/dist/browser/content/chart-inspection-core.js.map +1 -1
  61. package/dist/browser/content/chart-message-catalog.js +18 -0
  62. package/dist/browser/content/chart-message-catalog.js.map +1 -1
  63. package/dist/browser/content/index.js +1 -0
  64. package/dist/browser/content/index.js.map +1 -1
  65. package/dist/browser/content/thumbnail-strip-messages.js +24 -0
  66. package/dist/browser/content/thumbnail-strip-messages.js.map +1 -0
  67. package/dist/browser/inputs/ChoiceInputs.js +132 -101
  68. package/dist/browser/inputs/ChoiceInputs.js.map +1 -1
  69. package/dist/browser/inputs/index.js +1 -0
  70. package/dist/browser/inputs/index.js.map +1 -1
  71. package/dist/browser/inputs/markdown/MarkdownSelectionToolbar.js +330 -0
  72. package/dist/browser/inputs/markdown/MarkdownSelectionToolbar.js.map +1 -0
  73. package/dist/browser/internal/field.js +96 -77
  74. package/dist/browser/internal/field.js.map +1 -1
  75. package/dist/browser/layout/AppWorkspace.js +7 -5
  76. package/dist/browser/layout/AppWorkspace.js.map +1 -1
  77. package/dist/browser/layout/PanelDialog.js +92 -68
  78. package/dist/browser/layout/PanelDialog.js.map +1 -1
  79. package/dist/browser/surfaces/StatCell.js +82 -70
  80. package/dist/browser/surfaces/StatCell.js.map +1 -1
  81. package/dist/browser/surfaces/stat-cell-messages.js +4 -2
  82. package/dist/browser/surfaces/stat-cell-messages.js.map +1 -1
  83. package/dist/browser/widgets/DashboardFrame.js +93 -88
  84. package/dist/browser/widgets/DashboardFrame.js.map +1 -1
  85. package/dist/ssr/actions/Toolbar.js +8 -2
  86. package/dist/ssr/actions/Toolbar.js.map +1 -1
  87. package/dist/ssr/charts/core/bar-labels.js +5 -0
  88. package/dist/ssr/charts/core/bar-labels.js.map +1 -0
  89. package/dist/ssr/charts/core/bubble.js +166 -0
  90. package/dist/ssr/charts/core/bubble.js.map +1 -0
  91. package/dist/ssr/charts/core/default-styles.js +83 -0
  92. package/dist/ssr/charts/core/default-styles.js.map +1 -0
  93. package/dist/ssr/charts/core/funnel-layout.js +79 -0
  94. package/dist/ssr/charts/core/funnel-layout.js.map +1 -0
  95. package/dist/ssr/charts/core/funnel.js +27 -0
  96. package/dist/ssr/charts/core/funnel.js.map +1 -0
  97. package/dist/ssr/charts/core/index.js +6 -1
  98. package/dist/ssr/charts/core/index.js.map +1 -1
  99. package/dist/ssr/charts/core/pareto.js +120 -0
  100. package/dist/ssr/charts/core/pareto.js.map +1 -0
  101. package/dist/ssr/charts/core/radar.js +101 -0
  102. package/dist/ssr/charts/core/radar.js.map +1 -0
  103. package/dist/ssr/charts/core/registry.js +11 -1
  104. package/dist/ssr/charts/core/registry.js.map +1 -1
  105. package/dist/ssr/charts/core/render-size.js +3 -1
  106. package/dist/ssr/charts/core/render-size.js.map +1 -1
  107. package/dist/ssr/charts/core/render.js +19 -0
  108. package/dist/ssr/charts/core/render.js.map +1 -1
  109. package/dist/ssr/charts/core/snapshot.js +1 -1
  110. package/dist/ssr/charts/core/snapshot.js.map +1 -1
  111. package/dist/ssr/charts/core/svg-barGauge.js +67 -0
  112. package/dist/ssr/charts/core/svg-barGauge.js.map +1 -0
  113. package/dist/ssr/charts/core/svg-boxplot.js +127 -0
  114. package/dist/ssr/charts/core/svg-boxplot.js.map +1 -0
  115. package/dist/ssr/charts/core/svg-common.js +1065 -0
  116. package/dist/ssr/charts/core/svg-common.js.map +1 -0
  117. package/dist/ssr/charts/core/svg-gauge.js +185 -0
  118. package/dist/ssr/charts/core/svg-gauge.js.map +1 -0
  119. package/dist/ssr/charts/core/svg-heatmap.js +80 -0
  120. package/dist/ssr/charts/core/svg-heatmap.js.map +1 -0
  121. package/dist/ssr/charts/core/svg-histogram.js +73 -0
  122. package/dist/ssr/charts/core/svg-histogram.js.map +1 -0
  123. package/dist/ssr/charts/core/svg-renderers.js +9 -1777
  124. package/dist/ssr/charts/core/svg-renderers.js.map +1 -1
  125. package/dist/ssr/charts/core/svg-scatter.js +114 -0
  126. package/dist/ssr/charts/core/svg-scatter.js.map +1 -0
  127. package/dist/ssr/charts/core/treemap.js +138 -0
  128. package/dist/ssr/charts/core/treemap.js.map +1 -0
  129. package/dist/ssr/charts/description.js.map +1 -1
  130. package/dist/ssr/content/ChartRoot.js +21 -7
  131. package/dist/ssr/content/ChartRoot.js.map +1 -1
  132. package/dist/ssr/content/ChartSnapshotView.js +1 -0
  133. package/dist/ssr/content/ChartSnapshotView.js.map +1 -1
  134. package/dist/ssr/content/DataTable.js +12 -16
  135. package/dist/ssr/content/DataTable.js.map +1 -1
  136. package/dist/ssr/content/Lightbox.js +32 -3
  137. package/dist/ssr/content/Lightbox.js.map +1 -1
  138. package/dist/ssr/content/ThumbnailStrip.js +103 -0
  139. package/dist/ssr/content/ThumbnailStrip.js.map +1 -0
  140. package/dist/ssr/content/chart-bar.js +98 -0
  141. package/dist/ssr/content/chart-bar.js.map +1 -1
  142. package/dist/ssr/content/chart-inspection-core.js +28 -8
  143. package/dist/ssr/content/chart-inspection-core.js.map +1 -1
  144. package/dist/ssr/content/chart-message-catalog.js +18 -0
  145. package/dist/ssr/content/chart-message-catalog.js.map +1 -1
  146. package/dist/ssr/content/index.js +1 -0
  147. package/dist/ssr/content/index.js.map +1 -1
  148. package/dist/ssr/content/thumbnail-strip-messages.js +24 -0
  149. package/dist/ssr/content/thumbnail-strip-messages.js.map +1 -0
  150. package/dist/ssr/inputs/ChoiceInputs.js +55 -40
  151. package/dist/ssr/inputs/ChoiceInputs.js.map +1 -1
  152. package/dist/ssr/inputs/index.js +1 -0
  153. package/dist/ssr/inputs/index.js.map +1 -1
  154. package/dist/ssr/inputs/markdown/MarkdownSelectionToolbar.js +293 -0
  155. package/dist/ssr/inputs/markdown/MarkdownSelectionToolbar.js.map +1 -0
  156. package/dist/ssr/internal/field.js +36 -24
  157. package/dist/ssr/internal/field.js.map +1 -1
  158. package/dist/ssr/layout/AppWorkspace.js +5 -1
  159. package/dist/ssr/layout/AppWorkspace.js.map +1 -1
  160. package/dist/ssr/layout/PanelDialog.js +40 -26
  161. package/dist/ssr/layout/PanelDialog.js.map +1 -1
  162. package/dist/ssr/surfaces/StatCell.js +29 -18
  163. package/dist/ssr/surfaces/StatCell.js.map +1 -1
  164. package/dist/ssr/surfaces/stat-cell-messages.js +4 -2
  165. package/dist/ssr/surfaces/stat-cell-messages.js.map +1 -1
  166. package/dist/ssr/widgets/DashboardFrame.js +6 -11
  167. package/dist/ssr/widgets/DashboardFrame.js.map +1 -1
  168. package/dist/styles.css +1 -1
  169. package/dist/types/actions/Toolbar.d.ts +2 -0
  170. package/dist/types/charts/core/bar-labels.d.ts +1 -0
  171. package/dist/types/charts/core/bubble.d.ts +66 -0
  172. package/dist/types/charts/core/default-styles.d.ts +1 -0
  173. package/dist/types/charts/core/funnel-layout.d.ts +43 -0
  174. package/dist/types/charts/core/funnel.d.ts +13 -0
  175. package/dist/types/charts/core/index.d.ts +6 -1
  176. package/dist/types/charts/core/pareto.d.ts +40 -0
  177. package/dist/types/charts/core/radar.d.ts +47 -0
  178. package/dist/types/charts/core/registry.d.ts +10 -0
  179. package/dist/types/charts/core/render-size.d.ts +1 -1
  180. package/dist/types/charts/core/svg-barGauge.d.ts +3 -0
  181. package/dist/types/charts/core/svg-boxplot.d.ts +18 -0
  182. package/dist/types/charts/core/svg-common.d.ts +612 -0
  183. package/dist/types/charts/core/svg-gauge.d.ts +6 -0
  184. package/dist/types/charts/core/svg-heatmap.d.ts +6 -0
  185. package/dist/types/charts/core/svg-histogram.d.ts +16 -0
  186. package/dist/types/charts/core/svg-renderers.d.ts +8 -612
  187. package/dist/types/charts/core/svg-scatter.d.ts +23 -0
  188. package/dist/types/charts/core/treemap.d.ts +45 -0
  189. package/dist/types/charts/description.d.ts +63 -0
  190. package/dist/types/content/ChartRoot.d.ts +1 -1
  191. package/dist/types/content/Lightbox.d.ts +2 -0
  192. package/dist/types/content/ThumbnailStrip.d.ts +29 -0
  193. package/dist/types/content/chart-bar.d.ts +38 -2
  194. package/dist/types/content/chart-inspection-core.d.ts +7 -0
  195. package/dist/types/content/chart-inspection.d.ts +1 -0
  196. package/dist/types/content/chart-message-catalog.d.ts +8 -0
  197. package/dist/types/content/index.d.ts +1 -0
  198. package/dist/types/content/thumbnail-strip-messages.d.ts +10 -0
  199. package/dist/types/inputs/ChoiceInputs.d.ts +2 -0
  200. package/dist/types/inputs/index.d.ts +2 -0
  201. package/dist/types/inputs/markdown/MarkdownSelectionToolbar.d.ts +19 -0
  202. package/dist/types/internal/field.d.ts +2 -0
  203. package/dist/types/layout/PanelDialog.d.ts +5 -0
  204. package/dist/types/surfaces/stat-cell-messages.d.ts +1 -0
  205. package/dist/types/widgets/DashboardFrame.d.ts +4 -1
  206. package/package.json +2 -2
@@ -0,0 +1,114 @@
1
+ // Ported SVG algorithm, kept verbatim; separate module for per-kind frame loading.
2
+
3
+ import { chartDatumSvg, collectAxisValues, collectSeriesLegend, computeAxisScale, computeDomain, computePlotArea, DEFAULT_HEIGHT, DEFAULT_MARKERS, DEFAULT_WIDTH, emptyChart, fmt, isOnAxis, linearRegression, mapRange, markerPath, normalizePadding, pickMapper, pointValues, renderErrorBars, renderHeader, renderLegend, renderReferenceLines, renderXAxisNumeric, renderYAxis, svgRoot } from "./svg-common";
4
+ /**
5
+ * Render a scatter plot. Each series produces a group of circles, one per
6
+ * data point, classed `stdlib-chart-series-N` so series colors stay
7
+ * consistent across charts.
8
+ *
9
+ * Non-finite x or y values are filtered out. An empty `series` (or one with
10
+ * no finite points) renders an empty-state SVG with a "no data" label.
11
+ *
12
+ * @example
13
+ * charts.scatter({ series: [{ data: [{x:1,y:2},{x:2,y:5},{x:3,y:3}] }] });
14
+ *
15
+ * @example
16
+ * charts.scatter({
17
+ * series: [
18
+ * { label: "Group A", data: [...] },
19
+ * { label: "Group B", data: [...] },
20
+ * ],
21
+ * xAxis: { label: "Time (s)" },
22
+ * yAxis: { label: "Value", format: v => `${v}k` },
23
+ * });
24
+ */
25
+ export const scatter = opts => {
26
+ const width = opts.width ?? DEFAULT_WIDTH;
27
+ const height = opts.height ?? DEFAULT_HEIGHT;
28
+ const padding = normalizePadding(opts.padding);
29
+ const header = renderHeader(width, opts.title, opts.subtitle);
30
+ const legendItems = collectSeriesLegend(opts);
31
+ const legendHeight = legendItems.length > 0 ? 20 : 0;
32
+ const finitePoints = opts.series.flatMap(s => s.data).filter(p => Number.isFinite(p.x) && Number.isFinite(p.y));
33
+ if (finitePoints.length === 0) return emptyChart(width, height, opts.className);
34
+ const area = computePlotArea(width, height, padding, !!opts.xAxis?.label, !!opts.yAxis?.label, header.height, legendHeight);
35
+ const xAxis = computeAxisScale(collectAxisValues(opts.series, "x"), opts.xAxis);
36
+ const yAxis = computeAxisScale(collectAxisValues(opts.series, "y"), opts.yAxis);
37
+ const xMap = pickMapper(xAxis.scale);
38
+ const yMap = pickMapper(yAxis.scale);
39
+
40
+ // Size dimension: if any point has a finite `size`, normalize across all
41
+ // points to the configured pixel range; points without size fall back to
42
+ // the midpoint so they remain visible.
43
+ const sizes = finitePoints.map(p => p.size).filter(s => typeof s === "number" && Number.isFinite(s));
44
+ const sizeRange = opts.sizeRange ?? [3, 12];
45
+ const useSizes = sizes.length > 0;
46
+ const sizeDomain = useSizes ? computeDomain(sizes) : [0, 1];
47
+ const fallbackR = (sizeRange[0] + sizeRange[1]) / 2;
48
+ const body = [];
49
+ body.push(renderYAxis(yAxis, area, opts.yAxis));
50
+ body.push(renderXAxisNumeric(xAxis, area, height - legendHeight, opts.xAxis));
51
+
52
+ // Error bars render under the points so the points sit on top of the bar
53
+ // intersections — easier to read.
54
+ body.push(renderErrorBars(opts.series, xAxis, yAxis, area));
55
+ opts.series.forEach((s, i) => {
56
+ const cls = `stdlib-chart-series-${i % 8}`;
57
+ const shape = s.marker ?? (opts.autoVariant ? DEFAULT_MARKERS[i % DEFAULT_MARKERS.length] : "circle");
58
+ const elements = s.data.map((p, index) => ({
59
+ p,
60
+ index
61
+ })).filter(({
62
+ p
63
+ }) => isOnAxis(xAxis, p.x) && isOnAxis(yAxis, p.y)).map(({
64
+ p,
65
+ index
66
+ }) => {
67
+ const cx = xMap(p.x, xAxis.domain, [area.x0, area.x1]);
68
+ const cy = yMap(p.y, yAxis.domain, [area.y1, area.y0]);
69
+ const r = useSizes ? typeof p.size === "number" && Number.isFinite(p.size) ? mapRange(p.size, sizeDomain, sizeRange) : fallbackR : 3;
70
+ const datum = {
71
+ index,
72
+ seriesIndex: i,
73
+ role: "point",
74
+ label: s.label,
75
+ anchor: [cx, cy],
76
+ values: pointValues(p, opts.xAxis, opts.yAxis)
77
+ };
78
+ if (shape === "circle") {
79
+ return chartDatumSvg(`<circle class="stdlib-chart-point" cx="${fmt(cx)}" cy="${fmt(cy)}" r="${fmt(r)}"/>`, datum, opts.inspect);
80
+ }
81
+ return chartDatumSvg(`<path class="stdlib-chart-point" d="${markerPath(shape, cx, cy, r)}"/>`, datum, opts.inspect);
82
+ }).join("");
83
+ if (elements) body.push(`<g class="${cls}">${elements}</g>`);
84
+ });
85
+
86
+ // Optional trend line (across all series). Renders after data so it's visible.
87
+ if (opts.trendline) {
88
+ const reg = linearRegression(finitePoints);
89
+ if (reg !== null) {
90
+ const x0 = xAxis.domain[0];
91
+ const x1 = xAxis.domain[1];
92
+ const y0 = reg.slope * x0 + reg.intercept;
93
+ const y1 = reg.slope * x1 + reg.intercept;
94
+ // Clip to plot area in pixel space (visible portion of the regression).
95
+ const px0 = xMap(x0, xAxis.domain, [area.x0, area.x1]);
96
+ const px1 = xMap(x1, xAxis.domain, [area.x0, area.x1]);
97
+ const py0 = yMap(y0, yAxis.domain, [area.y1, area.y0]);
98
+ const py1 = yMap(y1, yAxis.domain, [area.y1, area.y0]);
99
+ body.push(`<line class="stdlib-chart-trendline" x1="${fmt(px0)}" y1="${fmt(py0)}" x2="${fmt(px1)}" y2="${fmt(py1)}"/>`);
100
+ }
101
+ }
102
+
103
+ // References render AFTER data so labels stay on top (z-order matters in SVG).
104
+ if (opts.references && opts.references.length > 0) {
105
+ body.push(renderReferenceLines(opts.references, xAxis, yAxis, area));
106
+ }
107
+ const legend = renderLegend(legendItems, width, height - legendHeight);
108
+ return svgRoot({
109
+ width,
110
+ height,
111
+ className: opts.className
112
+ }, header.svg + body.join("") + legend.svg);
113
+ };
114
+ //# sourceMappingURL=svg-scatter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"names":[],"sources":["charts/core/svg-scatter.ts"],"sourcesContent":["// Ported SVG algorithm, kept verbatim; separate module for per-kind frame loading.\n\nimport {\n chartDatumSvg,\n collectAxisValues,\n collectSeriesLegend,\n computeAxisScale,\n computeDomain,\n computePlotArea,\n DEFAULT_HEIGHT,\n DEFAULT_MARKERS,\n DEFAULT_WIDTH,\n emptyChart,\n fmt,\n isOnAxis,\n linearRegression,\n mapRange,\n markerPath,\n normalizePadding,\n pickMapper,\n pointValues,\n renderErrorBars,\n renderHeader,\n renderLegend,\n renderReferenceLines,\n renderXAxisNumeric,\n renderYAxis,\n type ScatterOptions,\n svgRoot,\n} from \"./svg-common\";\nimport type { ChartDatum } from \"./types\";\n/**\n * Render a scatter plot. Each series produces a group of circles, one per\n * data point, classed `stdlib-chart-series-N` so series colors stay\n * consistent across charts.\n *\n * Non-finite x or y values are filtered out. An empty `series` (or one with\n * no finite points) renders an empty-state SVG with a \"no data\" label.\n *\n * @example\n * charts.scatter({ series: [{ data: [{x:1,y:2},{x:2,y:5},{x:3,y:3}] }] });\n *\n * @example\n * charts.scatter({\n * series: [\n * { label: \"Group A\", data: [...] },\n * { label: \"Group B\", data: [...] },\n * ],\n * xAxis: { label: \"Time (s)\" },\n * yAxis: { label: \"Value\", format: v => `${v}k` },\n * });\n */\nexport const scatter = (opts: ScatterOptions): string => {\n const width = opts.width ?? DEFAULT_WIDTH;\n const height = opts.height ?? DEFAULT_HEIGHT;\n const padding = normalizePadding(opts.padding);\n const header = renderHeader(width, opts.title, opts.subtitle);\n const legendItems = collectSeriesLegend(opts);\n const legendHeight = legendItems.length > 0 ? 20 : 0;\n\n const finitePoints = opts.series.flatMap((s) => s.data).filter((p) => Number.isFinite(p.x) && Number.isFinite(p.y));\n if (finitePoints.length === 0) return emptyChart(width, height, opts.className);\n\n const area = computePlotArea(width, height, padding, !!opts.xAxis?.label, !!opts.yAxis?.label, header.height, legendHeight);\n\n const xAxis = computeAxisScale(collectAxisValues(opts.series, \"x\"), opts.xAxis);\n const yAxis = computeAxisScale(collectAxisValues(opts.series, \"y\"), opts.yAxis);\n const xMap = pickMapper(xAxis.scale);\n const yMap = pickMapper(yAxis.scale);\n\n // Size dimension: if any point has a finite `size`, normalize across all\n // points to the configured pixel range; points without size fall back to\n // the midpoint so they remain visible.\n const sizes = finitePoints.map((p) => p.size).filter((s): s is number => typeof s === \"number\" && Number.isFinite(s));\n const sizeRange = opts.sizeRange ?? [3, 12];\n const useSizes = sizes.length > 0;\n const sizeDomain = useSizes ? computeDomain(sizes) : ([0, 1] as [number, number]);\n const fallbackR = (sizeRange[0] + sizeRange[1]) / 2;\n\n const body: string[] = [];\n body.push(renderYAxis(yAxis, area, opts.yAxis));\n body.push(renderXAxisNumeric(xAxis, area, height - legendHeight, opts.xAxis));\n\n // Error bars render under the points so the points sit on top of the bar\n // intersections — easier to read.\n body.push(renderErrorBars(opts.series, xAxis, yAxis, area));\n\n opts.series.forEach((s, i) => {\n const cls = `stdlib-chart-series-${i % 8}`;\n const shape = s.marker ?? (opts.autoVariant ? DEFAULT_MARKERS[i % DEFAULT_MARKERS.length]! : \"circle\");\n const elements = s.data\n .map((p, index) => ({ p, index }))\n .filter(({ p }) => isOnAxis(xAxis, p.x) && isOnAxis(yAxis, p.y))\n .map(({ p, index }) => {\n const cx = xMap(p.x, xAxis.domain, [area.x0, area.x1]);\n const cy = yMap(p.y, yAxis.domain, [area.y1, area.y0]);\n const r = useSizes\n ? typeof p.size === \"number\" && Number.isFinite(p.size)\n ? mapRange(p.size, sizeDomain, sizeRange)\n : fallbackR\n : 3;\n const datum: ChartDatum = {\n index,\n seriesIndex: i,\n role: \"point\",\n label: s.label,\n anchor: [cx, cy],\n values: pointValues(p, opts.xAxis, opts.yAxis),\n };\n if (shape === \"circle\") {\n return chartDatumSvg(`<circle class=\"stdlib-chart-point\" cx=\"${fmt(cx)}\" cy=\"${fmt(cy)}\" r=\"${fmt(r)}\"/>`, datum, opts.inspect);\n }\n return chartDatumSvg(`<path class=\"stdlib-chart-point\" d=\"${markerPath(shape, cx, cy, r)}\"/>`, datum, opts.inspect);\n })\n .join(\"\");\n if (elements) body.push(`<g class=\"${cls}\">${elements}</g>`);\n });\n\n // Optional trend line (across all series). Renders after data so it's visible.\n if (opts.trendline) {\n const reg = linearRegression(finitePoints);\n if (reg !== null) {\n const x0 = xAxis.domain[0];\n const x1 = xAxis.domain[1];\n const y0 = reg.slope * x0 + reg.intercept;\n const y1 = reg.slope * x1 + reg.intercept;\n // Clip to plot area in pixel space (visible portion of the regression).\n const px0 = xMap(x0, xAxis.domain, [area.x0, area.x1]);\n const px1 = xMap(x1, xAxis.domain, [area.x0, area.x1]);\n const py0 = yMap(y0, yAxis.domain, [area.y1, area.y0]);\n const py1 = yMap(y1, yAxis.domain, [area.y1, area.y0]);\n body.push(`<line class=\"stdlib-chart-trendline\" x1=\"${fmt(px0)}\" y1=\"${fmt(py0)}\" x2=\"${fmt(px1)}\" y2=\"${fmt(py1)}\"/>`);\n }\n }\n\n // References render AFTER data so labels stay on top (z-order matters in SVG).\n if (opts.references && opts.references.length > 0) {\n body.push(renderReferenceLines(opts.references, xAxis, yAxis, area));\n }\n\n const legend = renderLegend(legendItems, width, height - legendHeight);\n return svgRoot({ width, height, className: opts.className }, header.svg + body.join(\"\") + legend.svg);\n};\n"],"mappings":"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","ignoreList":[]}
@@ -0,0 +1,138 @@
1
+ import { chartDatumSvg, escapeXml, renderHeader, svgRoot } from "./svg";
2
+ export function treemapLayout(spec, size = {
3
+ width: spec.width ?? 480,
4
+ height: spec.height ?? 280
5
+ }) {
6
+ const width = spec.displayWidth ?? size.width,
7
+ height = size.height;
8
+ const data = spec.data.map((d, index) => {
9
+ if (!Number.isFinite(d.value) || d.value < 0) throw new RangeError("Treemap values must be finite and nonnegative.");
10
+ if (!spec.groups.includes(d.group)) throw new RangeError("Treemap groups must contain every item group.");
11
+ return {
12
+ ...d,
13
+ index
14
+ };
15
+ });
16
+ const total = data.reduce((s, d) => s + d.value, 0);
17
+ if (!Number.isFinite(total)) throw new RangeError("Treemap total must be finite.");
18
+ const header = renderHeader(width, spec.title, spec.subtitle);
19
+ const legend = [];
20
+ let lx = 0,
21
+ lr = 0;
22
+ if (spec.legend !== false) spec.groups.forEach((label, index) => {
23
+ const itemWidth = Math.min(width, label.length * 12 + 28);
24
+ if (lx && lx + itemWidth > width) {
25
+ lx = 0;
26
+ lr++;
27
+ }
28
+ legend.push({
29
+ label,
30
+ index,
31
+ x: lx,
32
+ row: lr
33
+ });
34
+ lx += itemWidth;
35
+ });
36
+ const legendHeight = legend.length ? (lr + 1) * 20 + 4 : 0,
37
+ plotHeight = Math.max(0, height - header.height - legendHeight);
38
+ const entries = data.filter(d => d.value > 0).sort((a, b) => b.value - a.value).map(d => ({
39
+ ...d,
40
+ area: d.value / (total || 1) * width * plotHeight
41
+ })).filter(d => d.area > 0);
42
+ const cells = [];
43
+ let x = 0,
44
+ y = header.height,
45
+ w = width,
46
+ h = plotHeight;
47
+ const worst = (areas, side) => {
48
+ if (!areas.length || side <= 0) return Infinity;
49
+ const sum = areas.reduce((s, a) => s + a, 0),
50
+ lo = Math.min(...areas),
51
+ hi = Math.max(...areas);
52
+ return Math.max(side * side * hi / (sum * sum), sum * sum / (side * side * lo));
53
+ };
54
+ let offset = 0;
55
+ while (offset < entries.length) {
56
+ if (w <= 0 || h <= 0) break;
57
+ const row = [entries[offset++]];
58
+ const side = Math.min(w, h);
59
+ while (offset < entries.length && worst(row.map(c => c.area), side) >= worst([...row.map(c => c.area), entries[offset].area], side)) row.push(entries[offset++]);
60
+ const sum = row.reduce((s, c) => s + c.area, 0);
61
+ const vertical = w >= h;
62
+ const thickness = Math.min(vertical ? w : h, sum / (vertical ? h : w));
63
+ let pos = vertical ? y : x;
64
+ for (const d of row) {
65
+ const length = d.area / thickness;
66
+ cells.push({
67
+ ...d,
68
+ x: vertical ? x : pos,
69
+ y: vertical ? pos : y,
70
+ width: vertical ? thickness : length,
71
+ height: vertical ? length : thickness
72
+ });
73
+ pos += length;
74
+ }
75
+ if (vertical) {
76
+ x += thickness;
77
+ w = Math.max(0, w - thickness);
78
+ } else {
79
+ y += thickness;
80
+ h = Math.max(0, h - thickness);
81
+ }
82
+ }
83
+ for (const d of data.filter(d => !cells.some(c => c.index === d.index))) cells.push({
84
+ ...d,
85
+ x: 0,
86
+ y: 0,
87
+ width: 0,
88
+ height: 0
89
+ });
90
+ return {
91
+ width,
92
+ height,
93
+ total,
94
+ cells,
95
+ header,
96
+ legend,
97
+ legendTop: height - legendHeight,
98
+ plotHeight
99
+ };
100
+ }
101
+ export function renderTreemapSvg(spec) {
102
+ const scene = treemapLayout(spec);
103
+ const number = spec.format ?? new Intl.NumberFormat(spec.locale ?? "en", {
104
+ maximumSignificantDigits: 15
105
+ }).format;
106
+ const percent = new Intl.NumberFormat(spec.locale ?? "en", {
107
+ style: "percent",
108
+ maximumFractionDigits: 1
109
+ });
110
+ const body = scene.header.svg.replace(/<text class="stdlib-chart-title"/g, '<text fill="var(--k2b-text,#27272a)" font-size="14" class="stdlib-chart-title"').replace(/<text class="stdlib-chart-subtitle"/g, '<text fill="var(--k2b-text-muted,#45454e)" font-size="11" class="stdlib-chart-subtitle"') + scene.cells.map(c => {
111
+ const group = spec.groups.indexOf(c.group) % 8 + 1;
112
+ const color = `var(--stdlib-chart-c${group},${["#3b82f6", "#10b981", "#f59e0b", "#8b5cf6", "#ec4899", "#06b6d4", "#84cc16", "#f97316"][group - 1]})`;
113
+ const text = c.width * (spec.displayWidth ?? 296) / scene.width > Math.max(56, Math.max(c.label.length * 12, number(c.value).length * 11) + 16) && c.height * Math.min(1, 180 / scene.height) > 48 ? `<text data-treemap-name="true" style="translate:0 calc((var(--k2b-chart-inverse-y,1) - 1) * 18px)" x="${c.x + 8}" y="${c.y + 18}" font-size="12" font-weight="600" fill="var(--k2b-text,#27272a)">${escapeXml(c.label)}</text><text data-treemap-value="true" style="translate:0 calc((var(--k2b-chart-inverse-y,1) - 1) * 38px)" x="${c.x + 8}" y="${c.y + 38}" font-size="11" font-weight="400" fill="var(--k2b-text-muted,#45454e)">${escapeXml(number(c.value))}</text>` : "";
114
+ return chartDatumSvg(`<rect class="k2b-chart-treemap-cell" x="${c.x + 1}" y="${c.y + 1}" width="${Math.max(0, c.width - 2)}" height="${Math.max(0, c.height - 2)}" rx="4" fill="color-mix(in srgb,${color} 55%,var(--k2b-surface,#fff))" stroke="none"/>${text}`, {
115
+ role: "item",
116
+ index: c.index,
117
+ label: c.label,
118
+ anchor: [c.x + c.width / 2, c.y + c.height / 2],
119
+ values: [{
120
+ key: "group",
121
+ value: c.group
122
+ }, {
123
+ key: "value",
124
+ value: c.value,
125
+ formatted: number(c.value)
126
+ }, {
127
+ key: "percent",
128
+ value: scene.total ? c.value / scene.total * 100 : 0,
129
+ formatted: scene.total ? percent.format(c.value / scene.total) : "–"
130
+ }]
131
+ }, spec.inspect);
132
+ }).join("") + scene.legend.map(item => `<rect x="${item.x}" y="${scene.legendTop + item.row * 20 + 6}" width="8" height="8" rx="2" fill="var(--stdlib-chart-c${item.index % 8 + 1},${["#3b82f6", "#10b981", "#f59e0b", "#8b5cf6", "#ec4899", "#06b6d4", "#84cc16", "#f97316"][item.index % 8]})"/><text x="${item.x + 13}" y="${scene.legendTop + item.row * 20 + 14}" font-size="11" fill="var(--k2b-text-muted,#45454e)">${escapeXml(item.label.slice(0, Math.floor((scene.width - 28) / 12)))}</text>`).join("");
133
+ return svgRoot({
134
+ ...scene,
135
+ className: spec.className
136
+ }, body);
137
+ }
138
+ //# sourceMappingURL=treemap.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"names":[],"sources":["charts/core/treemap.ts"],"sourcesContent":["import { type ChartOptions, chartDatumSvg, escapeXml, renderHeader, svgRoot } from \"./svg\";\nexport type TreemapOptions = ChartOptions & {\n groups: string[];\n legend?: boolean;\n data: { label: string; group: string; value: number }[];\n locale?: string;\n format?: (value: number) => string;\n displayWidth?: number;\n};\nexport function treemapLayout(spec: TreemapOptions, size = { width: spec.width ?? 480, height: spec.height ?? 280 }) {\n const width = spec.displayWidth ?? size.width,\n height = size.height;\n const data = spec.data.map((d, index) => {\n if (!Number.isFinite(d.value) || d.value < 0) throw new RangeError(\"Treemap values must be finite and nonnegative.\");\n if (!spec.groups.includes(d.group)) throw new RangeError(\"Treemap groups must contain every item group.\");\n return { ...d, index };\n });\n const total = data.reduce((s, d) => s + d.value, 0);\n if (!Number.isFinite(total)) throw new RangeError(\"Treemap total must be finite.\");\n const header = renderHeader(width, spec.title, spec.subtitle);\n const legend: { label: string; x: number; row: number; index: number }[] = [];\n let lx = 0,\n lr = 0;\n if (spec.legend !== false)\n spec.groups.forEach((label, index) => {\n const itemWidth = Math.min(width, label.length * 12 + 28);\n if (lx && lx + itemWidth > width) {\n lx = 0;\n lr++;\n }\n legend.push({ label, index, x: lx, row: lr });\n lx += itemWidth;\n });\n const legendHeight = legend.length ? (lr + 1) * 20 + 4 : 0,\n plotHeight = Math.max(0, height - header.height - legendHeight);\n const entries = data\n .filter((d) => d.value > 0)\n .sort((a, b) => b.value - a.value)\n .map((d) => ({ ...d, area: (d.value / (total || 1)) * width * plotHeight }))\n .filter((d) => d.area > 0);\n const cells: ((typeof data)[number] & { x: number; y: number; width: number; height: number })[] = [];\n let x = 0,\n y = header.height,\n w = width,\n h = plotHeight;\n const worst = (areas: number[], side: number) => {\n if (!areas.length || side <= 0) return Infinity;\n const sum = areas.reduce((s, a) => s + a, 0),\n lo = Math.min(...areas),\n hi = Math.max(...areas);\n return Math.max((side * side * hi) / (sum * sum), (sum * sum) / (side * side * lo));\n };\n let offset = 0;\n while (offset < entries.length) {\n if (w <= 0 || h <= 0) break;\n const row = [entries[offset++]!];\n const side = Math.min(w, h);\n while (\n offset < entries.length &&\n worst(\n row.map((c) => c.area),\n side,\n ) >= worst([...row.map((c) => c.area), entries[offset]!.area], side)\n )\n row.push(entries[offset++]!);\n const sum = row.reduce((s, c) => s + c.area, 0);\n const vertical = w >= h;\n const thickness = Math.min(vertical ? w : h, sum / (vertical ? h : w));\n let pos = vertical ? y : x;\n for (const d of row) {\n const length = d.area / thickness;\n cells.push({\n ...d,\n x: vertical ? x : pos,\n y: vertical ? pos : y,\n width: vertical ? thickness : length,\n height: vertical ? length : thickness,\n });\n pos += length;\n }\n if (vertical) {\n x += thickness;\n w = Math.max(0, w - thickness);\n } else {\n y += thickness;\n h = Math.max(0, h - thickness);\n }\n }\n for (const d of data.filter((d) => !cells.some((c) => c.index === d.index))) cells.push({ ...d, x: 0, y: 0, width: 0, height: 0 });\n return { width, height, total, cells, header, legend, legendTop: height - legendHeight, plotHeight };\n}\nexport function renderTreemapSvg(spec: TreemapOptions): string {\n const scene = treemapLayout(spec);\n const number = spec.format ?? new Intl.NumberFormat(spec.locale ?? \"en\", { maximumSignificantDigits: 15 }).format;\n const percent = new Intl.NumberFormat(spec.locale ?? \"en\", { style: \"percent\", maximumFractionDigits: 1 });\n const body =\n scene.header.svg\n .replace(/<text class=\"stdlib-chart-title\"/g, '<text fill=\"var(--k2b-text,#27272a)\" font-size=\"14\" class=\"stdlib-chart-title\"')\n .replace(\n /<text class=\"stdlib-chart-subtitle\"/g,\n '<text fill=\"var(--k2b-text-muted,#45454e)\" font-size=\"11\" class=\"stdlib-chart-subtitle\"',\n ) +\n scene.cells\n .map((c) => {\n const group = (spec.groups.indexOf(c.group) % 8) + 1;\n const color = `var(--stdlib-chart-c${group},${[\"#3b82f6\", \"#10b981\", \"#f59e0b\", \"#8b5cf6\", \"#ec4899\", \"#06b6d4\", \"#84cc16\", \"#f97316\"][group - 1]})`;\n const text =\n (c.width * (spec.displayWidth ?? 296)) / scene.width >\n Math.max(56, Math.max(c.label.length * 12, number(c.value).length * 11) + 16) && c.height * Math.min(1, 180 / scene.height) > 48\n ? `<text data-treemap-name=\"true\" style=\"translate:0 calc((var(--k2b-chart-inverse-y,1) - 1) * 18px)\" x=\"${c.x + 8}\" y=\"${c.y + 18}\" font-size=\"12\" font-weight=\"600\" fill=\"var(--k2b-text,#27272a)\">${escapeXml(c.label)}</text><text data-treemap-value=\"true\" style=\"translate:0 calc((var(--k2b-chart-inverse-y,1) - 1) * 38px)\" x=\"${c.x + 8}\" y=\"${c.y + 38}\" font-size=\"11\" font-weight=\"400\" fill=\"var(--k2b-text-muted,#45454e)\">${escapeXml(number(c.value))}</text>`\n : \"\";\n return chartDatumSvg(\n `<rect class=\"k2b-chart-treemap-cell\" x=\"${c.x + 1}\" y=\"${c.y + 1}\" width=\"${Math.max(0, c.width - 2)}\" height=\"${Math.max(0, c.height - 2)}\" rx=\"4\" fill=\"color-mix(in srgb,${color} 55%,var(--k2b-surface,#fff))\" stroke=\"none\"/>${text}`,\n {\n role: \"item\",\n index: c.index,\n label: c.label,\n anchor: [c.x + c.width / 2, c.y + c.height / 2],\n values: [\n { key: \"group\", value: c.group },\n { key: \"value\", value: c.value, formatted: number(c.value) },\n {\n key: \"percent\",\n value: scene.total ? (c.value / scene.total) * 100 : 0,\n formatted: scene.total ? percent.format(c.value / scene.total) : \"–\",\n },\n ],\n },\n spec.inspect,\n );\n })\n .join(\"\") +\n scene.legend\n .map(\n (item) =>\n `<rect x=\"${item.x}\" y=\"${scene.legendTop + item.row * 20 + 6}\" width=\"8\" height=\"8\" rx=\"2\" fill=\"var(--stdlib-chart-c${(item.index % 8) + 1},${[\"#3b82f6\", \"#10b981\", \"#f59e0b\", \"#8b5cf6\", \"#ec4899\", \"#06b6d4\", \"#84cc16\", \"#f97316\"][item.index % 8]})\"/><text x=\"${item.x + 13}\" y=\"${scene.legendTop + item.row * 20 + 14}\" font-size=\"11\" fill=\"var(--k2b-text-muted,#45454e)\">${escapeXml(item.label.slice(0, Math.floor((scene.width - 28) / 12)))}</text>`,\n )\n .join(\"\");\n return svgRoot({ ...scene, className: spec.className }, body);\n}\n"],"mappings":"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","ignoreList":[]}
@@ -1 +1 @@
1
- {"version":3,"names":[],"sources":["charts/description.ts"],"sourcesContent":["/** JSON number formatting. Functions belong to the rendering host, never the saved description. */\nexport type ChartNumberFormat = {\n style?: \"number\" | \"percent\" | \"currency\";\n currency?: string;\n unit?: string;\n decimals?: number;\n compact?: boolean;\n};\nexport type ChartDateFormat = { date: \"day\" | \"minute\" | \"second\" };\nexport type ChartFormat = ChartNumberFormat | ChartDateFormat;\nexport type ChartLimits = { maxSeries: number; maxValues: number; maxMarks: number };\nexport type ChartDescriptionAxis = {\n label?: string;\n domain?: number[];\n ticks?: number;\n scale?: \"linear\" | \"log\";\n format?: ChartFormat;\n};\ntype Reference = {\n value: number | string;\n axis?: \"x\" | \"y\";\n label?: string;\n style?: \"solid\" | \"dashed\" | \"dotted\";\n tone?: \"neutral\" | \"info\" | \"success\" | \"warning\" | \"danger\";\n};\ntype Threshold = { value: number; label?: string };\ntype Series<N> = { label?: string; data: { x: N | string; y: N }[]; role?: \"reference\" };\ntype Common = { title: string; subtitle?: string; tooltip?: { label?: string; format?: ChartNumberFormat } };\ntype Variants<N> =\n | {\n kind: \"waterfall\";\n start: { label: string; value: number };\n steps: { label: string; value: number }[];\n end: { label: string };\n format?: ChartNumberFormat;\n }\n | {\n kind: \"area\";\n layout: \"stacked\";\n normalize?: boolean;\n x: (number | string)[];\n series: { label: string; data: (number | null)[] }[];\n legend?: boolean;\n format?: ChartNumberFormat;\n }\n | {\n kind: \"bar\";\n data: { label: string; value: N }[];\n x?: string[];\n series?: { label: string; data: (number | null)[] }[];\n layout?: \"columns\" | \"rows\" | \"grouped\" | \"stacked\";\n normalize?: boolean;\n compare?: { label: string; data: (number | null)[] };\n xAxis?: Pick<ChartDescriptionAxis, \"label\"> & { ordered?: boolean };\n yAxis?: ChartDescriptionAxis;\n colorByBar?: boolean;\n showValues?: boolean;\n legend?: boolean;\n references?: Reference[];\n }\n | {\n kind: \"hbar\";\n series: string[];\n rows: { group?: string; label: string; values: (number | null)[] }[];\n legend?: boolean;\n format?: ChartNumberFormat;\n }\n | {\n kind: \"line\";\n series: (Series<N> & { style?: \"solid\" | \"dashed\" | \"dotted\" })[];\n xAxis?: ChartDescriptionAxis;\n yAxis?: ChartDescriptionAxis;\n legend?: boolean;\n area?: boolean;\n smooth?: boolean;\n references?: Reference[];\n }\n | {\n kind: \"scatter\";\n series: Series<N>[];\n xAxis?: ChartDescriptionAxis;\n yAxis?: ChartDescriptionAxis;\n legend?: boolean;\n references?: Reference[];\n }\n | { kind: \"pie\" | \"donut\"; data: { label: string; value: N }[]; legend?: boolean; showLabels?: boolean; format?: ChartNumberFormat }\n | { kind: \"histogram\"; data: N[]; bins?: number | number[]; references?: Reference[] }\n | {\n kind: \"gauge\";\n value: N;\n min?: number;\n max?: number;\n label?: string;\n unit?: string;\n format?: ChartNumberFormat;\n thresholds?: Threshold[];\n }\n | { kind: \"sparkline\"; data: N[]; area?: boolean }\n | { kind: \"boxplot\"; data: { label: string; values: N[] }[]; yAxis?: ChartDescriptionAxis; references?: Reference[] }\n | {\n kind: \"heatmap\";\n layout: \"calendar\";\n data: [string, N | null][];\n scale: \"sequential\" | \"diverging\";\n label?: string;\n format?: ChartNumberFormat;\n locale?: string;\n }\n | {\n kind: \"heatmap\";\n layout?: undefined;\n data: { x: string; y: string; value: N }[];\n format?: ChartNumberFormat;\n showValues?: boolean;\n min?: number;\n max?: number;\n }\n | {\n kind: \"barGauge\";\n data: { label: string; value: N; min?: number; max?: number }[];\n min?: number;\n max?: number;\n unit?: string;\n format?: ChartNumberFormat;\n thresholds?: Threshold[];\n }\n | { kind: \"stat\"; value: N; format?: ChartNumberFormat; unit?: string; delta?: N; better?: \"up\" | \"down\"; sparkline?: N[] };\n\n/** Portable chart node. Dates are ISO text and missing observations are null, never zero. */\nexport type ChartDescription = Common &\n (\n | Exclude<Variants<number | null>, { kind: \"bar\" }>\n // Aligned series omit flat data; the validator supplies an empty array.\n | (Omit<Extract<Variants<number | null>, { kind: \"bar\" }>, \"data\"> & { data?: { label: string; value: number | null }[] })\n );\n/** Validated renderer input. The portable validator normalizes null gaps to non-finite numbers for drawing. */\nexport type ChartInput = Common &\n (\n | Exclude<Variants<number>, { kind: \"stat\" }>\n | (Omit<Extract<Variants<number>, { kind: \"stat\" }>, \"delta\"> & { delta: number | undefined })\n );\n"],"mappings":"","ignoreList":[]}
1
+ {"version":3,"names":[],"sources":["charts/description.ts"],"sourcesContent":["/** JSON number formatting. Functions belong to the rendering host, never the saved description. */\nexport type ChartNumberFormat = {\n style?: \"number\" | \"percent\" | \"currency\";\n currency?: string;\n unit?: string;\n decimals?: number;\n compact?: boolean;\n};\nexport type ChartDateFormat = { date: \"day\" | \"minute\" | \"second\" };\nexport type ChartFormat = ChartNumberFormat | ChartDateFormat;\nexport type ChartLimits = { maxSeries: number; maxValues: number; maxMarks: number };\nexport type ChartDescriptionAxis = {\n label?: string;\n domain?: number[];\n ticks?: number;\n scale?: \"linear\" | \"log\";\n format?: ChartFormat;\n};\ntype Reference = {\n value: number | string;\n axis?: \"x\" | \"y\";\n label?: string;\n style?: \"solid\" | \"dashed\" | \"dotted\";\n tone?: \"neutral\" | \"info\" | \"success\" | \"warning\" | \"danger\";\n};\ntype Threshold = { value: number; label?: string };\ntype Series<N> = { label?: string; data: { x: N | string; y: N }[]; role?: \"reference\" };\ntype Common = { title: string; subtitle?: string; tooltip?: { label?: string; format?: ChartNumberFormat } };\ntype Variants<N> =\n | {\n kind: \"waterfall\";\n start: { label: string; value: number };\n steps: { label: string; value: number }[];\n end: { label: string };\n format?: ChartNumberFormat;\n }\n | {\n kind: \"area\";\n layout: \"stacked\";\n normalize?: boolean;\n x: (number | string)[];\n series: { label: string; data: (number | null)[] }[];\n legend?: boolean;\n format?: ChartNumberFormat;\n }\n | {\n kind: \"bar\";\n data: { label: string; value: N }[];\n x?: string[];\n series?: { label: string; data: (number | null)[] }[];\n layout?: \"columns\" | \"rows\" | \"grouped\" | \"stacked\";\n normalize?: boolean;\n share?: boolean;\n limit?: number;\n tone?: \"in\" | \"out\";\n format?: ChartNumberFormat;\n compare?: { label: string; data: (number | null)[] };\n xAxis?: Pick<ChartDescriptionAxis, \"label\"> & { ordered?: boolean };\n yAxis?: ChartDescriptionAxis;\n colorByBar?: boolean;\n showValues?: boolean;\n legend?: boolean;\n references?: Reference[];\n }\n | { kind: \"funnel\"; data: { label: string; value: number | null }[]; format?: ChartNumberFormat }\n | {\n kind: \"hbar\";\n series: string[];\n rows: { group?: string; label: string; values: (number | null)[] }[];\n legend?: boolean;\n format?: ChartNumberFormat;\n }\n | {\n kind: \"line\";\n series: (Series<N> & { style?: \"solid\" | \"dashed\" | \"dotted\" })[];\n xAxis?: ChartDescriptionAxis;\n yAxis?: ChartDescriptionAxis;\n legend?: boolean;\n area?: boolean;\n smooth?: boolean;\n references?: Reference[];\n }\n | {\n kind: \"scatter\";\n series: Series<N>[];\n xAxis?: ChartDescriptionAxis;\n yAxis?: ChartDescriptionAxis;\n legend?: boolean;\n references?: Reference[];\n }\n | { kind: \"pie\" | \"donut\"; data: { label: string; value: N }[]; legend?: boolean; showLabels?: boolean; format?: ChartNumberFormat }\n | { kind: \"histogram\"; data: N[]; bins?: number | number[]; references?: Reference[] }\n | {\n kind: \"gauge\";\n value: N;\n min?: number;\n max?: number;\n label?: string;\n unit?: string;\n format?: ChartNumberFormat;\n thresholds?: Threshold[];\n }\n | { kind: \"sparkline\"; data: N[]; area?: boolean }\n | { kind: \"boxplot\"; data: { label: string; values: N[] }[]; yAxis?: ChartDescriptionAxis; references?: Reference[] }\n | {\n kind: \"heatmap\";\n layout: \"calendar\";\n data: [string, N | null][];\n scale: \"sequential\" | \"diverging\";\n label?: string;\n format?: ChartNumberFormat;\n locale?: string;\n }\n | {\n kind: \"heatmap\";\n layout?: undefined;\n data: { x: string; y: string; value: N }[];\n format?: ChartNumberFormat;\n showValues?: boolean;\n min?: number;\n max?: number;\n }\n | {\n kind: \"barGauge\";\n data: { label: string; value: N; min?: number; max?: number }[];\n min?: number;\n max?: number;\n unit?: string;\n format?: ChartNumberFormat;\n thresholds?: Threshold[];\n }\n | { kind: \"stat\"; value: N; format?: ChartNumberFormat; unit?: string; delta?: N; better?: \"up\" | \"down\"; sparkline?: N[] }\n | {\n kind: \"treemap\";\n groups: string[];\n data: { label: string; group: string; value: number }[];\n legend?: boolean;\n format?: ChartNumberFormat;\n locale?: string;\n }\n | {\n kind: \"pareto\";\n labels: [string, string];\n data: { label: string; value: number; prev: number }[];\n format?: ChartNumberFormat;\n locale?: string;\n }\n | {\n kind: \"bubble\";\n groups: string[];\n data: { label: string; group: string; x: number; y: number; size?: number }[];\n xLabel: string;\n yLabel: string;\n sizeLabel?: string;\n averages?: boolean;\n legend?: boolean;\n xFormat?: ChartNumberFormat;\n yFormat?: ChartNumberFormat;\n format?: ChartNumberFormat;\n locale?: string;\n }\n | {\n kind: \"radar\";\n axes: string[];\n max: number;\n unit?: string;\n series: { label: string; data: number[] }[];\n format?: ChartNumberFormat;\n legend?: boolean;\n locale?: string;\n };\n\n/** Portable chart node. Dates are ISO text and missing observations are null, never zero. */\nexport type ChartDescription = Common &\n (\n | Exclude<Variants<number | null>, { kind: \"bar\" }>\n // Aligned series omit flat data; the validator supplies an empty array.\n | (Omit<Extract<Variants<number | null>, { kind: \"bar\" }>, \"data\"> & { data?: { label: string; value: number | null }[] })\n );\n/** Validated renderer input. The portable validator normalizes null gaps to non-finite numbers for drawing. */\nexport type ChartInput = Common &\n (\n | Exclude<Variants<number>, { kind: \"stat\" }>\n | (Omit<Extract<Variants<number>, { kind: \"stat\" }>, \"delta\"> & { delta: number | undefined })\n );\n"],"mappings":"","ignoreList":[]}
@@ -88,10 +88,14 @@ import { useViewportMessages } from "./viewport-messages";
88
88
  * block on truly empty inputs; partially-filled series get rendered
89
89
  * as-is and the core handles the gaps. */
90
90
  const isEmpty = props => {
91
+ if (props.kind === "treemap" || props.kind === "pareto") return !props.data.length;
92
+ if (props.kind === "bubble") return !props.data.length;
93
+ if (props.kind === "radar") return !props.series.length;
91
94
  if (props.kind === "area") return !props.x.length || !props.series.length || props.x.every((_, i) => props.series.some(s => s.data[i] === null) || props.normalize && props.series.every(s => s.data[i] === 0));
92
95
  if (props.kind === "line" || props.kind === "scatter") {
93
96
  return !props.series?.length || props.series.every(s => !s.data.length);
94
97
  }
98
+ if (props.kind === "funnel") return !props.data.length;
95
99
  if (props.kind === "hbar") return !props.rows.length;
96
100
  if (props.kind === "bar") return props.series ? !props.x?.length || !props.series.some(s => s.data.some(v => v !== null && Number.isFinite(v))) : !props.data?.length;
97
101
  if (props.kind === "donut" || props.kind === "pie") {
@@ -109,7 +113,12 @@ const isEmpty = props => {
109
113
  return false;
110
114
  };
111
115
  export const renderChartRoot = (props, renderChartSvg) => {
112
- const [, drawingProps] = splitProps(props, ["class", "style", "labels", "interactive", "selected", "onSelect", "tooltip", "cursor"]);
116
+ const drawingProps = () => {
117
+ // Pareto's labels name its two measures; other kinds use labels only for host controls.
118
+ if (props.kind === "pareto") return props;
119
+ const [, drawing] = splitProps(props, ["class", "style", "labels", "interactive", "selected", "onSelect", "tooltip", "cursor"]);
120
+ return drawing;
121
+ };
113
122
  const messages = useUiMessages();
114
123
  const chartMessages = useChartMessages();
115
124
  const viewportMessages = useViewportMessages();
@@ -159,7 +168,7 @@ export const renderChartRoot = (props, renderChartSvg) => {
159
168
  const interactiveMap = () => props.kind === "map" && props.interactive === true;
160
169
  const interactiveTimeline = () => props.kind === "stateTimeline" && props.interactive === true;
161
170
  const draggable = () => interactiveMap() || interactiveTimeline();
162
- const labels = () => props.labels ?? {};
171
+ const labels = () => props.kind === "pareto" ? {} : props.labels ?? {};
163
172
  const accessibleName = () => {
164
173
  const override = props.kind === "map" ? labels().interactiveMap : props.kind === "stateTimeline" ? labels().interactiveTimeline : props.kind === "line" ? labels().interactiveLine : labels().interactiveChart;
165
174
  const title = "title" in props && typeof props.title === "string" && props.title.trim() ? props.title : undefined;
@@ -360,7 +369,7 @@ export const renderChartRoot = (props, renderChartSvg) => {
360
369
  select: selection => props.onSelect?.(selection),
361
370
  format: selection => {
362
371
  if (props.tooltip) return props.tooltip(selection);
363
- const fields = props.kind === "heatmap" && props.layout === "calendar" ? resolveChartMessages(props.locale ?? locale()).chartFields : chartMessages().chartFields;
372
+ const fields = props.kind === "bubble" || props.kind === "radar" || props.kind === "treemap" || props.kind === "pareto" || props.kind === "heatmap" && props.layout === "calendar" ? resolveChartMessages(props.locale ?? locale()).chartFields : chartMessages().chartFields;
364
373
  if (selection.kind === "area" && props.kind === "area") {
365
374
  const amount = selection.datum.values.find(f => f.key === "value");
366
375
  const share = selection.datum.values.find(f => f.key === "percent");
@@ -378,7 +387,11 @@ export const renderChartRoot = (props, renderChartSvg) => {
378
387
  return {
379
388
  title: selection.datum.label ?? (selection.datum.seriesIndex !== undefined ? `${labels().series ?? chartMessages().series} ${selection.datum.seriesIndex + 1}` : undefined),
380
389
  rows: selection.datum.values.filter(field => selection.kind !== "waterfall" || field.key !== "before").map(field => ({
381
- label: props.kind === "heatmap" && props.layout === "calendar" && field.key === "value" && props.label ? props.label : selection.kind === "line" && field.key === "y" ? selection.datum.label ?? labels().series ?? chartMessages().series : (props.kind === "bar" || props.kind === "hbar") && field.key.startsWith("series:") ? (props.kind === "hbar" ? props.series[Number(field.key.slice(7))] : props.series?.[Number(field.key.slice(7))]?.label) ?? fields({
390
+ label: props.kind === "bubble" && field.key === "x" ? props.xLabel : props.kind === "bubble" && field.key === "y" ? props.yLabel : props.kind === "bubble" && field.key === "size" ? props.sizeLabel ?? fields({
391
+ key: "size"
392
+ }) : props.kind === "radar" && field.key.startsWith("series:") ? props.series[Number(field.key.slice(7))]?.label ?? fields({
393
+ key: "series"
394
+ }) : props.kind === "pareto" && (field.key === "value" || field.key === "previous") ? props.labels[field.key === "value" ? 0 : 1] : props.kind === "heatmap" && props.layout === "calendar" && field.key === "value" && props.label ? props.label : selection.kind === "line" && field.key === "y" ? selection.datum.label ?? labels().series ?? chartMessages().series : (props.kind === "bar" || props.kind === "hbar") && field.key.startsWith("series:") ? (props.kind === "hbar" ? props.series[Number(field.key.slice(7))] : props.series?.[Number(field.key.slice(7))]?.label) ?? fields({
382
395
  key: field.key
383
396
  }) : props.kind === "bar" && field.key === "previous" ? props.compare?.label ?? fields({
384
397
  key: field.key
@@ -391,12 +404,12 @@ export const renderChartRoot = (props, renderChartSvg) => {
391
404
  }
392
405
  });
393
406
  const closeChartTooltip = () => inspection.close();
394
- const svgMarkup = createMemo(() => renderChartSvg(props.kind === "heatmap" && props.layout === "calendar" ? {
407
+ const svgMarkup = createMemo(() => renderChartSvg(props.kind === "bubble" || props.kind === "radar" || props.kind === "treemap" || props.kind === "pareto" || props.kind === "heatmap" && props.layout === "calendar" ? {
395
408
  ...props,
396
409
  locale: props.locale ?? locale()
397
410
  } : {
398
- ...drawingProps,
399
- ...(props.kind === "bar" || props.kind === "hbar" ? {
411
+ ...drawingProps(),
412
+ ...(props.kind === "bar" || props.kind === "hbar" || props.kind === "funnel" ? {
400
413
  locale: locale()
401
414
  } : {})
402
415
  }, size().width, size().height, interactiveMap() ? mapViewport() : undefined, interactiveTimeline() ? timelineViewport() : undefined, locale()));
@@ -418,6 +431,7 @@ export const renderChartRoot = (props, renderChartSvg) => {
418
431
  onCleanup(closeChartInspectionOnOutside({
419
432
  container: () => containerRef,
420
433
  tooltip: () => chartTooltipRef,
434
+ scroll: inspection.scroll,
421
435
  close: closeChartTooltip,
422
436
  keepScroll: () => props.kind === "heatmap" && props.layout === "calendar"
423
437
  }));