@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,1286 @@
1
+ import { ArcVegaChart } from "../../lib/vega/ArcVegaChart.js";
2
+ import { arcEmbedOptions } from "../../lib/vega/embed.js";
3
+ import { formatDate } from "../../lib/vega/formatters.js";
4
+ import { useChartTokens } from "../../lib/vega/tokens.js";
5
+ import { ArcTooltipRows, useArcTooltipHandler } from "../../lib/vega/tooltip.js";
6
+ import { HIGHLIGHT_BAND_DATASET, HIGHLIGHT_DRIVERS_DATASET, HIGHLIGHT_ENDPOINTS_DATASET, HIGHLIGHT_INSIDE_DATASET, HIGHLIGHT_OUTSIDE_DATASET, HIGHLIGHT_PEERS_DATASET, HIGHLIGHT_TABLE_DATASET, HIGHLIGHT_TYPICAL_DATASET, HIGHLIGHT_ZERO_DATASET, buildHighlightContextSpec, buildHighlightDriversSpec, buildHighlightPeersSpec } from "./HighlightCard.chart.js";
7
+ import { buildContextDescription, buildDriverRows, buildDriversDescription, buildHighlightAnalysisInsight, buildHighlightContextDatasets, buildHighlightDescription, buildPeerVerdict, buildPeersDescription, buildTypicalRows, createHighlightFormatters, deriveHighlightState, derivePeers, normalizeDrivers, normalizeHighlightSeries, resolveHighlightLabels, resolveHighlightWindow } from "./HighlightCard.model.js";
8
+ import { Fragment, forwardRef, useCallback, useEffect, useId, useLayoutEffect, useMemo, useRef, useState } from "react";
9
+ import { AILoader, Badge, Box, Button, Popover, PopoverPopup, PopoverTitle, PopoverTrigger, Skeleton, SkeletonGroup, SystemState, Table, TableBody, TableCaption, TableCell, TableContainer, TableHead, TableHeader, TableRow, Tooltip, TooltipPopup, TooltipTrigger, cn } from "@arcfusionz/arc-primitive-ui";
10
+ import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
11
+ //#region src/components/HighlightCard/HighlightCard.tsx
12
+ const sizeStyles = {
13
+ sm: {
14
+ padding: "sm",
15
+ gap: "gap-2",
16
+ gapPx: 8,
17
+ sectionGap: "gap-3",
18
+ label: "text-xs",
19
+ value: "text-2xl leading-8",
20
+ valueLinePx: 32,
21
+ body: "text-xs",
22
+ rowText: "text-xs",
23
+ badge: "sm",
24
+ headlinePx: 52,
25
+ contextHeight: 132,
26
+ peersHeight: 52,
27
+ step: 22,
28
+ barHeight: 10,
29
+ sectionTitlePx: 18
30
+ },
31
+ md: {
32
+ padding: "md",
33
+ gap: "gap-3",
34
+ gapPx: 12,
35
+ sectionGap: "gap-4",
36
+ label: "text-sm",
37
+ value: "text-3xl leading-9",
38
+ valueLinePx: 36,
39
+ body: "text-sm",
40
+ rowText: "text-sm",
41
+ badge: "md",
42
+ headlinePx: 60,
43
+ contextHeight: 176,
44
+ peersHeight: 60,
45
+ step: 26,
46
+ barHeight: 12,
47
+ sectionTitlePx: 20
48
+ },
49
+ lg: {
50
+ padding: "lg",
51
+ gap: "gap-3",
52
+ gapPx: 12,
53
+ sectionGap: "gap-5",
54
+ label: "text-sm",
55
+ value: "text-4xl leading-10",
56
+ valueLinePx: 40,
57
+ body: "text-sm",
58
+ rowText: "text-sm",
59
+ badge: "lg",
60
+ headlinePx: 64,
61
+ contextHeight: 216,
62
+ peersHeight: 68,
63
+ step: 30,
64
+ barHeight: 14,
65
+ sectionTitlePx: 20
66
+ }
67
+ };
68
+ const labelClasses = "min-w-0 font-sans font-medium text-muted-foreground";
69
+ const valueClasses = "font-heading font-semibold tracking-tight";
70
+ const captionClasses = "font-sans text-xs text-muted-foreground";
71
+ const sectionTitleClasses = "font-sans font-medium text-muted-foreground";
72
+ const changeTextClasses = {
73
+ positive: "text-chart-positive",
74
+ negative: "text-chart-negative",
75
+ neutral: "text-foreground"
76
+ };
77
+ const headerControlClasses = "size-6 min-w-0 text-muted-foreground hover:text-foreground";
78
+ const panelStateClasses = "w-full max-w-none gap-3 px-3 py-4 [--artwork-box:4rem]";
79
+ const driverGridClasses = "grid grid-cols-[fit-content(38%)_minmax(0,1fr)_auto_auto] gap-x-3";
80
+ const X_AXIS_HEIGHT_PX = 24;
81
+ const MISSING_NAME = "Highlight";
82
+ const SECTION_LEVEL = {
83
+ 2: 3,
84
+ 3: 4,
85
+ 4: 5,
86
+ 5: 6,
87
+ 6: 6
88
+ };
89
+ const DEFAULT_SECTIONS = [
90
+ "context",
91
+ "peers",
92
+ "drivers"
93
+ ];
94
+ const EMPTY_DRIVERS = [];
95
+ const ZERO_ROWS = [{ value: 0 }];
96
+ function InfoIcon(props) {
97
+ return /* @__PURE__ */ jsxs("svg", {
98
+ viewBox: "0 0 24 24",
99
+ fill: "none",
100
+ stroke: "currentColor",
101
+ strokeWidth: 2,
102
+ strokeLinecap: "round",
103
+ strokeLinejoin: "round",
104
+ "aria-hidden": "true",
105
+ ...props,
106
+ children: [/* @__PURE__ */ jsx("circle", {
107
+ cx: "12",
108
+ cy: "12",
109
+ r: "9"
110
+ }), /* @__PURE__ */ jsx("path", { d: "M12 11v5M12 8h.01" })]
111
+ });
112
+ }
113
+ function DeltaGlyph({ direction, ...props }) {
114
+ return /* @__PURE__ */ jsxs("svg", {
115
+ viewBox: "0 0 24 24",
116
+ fill: "none",
117
+ stroke: "currentColor",
118
+ strokeWidth: 2.5,
119
+ strokeLinecap: "round",
120
+ strokeLinejoin: "round",
121
+ "aria-hidden": "true",
122
+ ...props,
123
+ children: [
124
+ direction === "up" && /* @__PURE__ */ jsx("path", { d: "M7 17 17 7M7 7h10v10" }),
125
+ direction === "down" && /* @__PURE__ */ jsx("path", { d: "M7 7 17 17M7 17h10V7" }),
126
+ direction === "flat" && /* @__PURE__ */ jsx("path", { d: "M5 12h14M13 6l6 6-6 6" })
127
+ ]
128
+ });
129
+ }
130
+ /** A generated insight: words as text, values in bold. */
131
+ function InsightText({ segments }) {
132
+ return /* @__PURE__ */ jsx(Fragment$1, { children: segments.map((segment, index) => typeof segment === "string" ? /* @__PURE__ */ jsx(Fragment, { children: segment }, index) : /* @__PURE__ */ jsx("strong", { children: segment.value }, index)) });
133
+ }
134
+ /** The bar swatch beside a driver's name — the exact hue its bar wears. */
135
+ function DriverSwatch({ color, hollow }) {
136
+ return /* @__PURE__ */ jsx("svg", {
137
+ viewBox: "0 0 12 12",
138
+ fill: "none",
139
+ "aria-hidden": "true",
140
+ className: "size-3 shrink-0",
141
+ style: { color },
142
+ children: /* @__PURE__ */ jsx("rect", {
143
+ x: "1",
144
+ y: "3",
145
+ width: "10",
146
+ height: "6",
147
+ rx: "1.5",
148
+ fill: "currentColor",
149
+ fillOpacity: hollow ? .35 : 1
150
+ })
151
+ });
152
+ }
153
+ function useMergedRef(forwarded) {
154
+ const inner = useRef(null);
155
+ return [inner, useCallback((node) => {
156
+ inner.current = node;
157
+ if (typeof forwarded === "function") forwarded(node);
158
+ else if (forwarded) forwarded.current = node;
159
+ }, [forwarded])];
160
+ }
161
+ function usePlotWidth(ref, enabled) {
162
+ const [width, setWidth] = useState(0);
163
+ useEffect(() => {
164
+ if (!enabled) return;
165
+ const el = ref.current;
166
+ if (!el) return;
167
+ let frame = 0;
168
+ const measure = () => {
169
+ const next = Math.round(el.getBoundingClientRect().width);
170
+ setWidth((prev) => prev === next ? prev : next);
171
+ };
172
+ measure();
173
+ if (typeof ResizeObserver === "undefined") return;
174
+ const observer = new ResizeObserver(() => {
175
+ cancelAnimationFrame(frame);
176
+ frame = requestAnimationFrame(measure);
177
+ });
178
+ observer.observe(el);
179
+ return () => {
180
+ cancelAnimationFrame(frame);
181
+ observer.disconnect();
182
+ };
183
+ }, [ref, enabled]);
184
+ return width;
185
+ }
186
+ function usePlotBox(cellRef, wrapperRef, enabled, resetKey) {
187
+ const [box, setBox] = useState({
188
+ left: 0,
189
+ width: 0
190
+ });
191
+ useLayoutEffect(() => {
192
+ if (!enabled) return;
193
+ const cell = cellRef.current;
194
+ const wrapper = wrapperRef.current;
195
+ if (!cell || !wrapper) return;
196
+ let frame = 0;
197
+ const measure = () => {
198
+ const cellRect = cell.getBoundingClientRect();
199
+ const wrapperRect = wrapper.getBoundingClientRect();
200
+ const next = {
201
+ left: cellRect.left - wrapperRect.left,
202
+ width: Math.round(cellRect.width)
203
+ };
204
+ setBox((prev) => prev.left === next.left && prev.width === next.width ? prev : next);
205
+ };
206
+ measure();
207
+ if (typeof ResizeObserver === "undefined") return;
208
+ const observer = new ResizeObserver(() => {
209
+ cancelAnimationFrame(frame);
210
+ frame = requestAnimationFrame(measure);
211
+ });
212
+ observer.observe(cell);
213
+ observer.observe(wrapper);
214
+ return () => {
215
+ cancelAnimationFrame(frame);
216
+ observer.disconnect();
217
+ };
218
+ }, [
219
+ cellRef,
220
+ wrapperRef,
221
+ enabled,
222
+ resetKey
223
+ ]);
224
+ return box;
225
+ }
226
+ /**
227
+ * A highlight-analysis panel: one window of a metric, established and
228
+ * explained. The change across the window is the headline; the window is
229
+ * painted inside the whole series; its change is placed among the changes of
230
+ * every comparable window as a strip plot with a typical range and a verdict;
231
+ * and the change is decomposed into signed driver contributions that reconcile
232
+ * with it — plus every loading, empty, error and stale state, layout-stable,
233
+ * with generated text equivalents and a table for every plot.
234
+ *
235
+ * Where `MetricCard variant="highlight"` asserts that a window moved and by
236
+ * how much, HighlightCard answers "is that unusual, and what drove it".
237
+ */
238
+ const HighlightCard = forwardRef(({ label, name: nameProp, hint, series, highlight, drivers = EMPTY_DRIVERS, sections = DEFAULT_SECTIONS, peerMode = "rolling", baseline = "sigma", minPeers = 4, severityThresholds, driverLimit, driverTolerance, selectedDriver, defaultSelectedDriver, onDriverSelect, mark = "line", scale = "auto", axes = true, contextHeight: contextHeightProp, deltaMode = "percent", goodDirection = "up", format = "compact", formatValue, unit, granularity, insight, status = "ready", filtered = false, stale = false, updatedAt, updatedAtFormat = "day", onRetry, onClearFilters, size = "md", titleLevel = 3, locale, timeZone, labels: labelOverrides, description, action, defaultTableOpen = false, hideTableToggle = false, onEmbed, patchSpec, render, className, ...rest }, forwardedRef) => {
239
+ const labels = useMemo(() => resolveHighlightLabels(labelOverrides), [labelOverrides]);
240
+ const sizes = sizeStyles[size];
241
+ const contextHeight = contextHeightProp ?? sizes.contextHeight + (axes ? X_AXIS_HEIGHT_PX : 0);
242
+ const name = nameProp ?? (typeof label === "string" ? label : MISSING_NAME);
243
+ const id = useId();
244
+ const headingId = `${id}-label`;
245
+ const descriptionId = `${id}-description`;
246
+ const hintId = `${id}-hint`;
247
+ const tableId = `${id}-table`;
248
+ const windowExplanationId = `${id}-window`;
249
+ const peerExplanationId = `${id}-peer`;
250
+ const [rootRef, setRootRef] = useMergedRef(forwardedRef);
251
+ const contextRef = useRef(null);
252
+ const peersRef = useRef(null);
253
+ const driverRowsRef = useRef(null);
254
+ const driverCellRef = useRef(null);
255
+ const [tableOpen, setTableOpen] = useState(defaultTableOpen);
256
+ const [popoverContainer, setPopoverContainer] = useState(null);
257
+ const tokens = useChartTokens(rootRef);
258
+ const isDriverControlled = selectedDriver !== void 0;
259
+ const [innerDriver, setInnerDriver] = useState(defaultSelectedDriver);
260
+ const driverKey = isDriverControlled ? selectedDriver : innerDriver;
261
+ const pendingAnnounce = useRef(false);
262
+ const [announcement, setAnnouncement] = useState("");
263
+ const selectDriver = useCallback((key) => {
264
+ pendingAnnounce.current = true;
265
+ if (!isDriverControlled) setInnerDriver(key);
266
+ onDriverSelect?.(key);
267
+ }, [isDriverControlled, onDriverSelect]);
268
+ const summary = useMemo(() => normalizeHighlightSeries(series, granularity), [series, granularity]);
269
+ const formatters = useMemo(() => createHighlightFormatters({
270
+ format,
271
+ locale,
272
+ timeZone,
273
+ formatValue,
274
+ xType: summary?.xType ?? "temporal",
275
+ granularity: summary?.granularity ?? granularity ?? "day",
276
+ deltaMode,
277
+ percentagePoints: labels.percentagePoints
278
+ }), [
279
+ format,
280
+ locale,
281
+ timeZone,
282
+ formatValue,
283
+ summary,
284
+ granularity,
285
+ deltaMode,
286
+ labels.percentagePoints
287
+ ]);
288
+ const deltaOptions = useMemo(() => ({
289
+ deltaMode,
290
+ goodDirection,
291
+ format,
292
+ locale,
293
+ percentagePoints: labels.percentagePoints,
294
+ formatters
295
+ }), [
296
+ deltaMode,
297
+ goodDirection,
298
+ format,
299
+ locale,
300
+ labels.percentagePoints,
301
+ formatters
302
+ ]);
303
+ const info = useMemo(() => resolveHighlightWindow(summary, highlight, deltaOptions), [
304
+ summary,
305
+ highlight,
306
+ deltaOptions
307
+ ]);
308
+ const wantsPeers = sections.includes("peers");
309
+ const peers = useMemo(() => wantsPeers ? derivePeers(summary, info, {
310
+ ...deltaOptions,
311
+ mode: peerMode,
312
+ baseline,
313
+ minPeers,
314
+ thresholds: severityThresholds
315
+ }) : null, [
316
+ wantsPeers,
317
+ summary,
318
+ info,
319
+ deltaOptions,
320
+ peerMode,
321
+ baseline,
322
+ minPeers,
323
+ severityThresholds
324
+ ]);
325
+ const wantsDrivers = sections.includes("drivers");
326
+ const driverInfo = useMemo(() => wantsDrivers ? normalizeDrivers(drivers, info, {
327
+ goodDirection,
328
+ formatters,
329
+ limit: driverLimit,
330
+ otherLabel: labels.driverOther,
331
+ residualLabel: labels.driverResidual,
332
+ tolerance: driverTolerance
333
+ }) : null, [
334
+ wantsDrivers,
335
+ drivers,
336
+ info,
337
+ goodDirection,
338
+ formatters,
339
+ driverLimit,
340
+ labels.driverOther,
341
+ labels.driverResidual,
342
+ driverTolerance
343
+ ]);
344
+ const hasData = (summary?.count ?? 0) > 0;
345
+ const state = deriveHighlightState({
346
+ status,
347
+ hasData,
348
+ filtered
349
+ });
350
+ const showContent = state === "ready" || state === "refreshing";
351
+ const wantsContext = sections.includes("context");
352
+ const hasContext = showContent && wantsContext && summary != null && info != null && summary.count >= 2;
353
+ const hasPeers = showContent && peers != null;
354
+ const peersInsufficient = showContent && wantsPeers && info != null && peers == null;
355
+ const hasDrivers = showContent && driverInfo != null && driverInfo.rows.length > 0;
356
+ const unresolved = showContent && info == null;
357
+ const driverRows = driverInfo?.rows ?? [];
358
+ const interactive = onDriverSelect != null && hasDrivers;
359
+ const asOfDate = updatedAt == null ? null : updatedAt instanceof Date ? updatedAt : new Date(updatedAt);
360
+ const asOfValid = asOfDate != null && !Number.isNaN(asOfDate.getTime());
361
+ const asOfText = asOfValid ? typeof updatedAtFormat === "function" ? updatedAtFormat(asOfDate) : formatDate(asOfDate, {
362
+ granularity: updatedAtFormat,
363
+ locale,
364
+ timeZone
365
+ }) : void 0;
366
+ const cardDescription = description ?? buildHighlightDescription({
367
+ name,
368
+ state,
369
+ info,
370
+ peers,
371
+ drivers: driverInfo,
372
+ formatters,
373
+ asOfText,
374
+ labels
375
+ });
376
+ const selectedRow = driverKey == null ? null : driverRows.find((row) => row.key === driverKey) ?? null;
377
+ useEffect(() => {
378
+ if (!pendingAnnounce.current) return;
379
+ pendingAnnounce.current = false;
380
+ setAnnouncement(selectedRow ? labels.driverSelected({
381
+ label: selectedRow.label,
382
+ contribution: selectedRow.contributionText,
383
+ share: selectedRow.share == null ? null : selectedRow.shareText
384
+ }) : "");
385
+ }, [selectedRow, labels]);
386
+ const windowExplanationRows = info ? labels.windowExplanation({
387
+ periodText: info.rangeText,
388
+ startText: formatters.formatY(info.start.y),
389
+ endText: formatters.formatY(info.end.y),
390
+ changeText: info.delta.text,
391
+ secondaryText: info.delta.secondaryText,
392
+ renderedMode: info.delta.renderedMode,
393
+ direction: info.delta.direction,
394
+ peakText: info.peak ? `${formatters.formatY(info.peak.y)} · ${formatters.formatX(info.peak.x)}` : null,
395
+ troughText: info.trough ? `${formatters.formatY(info.trough.y)} · ${formatters.formatX(info.trough.x)}` : null,
396
+ steepestText: info.steepest ? `${info.steepest.changeText} · ${info.steepest.rangeText}` : null
397
+ }) : [];
398
+ const windowExplanation = windowExplanationRows.map(([key, value]) => `${key}: ${value}`).join(". ");
399
+ const peerExplanationRows = peers ? labels.peerExplanation(peers) : [];
400
+ const peerExplanation = peerExplanationRows.map(([key, value]) => `${key}: ${value}`).join(". ");
401
+ const contextWidth = usePlotWidth(contextRef, hasContext);
402
+ const peersWidth = usePlotWidth(peersRef, hasPeers);
403
+ const driverPlotHeight = driverRows.length * sizes.step;
404
+ const driverBox = usePlotBox(driverCellRef, driverRowsRef, hasDrivers, driverRows[0]?.key ?? "");
405
+ const contextTooltip = useArcTooltipHandler({ content: useCallback(({ item }) => {
406
+ const raw = item.datum;
407
+ const datum = raw && "datum" in raw && raw.datum ? raw.datum : raw;
408
+ if (!datum || datum.x == null) return null;
409
+ const rows = [[labels.tablePeriod, formatters.formatX(datum.x)], [name, formatters.formatY(datum.y)]];
410
+ if (datum.hl && info) {
411
+ rows.push([labels.windowPeriod, info.label ? `${info.label} · ${info.rangeText}` : info.rangeText]);
412
+ rows.push([labels.windowChange, info.delta.text]);
413
+ }
414
+ return /* @__PURE__ */ jsx(ArcTooltipRows, { rows });
415
+ }, [
416
+ labels,
417
+ formatters,
418
+ name,
419
+ info
420
+ ]) });
421
+ const peersTooltip = useArcTooltipHandler({ content: useCallback(({ item }) => {
422
+ const datum = item.datum;
423
+ if (!datum) return null;
424
+ if (datum.low != null && datum.high != null && peers) return /* @__PURE__ */ jsx(ArcTooltipRows, { rows: [[labels.peerTypical, peers.typicalText]] });
425
+ if (!datum.rangeText) return null;
426
+ const rows = [[labels.tableWindow, datum.rangeText], [labels.tableChange, datum.changeText ?? "—"]];
427
+ if (datum.isHighlight && peers) rows.push([labels.peerRank, peers.rankText]);
428
+ return /* @__PURE__ */ jsx(ArcTooltipRows, { rows });
429
+ }, [labels, peers]) });
430
+ const driversTooltip = useArcTooltipHandler({ content: useCallback(({ item }) => {
431
+ const datum = item.datum;
432
+ if (!datum || datum.label == null) return null;
433
+ const rows = [[labels.tableDriver, datum.label], [labels.driverContribution, formatters.formatAmountChange(datum.value ?? 0)]];
434
+ if (datum.share != null) rows.push([labels.tableShare, formatters.formatShare(datum.share)]);
435
+ return /* @__PURE__ */ jsx(ArcTooltipRows, { rows });
436
+ }, [labels, formatters]) });
437
+ const contextOptions = useMemo(() => arcEmbedOptions({ tooltip: contextTooltip.handler }), [contextTooltip.handler]);
438
+ const peersOptions = useMemo(() => arcEmbedOptions({ tooltip: peersTooltip.handler }), [peersTooltip.handler]);
439
+ const driversOptions = useMemo(() => arcEmbedOptions({ tooltip: driversTooltip.handler }), [driversTooltip.handler]);
440
+ const xType = summary?.xType;
441
+ const summaryGranularity = summary?.granularity;
442
+ const valueFormat = locale ? `${format}@${locale}` : format;
443
+ const changeFormat = deltaMode === "percent" ? locale ? `percent:1@${locale}` : "percent:1" : valueFormat;
444
+ const contextSpec = useMemo(() => {
445
+ if (!tokens || !xType || !summaryGranularity) return null;
446
+ const timeGrammar = (kind) => [
447
+ kind,
448
+ locale ?? "",
449
+ timeZone ?? ""
450
+ ].join("@").replace(/@+$/, "");
451
+ const axisGranularity = summaryGranularity === "day" ? "day-short" : summaryGranularity === "month" ? "month-short" : summaryGranularity === "datetime" ? "time" : summaryGranularity;
452
+ const built = buildHighlightContextSpec({
453
+ mark,
454
+ scale,
455
+ xType,
456
+ height: contextHeight,
457
+ axes,
458
+ tooltip: true,
459
+ xTitle: labels.tablePeriod,
460
+ yTitle: name,
461
+ valueFormat,
462
+ timeFormat: timeGrammar(summaryGranularity),
463
+ axisTimeFormat: timeGrammar(axisGranularity),
464
+ locale,
465
+ timeZone
466
+ }, tokens);
467
+ return patchSpec ? patchSpec(built, "context") : built;
468
+ }, [
469
+ tokens,
470
+ xType,
471
+ summaryGranularity,
472
+ mark,
473
+ scale,
474
+ contextHeight,
475
+ axes,
476
+ labels.tablePeriod,
477
+ name,
478
+ valueFormat,
479
+ locale,
480
+ timeZone,
481
+ patchSpec
482
+ ]);
483
+ const peersSpec = useMemo(() => {
484
+ if (!tokens) return null;
485
+ const built = buildHighlightPeersSpec({
486
+ height: sizes.peersHeight,
487
+ tooltip: true,
488
+ windowTitle: labels.tableWindow,
489
+ changeTitle: labels.tableChange,
490
+ changeFormat,
491
+ typicalTitle: labels.peerTypical,
492
+ locale,
493
+ timeZone
494
+ }, tokens);
495
+ return patchSpec ? patchSpec(built, "peers") : built;
496
+ }, [
497
+ tokens,
498
+ sizes.peersHeight,
499
+ labels.tableWindow,
500
+ labels.tableChange,
501
+ labels.peerTypical,
502
+ changeFormat,
503
+ locale,
504
+ timeZone,
505
+ patchSpec
506
+ ]);
507
+ const driversSpec = useMemo(() => {
508
+ if (!tokens) return null;
509
+ const built = buildHighlightDriversSpec({
510
+ barHeight: Math.min(sizes.barHeight, 24),
511
+ height: driverPlotHeight,
512
+ tooltip: true,
513
+ driverTitle: labels.tableDriver,
514
+ contributionTitle: labels.driverContribution,
515
+ shareTitle: labels.tableShare,
516
+ valueFormat,
517
+ locale,
518
+ timeZone
519
+ }, tokens);
520
+ return patchSpec ? patchSpec(built, "drivers") : built;
521
+ }, [
522
+ tokens,
523
+ sizes.barHeight,
524
+ driverPlotHeight,
525
+ labels.tableDriver,
526
+ labels.driverContribution,
527
+ labels.tableShare,
528
+ valueFormat,
529
+ locale,
530
+ timeZone,
531
+ patchSpec
532
+ ]);
533
+ const contextData = useMemo(() => summary && info ? buildHighlightContextDatasets(summary, info) : null, [summary, info]);
534
+ const contextDatasets = useMemo(() => ({
535
+ [HIGHLIGHT_TABLE_DATASET]: contextData?.table ?? [],
536
+ [HIGHLIGHT_OUTSIDE_DATASET]: contextData?.outside ?? [],
537
+ [HIGHLIGHT_INSIDE_DATASET]: contextData?.inside ?? [],
538
+ [HIGHLIGHT_ENDPOINTS_DATASET]: contextData?.endpoints ?? [],
539
+ [HIGHLIGHT_BAND_DATASET]: contextData?.band ?? []
540
+ }), [contextData]);
541
+ const peersDatasets = useMemo(() => ({
542
+ [HIGHLIGHT_PEERS_DATASET]: peers ? peers.peers : [],
543
+ [HIGHLIGHT_TYPICAL_DATASET]: peers ? buildTypicalRows(peers) : [],
544
+ [HIGHLIGHT_ZERO_DATASET]: ZERO_ROWS
545
+ }), [peers]);
546
+ const driversDatasets = useMemo(() => ({
547
+ [HIGHLIGHT_DRIVERS_DATASET]: driverInfo ? buildDriverRows(driverInfo) : [],
548
+ [HIGHLIGHT_ZERO_DATASET]: ZERO_ROWS
549
+ }), [driverInfo]);
550
+ const contextChartDescription = summary && info ? buildContextDescription({
551
+ name,
552
+ mark,
553
+ summary,
554
+ info,
555
+ formatters,
556
+ labels
557
+ }) : void 0;
558
+ const peersChartDescription = peers ? buildPeersDescription({
559
+ name,
560
+ stats: peers,
561
+ labels
562
+ }) : void 0;
563
+ const driversChartDescription = driverInfo ? buildDriversDescription({
564
+ name,
565
+ info: driverInfo,
566
+ labels
567
+ }) : void 0;
568
+ const handleContextEmbed = useCallback((result) => onEmbed?.(result, "context"), [onEmbed]);
569
+ const handlePeersEmbed = useCallback((result) => onEmbed?.(result, "peers"), [onEmbed]);
570
+ const handleDriversEmbed = useCallback((result) => onEmbed?.(result, "drivers"), [onEmbed]);
571
+ const handleDriverPlotClick = (event) => {
572
+ if (!interactive) return;
573
+ const top = event.currentTarget.getBoundingClientRect().top;
574
+ const row = driverRows[Math.floor((event.clientY - top) / sizes.step)];
575
+ if (row && row.role === "driver" && row.key !== driverKey) selectDriver(row.key);
576
+ };
577
+ const onDriverKeyDown = (event, key) => {
578
+ if (event.key !== "Enter" && event.key !== " ") return;
579
+ event.preventDefault();
580
+ selectDriver(key);
581
+ };
582
+ const insightContent = insight === void 0 ? info ? /* @__PURE__ */ jsx(InsightText, { segments: buildHighlightAnalysisInsight({
583
+ name,
584
+ info,
585
+ peers,
586
+ drivers: driverInfo,
587
+ labels
588
+ }) }) : null : insight;
589
+ const showInsight = insightContent != null && insightContent !== false;
590
+ const reserveContext = wantsContext;
591
+ const reservePeers = wantsPeers;
592
+ const reserveDrivers = wantsDrivers && drivers.length > 0;
593
+ const sectionBlock = (plot) => sizes.sectionTitlePx + sizes.gapPx / 2 + plot;
594
+ const bodyMinHeight = sizes.headlinePx + (reserveContext ? sizes.gapPx + contextHeight : 0) + (reservePeers ? sizes.gapPx + sectionBlock(sizes.peersHeight + sizes.gapPx + sizes.sectionTitlePx) : 0) + (reserveDrivers ? sizes.gapPx + sectionBlock(Math.min(drivers.length, driverLimit ?? drivers.length) * sizes.step) : 0) + (showInsight || insight === void 0 ? sizes.gapPx + sizes.sectionTitlePx : 0);
595
+ const HeadingTag = `h${titleLevel}`;
596
+ const SectionTag = `h${SECTION_LEVEL[titleLevel]}`;
597
+ const busy = state === "loading" || state === "refreshing";
598
+ const directionWord = info?.delta.direction === "up" ? labels.increase : info?.delta.direction === "down" ? labels.decrease : labels.unchanged;
599
+ const driverColor = useCallback((tone) => tokens ? tone === "positive" ? tokens.divergingPositive : tone === "negative" ? tokens.divergingNegative : tokens.emphasis : void 0, [tokens]);
600
+ return /* @__PURE__ */ jsxs(Box, {
601
+ ref: setRootRef,
602
+ render: render ?? /* @__PURE__ */ jsx("figure", {}),
603
+ padding: sizes.padding,
604
+ "aria-labelledby": headingId,
605
+ "aria-describedby": descriptionId,
606
+ "aria-busy": busy || void 0,
607
+ "data-status": status,
608
+ "data-state": state,
609
+ "data-size": size,
610
+ "data-mark": mark,
611
+ "data-sections": sections.join(" "),
612
+ "data-peers": wantsPeers ? peerMode : void 0,
613
+ "data-baseline": wantsPeers ? baseline : void 0,
614
+ "data-severity": hasPeers ? peers?.severity : void 0,
615
+ "data-window": showContent ? info ? "resolved" : "unresolved" : void 0,
616
+ "data-polarity": showContent && info ? info.delta.polarity : void 0,
617
+ "data-reconciles": hasDrivers ? driverInfo?.reconciles ? "" : "false" : void 0,
618
+ "data-folded": hasDrivers && driverInfo && driverInfo.folded > 0 ? driverInfo.folded : void 0,
619
+ "data-selected-driver": hasDrivers ? driverKey ?? void 0 : void 0,
620
+ "data-empty": state === "empty" || state === "no-match" ? "" : void 0,
621
+ "data-stale": stale ? "" : void 0,
622
+ ...rest,
623
+ className: cn("relative flex min-w-0 flex-col", sizes.gap, className),
624
+ children: [
625
+ /* @__PURE__ */ jsxs("div", {
626
+ className: "flex items-center justify-between gap-2",
627
+ children: [/* @__PURE__ */ jsxs("div", {
628
+ className: "flex min-w-0 items-center gap-1",
629
+ children: [/* @__PURE__ */ jsx(HeadingTag, {
630
+ id: headingId,
631
+ className: cn(labelClasses, sizes.label, "line-clamp-2"),
632
+ children: label
633
+ }), hint && /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsxs(Tooltip, { children: [/* @__PURE__ */ jsx(TooltipTrigger, {
634
+ render: /* @__PURE__ */ jsx(Button, {
635
+ variant: "ghost",
636
+ size: "sm",
637
+ iconOnly: true,
638
+ circular: true,
639
+ "aria-label": labels.about(name),
640
+ "aria-describedby": hintId,
641
+ className: headerControlClasses
642
+ }),
643
+ children: /* @__PURE__ */ jsx(InfoIcon, { className: "size-3.5" })
644
+ }), /* @__PURE__ */ jsx(TooltipPopup, { children: hint })] }), /* @__PURE__ */ jsx("span", {
645
+ id: hintId,
646
+ className: "sr-only",
647
+ children: hint
648
+ })] })]
649
+ }), /* @__PURE__ */ jsxs("div", {
650
+ className: "flex shrink-0 items-center gap-1",
651
+ children: [
652
+ stale && /* @__PURE__ */ jsx(Badge, {
653
+ variant: "warning",
654
+ appearance: "soft",
655
+ size: "sm",
656
+ children: labels.stale
657
+ }),
658
+ showContent && info && /* @__PURE__ */ jsxs(Popover, {
659
+ open: tableOpen,
660
+ onOpenChange: setTableOpen,
661
+ children: [!hideTableToggle && /* @__PURE__ */ jsx(PopoverTrigger, {
662
+ render: /* @__PURE__ */ jsx(Button, {
663
+ variant: "ghost",
664
+ size: "sm",
665
+ iconOnly: true,
666
+ circular: true,
667
+ "aria-label": tableOpen ? labels.hideTable(name) : labels.showTable(name),
668
+ className: headerControlClasses
669
+ }),
670
+ children: /* @__PURE__ */ jsx(InfoIcon, { className: "size-3.5" })
671
+ }), popoverContainer && /* @__PURE__ */ jsxs(PopoverPopup, {
672
+ id: tableId,
673
+ anchor: rootRef,
674
+ container: popoverContainer,
675
+ side: "right",
676
+ align: "start",
677
+ collisionPadding: 8,
678
+ className: "pointer-events-auto w-[min(30rem,var(--available-width))]",
679
+ "data-part": "data-popover",
680
+ children: [/* @__PURE__ */ jsx(PopoverTitle, {
681
+ className: "mb-3",
682
+ children: name
683
+ }), /* @__PURE__ */ jsxs("div", {
684
+ className: "flex max-h-[min(26rem,calc(var(--available-height)-5rem))] flex-col gap-4 overflow-y-auto",
685
+ tabIndex: 0,
686
+ role: "region",
687
+ "aria-label": labels.tableCaption(name),
688
+ "data-part": "tables",
689
+ children: [
690
+ /* @__PURE__ */ jsxs(TableContainer, {
691
+ variant: "plain",
692
+ "data-part": "window-table",
693
+ children: [/* @__PURE__ */ jsx("p", {
694
+ className: cn(sectionTitleClasses, "mb-1 text-xs"),
695
+ children: labels.contextTitle
696
+ }), /* @__PURE__ */ jsxs(Table, {
697
+ size: "sm",
698
+ stickyHeader: true,
699
+ hoverable: false,
700
+ children: [
701
+ /* @__PURE__ */ jsx(TableCaption, {
702
+ side: "top",
703
+ className: "sr-only",
704
+ children: labels.tableCaption(name)
705
+ }),
706
+ /* @__PURE__ */ jsx(TableHeader, { children: /* @__PURE__ */ jsxs(TableRow, { children: [
707
+ /* @__PURE__ */ jsx(TableHead, { children: labels.tablePeriod }),
708
+ /* @__PURE__ */ jsx(TableHead, {
709
+ className: "text-right",
710
+ children: labels.tableValue
711
+ }),
712
+ /* @__PURE__ */ jsx(TableHead, {
713
+ className: "text-right",
714
+ children: labels.tableStep
715
+ })
716
+ ] }) }),
717
+ /* @__PURE__ */ jsx(TableBody, { children: info.readings.map((reading, at) => {
718
+ const leg = at === 0 ? null : info.legs[at - 1];
719
+ const edge = at === 0 ? labels.windowStart : at === info.readings.length - 1 ? labels.windowEnd : null;
720
+ return /* @__PURE__ */ jsxs(TableRow, { children: [
721
+ /* @__PURE__ */ jsxs(TableCell, { children: [formatters.formatX(reading.x), edge && /* @__PURE__ */ jsx("span", {
722
+ className: "ms-1.5 text-muted-foreground text-xs",
723
+ children: edge
724
+ })] }),
725
+ /* @__PURE__ */ jsx(TableCell, {
726
+ className: "text-right tabular-nums",
727
+ children: formatters.formatY(reading.y)
728
+ }),
729
+ /* @__PURE__ */ jsx(TableCell, {
730
+ className: "text-right tabular-nums",
731
+ children: leg ? leg.changeText : "—"
732
+ })
733
+ ] }, `${reading.x}`);
734
+ }) })
735
+ ]
736
+ })]
737
+ }),
738
+ hasDrivers && driverInfo && /* @__PURE__ */ jsxs(TableContainer, {
739
+ variant: "plain",
740
+ "data-part": "drivers-table",
741
+ children: [/* @__PURE__ */ jsx("p", {
742
+ className: cn(sectionTitleClasses, "mb-1 text-xs"),
743
+ children: labels.driversTitle
744
+ }), /* @__PURE__ */ jsxs(Table, {
745
+ size: "sm",
746
+ hoverable: false,
747
+ children: [
748
+ /* @__PURE__ */ jsx(TableCaption, {
749
+ side: "top",
750
+ className: "sr-only",
751
+ children: labels.driversGroup(name)
752
+ }),
753
+ /* @__PURE__ */ jsx(TableHeader, { children: /* @__PURE__ */ jsxs(TableRow, { children: [
754
+ /* @__PURE__ */ jsx(TableHead, { children: labels.tableDriver }),
755
+ /* @__PURE__ */ jsx(TableHead, {
756
+ className: "text-right",
757
+ children: labels.driverContribution
758
+ }),
759
+ /* @__PURE__ */ jsx(TableHead, {
760
+ className: "text-right",
761
+ children: labels.tableShare
762
+ })
763
+ ] }) }),
764
+ /* @__PURE__ */ jsxs(TableBody, { children: [driverInfo.rows.map((row) => /* @__PURE__ */ jsxs(TableRow, {
765
+ "data-role": row.role,
766
+ children: [
767
+ /* @__PURE__ */ jsx(TableCell, { children: row.label }),
768
+ /* @__PURE__ */ jsx(TableCell, {
769
+ className: "text-right tabular-nums",
770
+ children: row.contributionText
771
+ }),
772
+ /* @__PURE__ */ jsx(TableCell, {
773
+ className: "text-right tabular-nums",
774
+ children: row.shareText
775
+ })
776
+ ]
777
+ }, row.key)), /* @__PURE__ */ jsxs(TableRow, { children: [
778
+ /* @__PURE__ */ jsx(TableCell, {
779
+ className: "font-medium",
780
+ children: labels.driverTotal
781
+ }),
782
+ /* @__PURE__ */ jsx(TableCell, {
783
+ className: "text-right font-medium tabular-nums",
784
+ children: driverInfo.totalText
785
+ }),
786
+ /* @__PURE__ */ jsx(TableCell, {})
787
+ ] })] })
788
+ ]
789
+ })]
790
+ }),
791
+ hasPeers && peers && /* @__PURE__ */ jsxs(TableContainer, {
792
+ variant: "plain",
793
+ "data-part": "peers-table",
794
+ children: [/* @__PURE__ */ jsx("p", {
795
+ className: cn(sectionTitleClasses, "mb-1 text-xs"),
796
+ children: labels.peersTitle
797
+ }), /* @__PURE__ */ jsxs(Table, {
798
+ size: "sm",
799
+ stickyHeader: true,
800
+ hoverable: false,
801
+ children: [
802
+ /* @__PURE__ */ jsx(TableCaption, {
803
+ side: "top",
804
+ className: "sr-only",
805
+ children: labels.peersCaption({
806
+ count: peers.count,
807
+ readings: peers.length + 1,
808
+ mode: peers.mode
809
+ })
810
+ }),
811
+ /* @__PURE__ */ jsx(TableHeader, { children: /* @__PURE__ */ jsxs(TableRow, { children: [/* @__PURE__ */ jsx(TableHead, { children: labels.tableWindow }), /* @__PURE__ */ jsx(TableHead, {
812
+ className: "text-right",
813
+ children: labels.tableChange
814
+ })] }) }),
815
+ /* @__PURE__ */ jsx(TableBody, { children: peers.peers.map((peer, at) => /* @__PURE__ */ jsxs(TableRow, {
816
+ "data-highlighted": peer.isHighlight ? "" : void 0,
817
+ children: [/* @__PURE__ */ jsxs(TableCell, { children: [
818
+ peer.rangeText,
819
+ peer.isHighlight && /* @__PURE__ */ jsxs("span", {
820
+ className: "sr-only",
821
+ children: [
822
+ " (",
823
+ labels.highlighted,
824
+ ")"
825
+ ]
826
+ }),
827
+ peer.isHighlight && /* @__PURE__ */ jsx("span", {
828
+ "aria-hidden": "true",
829
+ className: "ms-1.5 inline-block size-1.5 rounded-full align-middle",
830
+ style: { background: driverColor(peer.polarity) }
831
+ })
832
+ ] }), /* @__PURE__ */ jsx(TableCell, {
833
+ className: "text-right tabular-nums",
834
+ children: peer.changeText
835
+ })]
836
+ }, at)) })
837
+ ]
838
+ })]
839
+ })
840
+ ]
841
+ })]
842
+ })]
843
+ }),
844
+ action
845
+ ]
846
+ })]
847
+ }),
848
+ /* @__PURE__ */ jsx("p", {
849
+ id: descriptionId,
850
+ className: "sr-only",
851
+ children: cardDescription
852
+ }),
853
+ /* @__PURE__ */ jsx("p", {
854
+ role: "status",
855
+ "aria-live": "polite",
856
+ className: "sr-only",
857
+ "data-part": "announcement",
858
+ children: announcement
859
+ }),
860
+ /* @__PURE__ */ jsxs("div", {
861
+ className: cn("relative flex min-w-0 flex-col", sizes.sectionGap),
862
+ style: { minHeight: bodyMinHeight },
863
+ children: [
864
+ state === "loading" && /* @__PURE__ */ jsxs(SkeletonGroup, {
865
+ className: cn("flex flex-col", sizes.sectionGap),
866
+ children: [
867
+ /* @__PURE__ */ jsxs("div", {
868
+ className: "flex min-w-0 items-start justify-between gap-2",
869
+ style: { minHeight: sizes.headlinePx },
870
+ children: [/* @__PURE__ */ jsxs("div", {
871
+ className: "flex w-1/2 flex-col gap-1",
872
+ children: [/* @__PURE__ */ jsx(Skeleton, {
873
+ variant: "rounded",
874
+ className: "w-3/5",
875
+ style: { height: sizes.valueLinePx }
876
+ }), /* @__PURE__ */ jsx(Skeleton, {
877
+ variant: "rounded",
878
+ className: "w-full",
879
+ style: { height: sizes.sectionTitlePx }
880
+ })]
881
+ }), /* @__PURE__ */ jsx(Skeleton, {
882
+ variant: "rounded",
883
+ className: "w-20",
884
+ style: { height: sizes.sectionTitlePx + 4 }
885
+ })]
886
+ }),
887
+ reserveContext && /* @__PURE__ */ jsx(Skeleton, {
888
+ variant: "rounded",
889
+ className: "w-full",
890
+ style: { height: contextHeight }
891
+ }),
892
+ reservePeers && /* @__PURE__ */ jsxs("div", {
893
+ className: "flex flex-col gap-2",
894
+ children: [
895
+ /* @__PURE__ */ jsx(Skeleton, {
896
+ variant: "rounded",
897
+ className: "w-24",
898
+ style: { height: sizes.sectionTitlePx }
899
+ }),
900
+ /* @__PURE__ */ jsx(Skeleton, {
901
+ variant: "rounded",
902
+ className: "w-full",
903
+ style: { height: sizes.peersHeight }
904
+ }),
905
+ /* @__PURE__ */ jsx(Skeleton, {
906
+ variant: "rounded",
907
+ className: "w-4/5",
908
+ style: { height: sizes.sectionTitlePx }
909
+ })
910
+ ]
911
+ }),
912
+ reserveDrivers && /* @__PURE__ */ jsxs("div", {
913
+ className: "flex flex-col gap-2",
914
+ children: [/* @__PURE__ */ jsx(Skeleton, {
915
+ variant: "rounded",
916
+ className: "w-28",
917
+ style: { height: sizes.sectionTitlePx }
918
+ }), Array.from({ length: Math.min(4, drivers.length) }, (_, index) => /* @__PURE__ */ jsx(Skeleton, {
919
+ variant: "rounded",
920
+ style: {
921
+ height: sizes.barHeight,
922
+ width: `${Math.max(30, 90 - index * 18)}%`
923
+ }
924
+ }, index))]
925
+ })
926
+ ]
927
+ }),
928
+ state === "error" && /* @__PURE__ */ jsx(SystemState, {
929
+ variant: "server-error",
930
+ scope: "inline",
931
+ title: labels.error,
932
+ description: false,
933
+ action: onRetry ? /* @__PURE__ */ jsx(Button, {
934
+ size: "sm",
935
+ onClick: onRetry,
936
+ children: labels.retry
937
+ }) : void 0,
938
+ className: cn(panelStateClasses, "my-auto")
939
+ }),
940
+ state === "empty" && /* @__PURE__ */ jsx(SystemState, {
941
+ variant: "empty",
942
+ scope: "inline",
943
+ title: labels.noData,
944
+ description: false,
945
+ className: cn(panelStateClasses, "my-auto")
946
+ }),
947
+ state === "no-match" && /* @__PURE__ */ jsx(SystemState, {
948
+ variant: "no-results",
949
+ scope: "inline",
950
+ title: labels.noMatch,
951
+ description: false,
952
+ action: onClearFilters ? /* @__PURE__ */ jsx(Button, {
953
+ variant: "outline",
954
+ size: "sm",
955
+ onClick: onClearFilters,
956
+ children: labels.clearFilters
957
+ }) : void 0,
958
+ className: cn(panelStateClasses, "my-auto")
959
+ }),
960
+ unresolved && /* @__PURE__ */ jsx("p", {
961
+ className: cn(captionClasses, "my-auto"),
962
+ "data-part": "unresolved",
963
+ children: labels.unresolved
964
+ }),
965
+ showContent && info && /* @__PURE__ */ jsxs(Fragment$1, { children: [
966
+ /* @__PURE__ */ jsxs("div", {
967
+ className: "flex min-w-0 items-start justify-between gap-3",
968
+ style: { minHeight: sizes.headlinePx },
969
+ "data-part": "headline",
970
+ children: [/* @__PURE__ */ jsxs(Tooltip, { children: [/* @__PURE__ */ jsxs(TooltipTrigger, {
971
+ delay: 150,
972
+ render: /* @__PURE__ */ jsx("div", {
973
+ tabIndex: 0,
974
+ role: "group",
975
+ "aria-label": labels.windowDetails(name),
976
+ "aria-describedby": windowExplanationId,
977
+ className: cn("flex min-w-0 flex-col rounded-md", "focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ring"),
978
+ "data-part": "window"
979
+ }),
980
+ children: [
981
+ /* @__PURE__ */ jsx("span", {
982
+ id: windowExplanationId,
983
+ className: "sr-only",
984
+ children: windowExplanation
985
+ }),
986
+ /* @__PURE__ */ jsxs("span", {
987
+ className: "flex min-w-0 flex-wrap items-baseline gap-x-1.5",
988
+ "data-part": "value",
989
+ children: [/* @__PURE__ */ jsxs("span", {
990
+ className: cn(valueClasses, sizes.value, "min-w-0 wrap-break-word", changeTextClasses[info.delta.polarity]),
991
+ "data-direction": info.delta.direction,
992
+ children: [/* @__PURE__ */ jsxs("span", {
993
+ className: "sr-only",
994
+ children: [directionWord, " "]
995
+ }), info.delta.text]
996
+ }), unit != null && /* @__PURE__ */ jsx("span", {
997
+ className: cn("font-sans text-muted-foreground", sizes.body),
998
+ children: unit
999
+ })]
1000
+ }),
1001
+ /* @__PURE__ */ jsxs("span", {
1002
+ className: cn(captionClasses, "mt-1 min-w-0 wrap-break-word"),
1003
+ "data-part": "readings",
1004
+ children: [
1005
+ formatters.formatY(info.start.y),
1006
+ " → ",
1007
+ formatters.formatY(info.end.y),
1008
+ " ·",
1009
+ " ",
1010
+ labels.windowLine({
1011
+ range: info.rangeText,
1012
+ label: info.label
1013
+ })
1014
+ ]
1015
+ })
1016
+ ]
1017
+ }), /* @__PURE__ */ jsx(TooltipPopup, {
1018
+ arrow: false,
1019
+ side: "bottom",
1020
+ align: "start",
1021
+ sideOffset: 8,
1022
+ collisionPadding: 8,
1023
+ container: popoverContainer,
1024
+ className: "text-left",
1025
+ "data-part": "window-tooltip",
1026
+ children: /* @__PURE__ */ jsx(ArcTooltipRows, { rows: windowExplanationRows })
1027
+ })] }), hasPeers && peers && /* @__PURE__ */ jsxs(Badge, {
1028
+ variant: "neutral",
1029
+ appearance: "outline",
1030
+ size: sizes.badge,
1031
+ className: "shrink-0",
1032
+ "data-part": "severity",
1033
+ "data-severity": peers.severity,
1034
+ children: [/* @__PURE__ */ jsx(DeltaGlyph, { direction: info.delta.direction }), labels.severityNames[peers.severity]]
1035
+ })]
1036
+ }),
1037
+ sections.map((section) => {
1038
+ if (section === "context") {
1039
+ if (!hasContext) return null;
1040
+ return /* @__PURE__ */ jsx("div", {
1041
+ ref: contextRef,
1042
+ className: "relative w-full",
1043
+ style: { height: contextHeight },
1044
+ "data-part": "context",
1045
+ children: contextWidth > 0 && contextSpec ? /* @__PURE__ */ jsx(ArcVegaChart, {
1046
+ spec: contextSpec,
1047
+ options: contextOptions,
1048
+ datasets: contextDatasets,
1049
+ description: contextChartDescription,
1050
+ width: contextWidth,
1051
+ height: contextHeight,
1052
+ onEmbed: onEmbed ? handleContextEmbed : void 0,
1053
+ className: "absolute inset-0 [&_svg]:block [&_.vega-bindings:empty]:hidden",
1054
+ fallback: /* @__PURE__ */ jsx(Skeleton, {
1055
+ variant: "rounded",
1056
+ className: "size-full"
1057
+ })
1058
+ }) : /* @__PURE__ */ jsx(Skeleton, {
1059
+ variant: "rounded",
1060
+ className: "size-full"
1061
+ })
1062
+ }, section);
1063
+ }
1064
+ if (section === "peers") {
1065
+ if (!hasPeers && !peersInsufficient) return null;
1066
+ return /* @__PURE__ */ jsxs("div", {
1067
+ className: "flex min-w-0 flex-col gap-2",
1068
+ "data-part": "peers",
1069
+ children: [/* @__PURE__ */ jsx(SectionTag, {
1070
+ className: cn(sectionTitleClasses, sizes.body),
1071
+ children: labels.peersTitle
1072
+ }), peersInsufficient ? /* @__PURE__ */ jsx("p", {
1073
+ className: captionClasses,
1074
+ children: labels.insufficientPeers
1075
+ }) : peers && /* @__PURE__ */ jsxs(Fragment$1, { children: [
1076
+ /* @__PURE__ */ jsxs(Tooltip, { children: [/* @__PURE__ */ jsxs(TooltipTrigger, {
1077
+ delay: 150,
1078
+ render: /* @__PURE__ */ jsx("div", {
1079
+ tabIndex: 0,
1080
+ role: "group",
1081
+ "aria-label": labels.peersTitle,
1082
+ "aria-describedby": peerExplanationId,
1083
+ className: cn("relative w-full rounded-md", "focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ring"),
1084
+ style: { height: sizes.peersHeight },
1085
+ "data-part": "peers-plot"
1086
+ }),
1087
+ children: [/* @__PURE__ */ jsx("span", {
1088
+ id: peerExplanationId,
1089
+ className: "sr-only",
1090
+ children: peerExplanation
1091
+ }), /* @__PURE__ */ jsx("div", {
1092
+ ref: peersRef,
1093
+ className: "absolute inset-0",
1094
+ children: peersWidth > 0 && peersSpec ? /* @__PURE__ */ jsx(ArcVegaChart, {
1095
+ spec: peersSpec,
1096
+ options: peersOptions,
1097
+ datasets: peersDatasets,
1098
+ description: peersChartDescription,
1099
+ width: peersWidth,
1100
+ height: sizes.peersHeight,
1101
+ onEmbed: onEmbed ? handlePeersEmbed : void 0,
1102
+ className: "absolute inset-0 [&_svg]:block [&_.vega-bindings:empty]:hidden",
1103
+ fallback: /* @__PURE__ */ jsx(Skeleton, {
1104
+ variant: "rounded",
1105
+ className: "size-full"
1106
+ })
1107
+ }) : /* @__PURE__ */ jsx(Skeleton, {
1108
+ variant: "rounded",
1109
+ className: "size-full"
1110
+ })
1111
+ })]
1112
+ }), /* @__PURE__ */ jsx(TooltipPopup, {
1113
+ arrow: false,
1114
+ side: "bottom",
1115
+ align: "start",
1116
+ sideOffset: 8,
1117
+ collisionPadding: 8,
1118
+ container: popoverContainer,
1119
+ className: "text-left",
1120
+ "data-part": "peers-tooltip",
1121
+ children: /* @__PURE__ */ jsx(ArcTooltipRows, { rows: peerExplanationRows })
1122
+ })] }),
1123
+ /* @__PURE__ */ jsx("p", {
1124
+ className: cn("font-sans text-foreground", sizes.body),
1125
+ "data-part": "verdict",
1126
+ children: buildPeerVerdict(peers, labels)
1127
+ }),
1128
+ /* @__PURE__ */ jsx("p", {
1129
+ className: captionClasses,
1130
+ "data-part": "peers-caption",
1131
+ children: labels.peersCaption({
1132
+ count: peers.count,
1133
+ readings: peers.length + 1,
1134
+ mode: peers.mode
1135
+ })
1136
+ })
1137
+ ] })]
1138
+ }, section);
1139
+ }
1140
+ if (!hasDrivers || !driverInfo) return null;
1141
+ return /* @__PURE__ */ jsxs("div", {
1142
+ className: "flex min-w-0 flex-col gap-2",
1143
+ "data-part": "drivers",
1144
+ children: [
1145
+ /* @__PURE__ */ jsx(SectionTag, {
1146
+ className: cn(sectionTitleClasses, sizes.body),
1147
+ children: labels.driversTitle
1148
+ }),
1149
+ /* @__PURE__ */ jsxs("div", {
1150
+ ref: driverRowsRef,
1151
+ className: "relative w-full",
1152
+ children: [/* @__PURE__ */ jsx("ul", {
1153
+ "aria-label": labels.driversGroup(name),
1154
+ className: cn(driverGridClasses, "font-sans", sizes.rowText),
1155
+ style: { gridAutoRows: `${sizes.step}px` },
1156
+ "data-part": "driver-rows",
1157
+ children: driverInfo.rows.map((row, index) => {
1158
+ const choosable = interactive && row.role === "driver";
1159
+ const selected = row.key === driverKey;
1160
+ const cells = /* @__PURE__ */ jsxs(Fragment$1, { children: [
1161
+ /* @__PURE__ */ jsxs("span", {
1162
+ className: "flex min-w-0 items-center gap-1.5",
1163
+ "data-part": "driver-label",
1164
+ children: [/* @__PURE__ */ jsx(DriverSwatch, {
1165
+ color: driverColor(row.polarity),
1166
+ hollow: row.role === "residual"
1167
+ }), /* @__PURE__ */ jsx("span", {
1168
+ className: "min-w-0 truncate",
1169
+ children: row.label
1170
+ })]
1171
+ }),
1172
+ /* @__PURE__ */ jsx("span", {
1173
+ ref: index === 0 ? driverCellRef : void 0,
1174
+ "aria-hidden": "true",
1175
+ className: "min-w-0 self-stretch",
1176
+ "data-part": "driver-plot-cell"
1177
+ }),
1178
+ /* @__PURE__ */ jsx("span", {
1179
+ className: cn("text-right tabular-nums", changeTextClasses[row.polarity]),
1180
+ "data-part": "driver-value",
1181
+ children: row.contributionText
1182
+ }),
1183
+ /* @__PURE__ */ jsx("span", {
1184
+ className: "text-right text-muted-foreground tabular-nums",
1185
+ "data-part": "driver-share",
1186
+ children: row.shareText
1187
+ })
1188
+ ] });
1189
+ return /* @__PURE__ */ jsx("li", {
1190
+ className: "col-span-4 grid grid-cols-subgrid items-center",
1191
+ "data-part": "driver-row",
1192
+ "data-role": row.role,
1193
+ "data-selected": selected ? "" : void 0,
1194
+ children: choosable ? /* @__PURE__ */ jsx("button", {
1195
+ type: "button",
1196
+ onClick: () => selectDriver(row.key),
1197
+ onKeyDown: (event) => onDriverKeyDown(event, row.key),
1198
+ "aria-label": labels.selectDriver(row.label),
1199
+ "aria-pressed": selected,
1200
+ className: cn("col-span-4 grid cursor-pointer grid-cols-subgrid items-center rounded-md text-left", "focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ring", selected ? "bg-primary-50" : "hover:bg-muted"),
1201
+ children: cells
1202
+ }) : cells
1203
+ }, row.key);
1204
+ })
1205
+ }), driverBox.width > 0 && driversSpec && /* @__PURE__ */ jsx(ArcVegaChart, {
1206
+ spec: driversSpec,
1207
+ options: driversOptions,
1208
+ datasets: driversDatasets,
1209
+ description: driversChartDescription,
1210
+ width: driverBox.width,
1211
+ height: driverPlotHeight,
1212
+ onEmbed: onEmbed ? handleDriversEmbed : void 0,
1213
+ onClick: interactive ? handleDriverPlotClick : void 0,
1214
+ className: cn("absolute top-0 [&_svg]:block [&_.vega-bindings:empty]:hidden", interactive && "cursor-pointer"),
1215
+ style: {
1216
+ left: driverBox.left,
1217
+ width: driverBox.width,
1218
+ height: driverPlotHeight
1219
+ },
1220
+ "data-part": "drivers-plot",
1221
+ fallback: /* @__PURE__ */ jsx(Skeleton, {
1222
+ variant: "rounded",
1223
+ className: "absolute top-0",
1224
+ style: {
1225
+ left: driverBox.left,
1226
+ width: driverBox.width,
1227
+ height: driverPlotHeight
1228
+ }
1229
+ })
1230
+ })]
1231
+ }),
1232
+ /* @__PURE__ */ jsxs("p", {
1233
+ className: captionClasses,
1234
+ "data-part": "drivers-caption",
1235
+ children: [labels.driversCaption({
1236
+ total: driverInfo.totalText,
1237
+ explained: driverInfo.explainedText,
1238
+ residual: driverInfo.residualText,
1239
+ reconciles: driverInfo.reconciles
1240
+ }), driverInfo.folded > 0 && ` ${labels.foldedCaption(driverInfo.folded, labels.driverOther)}`]
1241
+ })
1242
+ ]
1243
+ }, section);
1244
+ }),
1245
+ showInsight && /* @__PURE__ */ jsxs("div", {
1246
+ className: cn("flex items-start gap-2 font-sans text-foreground", sizes.body),
1247
+ "data-part": "insight",
1248
+ children: [/* @__PURE__ */ jsx(AILoader, {
1249
+ size: "sm",
1250
+ state: "default",
1251
+ label: labels.insightMark,
1252
+ className: cn("shrink-0", size === "sm" ? "" : "mt-0.5")
1253
+ }), /* @__PURE__ */ jsx("p", {
1254
+ className: "min-w-0 flex-1 text-xs leading-[1.8] [&_strong]:font-semibold [&_strong]:text-foreground",
1255
+ children: insightContent
1256
+ })]
1257
+ })
1258
+ ] }),
1259
+ state === "refreshing" && /* @__PURE__ */ jsx("div", {
1260
+ "aria-hidden": "true",
1261
+ className: "absolute -inset-1 rounded-md bg-veil"
1262
+ })
1263
+ ]
1264
+ }),
1265
+ asOfValid && asOfDate && /* @__PURE__ */ jsx("p", {
1266
+ className: cn(captionClasses, "text-right"),
1267
+ "data-part": "as-of",
1268
+ children: /* @__PURE__ */ jsx("time", {
1269
+ dateTime: asOfDate.toISOString(),
1270
+ children: asOfText
1271
+ })
1272
+ }),
1273
+ /* @__PURE__ */ jsx("div", {
1274
+ ref: setPopoverContainer,
1275
+ className: "pointer-events-none absolute inset-0",
1276
+ "data-part": "popover-host"
1277
+ }),
1278
+ contextTooltip.tooltip,
1279
+ peersTooltip.tooltip,
1280
+ driversTooltip.tooltip
1281
+ ]
1282
+ });
1283
+ });
1284
+ HighlightCard.displayName = "HighlightCard";
1285
+ //#endregion
1286
+ export { HighlightCard };