@arcfusionz/arc-analytic-ui 0.0.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 (156) hide show
  1. package/dist/_virtual/_rolldown/runtime.js +13 -0
  2. package/dist/components/AnomalyCard/AnomalyCard.chart.d.ts +64 -0
  3. package/dist/components/AnomalyCard/AnomalyCard.chart.js +341 -0
  4. package/dist/components/AnomalyCard/AnomalyCard.d.ts +169 -0
  5. package/dist/components/AnomalyCard/AnomalyCard.js +1019 -0
  6. package/dist/components/AnomalyCard/AnomalyCard.model.d.ts +508 -0
  7. package/dist/components/AnomalyCard/AnomalyCard.model.js +553 -0
  8. package/dist/components/AnomalyCard/index.d.ts +4 -0
  9. package/dist/components/AnomalyCard/index.js +4 -0
  10. package/dist/components/ChartCard/ChartCard.chart.d.ts +72 -0
  11. package/dist/components/ChartCard/ChartCard.chart.js +1333 -0
  12. package/dist/components/ChartCard/ChartCard.d.ts +206 -0
  13. package/dist/components/ChartCard/ChartCard.js +718 -0
  14. package/dist/components/ChartCard/ChartCard.model.d.ts +452 -0
  15. package/dist/components/ChartCard/ChartCard.model.js +1223 -0
  16. package/dist/components/ChartCard/index.d.ts +4 -0
  17. package/dist/components/ChartCard/index.js +4 -0
  18. package/dist/components/ComparisonCard/ComparisonCard.chart.d.ts +80 -0
  19. package/dist/components/ComparisonCard/ComparisonCard.chart.js +337 -0
  20. package/dist/components/ComparisonCard/ComparisonCard.d.ts +200 -0
  21. package/dist/components/ComparisonCard/ComparisonCard.js +1325 -0
  22. package/dist/components/ComparisonCard/ComparisonCard.model.d.ts +309 -0
  23. package/dist/components/ComparisonCard/ComparisonCard.model.js +263 -0
  24. package/dist/components/ComparisonCard/index.d.ts +4 -0
  25. package/dist/components/ComparisonCard/index.js +4 -0
  26. package/dist/components/ForecastCard/ForecastCard.chart.d.ts +82 -0
  27. package/dist/components/ForecastCard/ForecastCard.chart.js +416 -0
  28. package/dist/components/ForecastCard/ForecastCard.d.ts +148 -0
  29. package/dist/components/ForecastCard/ForecastCard.js +1029 -0
  30. package/dist/components/ForecastCard/ForecastCard.model.d.ts +497 -0
  31. package/dist/components/ForecastCard/ForecastCard.model.js +554 -0
  32. package/dist/components/ForecastCard/index.d.ts +4 -0
  33. package/dist/components/ForecastCard/index.js +4 -0
  34. package/dist/components/HighlightCard/HighlightCard.chart.d.ts +122 -0
  35. package/dist/components/HighlightCard/HighlightCard.chart.js +660 -0
  36. package/dist/components/HighlightCard/HighlightCard.d.ts +202 -0
  37. package/dist/components/HighlightCard/HighlightCard.js +1286 -0
  38. package/dist/components/HighlightCard/HighlightCard.model.d.ts +692 -0
  39. package/dist/components/HighlightCard/HighlightCard.model.js +875 -0
  40. package/dist/components/HighlightCard/index.d.ts +4 -0
  41. package/dist/components/HighlightCard/index.js +4 -0
  42. package/dist/components/InsightCard/InsightCard.chart.d.ts +91 -0
  43. package/dist/components/InsightCard/InsightCard.chart.js +471 -0
  44. package/dist/components/InsightCard/InsightCard.d.ts +161 -0
  45. package/dist/components/InsightCard/InsightCard.js +961 -0
  46. package/dist/components/InsightCard/InsightCard.model.d.ts +405 -0
  47. package/dist/components/InsightCard/InsightCard.model.js +421 -0
  48. package/dist/components/InsightCard/index.d.ts +4 -0
  49. package/dist/components/InsightCard/index.js +4 -0
  50. package/dist/components/MetricCard/MetricCard.chart.d.ts +79 -0
  51. package/dist/components/MetricCard/MetricCard.chart.js +435 -0
  52. package/dist/components/MetricCard/MetricCard.d.ts +227 -0
  53. package/dist/components/MetricCard/MetricCard.js +1293 -0
  54. package/dist/components/MetricCard/MetricCard.model.d.ts +521 -0
  55. package/dist/components/MetricCard/MetricCard.model.js +627 -0
  56. package/dist/components/MetricCard/index.d.ts +4 -0
  57. package/dist/components/MetricCard/index.js +4 -0
  58. package/dist/components/ProfileCard/ProfileCard.chart.d.ts +64 -0
  59. package/dist/components/ProfileCard/ProfileCard.chart.js +427 -0
  60. package/dist/components/ProfileCard/ProfileCard.d.ts +140 -0
  61. package/dist/components/ProfileCard/ProfileCard.js +735 -0
  62. package/dist/components/ProfileCard/ProfileCard.model.d.ts +279 -0
  63. package/dist/components/ProfileCard/ProfileCard.model.js +244 -0
  64. package/dist/components/ProfileCard/index.d.ts +4 -0
  65. package/dist/components/ProfileCard/index.js +4 -0
  66. package/dist/components/ProgressCard/ProgressCard.chart.d.ts +73 -0
  67. package/dist/components/ProgressCard/ProgressCard.chart.js +373 -0
  68. package/dist/components/ProgressCard/ProgressCard.d.ts +137 -0
  69. package/dist/components/ProgressCard/ProgressCard.js +908 -0
  70. package/dist/components/ProgressCard/ProgressCard.model.d.ts +385 -0
  71. package/dist/components/ProgressCard/ProgressCard.model.js +389 -0
  72. package/dist/components/ProgressCard/index.d.ts +4 -0
  73. package/dist/components/ProgressCard/index.js +4 -0
  74. package/dist/components/ReportCard/ReportCard.chart.d.ts +45 -0
  75. package/dist/components/ReportCard/ReportCard.chart.js +231 -0
  76. package/dist/components/ReportCard/ReportCard.d.ts +153 -0
  77. package/dist/components/ReportCard/ReportCard.js +1118 -0
  78. package/dist/components/ReportCard/ReportCard.model.d.ts +392 -0
  79. package/dist/components/ReportCard/ReportCard.model.js +399 -0
  80. package/dist/components/ReportCard/index.d.ts +4 -0
  81. package/dist/components/ReportCard/index.js +4 -0
  82. package/dist/components/SankeyCard/SankeyCard.chart.d.ts +75 -0
  83. package/dist/components/SankeyCard/SankeyCard.chart.js +249 -0
  84. package/dist/components/SankeyCard/SankeyCard.d.ts +185 -0
  85. package/dist/components/SankeyCard/SankeyCard.js +788 -0
  86. package/dist/components/SankeyCard/SankeyCard.model.d.ts +501 -0
  87. package/dist/components/SankeyCard/SankeyCard.model.js +789 -0
  88. package/dist/components/SankeyCard/index.d.ts +4 -0
  89. package/dist/components/SankeyCard/index.js +4 -0
  90. package/dist/components/StatusCard/StatusCard.chart.d.ts +60 -0
  91. package/dist/components/StatusCard/StatusCard.chart.js +188 -0
  92. package/dist/components/StatusCard/StatusCard.d.ts +176 -0
  93. package/dist/components/StatusCard/StatusCard.js +989 -0
  94. package/dist/components/StatusCard/StatusCard.model.d.ts +555 -0
  95. package/dist/components/StatusCard/StatusCard.model.js +776 -0
  96. package/dist/components/StatusCard/index.d.ts +4 -0
  97. package/dist/components/StatusCard/index.js +4 -0
  98. package/dist/components/TableCard/TableCard.chart.d.ts +53 -0
  99. package/dist/components/TableCard/TableCard.chart.js +192 -0
  100. package/dist/components/TableCard/TableCard.d.ts +140 -0
  101. package/dist/components/TableCard/TableCard.js +811 -0
  102. package/dist/components/TableCard/TableCard.model.d.ts +381 -0
  103. package/dist/components/TableCard/TableCard.model.js +475 -0
  104. package/dist/components/TableCard/index.d.ts +4 -0
  105. package/dist/components/TableCard/index.js +4 -0
  106. package/dist/components/TargetCard/TargetCard.chart.d.ts +52 -0
  107. package/dist/components/TargetCard/TargetCard.chart.js +273 -0
  108. package/dist/components/TargetCard/TargetCard.d.ts +143 -0
  109. package/dist/components/TargetCard/TargetCard.js +910 -0
  110. package/dist/components/TargetCard/TargetCard.model.d.ts +468 -0
  111. package/dist/components/TargetCard/TargetCard.model.js +581 -0
  112. package/dist/components/TargetCard/index.d.ts +4 -0
  113. package/dist/components/TargetCard/index.js +4 -0
  114. package/dist/components/TreeCard/TreeCard.chart.d.ts +80 -0
  115. package/dist/components/TreeCard/TreeCard.chart.js +178 -0
  116. package/dist/components/TreeCard/TreeCard.d.ts +182 -0
  117. package/dist/components/TreeCard/TreeCard.js +1187 -0
  118. package/dist/components/TreeCard/TreeCard.model.d.ts +330 -0
  119. package/dist/components/TreeCard/TreeCard.model.js +401 -0
  120. package/dist/components/TreeCard/index.d.ts +4 -0
  121. package/dist/components/TreeCard/index.js +4 -0
  122. package/dist/components/TrendCard/TrendCard.chart.d.ts +75 -0
  123. package/dist/components/TrendCard/TrendCard.chart.js +393 -0
  124. package/dist/components/TrendCard/TrendCard.d.ts +224 -0
  125. package/dist/components/TrendCard/TrendCard.js +1053 -0
  126. package/dist/components/TrendCard/TrendCard.model.d.ts +545 -0
  127. package/dist/components/TrendCard/TrendCard.model.js +623 -0
  128. package/dist/components/TrendCard/index.d.ts +4 -0
  129. package/dist/components/TrendCard/index.js +4 -0
  130. package/dist/index.d.ts +76 -0
  131. package/dist/index.js +57 -0
  132. package/dist/lib/vega/ArcVegaChart.d.ts +24 -0
  133. package/dist/lib/vega/ArcVegaChart.js +42 -0
  134. package/dist/lib/vega/ArcVegaSurface.d.ts +13 -0
  135. package/dist/lib/vega/ArcVegaSurface.js +38 -0
  136. package/dist/lib/vega/axisTicks.js +156 -0
  137. package/dist/lib/vega/config.d.ts +29 -0
  138. package/dist/lib/vega/config.js +113 -0
  139. package/dist/lib/vega/embed.d.ts +29 -0
  140. package/dist/lib/vega/embed.js +29 -0
  141. package/dist/lib/vega/engine.d.ts +10 -0
  142. package/dist/lib/vega/engine.js +6 -0
  143. package/dist/lib/vega/formatters.d.ts +125 -0
  144. package/dist/lib/vega/formatters.js +277 -0
  145. package/dist/lib/vega/index.d.ts +11 -0
  146. package/dist/lib/vega/load.d.ts +13 -0
  147. package/dist/lib/vega/load.js +21 -0
  148. package/dist/lib/vega/time.d.ts +45 -0
  149. package/dist/lib/vega/time.js +105 -0
  150. package/dist/lib/vega/tokens.d.ts +73 -0
  151. package/dist/lib/vega/tokens.js +137 -0
  152. package/dist/lib/vega/tooltip.d.ts +45 -0
  153. package/dist/lib/vega/tooltip.js +124 -0
  154. package/dist/lib/vega/useArcVega.d.ts +43 -0
  155. package/dist/lib/vega/useArcVega.js +121 -0
  156. package/package.json +60 -0
@@ -0,0 +1,718 @@
1
+ import { ArcVegaChart } from "../../lib/vega/ArcVegaChart.js";
2
+ import { arcEmbedOptions } from "../../lib/vega/embed.js";
3
+ import { useChartTokens } from "../../lib/vega/tokens.js";
4
+ import { ArcTooltipRows, useArcTooltipHandler } from "../../lib/vega/tooltip.js";
5
+ import { CHART_TYPES, buildChartDescription, buildChartPlotDescription, buildChartTableModel, buildRadarWebRows, buildRampBuckets, createChartFormatters, deriveChartState, hasEnoughData, inferFieldType, missingChartFields, normalizeChartData, resolveChartColorMode, resolveChartLabels } from "./ChartCard.model.js";
6
+ import { CHART_AXES_DATASET, CHART_SERIES_NAMES_PARAM, CHART_TABLE_DATASET, buildChartSpec } from "./ChartCard.chart.js";
7
+ import { forwardRef, useCallback, useEffect, useId, useMemo, useRef, useState } from "react";
8
+ import { Badge, Box, Button, HorizontalScroll, Popover, PopoverPopup, PopoverTitle, PopoverTrigger, Skeleton, SkeletonGroup, SystemState, Table, TableBody, TableCaption, TableCell, TableContainer, TableHead, TableHeader, TableRow, Toggle, cn } from "@arcfusionz/arc-primitive-ui";
9
+ import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
10
+ //#region src/components/ChartCard/ChartCard.tsx
11
+ const sizeStyles = {
12
+ sm: {
13
+ padding: "sm",
14
+ gap: "gap-2",
15
+ gapPx: 8,
16
+ title: "text-base",
17
+ titleLinePx: 24,
18
+ legendLinePx: 20,
19
+ plotHeight: 180
20
+ },
21
+ md: {
22
+ padding: "md",
23
+ gap: "gap-3",
24
+ gapPx: 12,
25
+ title: "text-lg",
26
+ titleLinePx: 28,
27
+ legendLinePx: 22,
28
+ plotHeight: 240
29
+ },
30
+ lg: {
31
+ padding: "lg",
32
+ gap: "gap-4",
33
+ gapPx: 16,
34
+ title: "text-xl",
35
+ titleLinePx: 32,
36
+ legendLinePx: 24,
37
+ plotHeight: 320
38
+ }
39
+ };
40
+ const titleClasses = "font-heading font-semibold tracking-tight text-foreground";
41
+ const legendLabelClasses = "font-sans text-xs font-medium uppercase tracking-widest";
42
+ const panelStateClasses = "w-full max-w-none gap-3 px-3 py-4 [--artwork-box:4rem]";
43
+ const X_AXIS_HEIGHT_PX = 24;
44
+ const PX_PER_TICK = 52;
45
+ const MISSING_NAME = "Chart";
46
+ const NO_SERIES = [];
47
+ function TableIcon(props) {
48
+ return /* @__PURE__ */ jsxs("svg", {
49
+ viewBox: "0 0 24 24",
50
+ fill: "none",
51
+ stroke: "currentColor",
52
+ strokeWidth: 2,
53
+ strokeLinecap: "round",
54
+ strokeLinejoin: "round",
55
+ "aria-hidden": "true",
56
+ ...props,
57
+ children: [/* @__PURE__ */ jsx("rect", {
58
+ x: "3",
59
+ y: "4",
60
+ width: "18",
61
+ height: "16",
62
+ rx: "2"
63
+ }), /* @__PURE__ */ jsx("path", { d: "M3 10h18M9 10v10" })]
64
+ });
65
+ }
66
+ /**
67
+ * A legend swatch in the plot's own mark shape, so a series' meaning never
68
+ * rests on colour alone — the shape and the label carry it too.
69
+ */
70
+ function ChartKey({ mark, color }) {
71
+ return /* @__PURE__ */ jsx("svg", {
72
+ width: "14",
73
+ height: "14",
74
+ viewBox: "0 0 14 14",
75
+ fill: "none",
76
+ "aria-hidden": "true",
77
+ className: "shrink-0",
78
+ style: { color },
79
+ children: mark === "line" ? /* @__PURE__ */ jsx("line", {
80
+ x1: "1",
81
+ y1: "7",
82
+ x2: "13",
83
+ y2: "7",
84
+ stroke: "currentColor",
85
+ strokeWidth: 2.5,
86
+ strokeLinecap: "round"
87
+ }) : mark === "area" || mark === "band" ? /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsx("rect", {
88
+ x: "1",
89
+ y: "6",
90
+ width: "12",
91
+ height: "5",
92
+ rx: "1",
93
+ fill: "currentColor",
94
+ opacity: "0.3"
95
+ }), /* @__PURE__ */ jsx("line", {
96
+ x1: "1",
97
+ y1: "6",
98
+ x2: "13",
99
+ y2: "6",
100
+ stroke: "currentColor",
101
+ strokeWidth: 2,
102
+ strokeLinecap: "round"
103
+ })] }) : mark === "bar" || mark === "rect" || mark === "boxplot" ? /* @__PURE__ */ jsx("rect", {
104
+ x: "2",
105
+ y: "2",
106
+ width: "10",
107
+ height: "10",
108
+ rx: "2",
109
+ fill: "currentColor"
110
+ }) : mark === "tick" || mark === "rule" ? /* @__PURE__ */ jsx("line", {
111
+ x1: "7",
112
+ y1: "1",
113
+ x2: "7",
114
+ y2: "13",
115
+ stroke: "currentColor",
116
+ strokeWidth: 2.5,
117
+ strokeLinecap: "round"
118
+ }) : /* @__PURE__ */ jsx("circle", {
119
+ cx: "7",
120
+ cy: "7",
121
+ r: "4.5",
122
+ fill: "currentColor"
123
+ })
124
+ });
125
+ }
126
+ function useMergedRef(forwarded) {
127
+ const inner = useRef(null);
128
+ return [inner, useCallback((node) => {
129
+ inner.current = node;
130
+ if (typeof forwarded === "function") forwarded(node);
131
+ else if (forwarded) forwarded.current = node;
132
+ }, [forwarded])];
133
+ }
134
+ function usePlotWidth(ref, enabled) {
135
+ const [width, setWidth] = useState(0);
136
+ useEffect(() => {
137
+ if (!enabled) return;
138
+ const el = ref.current;
139
+ if (!el) return;
140
+ let frame = 0;
141
+ const measure = () => {
142
+ const next = Math.round(el.getBoundingClientRect().width);
143
+ setWidth((prev) => prev === next ? prev : next);
144
+ };
145
+ measure();
146
+ if (typeof ResizeObserver === "undefined") return;
147
+ const observer = new ResizeObserver(() => {
148
+ cancelAnimationFrame(frame);
149
+ frame = requestAnimationFrame(measure);
150
+ });
151
+ observer.observe(el);
152
+ return () => {
153
+ cancelAnimationFrame(frame);
154
+ observer.disconnect();
155
+ };
156
+ }, [ref, enabled]);
157
+ return width;
158
+ }
159
+ /**
160
+ * A chart panel for dashboards: a title, a legend, and a chart — nothing else
161
+ * on its face. `type` picks one of 36 Vega-Lite forms, `data` is plain records
162
+ * and the remaining fields are field NAMES, so the same card draws a stacked
163
+ * bar, a donut, a heatmap, a box plot or a radar without changing shape.
164
+ *
165
+ * Where the other cards in this package answer one fixed question in text and
166
+ * illustrate it with a plot, ChartCard is the plot: the comparison is the form
167
+ * the consumer chose. Every state is handled and layout-stable, and the text
168
+ * equivalents the chart owes a screen reader — a generated description, a
169
+ * generated plot label, and a table twin holding every value — are all there,
170
+ * none of them on the card's face.
171
+ */
172
+ const ChartCard = forwardRef(({ title, name: nameProp, type, data, x: xField, y: yField, series: seriesField, value: valueField, sizeBy: sizeField, x2: x2Field, xType: xTypeProp, yType: yTypeProp, xLabel, yLabel, valueLabel, seriesLabel, sizeLabel, axisTitles = false, axes = true, grid = true, legend, legendPosition = "top", visibleSeries, defaultVisibleSeries, onVisibleSeriesChange, seriesColor = "auto", maxSeries, sort = "none", aggregate, maxBins = 20, extent, innerRadius = .6, format = "compact", formatValue, height: heightProp, tooltip = true, status = "ready", filtered = false, stale = false, onRetry, onClearFilters, size = "md", titleLevel = 3, locale, timeZone, labels: labelOverrides, description, action, tableToggle = "focus", defaultTableOpen = false, onEmbed, patchSpec, render, className, ...rest }, forwardedRef) => {
173
+ const labels = useMemo(() => resolveChartLabels(labelOverrides), [labelOverrides]);
174
+ const sizes = sizeStyles[size];
175
+ const info = CHART_TYPES[type];
176
+ const plotHeight = heightProp ?? sizes.plotHeight + (axes && !info.polar ? X_AXIS_HEIGHT_PX : 0);
177
+ const name = nameProp ?? (typeof title === "string" ? title : MISSING_NAME);
178
+ const id = useId();
179
+ const headingId = `${id}-title`;
180
+ const descriptionId = `${id}-description`;
181
+ const tableId = `${id}-table`;
182
+ const [rootRef, setRootRef] = useMergedRef(forwardedRef);
183
+ const plotRef = useRef(null);
184
+ const [tableOpen, setTableOpen] = useState(defaultTableOpen);
185
+ const [popoverContainer, setPopoverContainer] = useState(null);
186
+ const tokens = useChartTokens(rootRef);
187
+ const isVisibleControlled = visibleSeries !== void 0;
188
+ const [innerVisible, setInnerVisible] = useState(defaultVisibleSeries);
189
+ const visibleRequest = isVisibleControlled ? visibleSeries : innerVisible;
190
+ const pendingAnnounce = useRef(false);
191
+ const [announcement, setAnnouncement] = useState("");
192
+ const xType = xTypeProp ?? inferFieldType(data, xField) ?? "nominal";
193
+ const yType = yTypeProp ?? inferFieldType(data, yField) ?? "quantitative";
194
+ const missing = useMemo(() => missingChartFields(type, {
195
+ x: xField,
196
+ y: yField,
197
+ series: seriesField,
198
+ value: valueField,
199
+ size: sizeField,
200
+ x2: x2Field
201
+ }), [
202
+ type,
203
+ xField,
204
+ yField,
205
+ seriesField,
206
+ valueField,
207
+ sizeField,
208
+ x2Field
209
+ ]);
210
+ const seriesCount = useMemo(() => {
211
+ if (!seriesField) return 1;
212
+ const seen = /* @__PURE__ */ new Set();
213
+ for (const record of data) {
214
+ const raw = record[seriesField];
215
+ if (raw != null) seen.add(String(raw));
216
+ }
217
+ return Math.max(1, seen.size);
218
+ }, [data, seriesField]);
219
+ const slots = tokens?.category.length ?? 3;
220
+ const colorMode = resolveChartColorMode(seriesColor, type, maxSeries != null ? Math.min(seriesCount, maxSeries) : seriesCount, slots);
221
+ const summary = useMemo(() => tokens && missing.length === 0 ? normalizeChartData(data, {
222
+ type,
223
+ x: xField,
224
+ y: yField,
225
+ series: seriesField,
226
+ value: valueField,
227
+ size: sizeField,
228
+ x2: x2Field,
229
+ xType,
230
+ yType,
231
+ visible: visibleRequest,
232
+ maxSeries,
233
+ otherLabel: labels.other,
234
+ sort,
235
+ colorMode,
236
+ tokens
237
+ }) : null, [
238
+ tokens,
239
+ missing,
240
+ data,
241
+ type,
242
+ xField,
243
+ yField,
244
+ seriesField,
245
+ valueField,
246
+ sizeField,
247
+ x2Field,
248
+ xType,
249
+ yType,
250
+ visibleRequest,
251
+ maxSeries,
252
+ labels.other,
253
+ sort,
254
+ colorMode
255
+ ]);
256
+ const dimensionName = xLabel ?? xField ?? seriesLabel ?? seriesField ?? "category";
257
+ const measureName = valueLabel ?? valueField ?? yLabel ?? yField ?? "value";
258
+ const seriesName = seriesLabel ?? seriesField ?? labels.tableSeries;
259
+ const positionName = yLabel ?? yField ?? measureName;
260
+ const formatters = useMemo(() => createChartFormatters({
261
+ format,
262
+ locale,
263
+ timeZone,
264
+ formatValue,
265
+ xType,
266
+ yType,
267
+ granularity: summary?.granularity ?? "day"
268
+ }), [
269
+ format,
270
+ locale,
271
+ timeZone,
272
+ formatValue,
273
+ xType,
274
+ yType,
275
+ summary
276
+ ]);
277
+ const hasData = data.length > 0;
278
+ const hasChart = missing.length === 0 && hasEnoughData(type, summary);
279
+ const state = deriveChartState({
280
+ status,
281
+ hasChart,
282
+ hasData,
283
+ filtered
284
+ });
285
+ const showContent = state === "ready" || state === "refreshing";
286
+ const allSeries = summary?.series ?? NO_SERIES;
287
+ const visibleKeys = useMemo(() => {
288
+ const known = new Set(allSeries.map((entry) => entry.key));
289
+ const requested = visibleRequest?.filter((key) => known.has(key));
290
+ return requested && requested.length > 0 ? requested : [...known];
291
+ }, [allSeries, visibleRequest]);
292
+ const visibleSet = useMemo(() => new Set(visibleKeys), [visibleKeys]);
293
+ const toggleSeries = useCallback((key, shown) => {
294
+ const keys = allSeries.map((entry) => entry.key);
295
+ const next = shown ? keys.filter((entry) => visibleSet.has(entry) || entry === key) : visibleKeys.filter((entry) => entry !== key);
296
+ if (next.length === 0) return;
297
+ pendingAnnounce.current = true;
298
+ if (!isVisibleControlled) setInnerVisible(next);
299
+ onVisibleSeriesChange?.(next);
300
+ }, [
301
+ allSeries,
302
+ visibleSet,
303
+ visibleKeys,
304
+ isVisibleControlled,
305
+ onVisibleSeriesChange
306
+ ]);
307
+ const cardDescription = description ?? buildChartDescription({
308
+ name,
309
+ type,
310
+ state,
311
+ summary,
312
+ visibleKeys,
313
+ formatters,
314
+ labels,
315
+ measureName,
316
+ dimensionName,
317
+ aggregate,
318
+ stale
319
+ });
320
+ useEffect(() => {
321
+ if (!pendingAnnounce.current) return;
322
+ pendingAnnounce.current = false;
323
+ setAnnouncement(cardDescription);
324
+ }, [cardDescription]);
325
+ const plotWidth = usePlotWidth(plotRef, showContent && hasChart);
326
+ const plotSize = info.square ? Math.max(0, Math.min(plotWidth, plotHeight)) : plotWidth;
327
+ const tooltipContent = useCallback(({ value }) => {
328
+ if (value == null) return null;
329
+ if (typeof value !== "object") return String(value);
330
+ const rows = Object.entries(value).filter(([label]) => label !== "" && label !== "c" && label !== "s" && label !== "o" && label !== "i").map(([label, entry]) => [label, String(entry)]);
331
+ return rows.length > 0 ? /* @__PURE__ */ jsx(ArcTooltipRows, { rows }) : null;
332
+ }, []);
333
+ const { handler: tooltipHandler, tooltip: tooltipNode } = useArcTooltipHandler({ content: tooltipContent });
334
+ const embedOptions = useMemo(() => arcEmbedOptions({ tooltip: tooltip ? tooltipHandler : false }), [tooltip, tooltipHandler]);
335
+ const yTicks = Math.max(2, Math.round(plotHeight / PX_PER_TICK));
336
+ const timeGrammar = (kind) => [
337
+ kind,
338
+ locale ?? "",
339
+ timeZone ?? ""
340
+ ].join("@").replace(/@+$/, "");
341
+ const axisGranularity = summary?.granularity === "datetime" ? "day-time" : summary?.granularity ?? "day";
342
+ const valueFormatGrammar = locale ? `${format}@${locale}` : format;
343
+ const timeFormatGrammar = timeGrammar(summary?.granularity ?? "day");
344
+ const axisTimeFormatGrammar = timeGrammar(axisGranularity === "day" ? "day-short" : axisGranularity);
345
+ const xAxisTitle = axisTitles ? dimensionName : null;
346
+ const yAxisTitle = axisTitles ? positionName : null;
347
+ const spec = useMemo(() => {
348
+ if (!tokens) return null;
349
+ const built = buildChartSpec({
350
+ type,
351
+ xType,
352
+ yType,
353
+ height: plotHeight,
354
+ axes,
355
+ grid,
356
+ axisTitles,
357
+ xTitle: xAxisTitle,
358
+ yTitle: yAxisTitle,
359
+ valueTitle: measureName,
360
+ seriesTitle: seriesName,
361
+ sizeTitle: sizeLabel ?? sizeField,
362
+ valueFormat: valueFormatGrammar,
363
+ timeFormat: timeFormatGrammar,
364
+ axisTimeFormat: axisTimeFormatGrammar,
365
+ aggregate,
366
+ maxBins,
367
+ extent,
368
+ tooltip,
369
+ hasX: xField != null,
370
+ innerRadius,
371
+ yTicks,
372
+ locale,
373
+ timeZone
374
+ }, tokens);
375
+ return patchSpec ? patchSpec(built) : built;
376
+ }, [
377
+ tokens,
378
+ type,
379
+ xType,
380
+ yType,
381
+ plotHeight,
382
+ axes,
383
+ grid,
384
+ axisTitles,
385
+ xAxisTitle,
386
+ yAxisTitle,
387
+ measureName,
388
+ seriesName,
389
+ sizeLabel,
390
+ sizeField,
391
+ valueFormatGrammar,
392
+ timeFormatGrammar,
393
+ axisTimeFormatGrammar,
394
+ aggregate,
395
+ maxBins,
396
+ extent,
397
+ tooltip,
398
+ xField,
399
+ innerRadius,
400
+ yTicks,
401
+ locale,
402
+ timeZone,
403
+ patchSpec
404
+ ]);
405
+ const rows = summary?.rows;
406
+ const webRows = useMemo(() => type === "radar" ? buildRadarWebRows(summary) : [], [type, summary]);
407
+ const datasets = useMemo(() => ({
408
+ [CHART_TABLE_DATASET]: rows ?? [],
409
+ [CHART_AXES_DATASET]: webRows
410
+ }), [rows, webRows]);
411
+ const seriesNames = useMemo(() => allSeries.map((entry) => entry.label), [allSeries]);
412
+ const signals = useMemo(() => type === "violin" ? { [CHART_SERIES_NAMES_PARAM]: seriesNames } : void 0, [type, seriesNames]);
413
+ const plotDescription = buildChartPlotDescription({
414
+ type,
415
+ measureName,
416
+ dimensionName,
417
+ labels
418
+ });
419
+ const rampBuckets = useMemo(() => info.legend === "ramp" && tokens ? buildRampBuckets(summary?.domain ?? null, tokens, formatters.formatValue) : [], [
420
+ info.legend,
421
+ tokens,
422
+ summary,
423
+ formatters
424
+ ]);
425
+ const legendKind = info.legend === "ramp" ? "ramp" : allSeries.length > 1 ? "series" : "none";
426
+ const showLegend = (legend ?? legendKind !== "none") && showContent && legendKind !== "none";
427
+ const reserveLegend = legend ?? (seriesCount > 1 || info.legend === "ramp");
428
+ const legendNode = showLegend && /* @__PURE__ */ jsx(HorizontalScroll, {
429
+ fade: true,
430
+ className: "min-w-0",
431
+ "data-part": "legend-scroll",
432
+ children: /* @__PURE__ */ jsx("div", {
433
+ role: legendKind === "series" ? "group" : void 0,
434
+ "aria-label": legendKind === "series" ? labels.legendGroup(name) : void 0,
435
+ className: "flex min-w-0 flex-wrap items-center gap-x-4 gap-y-1",
436
+ style: { minHeight: sizes.legendLinePx },
437
+ "data-part": "legend",
438
+ children: legendKind === "series" ? allSeries.map((entry) => {
439
+ const shown = visibleSet.has(entry.key);
440
+ return /* @__PURE__ */ jsxs(Toggle, {
441
+ variant: "ghost",
442
+ size: "sm",
443
+ pressed: shown,
444
+ onPressedChange: (next) => toggleSeries(entry.key, next),
445
+ disabled: shown && visibleKeys.length <= 1,
446
+ "aria-label": shown ? labels.hideSeries(entry.label) : labels.showSeries(entry.label),
447
+ className: cn("h-auto max-w-full gap-1.5 bg-transparent px-1 py-0.5 hover:bg-muted", "data-pressed:bg-transparent data-pressed:hover:bg-muted", shown ? "" : "opacity-50"),
448
+ "data-part": "legend-chip",
449
+ "data-shown": shown ? "" : void 0,
450
+ children: [/* @__PURE__ */ jsx(ChartKey, {
451
+ mark: info.mark,
452
+ color: shown ? entry.color : tokens?.mutedForeground ?? "currentColor"
453
+ }), /* @__PURE__ */ jsx("span", {
454
+ className: cn(legendLabelClasses, "min-w-0 truncate text-muted-foreground"),
455
+ children: entry.label
456
+ })]
457
+ }, entry.key);
458
+ }) : rampBuckets.map((bucket, at) => /* @__PURE__ */ jsxs("span", {
459
+ className: "inline-flex max-w-full items-center gap-1.5 px-1 py-0.5",
460
+ "data-part": "legend-bucket",
461
+ children: [/* @__PURE__ */ jsx("span", {
462
+ "aria-hidden": "true",
463
+ className: "size-3 shrink-0 rounded-sm",
464
+ style: { background: bucket.color }
465
+ }), /* @__PURE__ */ jsx("span", {
466
+ className: cn(legendLabelClasses, "min-w-0 truncate text-muted-foreground tabular-nums"),
467
+ children: bucket.label
468
+ })]
469
+ }, at))
470
+ })
471
+ });
472
+ const tableModel = useMemo(() => showContent ? buildChartTableModel({
473
+ type,
474
+ summary,
475
+ formatters,
476
+ labels,
477
+ measureName,
478
+ dimensionName,
479
+ seriesName,
480
+ sizeName: sizeLabel ?? sizeField
481
+ }) : null, [
482
+ showContent,
483
+ type,
484
+ summary,
485
+ formatters,
486
+ labels,
487
+ measureName,
488
+ dimensionName,
489
+ seriesName,
490
+ sizeLabel,
491
+ sizeField
492
+ ]);
493
+ const hasTable = tableModel != null && tableToggle !== "none";
494
+ const bodyMinHeight = plotHeight + (reserveLegend ? sizes.legendLinePx + sizes.gapPx : 0);
495
+ const HeadingTag = `h${titleLevel}`;
496
+ const busy = state === "loading" || state === "refreshing";
497
+ return /* @__PURE__ */ jsxs(Box, {
498
+ ref: setRootRef,
499
+ render: render ?? /* @__PURE__ */ jsx("figure", {}),
500
+ padding: sizes.padding,
501
+ "aria-labelledby": headingId,
502
+ "aria-describedby": descriptionId,
503
+ "aria-busy": busy || void 0,
504
+ "data-status": status,
505
+ "data-state": state,
506
+ "data-size": size,
507
+ "data-type": type,
508
+ "data-intent": info.intent,
509
+ "data-series-color": colorMode,
510
+ "data-legend": showLegend ? legendKind : void 0,
511
+ "data-empty": state === "empty" || state === "no-match" ? "" : void 0,
512
+ "data-insufficient": state === "insufficient" ? "" : void 0,
513
+ "data-stale": stale ? "" : void 0,
514
+ "data-folded": showContent && summary && summary.folded > 0 ? summary.folded : void 0,
515
+ "data-hidden": showContent ? allSeries.length - visibleKeys.length || void 0 : void 0,
516
+ ...rest,
517
+ className: cn("relative flex min-w-0 flex-col", sizes.gap, className),
518
+ children: [
519
+ /* @__PURE__ */ jsxs("div", {
520
+ className: "flex items-start justify-between gap-2",
521
+ children: [/* @__PURE__ */ jsx(HeadingTag, {
522
+ id: headingId,
523
+ className: cn(titleClasses, sizes.title, "min-w-0 line-clamp-2"),
524
+ children: title
525
+ }), /* @__PURE__ */ jsxs("div", {
526
+ className: "flex shrink-0 items-center gap-1",
527
+ children: [
528
+ stale && /* @__PURE__ */ jsx(Badge, {
529
+ variant: "warning",
530
+ appearance: "soft",
531
+ size: "sm",
532
+ children: labels.stale
533
+ }),
534
+ hasTable && /* @__PURE__ */ jsxs(Popover, {
535
+ open: tableOpen,
536
+ onOpenChange: setTableOpen,
537
+ children: [/* @__PURE__ */ jsx(PopoverTrigger, {
538
+ render: /* @__PURE__ */ jsx(Button, {
539
+ variant: "ghost",
540
+ size: "sm",
541
+ iconOnly: true,
542
+ circular: true,
543
+ "aria-label": tableOpen ? labels.hideTable(name) : labels.showTable(name),
544
+ className: cn("size-6 min-w-0 text-muted-foreground hover:text-foreground", tableToggle === "focus" && !tableOpen && "sr-only focus-visible:not-sr-only focus-visible:relative focus-visible:size-6")
545
+ }),
546
+ children: /* @__PURE__ */ jsx(TableIcon, { className: "size-3.5" })
547
+ }), popoverContainer && /* @__PURE__ */ jsxs(PopoverPopup, {
548
+ id: tableId,
549
+ anchor: rootRef,
550
+ container: popoverContainer,
551
+ side: "right",
552
+ align: "start",
553
+ collisionPadding: 8,
554
+ className: "pointer-events-auto w-[min(32rem,var(--available-width))]",
555
+ "data-part": "data-popover",
556
+ children: [
557
+ /* @__PURE__ */ jsx(PopoverTitle, {
558
+ className: "mb-3",
559
+ children: name
560
+ }),
561
+ /* @__PURE__ */ jsx(TableContainer, {
562
+ variant: "plain",
563
+ tabIndex: 0,
564
+ role: "region",
565
+ "aria-label": labels.tableCaption(name),
566
+ className: "max-h-[min(22rem,calc(var(--available-height)-5rem))] focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ring",
567
+ "data-part": "table",
568
+ children: /* @__PURE__ */ jsxs(Table, {
569
+ size: "sm",
570
+ stickyHeader: true,
571
+ hoverable: false,
572
+ children: [
573
+ /* @__PURE__ */ jsx(TableCaption, {
574
+ side: "top",
575
+ className: "sr-only",
576
+ children: labels.tableCaption(name)
577
+ }),
578
+ /* @__PURE__ */ jsx(TableHeader, { children: /* @__PURE__ */ jsx(TableRow, { children: tableModel.columns.map((column) => /* @__PURE__ */ jsx(TableHead, {
579
+ className: column.numeric ? "text-right" : void 0,
580
+ children: column.label
581
+ }, column.key)) }) }),
582
+ /* @__PURE__ */ jsx(TableBody, { children: tableModel.rows.map((cells, at) => /* @__PURE__ */ jsx(TableRow, { children: cells.map((cell, index) => /* @__PURE__ */ jsx(TableCell, {
583
+ className: tableModel.columns[index].numeric ? "text-right tabular-nums" : void 0,
584
+ children: cell
585
+ }, tableModel.columns[index].key)) }, at)) })
586
+ ]
587
+ })
588
+ }),
589
+ tableModel.shown < tableModel.total && /* @__PURE__ */ jsx("p", {
590
+ className: "mt-3 font-sans text-xs text-muted-foreground",
591
+ children: labels.tableTruncated(tableModel.shown, tableModel.total)
592
+ })
593
+ ]
594
+ })]
595
+ }),
596
+ action
597
+ ]
598
+ })]
599
+ }),
600
+ /* @__PURE__ */ jsx("p", {
601
+ id: descriptionId,
602
+ className: "sr-only",
603
+ children: cardDescription
604
+ }),
605
+ /* @__PURE__ */ jsx("p", {
606
+ role: "status",
607
+ "aria-live": "polite",
608
+ className: "sr-only",
609
+ "data-part": "announcement",
610
+ children: announcement
611
+ }),
612
+ /* @__PURE__ */ jsxs("div", {
613
+ className: cn("relative flex min-w-0 flex-col", sizes.gap),
614
+ style: { minHeight: bodyMinHeight },
615
+ children: [
616
+ state === "loading" && /* @__PURE__ */ jsxs(SkeletonGroup, {
617
+ className: cn("flex flex-col", sizes.gap),
618
+ children: [reserveLegend && /* @__PURE__ */ jsx("div", {
619
+ className: "flex flex-wrap gap-3",
620
+ children: Array.from({ length: Math.max(2, Math.min(4, seriesCount || 3)) }, (_, index) => /* @__PURE__ */ jsx(Skeleton, {
621
+ variant: "rounded",
622
+ className: "w-20",
623
+ style: { height: sizes.legendLinePx }
624
+ }, index))
625
+ }), /* @__PURE__ */ jsx(Skeleton, {
626
+ variant: "rounded",
627
+ className: "w-full",
628
+ style: { height: plotHeight }
629
+ })]
630
+ }),
631
+ state === "error" && /* @__PURE__ */ jsx(SystemState, {
632
+ variant: "server-error",
633
+ scope: "inline",
634
+ title: labels.error,
635
+ description: false,
636
+ action: onRetry ? /* @__PURE__ */ jsx(Button, {
637
+ size: "sm",
638
+ onClick: onRetry,
639
+ children: labels.retry
640
+ }) : void 0,
641
+ className: cn(panelStateClasses, "my-auto")
642
+ }),
643
+ state === "empty" && /* @__PURE__ */ jsx(SystemState, {
644
+ variant: "empty",
645
+ scope: "inline",
646
+ title: labels.noData,
647
+ description: false,
648
+ className: cn(panelStateClasses, "my-auto")
649
+ }),
650
+ state === "no-match" && /* @__PURE__ */ jsx(SystemState, {
651
+ variant: "no-results",
652
+ scope: "inline",
653
+ title: labels.noMatch,
654
+ description: false,
655
+ action: onClearFilters ? /* @__PURE__ */ jsx(Button, {
656
+ variant: "outline",
657
+ size: "sm",
658
+ onClick: onClearFilters,
659
+ children: labels.clearFilters
660
+ }) : void 0,
661
+ className: cn(panelStateClasses, "my-auto")
662
+ }),
663
+ state === "insufficient" && /* @__PURE__ */ jsx(SystemState, {
664
+ variant: "empty",
665
+ scope: "inline",
666
+ title: labels.insufficient,
667
+ description: false,
668
+ className: cn(panelStateClasses, "my-auto")
669
+ }),
670
+ showContent && /* @__PURE__ */ jsxs("div", {
671
+ className: cn("flex min-w-0 flex-col", sizes.gap),
672
+ children: [
673
+ legendPosition === "top" && legendNode,
674
+ /* @__PURE__ */ jsx("div", {
675
+ ref: plotRef,
676
+ className: cn("relative w-full", info.square && "flex items-center justify-center"),
677
+ style: { height: plotHeight },
678
+ "data-part": "plot",
679
+ children: plotSize > 0 && spec ? /* @__PURE__ */ jsx(ArcVegaChart, {
680
+ spec,
681
+ options: embedOptions,
682
+ datasets,
683
+ signals,
684
+ description: plotDescription,
685
+ width: plotSize,
686
+ height: info.square ? plotSize : plotHeight,
687
+ onEmbed,
688
+ className: cn("[&_svg]:block [&_.vega-bindings:empty]:hidden", info.square ? "shrink-0" : "absolute inset-0"),
689
+ fallback: /* @__PURE__ */ jsx(Skeleton, {
690
+ variant: "rounded",
691
+ className: "size-full"
692
+ })
693
+ }) : /* @__PURE__ */ jsx(Skeleton, {
694
+ variant: "rounded",
695
+ className: "size-full"
696
+ })
697
+ }),
698
+ legendPosition === "bottom" && legendNode
699
+ ]
700
+ }),
701
+ state === "refreshing" && /* @__PURE__ */ jsx("div", {
702
+ "aria-hidden": "true",
703
+ className: "absolute -inset-1 rounded-md bg-veil"
704
+ })
705
+ ]
706
+ }),
707
+ /* @__PURE__ */ jsx("div", {
708
+ ref: setPopoverContainer,
709
+ className: "pointer-events-none absolute inset-0",
710
+ "data-part": "popover-host"
711
+ }),
712
+ tooltipNode
713
+ ]
714
+ });
715
+ });
716
+ ChartCard.displayName = "ChartCard";
717
+ //#endregion
718
+ export { ChartCard };