@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":[]}
@@ -98,10 +98,14 @@ import { useViewportMessages } from "./viewport-messages";
98
98
  * block on truly empty inputs; partially-filled series get rendered
99
99
  * as-is and the core handles the gaps. */
100
100
  const isEmpty = props => {
101
+ if (props.kind === "treemap" || props.kind === "pareto") return !props.data.length;
102
+ if (props.kind === "bubble") return !props.data.length;
103
+ if (props.kind === "radar") return !props.series.length;
101
104
  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));
102
105
  if (props.kind === "line" || props.kind === "scatter") {
103
106
  return !props.series?.length || props.series.every(s => !s.data.length);
104
107
  }
108
+ if (props.kind === "funnel") return !props.data.length;
105
109
  if (props.kind === "hbar") return !props.rows.length;
106
110
  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;
107
111
  if (props.kind === "donut" || props.kind === "pie") {
@@ -119,7 +123,12 @@ const isEmpty = props => {
119
123
  return false;
120
124
  };
121
125
  export const renderChartRoot = (props, renderChartSvg) => {
122
- const [, drawingProps] = splitProps(props, ["class", "style", "labels", "interactive", "selected", "onSelect", "tooltip", "cursor"]);
126
+ const drawingProps = () => {
127
+ // Pareto's labels name its two measures; other kinds use labels only for host controls.
128
+ if (props.kind === "pareto") return props;
129
+ const [, drawing] = splitProps(props, ["class", "style", "labels", "interactive", "selected", "onSelect", "tooltip", "cursor"]);
130
+ return drawing;
131
+ };
123
132
  const messages = useUiMessages();
124
133
  const chartMessages = useChartMessages();
125
134
  const viewportMessages = useViewportMessages();
@@ -169,7 +178,7 @@ export const renderChartRoot = (props, renderChartSvg) => {
169
178
  const interactiveMap = () => props.kind === "map" && props.interactive === true;
170
179
  const interactiveTimeline = () => props.kind === "stateTimeline" && props.interactive === true;
171
180
  const draggable = () => interactiveMap() || interactiveTimeline();
172
- const labels = () => props.labels ?? {};
181
+ const labels = () => props.kind === "pareto" ? {} : props.labels ?? {};
173
182
  const accessibleName = () => {
174
183
  const override = props.kind === "map" ? labels().interactiveMap : props.kind === "stateTimeline" ? labels().interactiveTimeline : props.kind === "line" ? labels().interactiveLine : labels().interactiveChart;
175
184
  const title = "title" in props && typeof props.title === "string" && props.title.trim() ? props.title : undefined;
@@ -370,7 +379,7 @@ export const renderChartRoot = (props, renderChartSvg) => {
370
379
  select: selection => props.onSelect?.(selection),
371
380
  format: selection => {
372
381
  if (props.tooltip) return props.tooltip(selection);
373
- const fields = props.kind === "heatmap" && props.layout === "calendar" ? resolveChartMessages(props.locale ?? locale()).chartFields : chartMessages().chartFields;
382
+ 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;
374
383
  if (selection.kind === "area" && props.kind === "area") {
375
384
  const amount = selection.datum.values.find(f => f.key === "value");
376
385
  const share = selection.datum.values.find(f => f.key === "percent");
@@ -388,7 +397,11 @@ export const renderChartRoot = (props, renderChartSvg) => {
388
397
  return {
389
398
  title: selection.datum.label ?? (selection.datum.seriesIndex !== undefined ? `${labels().series ?? chartMessages().series} ${selection.datum.seriesIndex + 1}` : undefined),
390
399
  rows: selection.datum.values.filter(field => selection.kind !== "waterfall" || field.key !== "before").map(field => ({
391
- 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({
400
+ 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({
401
+ key: "size"
402
+ }) : props.kind === "radar" && field.key.startsWith("series:") ? props.series[Number(field.key.slice(7))]?.label ?? fields({
403
+ key: "series"
404
+ }) : 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({
392
405
  key: field.key
393
406
  }) : props.kind === "bar" && field.key === "previous" ? props.compare?.label ?? fields({
394
407
  key: field.key
@@ -401,12 +414,12 @@ export const renderChartRoot = (props, renderChartSvg) => {
401
414
  }
402
415
  });
403
416
  const closeChartTooltip = () => inspection.close();
404
- const svgMarkup = createMemo(() => renderChartSvg(props.kind === "heatmap" && props.layout === "calendar" ? {
417
+ const svgMarkup = createMemo(() => renderChartSvg(props.kind === "bubble" || props.kind === "radar" || props.kind === "treemap" || props.kind === "pareto" || props.kind === "heatmap" && props.layout === "calendar" ? {
405
418
  ...props,
406
419
  locale: props.locale ?? locale()
407
420
  } : {
408
- ...drawingProps,
409
- ...(props.kind === "bar" || props.kind === "hbar" ? {
421
+ ...drawingProps(),
422
+ ...(props.kind === "bar" || props.kind === "hbar" || props.kind === "funnel" ? {
410
423
  locale: locale()
411
424
  } : {})
412
425
  }, size().width, size().height, interactiveMap() ? mapViewport() : undefined, interactiveTimeline() ? timelineViewport() : undefined, locale()));
@@ -428,6 +441,7 @@ export const renderChartRoot = (props, renderChartSvg) => {
428
441
  onCleanup(closeChartInspectionOnOutside({
429
442
  container: () => containerRef,
430
443
  tooltip: () => chartTooltipRef,
444
+ scroll: inspection.scroll,
431
445
  close: closeChartTooltip,
432
446
  keepScroll: () => props.kind === "heatmap" && props.layout === "calendar"
433
447
  }));