@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,910 @@
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 { TARGET_ACTUAL_DATASET, TARGET_BOUNDS_DATASET, TARGET_MARKERS_DATASET, TARGET_PROJECTION_DATASET, buildTargetBulletSpec } from "./TargetCard.chart.js";
7
+ import { buildTargetChartDescription, buildTargetDescription, buildTargetDetailRows, buildTargetExplanation, createTargetFormatters, deriveDays, deriveElapsed, deriveTargetProgress, deriveTargetState, formatPeriod, formatValuePair, paceWord, resolvePeriod, resolveTargetLabels } from "./TargetCard.model.js";
8
+ import { forwardRef, useCallback, useEffect, useId, 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/TargetCard/TargetCard.tsx
12
+ const sizeStyles = {
13
+ sm: {
14
+ padding: "sm",
15
+ gap: "gap-2",
16
+ gapPx: 8,
17
+ label: "text-xs",
18
+ value: "text-2xl leading-8",
19
+ valueLinePx: 32,
20
+ subLinePx: 20,
21
+ unit: "text-xs",
22
+ body: "text-xs",
23
+ badge: "sm",
24
+ deltaLinePx: 20,
25
+ comparisonLinePx: 16,
26
+ barHeight: 8,
27
+ plotHeight: 20,
28
+ chip: "sm",
29
+ statusLinePx: 20
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
+ subLinePx: 20,
39
+ unit: "text-sm",
40
+ body: "text-sm",
41
+ badge: "md",
42
+ deltaLinePx: 24,
43
+ comparisonLinePx: 20,
44
+ barHeight: 10,
45
+ plotHeight: 24,
46
+ chip: "sm",
47
+ statusLinePx: 20
48
+ },
49
+ lg: {
50
+ padding: "lg",
51
+ gap: "gap-2",
52
+ gapPx: 8,
53
+ label: "text-sm",
54
+ value: "text-4xl leading-10",
55
+ valueLinePx: 40,
56
+ subLinePx: 24,
57
+ unit: "text-base",
58
+ body: "text-sm",
59
+ badge: "lg",
60
+ deltaLinePx: 28,
61
+ comparisonLinePx: 20,
62
+ barHeight: 12,
63
+ plotHeight: 28,
64
+ chip: "md",
65
+ statusLinePx: 24
66
+ }
67
+ };
68
+ const labelClasses = "min-w-0 font-sans font-medium text-muted-foreground";
69
+ const valueClasses = "font-heading font-semibold tracking-tight text-foreground";
70
+ const HEADLINE_GAP_PX = 4;
71
+ const captionClasses = "font-sans text-xs text-muted-foreground";
72
+ const polarityBadgeVariant = {
73
+ positive: "success",
74
+ negative: "destructive",
75
+ neutral: "neutral"
76
+ };
77
+ const trackBadgeVariant = {
78
+ achieved: "success",
79
+ "on-track": "success",
80
+ "at-risk": "warning",
81
+ "off-track": "destructive",
82
+ missed: "destructive"
83
+ };
84
+ const changeTextClasses = {
85
+ positive: "text-chart-positive",
86
+ negative: "text-chart-negative",
87
+ neutral: "text-foreground"
88
+ };
89
+ const deltaStackClasses = "ms-auto flex min-w-0 max-w-1/2 shrink-0 flex-col items-end gap-1 rounded-md text-right font-sans focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ring";
90
+ const headerControlClasses = "size-6 min-w-0 text-muted-foreground hover:text-foreground";
91
+ const tileStateClasses = "w-full max-w-none gap-2 px-2 py-2 [--artwork-box:3rem]";
92
+ const MISSING_NAME = "Target";
93
+ function toneHue(tokens, tone) {
94
+ return tone === "positive" ? tokens.divergingPositive : tone === "negative" ? tokens.divergingNegative : tokens.emphasis;
95
+ }
96
+ /** Match the plot's mark shapes so their meaning never depends on colour alone: the bar, its projected extension, the pace tick and the target tick. */
97
+ function TargetKey({ label, mark, color }) {
98
+ return /* @__PURE__ */ jsxs("span", {
99
+ className: "inline-flex items-center gap-2",
100
+ children: [/* @__PURE__ */ jsxs("svg", {
101
+ width: "24",
102
+ height: "12",
103
+ viewBox: "0 0 24 12",
104
+ fill: "none",
105
+ "aria-hidden": "true",
106
+ className: "shrink-0",
107
+ style: { color },
108
+ children: [
109
+ mark === "actual" && /* @__PURE__ */ jsx("rect", {
110
+ x: "1",
111
+ y: "2",
112
+ width: "22",
113
+ height: "8",
114
+ rx: "1",
115
+ fill: "currentColor"
116
+ }),
117
+ mark === "projection" && /* @__PURE__ */ jsx("rect", {
118
+ x: "1",
119
+ y: "2",
120
+ width: "22",
121
+ height: "8",
122
+ rx: "1",
123
+ fill: "currentColor",
124
+ opacity: "0.25"
125
+ }),
126
+ mark === "pace" && /* @__PURE__ */ jsx("line", {
127
+ x1: "12",
128
+ y1: "0",
129
+ x2: "12",
130
+ y2: "12",
131
+ stroke: "currentColor",
132
+ strokeWidth: 1.5
133
+ }),
134
+ mark === "target" && /* @__PURE__ */ jsx("line", {
135
+ x1: "12",
136
+ y1: "0",
137
+ x2: "12",
138
+ y2: "12",
139
+ stroke: "currentColor",
140
+ strokeWidth: 2.5
141
+ })
142
+ ]
143
+ }), label]
144
+ });
145
+ }
146
+ function InfoIcon(props) {
147
+ return /* @__PURE__ */ jsxs("svg", {
148
+ viewBox: "0 0 24 24",
149
+ fill: "none",
150
+ stroke: "currentColor",
151
+ strokeWidth: 2,
152
+ strokeLinecap: "round",
153
+ strokeLinejoin: "round",
154
+ "aria-hidden": "true",
155
+ ...props,
156
+ children: [/* @__PURE__ */ jsx("circle", {
157
+ cx: "12",
158
+ cy: "12",
159
+ r: "9"
160
+ }), /* @__PURE__ */ jsx("path", { d: "M12 11v5M12 8h.01" })]
161
+ });
162
+ }
163
+ function DeltaGlyph({ direction, ...props }) {
164
+ return /* @__PURE__ */ jsxs("svg", {
165
+ viewBox: "0 0 24 24",
166
+ fill: "none",
167
+ stroke: "currentColor",
168
+ strokeWidth: 2.5,
169
+ strokeLinecap: "round",
170
+ strokeLinejoin: "round",
171
+ "aria-hidden": "true",
172
+ ...props,
173
+ children: [
174
+ direction === "up" && /* @__PURE__ */ jsx("path", { d: "M7 17 17 7M7 7h10v10" }),
175
+ direction === "down" && /* @__PURE__ */ jsx("path", { d: "M7 7 17 17M7 17h10V7" }),
176
+ direction === "flat" && /* @__PURE__ */ jsx("path", { d: "M5 12h14M13 6l6 6-6 6" })
177
+ ]
178
+ });
179
+ }
180
+ function TrackGlyph({ track, ...props }) {
181
+ return /* @__PURE__ */ jsxs("svg", {
182
+ viewBox: "0 0 24 24",
183
+ fill: "none",
184
+ stroke: "currentColor",
185
+ strokeWidth: 2.5,
186
+ strokeLinecap: "round",
187
+ strokeLinejoin: "round",
188
+ "aria-hidden": "true",
189
+ ...props,
190
+ children: [
191
+ track === "achieved" && /* @__PURE__ */ jsx("path", { d: "m5 12 5 5L20 7" }),
192
+ track === "missed" && /* @__PURE__ */ jsx("path", { d: "m6 6 12 12M18 6 6 18" }),
193
+ track === "on-track" && /* @__PURE__ */ jsx("circle", {
194
+ cx: "12",
195
+ cy: "12",
196
+ r: "5",
197
+ fill: "currentColor",
198
+ stroke: "none"
199
+ }),
200
+ track === "at-risk" && /* @__PURE__ */ jsx("path", { d: "M12 4 3 20h18L12 4Zm0 6v4m0 3h.01" }),
201
+ track === "off-track" && /* @__PURE__ */ jsx("path", { d: "M12 8v5m0 3h.01M12 3a9 9 0 1 0 0 18 9 9 0 0 0 0-18Z" })
202
+ ]
203
+ });
204
+ }
205
+ function useMergedRef(forwarded) {
206
+ const inner = useRef(null);
207
+ return [inner, useCallback((node) => {
208
+ inner.current = node;
209
+ if (typeof forwarded === "function") forwarded(node);
210
+ else if (forwarded) forwarded.current = node;
211
+ }, [forwarded])];
212
+ }
213
+ function usePlotWidth(ref, enabled) {
214
+ const [width, setWidth] = useState(0);
215
+ useEffect(() => {
216
+ if (!enabled) return;
217
+ const el = ref.current;
218
+ if (!el) return;
219
+ let frame = 0;
220
+ const measure = () => {
221
+ const next = Math.round(el.getBoundingClientRect().width);
222
+ setWidth((prev) => prev === next ? prev : next);
223
+ };
224
+ measure();
225
+ if (typeof ResizeObserver === "undefined") return;
226
+ const observer = new ResizeObserver(() => {
227
+ cancelAnimationFrame(frame);
228
+ frame = requestAnimationFrame(measure);
229
+ });
230
+ observer.observe(el);
231
+ return () => {
232
+ cancelAnimationFrame(frame);
233
+ observer.disconnect();
234
+ };
235
+ }, [ref, enabled]);
236
+ return width;
237
+ }
238
+ /**
239
+ * A goal-tracking tile for dashboard grids: how much of a target is reached,
240
+ * whether the reading is ahead of or behind the plan at this point in the
241
+ * period, where the run rate lands, and the verdict — on a Few-style bullet
242
+ * graph (the reading as a bar from zero, a pace tick and a target tick, the
243
+ * projection as a lighter extension) with every loading, empty, error and
244
+ * stale state — layout-stable, with a generated text equivalent and a details
245
+ * table in an info popover.
246
+ */
247
+ const TargetCard = forwardRef(({ label, name: nameProp, hint, value, target, format = "compact", formatValue, unit, goodDirection = "up", headline = "attainment", period, expected, projected, tolerance = .05, deltaMode = "percent", markerLabels = true, 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) => {
248
+ const labels = useMemo(() => resolveTargetLabels(labelOverrides), [labelOverrides]);
249
+ const sizes = sizeStyles[size];
250
+ const plotHeight = sizes.plotHeight + (markerLabels ? 16 : 0);
251
+ const name = nameProp ?? (typeof label === "string" ? label : MISSING_NAME);
252
+ const id = useId();
253
+ const headingId = `${id}-label`;
254
+ const descriptionId = `${id}-description`;
255
+ const hintId = `${id}-hint`;
256
+ const tableId = `${id}-table`;
257
+ const explanationId = `${id}-explanation`;
258
+ const [rootRef, setRootRef] = useMergedRef(forwardedRef);
259
+ const plotRef = useRef(null);
260
+ const [tableOpen, setTableOpen] = useState(defaultTableOpen);
261
+ const [popoverContainer, setPopoverContainer] = useState(null);
262
+ const periodStart = period?.start;
263
+ const periodEnd = period?.end;
264
+ const periodLabel = period?.label;
265
+ const bounds = useMemo(() => periodStart != null && periodEnd != null ? resolvePeriod({
266
+ start: periodStart,
267
+ end: periodEnd,
268
+ label: periodLabel
269
+ }, timeZone) : null, [
270
+ periodStart,
271
+ periodEnd,
272
+ periodLabel,
273
+ timeZone
274
+ ]);
275
+ const asOfDate = updatedAt == null ? null : updatedAt instanceof Date ? updatedAt : new Date(updatedAt);
276
+ const asOfValid = asOfDate != null && !Number.isNaN(asOfDate.getTime());
277
+ const asOfMs = asOfValid ? asOfDate.getTime() : null;
278
+ const asOfText = asOfValid ? typeof updatedAtFormat === "function" ? updatedAtFormat(asOfDate) : formatDate(asOfDate, {
279
+ granularity: updatedAtFormat,
280
+ locale,
281
+ timeZone
282
+ }) : void 0;
283
+ const elapsed = useMemo(() => deriveElapsed(bounds, asOfMs, locale), [
284
+ bounds,
285
+ asOfMs,
286
+ locale
287
+ ]);
288
+ const days = useMemo(() => deriveDays(bounds, asOfMs, timeZone), [
289
+ bounds,
290
+ asOfMs,
291
+ timeZone
292
+ ]);
293
+ const formatters = useMemo(() => createTargetFormatters({
294
+ format,
295
+ locale,
296
+ formatValue
297
+ }), [
298
+ format,
299
+ locale,
300
+ formatValue
301
+ ]);
302
+ const hasValue = typeof value === "number" && Number.isFinite(value);
303
+ const state = deriveTargetState({
304
+ status,
305
+ hasValue,
306
+ hasData: hasValue || typeof target === "number" && Number.isFinite(target) && target !== 0,
307
+ filtered
308
+ });
309
+ const showContent = state === "ready" || state === "refreshing";
310
+ const progress = useMemo(() => deriveTargetProgress({
311
+ value,
312
+ target,
313
+ formatters,
314
+ tolerance,
315
+ elapsed,
316
+ expected,
317
+ projected,
318
+ deltaMode,
319
+ goodDirection,
320
+ format,
321
+ locale,
322
+ percentagePoints: labels.percentagePoints
323
+ }), [
324
+ value,
325
+ target,
326
+ formatters,
327
+ tolerance,
328
+ elapsed,
329
+ expected,
330
+ projected,
331
+ deltaMode,
332
+ goodDirection,
333
+ format,
334
+ locale,
335
+ labels.percentagePoints
336
+ ]);
337
+ const noTarget = showContent && hasValue && progress == null;
338
+ const periodText = useMemo(() => formatPeriod(bounds, {
339
+ locale,
340
+ timeZone,
341
+ labels
342
+ }), [
343
+ bounds,
344
+ locale,
345
+ timeZone,
346
+ labels
347
+ ]);
348
+ const valueText = formatters.formatValue(hasValue ? value : null);
349
+ const delta = progress ? progress.pace ? progress.pace.delta : progress.targetDelta : null;
350
+ const comparisonText = progress ? progress.pace ? paceWord(progress.pace, goodDirection, labels) : labels.vsTarget : "";
351
+ const explanationRows = progress ? buildTargetExplanation(progress, goodDirection, labels) : [];
352
+ const explanation = explanationRows.map(([key, entry]) => `${key}: ${entry}`).join(". ");
353
+ const detailRows = useMemo(() => progress ? buildTargetDetailRows({
354
+ progress,
355
+ direction: goodDirection,
356
+ periodText: periodText ? periodText.labelled ? `${periodText.name} · ${periodText.range}` : periodText.range : null,
357
+ days,
358
+ labels
359
+ }) : [], [
360
+ progress,
361
+ goodDirection,
362
+ periodText,
363
+ days,
364
+ labels
365
+ ]);
366
+ const cardDescription = description ?? buildTargetDescription({
367
+ name,
368
+ state,
369
+ valueText,
370
+ progress,
371
+ direction: goodDirection,
372
+ periodText: periodText && {
373
+ ...periodText,
374
+ name: periodText.labelled ? periodText.name : periodText.range
375
+ },
376
+ asOfText,
377
+ labels
378
+ });
379
+ const tokens = useChartTokens(rootRef);
380
+ const plotWidth = usePlotWidth(plotRef, showContent && progress != null);
381
+ const barColor = tokens && progress ? toneHue(tokens, progress.tone) : void 0;
382
+ const tooltipContent = useCallback(({ item }) => {
383
+ if (!progress) return null;
384
+ const datum = item.datum;
385
+ if (!datum) return null;
386
+ const rows = [];
387
+ if (datum.role === "target") rows.push([/* @__PURE__ */ jsx(TargetKey, {
388
+ label: labels.target,
389
+ mark: "target",
390
+ color: tokens?.foreground
391
+ }, "k"), progress.targetText]);
392
+ else if (datum.role === "pace" && progress.pace) {
393
+ rows.push([/* @__PURE__ */ jsx(TargetKey, {
394
+ label: labels.expectedByNow,
395
+ mark: "pace",
396
+ color: tokens?.reference
397
+ }, "k"), progress.pace.expectedText], [labels.ofTarget, progress.pace.expectedPercentText]);
398
+ if (progress.elapsed) rows.push([labels.elapsedRow, progress.elapsed.percentText]);
399
+ } else if ("to" in datum && progress.projection) rows.push([/* @__PURE__ */ jsx(TargetKey, {
400
+ label: labels.projected,
401
+ mark: "projection",
402
+ color: barColor
403
+ }, "k"), progress.projection.text], [labels.ofTarget, progress.projection.percentText]);
404
+ else if ("zero" in datum) rows.push([/* @__PURE__ */ jsx(TargetKey, {
405
+ label: labels.reached,
406
+ mark: "actual",
407
+ color: barColor
408
+ }, "k"), progress.valueText], [labels.ofTarget, progress.percentText]);
409
+ else return null;
410
+ return /* @__PURE__ */ jsx(ArcTooltipRows, { rows });
411
+ }, [
412
+ progress,
413
+ labels,
414
+ tokens,
415
+ barColor
416
+ ]);
417
+ const { handler: tooltipHandler, tooltip } = useArcTooltipHandler({ content: tooltipContent });
418
+ const embedOptions = useMemo(() => arcEmbedOptions({ tooltip: tooltipHandler }), [tooltipHandler]);
419
+ const spec = useMemo(() => {
420
+ if (!tokens) return null;
421
+ const built = buildTargetBulletSpec({
422
+ barHeight: sizes.barHeight,
423
+ height: plotHeight,
424
+ labels: markerLabels,
425
+ tooltip: true,
426
+ targetLabel: labels.markNames.target,
427
+ paceLabel: labels.markNames.pace,
428
+ valueFormat: locale ? `${format}@${locale}` : format,
429
+ valueTitle: labels.reached,
430
+ targetTitle: labels.target,
431
+ paceTitle: labels.expectedByNow,
432
+ projectionTitle: labels.projected,
433
+ locale
434
+ }, tokens);
435
+ return patchSpec ? patchSpec(built) : built;
436
+ }, [
437
+ tokens,
438
+ sizes.barHeight,
439
+ plotHeight,
440
+ markerLabels,
441
+ labels,
442
+ format,
443
+ locale,
444
+ patchSpec
445
+ ]);
446
+ const datasets = useMemo(() => {
447
+ if (!progress) return void 0;
448
+ return {
449
+ [TARGET_ACTUAL_DATASET]: [{
450
+ value: progress.value,
451
+ zero: 0,
452
+ ratio: progress.ratio,
453
+ tone: progress.tone
454
+ }],
455
+ [TARGET_BOUNDS_DATASET]: [{
456
+ min: progress.bounds.min,
457
+ max: progress.bounds.max,
458
+ target: progress.target
459
+ }],
460
+ [TARGET_MARKERS_DATASET]: [{
461
+ value: progress.target,
462
+ ratio: 1,
463
+ role: "target"
464
+ }, ...progress.pace ? [{
465
+ value: progress.pace.expected,
466
+ ratio: progress.pace.expectedRatio,
467
+ role: "pace"
468
+ }] : []],
469
+ [TARGET_PROJECTION_DATASET]: progress.projection?.extends ? [{
470
+ from: progress.value,
471
+ to: progress.projection.value,
472
+ ratio: progress.projection.ratio,
473
+ tone: progress.tone
474
+ }] : []
475
+ };
476
+ }, [progress]);
477
+ const chartDescription = progress ? buildTargetChartDescription({
478
+ name,
479
+ progress,
480
+ labels
481
+ }) : void 0;
482
+ const hasPaceInputs = period !== void 0 || expected !== void 0 || projected !== void 0;
483
+ const reserveStatus = hasPaceInputs || progress != null;
484
+ const reserveInsight = insight != null && insight !== false;
485
+ const showTrack = progress?.track != null;
486
+ const reserveTrack = showTrack || state === "loading" && hasPaceInputs;
487
+ const headlineMinHeight = Math.max(sizes.valueLinePx + (reserveTrack ? HEADLINE_GAP_PX + sizes.subLinePx : 0), sizes.deltaLinePx + 4 + sizes.comparisonLinePx);
488
+ const bodyMinHeight = headlineMinHeight + (reserveTrack ? sizes.gapPx : 0) + plotHeight + (reserveStatus ? sizes.statusLinePx : 0) + (reserveInsight ? sizes.gapPx + sizes.comparisonLinePx : 0);
489
+ const HeadingTag = `h${titleLevel}`;
490
+ const busy = state === "loading" || state === "refreshing";
491
+ const directionWordFor = (direction) => direction === "up" ? labels.increase : direction === "down" ? labels.decrease : labels.unchanged;
492
+ const bigText = progress ? headline === "attainment" ? progress.percentText : progress.valueText : valueText;
493
+ const bigUnit = unit != null && (headline === "value" || !progress);
494
+ const subLine = progress ? headline === "attainment" ? formatValuePair(progress.valueText, progress.targetText, labels.pairSeparator) ?? labels.readingLine(progress.valueText, progress.targetText) : labels.attainmentLine(progress.percentText, progress.targetText) : null;
495
+ return /* @__PURE__ */ jsxs(Box, {
496
+ ref: setRootRef,
497
+ render: render ?? /* @__PURE__ */ jsx("figure", {}),
498
+ padding: sizes.padding,
499
+ "aria-labelledby": headingId,
500
+ "aria-describedby": descriptionId,
501
+ "aria-busy": busy || void 0,
502
+ "data-status": status,
503
+ "data-state": state,
504
+ "data-size": size,
505
+ "data-headline": headline,
506
+ "data-direction": goodDirection,
507
+ "data-period": periodText?.shape,
508
+ "data-track": showContent && progress?.track ? progress.track : void 0,
509
+ "data-pace": showContent && progress?.pace ? progress.pace.status : void 0,
510
+ "data-polarity": showContent && delta ? delta.polarity : void 0,
511
+ "data-empty": state === "empty" || state === "no-match" ? "" : void 0,
512
+ "data-stale": stale ? "" : void 0,
513
+ "data-insufficient": noTarget ? "" : void 0,
514
+ ...rest,
515
+ className: cn("relative flex min-w-0 flex-col", sizes.gap, className),
516
+ children: [
517
+ /* @__PURE__ */ jsxs("div", {
518
+ className: "flex items-center justify-between gap-2",
519
+ children: [/* @__PURE__ */ jsxs("div", {
520
+ className: "flex min-w-0 items-center gap-1",
521
+ children: [/* @__PURE__ */ jsx(HeadingTag, {
522
+ id: headingId,
523
+ className: cn(labelClasses, sizes.label, "line-clamp-2"),
524
+ children: label
525
+ }), hint && /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsxs(Tooltip, { children: [/* @__PURE__ */ jsx(TooltipTrigger, {
526
+ render: /* @__PURE__ */ jsx(Button, {
527
+ variant: "ghost",
528
+ size: "sm",
529
+ iconOnly: true,
530
+ circular: true,
531
+ "aria-label": labels.about(name),
532
+ "aria-describedby": hintId,
533
+ className: headerControlClasses
534
+ }),
535
+ children: /* @__PURE__ */ jsx(InfoIcon, { className: "size-3.5" })
536
+ }), /* @__PURE__ */ jsx(TooltipPopup, { children: hint })] }), /* @__PURE__ */ jsx("span", {
537
+ id: hintId,
538
+ className: "sr-only",
539
+ children: hint
540
+ })] })]
541
+ }), /* @__PURE__ */ jsxs("div", {
542
+ className: "flex shrink-0 items-center gap-1",
543
+ children: [
544
+ stale && /* @__PURE__ */ jsx(Badge, {
545
+ variant: "warning",
546
+ appearance: "soft",
547
+ size: "sm",
548
+ children: labels.stale
549
+ }),
550
+ showContent && progress && /* @__PURE__ */ jsxs(Popover, {
551
+ open: tableOpen,
552
+ onOpenChange: setTableOpen,
553
+ children: [!hideTableToggle && /* @__PURE__ */ jsx(PopoverTrigger, {
554
+ render: /* @__PURE__ */ jsx(Button, {
555
+ variant: "ghost",
556
+ size: "sm",
557
+ iconOnly: true,
558
+ circular: true,
559
+ "aria-label": tableOpen ? labels.hideTable(name) : labels.showTable(name),
560
+ className: headerControlClasses
561
+ }),
562
+ children: /* @__PURE__ */ jsx(InfoIcon, { className: "size-3.5" })
563
+ }), popoverContainer && /* @__PURE__ */ jsxs(PopoverPopup, {
564
+ id: tableId,
565
+ anchor: rootRef,
566
+ container: popoverContainer,
567
+ side: "right",
568
+ align: "start",
569
+ collisionPadding: 8,
570
+ className: "pointer-events-auto w-80",
571
+ "data-part": "data-popover",
572
+ children: [
573
+ /* @__PURE__ */ jsx(PopoverTitle, {
574
+ className: "mb-3",
575
+ children: name
576
+ }),
577
+ /* @__PURE__ */ jsxs("div", {
578
+ className: "mb-3 flex flex-wrap gap-x-3 gap-y-1 text-xs text-muted-foreground",
579
+ "data-part": "chart-legend",
580
+ children: [
581
+ /* @__PURE__ */ jsx(TargetKey, {
582
+ label: labels.markNames.actual,
583
+ mark: "actual",
584
+ color: barColor
585
+ }),
586
+ progress.pace && /* @__PURE__ */ jsx(TargetKey, {
587
+ label: labels.markNames.pace,
588
+ mark: "pace",
589
+ color: tokens?.reference
590
+ }),
591
+ /* @__PURE__ */ jsx(TargetKey, {
592
+ label: labels.markNames.target,
593
+ mark: "target",
594
+ color: tokens?.foreground
595
+ }),
596
+ progress.projection?.extends && /* @__PURE__ */ jsx(TargetKey, {
597
+ label: labels.markNames.projection,
598
+ mark: "projection",
599
+ color: barColor
600
+ })
601
+ ]
602
+ }),
603
+ /* @__PURE__ */ jsx(TableContainer, {
604
+ variant: "plain",
605
+ tabIndex: 0,
606
+ role: "region",
607
+ "aria-label": labels.tableCaption(name),
608
+ className: "max-h-[min(16rem,calc(var(--available-height)-4.5rem))] focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ring",
609
+ "data-part": "table",
610
+ children: /* @__PURE__ */ jsxs(Table, {
611
+ size: "sm",
612
+ stickyHeader: true,
613
+ hoverable: false,
614
+ children: [
615
+ /* @__PURE__ */ jsx(TableCaption, {
616
+ side: "top",
617
+ className: "sr-only",
618
+ children: labels.tableCaption(name)
619
+ }),
620
+ /* @__PURE__ */ jsx(TableHeader, { children: /* @__PURE__ */ jsxs(TableRow, { children: [/* @__PURE__ */ jsx(TableHead, { children: labels.tableItem }), /* @__PURE__ */ jsx(TableHead, {
621
+ className: "text-right",
622
+ children: labels.tableValue
623
+ })] }) }),
624
+ /* @__PURE__ */ jsx(TableBody, { children: detailRows.map((row) => /* @__PURE__ */ jsxs(TableRow, {
625
+ "data-row": row.key,
626
+ children: [/* @__PURE__ */ jsx(TableCell, { children: row.label }), /* @__PURE__ */ jsx(TableCell, {
627
+ className: cn("text-right tabular-nums", row.tone && cn("font-medium", changeTextClasses[row.tone])),
628
+ children: row.value
629
+ })]
630
+ }, row.key)) })
631
+ ]
632
+ })
633
+ })
634
+ ]
635
+ })]
636
+ }),
637
+ action
638
+ ]
639
+ })]
640
+ }),
641
+ /* @__PURE__ */ jsx("p", {
642
+ id: descriptionId,
643
+ className: "sr-only",
644
+ children: cardDescription
645
+ }),
646
+ /* @__PURE__ */ jsxs("div", {
647
+ className: "relative flex min-w-0 flex-col",
648
+ style: { minHeight: bodyMinHeight },
649
+ children: [
650
+ state === "loading" && /* @__PURE__ */ jsxs(SkeletonGroup, {
651
+ className: "flex flex-col",
652
+ children: [
653
+ /* @__PURE__ */ jsxs("div", {
654
+ className: cn("flex min-w-0 items-start justify-between gap-2", reserveTrack && "mb-2"),
655
+ style: { minHeight: headlineMinHeight },
656
+ children: [/* @__PURE__ */ jsxs("div", {
657
+ className: "flex w-3/5 flex-col gap-1",
658
+ children: [/* @__PURE__ */ jsx(Skeleton, {
659
+ variant: "rounded",
660
+ className: "w-2/3",
661
+ style: { height: sizes.valueLinePx }
662
+ }), reserveTrack && /* @__PURE__ */ jsx(Skeleton, {
663
+ variant: "rounded",
664
+ className: "w-24 max-w-full",
665
+ style: { height: sizes.subLinePx }
666
+ })]
667
+ }), /* @__PURE__ */ jsxs("div", {
668
+ className: "flex w-2/5 flex-col items-end gap-1",
669
+ children: [/* @__PURE__ */ jsx(Skeleton, {
670
+ variant: "rounded",
671
+ className: "w-20 max-w-full",
672
+ style: { height: sizes.deltaLinePx }
673
+ }), /* @__PURE__ */ jsx(Skeleton, {
674
+ variant: "rounded",
675
+ className: "w-full",
676
+ style: { height: sizes.comparisonLinePx }
677
+ })]
678
+ })]
679
+ }),
680
+ /* @__PURE__ */ jsxs("div", {
681
+ className: "flex flex-col",
682
+ children: [hasPaceInputs && /* @__PURE__ */ jsx(Skeleton, {
683
+ variant: "rounded",
684
+ className: "w-3/5",
685
+ style: { height: sizes.statusLinePx }
686
+ }), /* @__PURE__ */ jsx(Skeleton, {
687
+ variant: "rounded",
688
+ className: "w-full",
689
+ style: { height: plotHeight }
690
+ })]
691
+ }),
692
+ reserveInsight && /* @__PURE__ */ jsxs("div", {
693
+ className: "mt-2 flex items-start gap-2",
694
+ children: [/* @__PURE__ */ jsx(Skeleton, {
695
+ variant: "rounded",
696
+ className: "size-4 shrink-0"
697
+ }), /* @__PURE__ */ jsx(Skeleton, {
698
+ variant: "rounded",
699
+ className: "w-full",
700
+ style: { height: sizes.comparisonLinePx }
701
+ })]
702
+ })
703
+ ]
704
+ }),
705
+ state === "error" && /* @__PURE__ */ jsx(SystemState, {
706
+ variant: "server-error",
707
+ scope: "inline",
708
+ title: labels.error,
709
+ description: false,
710
+ action: onRetry ? /* @__PURE__ */ jsx(Button, {
711
+ size: "sm",
712
+ onClick: onRetry,
713
+ children: labels.retry
714
+ }) : void 0,
715
+ className: cn(tileStateClasses, "my-auto")
716
+ }),
717
+ state === "empty" && /* @__PURE__ */ jsx(SystemState, {
718
+ variant: "empty",
719
+ scope: "inline",
720
+ title: labels.noData,
721
+ description: false,
722
+ className: cn(tileStateClasses, "my-auto")
723
+ }),
724
+ state === "no-match" && /* @__PURE__ */ jsx(SystemState, {
725
+ variant: "no-results",
726
+ scope: "inline",
727
+ title: labels.noMatch,
728
+ description: false,
729
+ action: onClearFilters ? /* @__PURE__ */ jsx(Button, {
730
+ variant: "outline",
731
+ size: "sm",
732
+ onClick: onClearFilters,
733
+ children: labels.clearFilters
734
+ }) : void 0,
735
+ className: cn(tileStateClasses, "my-auto")
736
+ }),
737
+ showContent && /* @__PURE__ */ jsxs("div", {
738
+ className: "flex min-w-0 flex-col",
739
+ children: [
740
+ /* @__PURE__ */ jsxs("div", {
741
+ className: cn("flex min-w-0 items-start justify-between gap-2", showTrack && "mb-2"),
742
+ style: { minHeight: headlineMinHeight },
743
+ "data-part": "headline",
744
+ children: [/* @__PURE__ */ jsxs("div", {
745
+ className: "flex min-w-0 flex-col items-start gap-1",
746
+ "data-part": "value",
747
+ children: [/* @__PURE__ */ jsxs("span", {
748
+ className: "flex min-w-0 flex-wrap items-baseline gap-x-1.5",
749
+ children: [/* @__PURE__ */ jsx("span", {
750
+ className: cn(valueClasses, sizes.value, "min-w-0 wrap-break-word"),
751
+ children: bigText
752
+ }), bigUnit && /* @__PURE__ */ jsx("span", {
753
+ className: cn("font-sans text-muted-foreground", sizes.unit),
754
+ children: unit
755
+ })]
756
+ }), showTrack && progress?.track && /* @__PURE__ */ jsxs(Badge, {
757
+ variant: trackBadgeVariant[progress.track],
758
+ appearance: "soft",
759
+ size: sizes.chip,
760
+ role: "status",
761
+ "aria-label": `${labels.trackGroup(name)}: ${labels.trackNames[progress.track]}`,
762
+ "data-part": "track",
763
+ "data-track": progress.track,
764
+ children: [/* @__PURE__ */ jsx(TrackGlyph, { track: progress.track }), labels.trackNames[progress.track]]
765
+ })]
766
+ }), delta && progress && /* @__PURE__ */ jsxs(Tooltip, { children: [/* @__PURE__ */ jsxs(TooltipTrigger, {
767
+ delay: 150,
768
+ render: /* @__PURE__ */ jsx("p", {
769
+ tabIndex: 0,
770
+ role: "group",
771
+ "aria-label": labels.progressGroup(name),
772
+ "aria-describedby": explanationId,
773
+ className: cn(deltaStackClasses, sizes.body),
774
+ "data-part": "delta",
775
+ "data-kind": progress.pace ? "pace" : "target"
776
+ }),
777
+ children: [
778
+ /* @__PURE__ */ jsx("span", {
779
+ id: explanationId,
780
+ className: "sr-only",
781
+ children: explanation
782
+ }),
783
+ /* @__PURE__ */ jsxs(Badge, {
784
+ variant: polarityBadgeVariant[delta.polarity],
785
+ appearance: "soft",
786
+ size: sizes.badge,
787
+ "data-direction": delta.direction,
788
+ children: [
789
+ /* @__PURE__ */ jsx(DeltaGlyph, { direction: delta.direction }),
790
+ /* @__PURE__ */ jsxs("span", {
791
+ className: "sr-only",
792
+ children: [directionWordFor(delta.direction), " "]
793
+ }),
794
+ delta.text
795
+ ]
796
+ }),
797
+ /* @__PURE__ */ jsx("span", {
798
+ className: "max-w-full text-muted-foreground wrap-break-word text-xs",
799
+ children: comparisonText
800
+ })
801
+ ]
802
+ }), /* @__PURE__ */ jsx(TooltipPopup, {
803
+ arrow: false,
804
+ side: "bottom",
805
+ align: "end",
806
+ sideOffset: 8,
807
+ collisionPadding: 8,
808
+ container: popoverContainer,
809
+ className: "text-left",
810
+ "data-part": "delta-tooltip",
811
+ children: /* @__PURE__ */ jsx(ArcTooltipRows, { rows: explanationRows })
812
+ })] })]
813
+ }),
814
+ /* @__PURE__ */ jsxs("div", {
815
+ className: "flex min-w-0 flex-col",
816
+ children: [reserveStatus && /* @__PURE__ */ jsxs("div", {
817
+ className: cn("flex min-w-0 flex-wrap items-baseline justify-between gap-x-3 gap-y-1 font-sans", sizes.body),
818
+ style: { minHeight: sizes.statusLinePx },
819
+ "data-part": "status",
820
+ children: [subLine && /* @__PURE__ */ jsxs("span", {
821
+ className: "min-w-0 font-sans text-muted-foreground text-xs tabular-nums",
822
+ "data-part": "sub-line",
823
+ children: [subLine, unit != null && headline === "attainment" && /* @__PURE__ */ jsxs("span", { children: [" ", unit] })]
824
+ }), progress?.projection && /* @__PURE__ */ jsxs("span", {
825
+ className: "min-w-0 text-muted-foreground text-xs tabular-nums",
826
+ "data-part": "projection",
827
+ children: [
828
+ labels.projected,
829
+ " ",
830
+ /* @__PURE__ */ jsx("span", {
831
+ className: "font-medium text-foreground",
832
+ children: progress.projection.text
833
+ }),
834
+ " (",
835
+ progress.projection.percentText,
836
+ ")"
837
+ ]
838
+ })]
839
+ }), /* @__PURE__ */ jsx("div", {
840
+ ref: plotRef,
841
+ className: "relative w-full",
842
+ style: { height: plotHeight },
843
+ "data-part": "plot",
844
+ children: progress ? plotWidth > 0 && spec && datasets ? /* @__PURE__ */ jsx(ArcVegaChart, {
845
+ spec,
846
+ options: embedOptions,
847
+ datasets,
848
+ description: chartDescription,
849
+ width: plotWidth,
850
+ height: plotHeight,
851
+ onEmbed,
852
+ className: "absolute inset-0 [&_svg]:block [&_.vega-bindings:empty]:hidden",
853
+ fallback: /* @__PURE__ */ jsx(Skeleton, {
854
+ variant: "rounded",
855
+ className: "size-full"
856
+ })
857
+ }) : /* @__PURE__ */ jsx(Skeleton, {
858
+ variant: "rounded",
859
+ className: "size-full"
860
+ }) : /* @__PURE__ */ jsx("p", {
861
+ className: cn(captionClasses, "flex h-full items-center"),
862
+ "data-part": "no-target",
863
+ children: labels.noTarget
864
+ })
865
+ })]
866
+ }),
867
+ reserveInsight && /* @__PURE__ */ jsxs("div", {
868
+ className: cn("mt-2 flex items-start gap-2 font-sans text-foreground", sizes.body),
869
+ "data-part": "insight",
870
+ children: [/* @__PURE__ */ jsx(AILoader, {
871
+ size: "sm",
872
+ state: "default",
873
+ label: labels.insightMark,
874
+ className: cn("shrink-0", size === "sm" ? "" : "mt-0.5")
875
+ }), /* @__PURE__ */ jsx("p", {
876
+ className: "min-w-0 flex-1 [&_strong]:font-semibold [&_strong]:text-foreground text-xs leading-[1.8]",
877
+ children: insight
878
+ })]
879
+ })
880
+ ]
881
+ }),
882
+ state === "refreshing" && /* @__PURE__ */ jsx("div", {
883
+ "aria-hidden": "true",
884
+ className: "absolute -inset-1 rounded-md bg-veil"
885
+ })
886
+ ]
887
+ }),
888
+ asOfValid && asOfDate && /* @__PURE__ */ jsx("p", {
889
+ className: cn(captionClasses, "mt-1 text-right"),
890
+ "data-part": "footer",
891
+ children: /* @__PURE__ */ jsx("span", {
892
+ "data-part": "as-of",
893
+ children: /* @__PURE__ */ jsx("time", {
894
+ dateTime: asOfDate.toISOString(),
895
+ children: asOfText
896
+ })
897
+ })
898
+ }),
899
+ /* @__PURE__ */ jsx("div", {
900
+ ref: setPopoverContainer,
901
+ className: "pointer-events-none absolute inset-0",
902
+ "data-part": "popover-host"
903
+ }),
904
+ tooltip
905
+ ]
906
+ });
907
+ });
908
+ TargetCard.displayName = "TargetCard";
909
+ //#endregion
910
+ export { TargetCard };