@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,908 @@
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 { PROGRESS_COMPLETED_DATASET, PROGRESS_FINISH_DATASET, PROGRESS_IDEAL_DATASET, PROGRESS_LATEST_DATASET, PROGRESS_PROJECTION_DATASET, PROGRESS_SCOPE_DATASET, buildProgressBurnUpSpec } from "./ProgressCard.chart.js";
6
+ import { buildProgressChartDescription, buildProgressDescription, buildProgressExplanation, buildProgressHeadline, buildProgressReadingRows, buildProgressStageRows, buildStageBarLabel, createProgressFormatters, deriveProgressRun, deriveProgressState, resolveProgressLabels } from "./ProgressCard.model.js";
7
+ import { forwardRef, useCallback, useEffect, useId, useMemo, useRef, useState } from "react";
8
+ import { AILoader, Badge, Box, Button, Popover, PopoverPopup, PopoverTitle, PopoverTrigger, Skeleton, SkeletonGroup, SystemState, Table, TableBody, TableCaption, TableCell, TableContainer, TableHead, TableHeader, TableRow, Tooltip, TooltipPopup, TooltipTrigger, cn, progressIndicatorVariants, progressTrackVariants } from "@arcfusionz/arc-primitive-ui";
9
+ import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
10
+ //#region src/components/ProgressCard/ProgressCard.tsx
11
+ const sizeStyles = {
12
+ sm: {
13
+ padding: "sm",
14
+ gap: "gap-2",
15
+ gapPx: 8,
16
+ label: "text-xs",
17
+ value: "text-2xl leading-8",
18
+ valueLinePx: 32,
19
+ unit: "text-xs",
20
+ body: "text-xs",
21
+ subLinePx: 16,
22
+ chip: "sm",
23
+ chipLinePx: 20,
24
+ plotHeight: 88,
25
+ barSize: "sm",
26
+ barRowPx: 4,
27
+ legendLinePx: 18,
28
+ legendRows: 2,
29
+ legendText: "text-xs"
30
+ },
31
+ md: {
32
+ padding: "md",
33
+ gap: "gap-2",
34
+ gapPx: 8,
35
+ label: "text-sm",
36
+ value: "text-3xl leading-9",
37
+ valueLinePx: 36,
38
+ unit: "text-sm",
39
+ body: "text-sm",
40
+ subLinePx: 20,
41
+ chip: "sm",
42
+ chipLinePx: 20,
43
+ plotHeight: 116,
44
+ barSize: "md",
45
+ barRowPx: 8,
46
+ legendLinePx: 20,
47
+ legendRows: 1,
48
+ legendText: "text-xs"
49
+ },
50
+ lg: {
51
+ padding: "lg",
52
+ gap: "gap-2",
53
+ gapPx: 8,
54
+ label: "text-sm",
55
+ value: "text-4xl leading-10",
56
+ valueLinePx: 40,
57
+ unit: "text-base",
58
+ body: "text-sm",
59
+ subLinePx: 20,
60
+ chip: "md",
61
+ chipLinePx: 24,
62
+ plotHeight: 148,
63
+ barSize: "lg",
64
+ barRowPx: 12,
65
+ legendLinePx: 22,
66
+ legendRows: 1,
67
+ legendText: "text-sm"
68
+ }
69
+ };
70
+ const labelClasses = "min-w-0 font-sans font-medium text-muted-foreground";
71
+ const valueClasses = "font-heading font-semibold tracking-tight text-foreground";
72
+ const captionClasses = "font-sans text-xs text-muted-foreground";
73
+ const headlineStackClasses = "flex min-w-0 flex-col items-start gap-1 rounded-md font-sans focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ring";
74
+ const headerControlClasses = "size-6 min-w-0 text-muted-foreground hover:text-foreground";
75
+ const tileStateClasses = "w-full max-w-none gap-2 px-2 py-2 [--artwork-box:3rem]";
76
+ const MISSING_NAME = "Progress";
77
+ const FOOTER_ROW_PX = 16;
78
+ const HEADER_ROW_PX = 24;
79
+ const verdictBadgeVariant = {
80
+ done: "success",
81
+ "on-time": "success",
82
+ "at-risk": "warning",
83
+ late: "destructive",
84
+ stalled: "destructive"
85
+ };
86
+ /**
87
+ * The composed stage bar's fills, by what the stage means — the same four
88
+ * token roles the plot uses, as Tailwind classes rather than resolved values,
89
+ * because this bar is CSS and flips with the theme on its own. `remaining`
90
+ * draws nothing: the muted Progress track already stands for work not
91
+ * started, and gray is the colour of context.
92
+ */
93
+ const stageFillClasses = {
94
+ complete: "bg-chart-emphasis",
95
+ active: "bg-chart-sequential-5",
96
+ blocked: "bg-chart-diverging-negative",
97
+ remaining: "bg-transparent"
98
+ };
99
+ /**
100
+ * The plot's legend keys, drawn as the marks they name so their meaning never
101
+ * depends on colour alone: a solid 2px line for what happened, a stepped gray
102
+ * line for the scope, a dashed reference for the required pace, and a dashed
103
+ * polarity line for the projection.
104
+ */
105
+ function MarkKey({ kind, label, color }) {
106
+ return /* @__PURE__ */ jsxs("span", {
107
+ className: "inline-flex min-w-0 items-center gap-1",
108
+ children: [/* @__PURE__ */ jsxs("svg", {
109
+ width: "16",
110
+ height: "10",
111
+ viewBox: "0 0 18 10",
112
+ fill: "none",
113
+ "aria-hidden": "true",
114
+ className: "shrink-0",
115
+ style: { color },
116
+ children: [
117
+ kind === "completed" && /* @__PURE__ */ jsx("path", {
118
+ d: "M1 8 L6 5 L12 3 L17 2",
119
+ stroke: "currentColor",
120
+ strokeWidth: 2,
121
+ strokeLinecap: "round"
122
+ }),
123
+ kind === "scope" && /* @__PURE__ */ jsx("path", {
124
+ d: "M1 7 L9 7 L9 3 L17 3",
125
+ stroke: "currentColor",
126
+ strokeWidth: 1.5,
127
+ strokeLinecap: "round"
128
+ }),
129
+ kind === "ideal" && /* @__PURE__ */ jsx("path", {
130
+ d: "M1 8 L17 2",
131
+ stroke: "currentColor",
132
+ strokeWidth: 1.5,
133
+ strokeDasharray: "4 3",
134
+ strokeLinecap: "round"
135
+ }),
136
+ kind === "projection" && /* @__PURE__ */ jsx("path", {
137
+ d: "M1 8 L17 2",
138
+ stroke: "currentColor",
139
+ strokeWidth: 2,
140
+ strokeDasharray: "3 3",
141
+ strokeLinecap: "round"
142
+ })
143
+ ]
144
+ }), /* @__PURE__ */ jsx("span", {
145
+ className: "min-w-0 truncate",
146
+ children: label
147
+ })]
148
+ });
149
+ }
150
+ function StageSwatch({ tone }) {
151
+ return /* @__PURE__ */ jsx("span", {
152
+ "aria-hidden": "true",
153
+ className: cn("mt-px size-2.5 shrink-0 rounded-xs", stageFillClasses[tone], tone === "remaining" && "border border-border bg-muted"),
154
+ "data-tone": tone
155
+ });
156
+ }
157
+ function InfoIcon(props) {
158
+ return /* @__PURE__ */ jsxs("svg", {
159
+ viewBox: "0 0 24 24",
160
+ fill: "none",
161
+ stroke: "currentColor",
162
+ strokeWidth: 2,
163
+ strokeLinecap: "round",
164
+ strokeLinejoin: "round",
165
+ "aria-hidden": "true",
166
+ ...props,
167
+ children: [/* @__PURE__ */ jsx("circle", {
168
+ cx: "12",
169
+ cy: "12",
170
+ r: "9"
171
+ }), /* @__PURE__ */ jsx("path", { d: "M12 11v5M12 8h.01" })]
172
+ });
173
+ }
174
+ /** A glyph per verdict — a check when it is done, a warning when at risk, a cross when late, a pause when stalled. */
175
+ function VerdictGlyph({ verdict, ...props }) {
176
+ return /* @__PURE__ */ jsxs("svg", {
177
+ viewBox: "0 0 24 24",
178
+ fill: "none",
179
+ stroke: "currentColor",
180
+ strokeWidth: 2.5,
181
+ strokeLinecap: "round",
182
+ strokeLinejoin: "round",
183
+ "aria-hidden": "true",
184
+ ...props,
185
+ children: [
186
+ verdict === "done" && /* @__PURE__ */ jsx("path", { d: "m5 12 5 5L20 7" }),
187
+ verdict === "on-time" && /* @__PURE__ */ jsx("circle", {
188
+ cx: "12",
189
+ cy: "12",
190
+ r: "5",
191
+ fill: "currentColor",
192
+ stroke: "none"
193
+ }),
194
+ verdict === "at-risk" && /* @__PURE__ */ jsx("path", { d: "M12 4 3 20h18L12 4Zm0 6v4m0 3h.01" }),
195
+ verdict === "late" && /* @__PURE__ */ jsx("path", { d: "M12 8v5m0 3h.01M12 3a9 9 0 1 0 0 18 9 9 0 0 0 0-18Z" }),
196
+ verdict === "stalled" && /* @__PURE__ */ jsx("path", { d: "M9 6v12M15 6v12" })
197
+ ]
198
+ });
199
+ }
200
+ function useMergedRef(forwarded) {
201
+ const inner = useRef(null);
202
+ return [inner, useCallback((node) => {
203
+ inner.current = node;
204
+ if (typeof forwarded === "function") forwarded(node);
205
+ else if (forwarded) forwarded.current = node;
206
+ }, [forwarded])];
207
+ }
208
+ function usePlotWidth(ref, enabled) {
209
+ const [width, setWidth] = useState(0);
210
+ useEffect(() => {
211
+ if (!enabled) return;
212
+ const el = ref.current;
213
+ if (!el) return;
214
+ let frame = 0;
215
+ const measure = () => {
216
+ const next = Math.round(el.getBoundingClientRect().width);
217
+ setWidth((prev) => prev === next ? prev : next);
218
+ };
219
+ measure();
220
+ if (typeof ResizeObserver === "undefined") return;
221
+ const observer = new ResizeObserver(() => {
222
+ cancelAnimationFrame(frame);
223
+ frame = requestAnimationFrame(measure);
224
+ });
225
+ observer.observe(el);
226
+ return () => {
227
+ cancelAnimationFrame(frame);
228
+ observer.disconnect();
229
+ };
230
+ }, [ref, enabled]);
231
+ return width;
232
+ }
233
+ /**
234
+ * A delivery tile for dashboard grids: how much of a finite body of work is
235
+ * finished, the rate it is being finished at, when that rate lands it, and
236
+ * whether that is before the deadline — on a burn-up chart (completion rising
237
+ * from zero, the scope it is chasing, the pace the deadline requires, and a
238
+ * dashed projection to the crossing) with every loading, empty, error and
239
+ * stale state, a generated text equivalent and a reading-by-reading table.
240
+ */
241
+ const ProgressCard = forwardRef(({ label, name: nameProp, hint, snapshots, total, due, projected, rateWindow, tolerance = 2, stages: stageInput, format = "integer", formatValue, percentDigits = 0, unit, headline = "percent", dateFormat = "day", axes = true, showIdeal = true, showProjection = true, showGap = true, legend = true, plotHeight: plotHeightProp, insight, status = "ready", filtered = false, stale = false, onRetry, onClearFilters, size = "md", titleLevel = 3, locale, timeZone, labels: labelOverrides, description, action, defaultTableOpen = false, hideTableToggle = false, onEmbed, patchSpec, render, className, ...rest }, forwardedRef) => {
242
+ const labels = useMemo(() => resolveProgressLabels(labelOverrides), [labelOverrides]);
243
+ const sizes = sizeStyles[size];
244
+ const name = nameProp ?? (typeof label === "string" ? label : MISSING_NAME);
245
+ const plotHeight = plotHeightProp ?? sizes.plotHeight;
246
+ const id = useId();
247
+ const headingId = `${id}-label`;
248
+ const descriptionId = `${id}-description`;
249
+ const hintId = `${id}-hint`;
250
+ const tableId = `${id}-table`;
251
+ const explanationId = `${id}-explanation`;
252
+ const [rootRef, setRootRef] = useMergedRef(forwardedRef);
253
+ const plotRef = useRef(null);
254
+ const [tableOpen, setTableOpen] = useState(defaultTableOpen);
255
+ const [popoverContainer, setPopoverContainer] = useState(null);
256
+ const formatters = useMemo(() => createProgressFormatters({
257
+ format,
258
+ locale,
259
+ timeZone,
260
+ formatValue,
261
+ percentDigits,
262
+ ...typeof dateFormat === "function" ? { formatDateValue: dateFormat } : { dateGranularity: dateFormat }
263
+ }), [
264
+ format,
265
+ locale,
266
+ timeZone,
267
+ formatValue,
268
+ percentDigits,
269
+ dateFormat
270
+ ]);
271
+ const run = useMemo(() => deriveProgressRun({
272
+ snapshots,
273
+ total,
274
+ due,
275
+ rateWindow,
276
+ projected,
277
+ tolerance,
278
+ stages: stageInput,
279
+ formatters,
280
+ labels,
281
+ timeZone
282
+ }), [
283
+ snapshots,
284
+ total,
285
+ due,
286
+ rateWindow,
287
+ projected,
288
+ tolerance,
289
+ stageInput,
290
+ formatters,
291
+ labels,
292
+ timeZone
293
+ ]);
294
+ const hasValues = snapshots.some((snapshot) => snapshot.completed != null && Number.isFinite(snapshot.completed) && snapshot.completed >= 0);
295
+ const state = deriveProgressState({
296
+ status,
297
+ hasValues,
298
+ hasData: run != null,
299
+ hasTrend: run != null && !run.insufficient,
300
+ filtered
301
+ });
302
+ const showContent = (state === "ready" || state === "refreshing" || state === "insufficient") && run != null;
303
+ const showPlot = showContent && run != null && !run.insufficient;
304
+ const cardDescription = description ?? buildProgressDescription({
305
+ name,
306
+ state,
307
+ run,
308
+ labels
309
+ });
310
+ const explanationRows = run ? buildProgressExplanation(run, labels) : [];
311
+ const explanation = explanationRows.map(([key, entry]) => `${key}: ${entry}`).join(". ");
312
+ const headlineText = run ? buildProgressHeadline(run, headline, labels) : null;
313
+ const readingRows = useMemo(() => run ? buildProgressReadingRows(run, formatters, labels) : [], [
314
+ run,
315
+ formatters,
316
+ labels
317
+ ]);
318
+ const stageRows = useMemo(() => run ? buildProgressStageRows(run, labels) : [], [run, labels]);
319
+ const tokens = useChartTokens(rootRef);
320
+ const plotWidth = usePlotWidth(plotRef, showPlot);
321
+ const hasIdeal = showIdeal && run?.ideal != null;
322
+ const hasProjection = showProjection && run?.projection != null;
323
+ const hasDue = run?.due != null;
324
+ const tooltipContent = useCallback(({ item }) => {
325
+ if (!run) return null;
326
+ const datum = item.datum;
327
+ if (!datum || typeof datum.t !== "number") return null;
328
+ const completed = typeof datum.y === "number" ? datum.y : null;
329
+ const scope = typeof datum.total === "number" ? datum.total : run.scope;
330
+ const rows = [
331
+ [formatters.formatDay(datum.t), ""],
332
+ [/* @__PURE__ */ jsx(MarkKey, {
333
+ kind: "completed",
334
+ label: labels.markNames.completed,
335
+ color: tokens?.emphasis
336
+ }, "c"), completed == null ? formatters.formatValue(null) : `${formatters.formatValue(completed)} (${formatters.formatShare(scope > 0 ? completed / scope : 0)})`],
337
+ [/* @__PURE__ */ jsx(MarkKey, {
338
+ kind: "scope",
339
+ label: labels.markNames.scope,
340
+ color: tokens?.context
341
+ }, "s"), formatters.formatValue(scope)]
342
+ ];
343
+ if (completed != null) rows.push([labels.remaining, formatters.formatValue(Math.max(0, scope - completed))]);
344
+ return /* @__PURE__ */ jsx(ArcTooltipRows, { rows });
345
+ }, [
346
+ run,
347
+ formatters,
348
+ labels,
349
+ tokens
350
+ ]);
351
+ const { handler: tooltipHandler, tooltip } = useArcTooltipHandler({ content: tooltipContent });
352
+ const embedOptions = useMemo(() => arcEmbedOptions({ tooltip: tooltipHandler }), [tooltipHandler]);
353
+ const spec = useMemo(() => {
354
+ if (!tokens) return null;
355
+ const built = buildProgressBurnUpSpec({
356
+ height: plotHeight,
357
+ axes,
358
+ ideal: hasIdeal,
359
+ projection: hasProjection,
360
+ due: hasDue,
361
+ dueLabel: labels.markNames.due,
362
+ gap: showGap,
363
+ tooltip: true,
364
+ dateTitle: labels.tableDate,
365
+ completedTitle: labels.markNames.completed,
366
+ scopeTitle: labels.markNames.scope,
367
+ valueFormat: locale ? `${format}@${locale}` : format,
368
+ timeFormat: [
369
+ "day",
370
+ locale ?? "",
371
+ timeZone ?? ""
372
+ ].join("@").replace(/@+$/, ""),
373
+ axisTimeFormat: [
374
+ "day-short",
375
+ locale ?? "",
376
+ timeZone ?? ""
377
+ ].join("@").replace(/@+$/, ""),
378
+ locale
379
+ }, tokens);
380
+ return patchSpec ? patchSpec(built) : built;
381
+ }, [
382
+ tokens,
383
+ plotHeight,
384
+ axes,
385
+ hasIdeal,
386
+ hasProjection,
387
+ hasDue,
388
+ showGap,
389
+ labels,
390
+ format,
391
+ locale,
392
+ timeZone,
393
+ patchSpec
394
+ ]);
395
+ const datasets = useMemo(() => {
396
+ if (!run) return void 0;
397
+ const tone = run.projection?.late ? "negative" : "positive";
398
+ return {
399
+ [PROGRESS_COMPLETED_DATASET]: run.points.map((point) => ({
400
+ t: point.t,
401
+ y: point.completed,
402
+ total: point.total,
403
+ aria: `${formatters.formatDay(point.t)}: ${formatters.formatValue(point.completed)}`
404
+ })),
405
+ [PROGRESS_SCOPE_DATASET]: [...run.points.map((point) => ({
406
+ t: point.t,
407
+ y: point.total,
408
+ aria: `${labels.markNames.scope} ${formatters.formatValue(point.total)}`
409
+ })), ...run.bounds.max > run.asOf ? [{
410
+ t: run.bounds.max,
411
+ y: run.scope,
412
+ aria: `${labels.markNames.scope} ${run.scopeText}`
413
+ }] : []],
414
+ ["gap"]: run.points.filter((point) => point.completed != null).map((point) => ({
415
+ t: point.t,
416
+ y: point.completed,
417
+ y2: point.total
418
+ })),
419
+ [PROGRESS_IDEAL_DATASET]: run.ideal ? [run.ideal.from, run.ideal.to] : [],
420
+ [PROGRESS_PROJECTION_DATASET]: run.projection ? [{
421
+ t: run.asOf,
422
+ y: run.completed,
423
+ tone,
424
+ aria: labels.markNames.projection
425
+ }, {
426
+ t: run.projection.date,
427
+ y: run.scope,
428
+ tone,
429
+ aria: run.projection.text
430
+ }] : [],
431
+ ["due"]: run.due != null ? [{ t: run.due }] : [],
432
+ [PROGRESS_LATEST_DATASET]: [{
433
+ t: run.asOf,
434
+ y: run.completed
435
+ }],
436
+ [PROGRESS_FINISH_DATASET]: run.projection ? [{
437
+ t: run.projection.date,
438
+ y: run.scope,
439
+ tone
440
+ }] : []
441
+ };
442
+ }, [
443
+ run,
444
+ formatters,
445
+ labels
446
+ ]);
447
+ const chartDescription = run ? buildProgressChartDescription(name, run, labels) : void 0;
448
+ const reserveInsight = insight != null && insight !== false;
449
+ const hasStages = (run?.stages.length ?? 0) > 0;
450
+ const showVerdict = showContent && run?.verdict != null;
451
+ const verdictTextLines = due != null ? 2 : 1;
452
+ const headlineMinHeight = Math.max(sizes.valueLinePx + 4 + sizes.subLinePx, sizes.chipLinePx + 4 + sizes.subLinePx * verdictTextLines);
453
+ const legendReserve = legend ? sizes.gapPx + sizes.legendLinePx * sizes.legendRows : 0;
454
+ const bodyMinHeight = headlineMinHeight + sizes.gapPx + (hasStages ? sizes.barRowPx + sizes.gapPx : 0) + plotHeight + legendReserve + (reserveInsight ? sizes.gapPx + sizes.legendLinePx : 0);
455
+ const legendRow = legend ? /* @__PURE__ */ jsxs("div", {
456
+ className: cn("flex min-w-0 flex-wrap items-center gap-x-2.5 gap-y-0.5 font-sans text-muted-foreground", sizes.legendText),
457
+ style: { minHeight: sizes.legendLinePx },
458
+ "data-part": "legend",
459
+ children: [
460
+ /* @__PURE__ */ jsx(MarkKey, {
461
+ kind: "completed",
462
+ label: labels.markNames.completed,
463
+ color: tokens?.emphasis
464
+ }),
465
+ /* @__PURE__ */ jsx(MarkKey, {
466
+ kind: "scope",
467
+ label: labels.markNames.scope,
468
+ color: tokens?.context
469
+ }),
470
+ showIdeal && due != null && /* @__PURE__ */ jsx(MarkKey, {
471
+ kind: "ideal",
472
+ label: labels.markNames.ideal,
473
+ color: tokens?.reference
474
+ }),
475
+ showProjection && /* @__PURE__ */ jsx(MarkKey, {
476
+ kind: "projection",
477
+ label: labels.markNames.projection,
478
+ color: run?.projection == null ? tokens?.mutedForeground : run.projection.late ? tokens?.divergingNegative : tokens?.divergingPositive
479
+ })
480
+ ]
481
+ }) : null;
482
+ const HeadingTag = `h${titleLevel}`;
483
+ const busy = state === "loading" || state === "refreshing";
484
+ return /* @__PURE__ */ jsxs(Box, {
485
+ ref: setRootRef,
486
+ render: render ?? /* @__PURE__ */ jsx("figure", {}),
487
+ padding: sizes.padding,
488
+ "aria-labelledby": headingId,
489
+ "aria-describedby": descriptionId,
490
+ "aria-busy": busy || void 0,
491
+ "data-status": status,
492
+ "data-state": state,
493
+ "data-size": size,
494
+ "data-headline": headline,
495
+ "data-verdict": showContent && run?.verdict ? run.verdict : void 0,
496
+ "data-polarity": showContent && run ? run.polarity : void 0,
497
+ "data-stalled": showContent && run?.stalled ? "" : void 0,
498
+ "data-scope-changed": showContent && run?.scopeChanged ? "" : void 0,
499
+ "data-empty": state === "empty" || state === "no-match" ? "" : void 0,
500
+ "data-insufficient": state === "insufficient" ? "" : void 0,
501
+ "data-stale": stale ? "" : void 0,
502
+ ...rest,
503
+ className: cn("relative flex min-w-0 flex-col", sizes.gap, className),
504
+ children: [
505
+ /* @__PURE__ */ jsxs("div", {
506
+ className: "flex items-center justify-between gap-2",
507
+ style: { minHeight: HEADER_ROW_PX },
508
+ children: [/* @__PURE__ */ jsxs("div", {
509
+ className: "flex min-w-0 items-center gap-1",
510
+ children: [/* @__PURE__ */ jsx(HeadingTag, {
511
+ id: headingId,
512
+ className: cn(labelClasses, sizes.label, "line-clamp-2"),
513
+ children: label
514
+ }), hint && /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsxs(Tooltip, { children: [/* @__PURE__ */ jsx(TooltipTrigger, {
515
+ render: /* @__PURE__ */ jsx(Button, {
516
+ variant: "ghost",
517
+ size: "sm",
518
+ iconOnly: true,
519
+ circular: true,
520
+ "aria-label": labels.about(name),
521
+ "aria-describedby": hintId,
522
+ className: headerControlClasses
523
+ }),
524
+ children: /* @__PURE__ */ jsx(InfoIcon, { className: "size-3.5" })
525
+ }), /* @__PURE__ */ jsx(TooltipPopup, { children: hint })] }), /* @__PURE__ */ jsx("span", {
526
+ id: hintId,
527
+ className: "sr-only",
528
+ children: hint
529
+ })] })]
530
+ }), /* @__PURE__ */ jsxs("div", {
531
+ className: "flex shrink-0 items-center gap-1",
532
+ children: [
533
+ stale && /* @__PURE__ */ jsx(Badge, {
534
+ variant: "warning",
535
+ appearance: "soft",
536
+ size: "sm",
537
+ children: labels.stale
538
+ }),
539
+ showContent && run && /* @__PURE__ */ jsxs(Popover, {
540
+ open: tableOpen,
541
+ onOpenChange: setTableOpen,
542
+ children: [!hideTableToggle && /* @__PURE__ */ jsx(PopoverTrigger, {
543
+ render: /* @__PURE__ */ jsx(Button, {
544
+ variant: "ghost",
545
+ size: "sm",
546
+ iconOnly: true,
547
+ circular: true,
548
+ "aria-label": tableOpen ? labels.hideTable(name) : labels.showTable(name),
549
+ className: headerControlClasses
550
+ }),
551
+ children: /* @__PURE__ */ jsx(InfoIcon, { className: "size-3.5" })
552
+ }), popoverContainer && /* @__PURE__ */ jsxs(PopoverPopup, {
553
+ id: tableId,
554
+ anchor: rootRef,
555
+ container: popoverContainer,
556
+ side: "right",
557
+ align: "start",
558
+ collisionPadding: 8,
559
+ className: "pointer-events-auto w-80",
560
+ "data-part": "data-popover",
561
+ children: [/* @__PURE__ */ jsx(PopoverTitle, {
562
+ className: "mb-3",
563
+ children: name
564
+ }), /* @__PURE__ */ jsx(TableContainer, {
565
+ variant: "plain",
566
+ tabIndex: 0,
567
+ role: "region",
568
+ "aria-label": labels.tableCaption(name),
569
+ className: "max-h-[min(18rem,calc(var(--available-height)-4.5rem))] focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ring",
570
+ "data-part": "table",
571
+ children: /* @__PURE__ */ jsxs(Table, {
572
+ size: "sm",
573
+ stickyHeader: true,
574
+ hoverable: false,
575
+ children: [
576
+ /* @__PURE__ */ jsx(TableCaption, {
577
+ side: "top",
578
+ className: "sr-only",
579
+ children: labels.tableCaption(name)
580
+ }),
581
+ /* @__PURE__ */ jsx(TableHeader, { children: /* @__PURE__ */ jsxs(TableRow, { children: [
582
+ /* @__PURE__ */ jsx(TableHead, { children: labels.tableDate }),
583
+ /* @__PURE__ */ jsx(TableHead, {
584
+ className: "text-right",
585
+ children: labels.tableCompleted
586
+ }),
587
+ /* @__PURE__ */ jsx(TableHead, {
588
+ className: "text-right",
589
+ children: labels.tableShare
590
+ })
591
+ ] }) }),
592
+ /* @__PURE__ */ jsxs(TableBody, { children: [readingRows.map((row) => /* @__PURE__ */ jsxs(TableRow, {
593
+ "data-row": row.key,
594
+ "data-summary": row.summary ? "" : void 0,
595
+ children: [
596
+ /* @__PURE__ */ jsx(TableCell, {
597
+ className: cn(row.summary && "font-medium text-foreground"),
598
+ children: row.label
599
+ }),
600
+ /* @__PURE__ */ jsx(TableCell, {
601
+ className: cn("text-right tabular-nums", row.summary && "font-medium text-foreground"),
602
+ children: row.value
603
+ }),
604
+ /* @__PURE__ */ jsx(TableCell, {
605
+ className: cn("text-right tabular-nums", row.summary && "font-medium text-foreground"),
606
+ children: row.share
607
+ })
608
+ ]
609
+ }, row.key)), stageRows.length > 0 && /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsx(TableRow, {
610
+ "data-row": "__stages__",
611
+ children: /* @__PURE__ */ jsx(TableCell, {
612
+ colSpan: 3,
613
+ className: "pt-3 font-medium text-foreground",
614
+ children: labels.tableItem
615
+ })
616
+ }), stageRows.map((row) => /* @__PURE__ */ jsxs(TableRow, {
617
+ "data-row": row.key,
618
+ "data-summary": row.summary ? "" : void 0,
619
+ children: [
620
+ /* @__PURE__ */ jsx(TableCell, { children: /* @__PURE__ */ jsxs("span", {
621
+ className: "flex min-w-0 items-start gap-2",
622
+ children: [row.stage && /* @__PURE__ */ jsx(StageSwatch, { tone: row.stage.tone }), /* @__PURE__ */ jsx("span", {
623
+ className: "min-w-0",
624
+ children: row.label
625
+ })]
626
+ }) }),
627
+ /* @__PURE__ */ jsx(TableCell, {
628
+ className: cn("text-right tabular-nums", row.summary && "font-medium text-foreground"),
629
+ children: row.value
630
+ }),
631
+ /* @__PURE__ */ jsx(TableCell, {
632
+ className: cn("text-right tabular-nums", row.summary && "font-medium text-foreground"),
633
+ children: row.share
634
+ })
635
+ ]
636
+ }, row.key))] })] })
637
+ ]
638
+ })
639
+ })]
640
+ })]
641
+ }),
642
+ action
643
+ ]
644
+ })]
645
+ }),
646
+ /* @__PURE__ */ jsx("p", {
647
+ id: descriptionId,
648
+ className: "sr-only",
649
+ children: cardDescription
650
+ }),
651
+ /* @__PURE__ */ jsxs("div", {
652
+ className: "relative flex min-w-0 flex-col",
653
+ style: { minHeight: bodyMinHeight },
654
+ children: [
655
+ state === "loading" && /* @__PURE__ */ jsxs(SkeletonGroup, {
656
+ className: cn("flex flex-col", sizes.gap),
657
+ children: [
658
+ /* @__PURE__ */ jsxs("div", {
659
+ className: "flex min-w-0 items-start justify-between gap-2",
660
+ style: { minHeight: headlineMinHeight },
661
+ children: [/* @__PURE__ */ jsxs("div", {
662
+ className: "flex w-2/5 flex-col gap-1",
663
+ children: [/* @__PURE__ */ jsx(Skeleton, {
664
+ variant: "rounded",
665
+ className: "w-full",
666
+ style: { height: sizes.valueLinePx }
667
+ }), /* @__PURE__ */ jsx(Skeleton, {
668
+ variant: "rounded",
669
+ className: "w-3/4",
670
+ style: { height: sizes.subLinePx }
671
+ })]
672
+ }), /* @__PURE__ */ jsxs("div", {
673
+ className: "flex w-1/3 flex-col items-end gap-1",
674
+ children: [/* @__PURE__ */ jsx(Skeleton, {
675
+ variant: "rounded",
676
+ className: "w-24 max-w-full",
677
+ style: { height: sizes.chipLinePx }
678
+ }), /* @__PURE__ */ jsx(Skeleton, {
679
+ variant: "rounded",
680
+ className: "w-full",
681
+ style: { height: sizes.subLinePx }
682
+ })]
683
+ })]
684
+ }),
685
+ /* @__PURE__ */ jsx(Skeleton, {
686
+ variant: "rounded",
687
+ className: "w-full",
688
+ style: { height: plotHeight }
689
+ }),
690
+ legendRow,
691
+ reserveInsight && /* @__PURE__ */ jsxs("div", {
692
+ className: "flex items-start gap-2",
693
+ children: [/* @__PURE__ */ jsx(Skeleton, {
694
+ variant: "rounded",
695
+ className: "size-4 shrink-0"
696
+ }), /* @__PURE__ */ jsx(Skeleton, {
697
+ variant: "rounded",
698
+ className: "w-full",
699
+ style: { height: sizes.legendLinePx }
700
+ })]
701
+ })
702
+ ]
703
+ }),
704
+ state === "error" && /* @__PURE__ */ jsx(SystemState, {
705
+ variant: "server-error",
706
+ scope: "inline",
707
+ title: labels.error,
708
+ description: false,
709
+ action: onRetry ? /* @__PURE__ */ jsx(Button, {
710
+ size: "sm",
711
+ onClick: onRetry,
712
+ children: labels.retry
713
+ }) : void 0,
714
+ className: cn(tileStateClasses, "my-auto")
715
+ }),
716
+ state === "empty" && /* @__PURE__ */ jsx(SystemState, {
717
+ variant: "empty",
718
+ scope: "inline",
719
+ title: labels.noData,
720
+ description: false,
721
+ className: cn(tileStateClasses, "my-auto")
722
+ }),
723
+ state === "no-match" && /* @__PURE__ */ jsx(SystemState, {
724
+ variant: "no-results",
725
+ scope: "inline",
726
+ title: labels.noMatch,
727
+ description: false,
728
+ action: onClearFilters ? /* @__PURE__ */ jsx(Button, {
729
+ variant: "outline",
730
+ size: "sm",
731
+ onClick: onClearFilters,
732
+ children: labels.clearFilters
733
+ }) : void 0,
734
+ className: cn(tileStateClasses, "my-auto")
735
+ }),
736
+ showContent && run && headlineText && /* @__PURE__ */ jsxs("div", {
737
+ className: cn("flex min-w-0 flex-col", sizes.gap),
738
+ children: [
739
+ /* @__PURE__ */ jsxs("div", {
740
+ className: "flex min-w-0 items-start justify-between gap-3",
741
+ style: { minHeight: headlineMinHeight },
742
+ "data-part": "headline",
743
+ children: [/* @__PURE__ */ jsxs(Tooltip, { children: [/* @__PURE__ */ jsxs(TooltipTrigger, {
744
+ delay: 150,
745
+ render: /* @__PURE__ */ jsx("div", {
746
+ tabIndex: 0,
747
+ role: "group",
748
+ "aria-label": labels.progressGroup(name),
749
+ "aria-describedby": explanationId,
750
+ className: cn(headlineStackClasses, sizes.body),
751
+ "data-part": "value"
752
+ }),
753
+ children: [
754
+ /* @__PURE__ */ jsx("span", {
755
+ id: explanationId,
756
+ className: "sr-only",
757
+ children: explanation
758
+ }),
759
+ /* @__PURE__ */ jsxs("span", {
760
+ className: "flex min-w-0 flex-wrap items-baseline gap-x-1.5",
761
+ children: [/* @__PURE__ */ jsx("span", {
762
+ className: cn(valueClasses, sizes.value, "min-w-0 wrap-break-word"),
763
+ children: headlineText.big
764
+ }), unit != null && headline === "remaining" && /* @__PURE__ */ jsx("span", {
765
+ className: cn("font-sans text-muted-foreground", sizes.unit),
766
+ children: unit
767
+ })]
768
+ }),
769
+ /* @__PURE__ */ jsxs("span", {
770
+ className: "min-w-0 font-sans text-muted-foreground text-xs tabular-nums",
771
+ "data-part": "sub-line",
772
+ children: [headlineText.sub, unit != null && headline === "percent" && /* @__PURE__ */ jsxs("span", { children: [" ", unit] })]
773
+ })
774
+ ]
775
+ }), /* @__PURE__ */ jsx(TooltipPopup, {
776
+ arrow: false,
777
+ side: "bottom",
778
+ align: "start",
779
+ sideOffset: 8,
780
+ collisionPadding: 8,
781
+ container: popoverContainer,
782
+ className: "text-left",
783
+ "data-part": "headline-tooltip",
784
+ children: /* @__PURE__ */ jsx(ArcTooltipRows, { rows: explanationRows })
785
+ })] }), /* @__PURE__ */ jsxs("div", {
786
+ className: "flex min-w-0 shrink-0 flex-col items-end gap-1 text-right",
787
+ "data-part": "verdict-stack",
788
+ children: [
789
+ showVerdict && run.verdict && /* @__PURE__ */ jsxs(Badge, {
790
+ variant: verdictBadgeVariant[run.verdict],
791
+ appearance: "soft",
792
+ size: sizes.chip,
793
+ role: "status",
794
+ "data-part": "verdict",
795
+ "data-verdict": run.verdict,
796
+ children: [/* @__PURE__ */ jsx(VerdictGlyph, { verdict: run.verdict }), labels.verdicts[run.verdict]]
797
+ }),
798
+ run.projection && !run.done && /* @__PURE__ */ jsx("span", {
799
+ className: "min-w-0 font-sans text-muted-foreground text-xs tabular-nums",
800
+ "data-part": "projection",
801
+ children: labels.finishes(run.projection.dateText)
802
+ }),
803
+ run.rate?.requiredText && !run.done && /* @__PURE__ */ jsxs("span", {
804
+ className: "min-w-0 font-sans text-muted-foreground text-xs tabular-nums",
805
+ "data-part": "rate",
806
+ children: [
807
+ labels.perDay(run.rate.text),
808
+ " · ",
809
+ labels.needsPerDay(run.rate.requiredText)
810
+ ]
811
+ })
812
+ ]
813
+ })]
814
+ }),
815
+ hasStages && /* @__PURE__ */ jsx("div", {
816
+ role: "img",
817
+ "aria-label": buildStageBarLabel(name, run, labels),
818
+ className: progressTrackVariants({ size: sizes.barSize }),
819
+ "data-part": "stage-bar",
820
+ children: /* @__PURE__ */ jsx("div", {
821
+ className: "flex h-full gap-px",
822
+ children: run.stages.map((stage) => /* @__PURE__ */ jsx("div", {
823
+ className: progressIndicatorVariants({ className: cn("rounded-none", stageFillClasses[stage.tone]) }),
824
+ style: { width: stage.width },
825
+ "data-tone": stage.tone
826
+ }, stage.key))
827
+ })
828
+ }),
829
+ /* @__PURE__ */ jsx("div", {
830
+ ref: plotRef,
831
+ className: "relative w-full",
832
+ style: { height: plotHeight },
833
+ "data-part": "plot",
834
+ children: showPlot ? plotWidth > 0 && spec && datasets ? /* @__PURE__ */ jsx(ArcVegaChart, {
835
+ spec,
836
+ options: embedOptions,
837
+ datasets,
838
+ description: chartDescription,
839
+ width: plotWidth,
840
+ height: plotHeight,
841
+ onEmbed,
842
+ className: "absolute inset-0 [&_svg]:block [&_.vega-bindings:empty]:hidden",
843
+ fallback: /* @__PURE__ */ jsx(Skeleton, {
844
+ variant: "rounded",
845
+ className: "size-full"
846
+ })
847
+ }) : /* @__PURE__ */ jsx(Skeleton, {
848
+ variant: "rounded",
849
+ className: "size-full"
850
+ }) : /* @__PURE__ */ jsx("p", {
851
+ className: cn(captionClasses, "flex h-full items-center"),
852
+ "data-part": "needs-history",
853
+ children: labels.needsHistory
854
+ })
855
+ }),
856
+ legendRow,
857
+ reserveInsight && /* @__PURE__ */ jsxs("div", {
858
+ className: cn("flex items-start gap-2 font-sans text-foreground", sizes.body),
859
+ "data-part": "insight",
860
+ children: [/* @__PURE__ */ jsx(AILoader, {
861
+ size: "sm",
862
+ state: "default",
863
+ label: labels.insightMark,
864
+ className: cn("shrink-0", size === "sm" ? "" : "mt-0.5")
865
+ }), /* @__PURE__ */ jsx("p", {
866
+ className: "min-w-0 flex-1 [&_strong]:font-semibold [&_strong]:text-foreground text-xs leading-[1.8]",
867
+ children: insight
868
+ })]
869
+ })
870
+ ]
871
+ }),
872
+ state === "refreshing" && /* @__PURE__ */ jsx("div", {
873
+ "aria-hidden": "true",
874
+ className: "absolute -inset-1 rounded-md bg-veil"
875
+ })
876
+ ]
877
+ }),
878
+ /* @__PURE__ */ jsxs("p", {
879
+ className: cn(captionClasses, "mt-1 flex flex-wrap justify-between gap-x-3"),
880
+ style: { minHeight: FOOTER_ROW_PX },
881
+ "data-part": "footer",
882
+ children: [showContent && run?.scopeChanged ? /* @__PURE__ */ jsx("span", {
883
+ "data-part": "scope-note",
884
+ children: labels.scopeMoved(run.openingScopeText, run.scopeText)
885
+ }) : /* @__PURE__ */ jsx("span", {}), showContent && run && /* @__PURE__ */ jsxs("span", {
886
+ "data-part": "as-of",
887
+ children: [
888
+ labels.asOf,
889
+ " ",
890
+ /* @__PURE__ */ jsx("time", {
891
+ dateTime: new Date(run.asOf).toISOString(),
892
+ children: run.asOfText
893
+ })
894
+ ]
895
+ })]
896
+ }),
897
+ /* @__PURE__ */ jsx("div", {
898
+ ref: setPopoverContainer,
899
+ className: "pointer-events-none absolute inset-0",
900
+ "data-part": "popover-host"
901
+ }),
902
+ tooltip
903
+ ]
904
+ });
905
+ });
906
+ ProgressCard.displayName = "ProgressCard";
907
+ //#endregion
908
+ export { ProgressCard };