@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,1187 @@
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 { TREE_TABLE_DATASET, TREE_TOTAL_PARAM, TREE_ZERO_DATASET, buildTreeBarsSpec, treeBarColor } from "./TreeCard.chart.js";
7
+ import { buildTree, buildTreeChartDescription, buildTreeDescription, createTreeFormatters, deriveTreeDelta, deriveTreeState, resolveTreeLabels } from "./TreeCard.model.js";
8
+ import { 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/TreeCard/TreeCard.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
+ unit: "text-xs",
21
+ body: "text-xs",
22
+ badge: "sm",
23
+ deltaLinePx: 20,
24
+ comparisonLinePx: 16,
25
+ chip: "sm",
26
+ chipLinePx: 20,
27
+ step: 24,
28
+ barHeight: 10,
29
+ rowText: "text-xs",
30
+ indent: 10,
31
+ twisty: 14,
32
+ plotMin: 40
33
+ },
34
+ md: {
35
+ padding: "md",
36
+ gap: "gap-2",
37
+ gapPx: 8,
38
+ label: "text-sm",
39
+ value: "text-3xl leading-9",
40
+ valueLinePx: 36,
41
+ unit: "text-sm",
42
+ body: "text-sm",
43
+ badge: "md",
44
+ deltaLinePx: 24,
45
+ comparisonLinePx: 20,
46
+ chip: "sm",
47
+ chipLinePx: 20,
48
+ step: 28,
49
+ barHeight: 12,
50
+ rowText: "text-sm",
51
+ indent: 12,
52
+ twisty: 16,
53
+ plotMin: 48
54
+ },
55
+ lg: {
56
+ padding: "lg",
57
+ gap: "gap-2",
58
+ gapPx: 8,
59
+ label: "text-sm",
60
+ value: "text-4xl leading-10",
61
+ valueLinePx: 40,
62
+ unit: "text-base",
63
+ body: "text-sm",
64
+ badge: "lg",
65
+ deltaLinePx: 28,
66
+ comparisonLinePx: 20,
67
+ chip: "md",
68
+ chipLinePx: 24,
69
+ step: 32,
70
+ barHeight: 14,
71
+ rowText: "text-sm",
72
+ indent: 14,
73
+ twisty: 18,
74
+ plotMin: 56
75
+ }
76
+ };
77
+ const CHIP_GAP_PX = 4;
78
+ const deltaStackClasses = "flex min-w-0 shrink-0 flex-col gap-1 font-sans focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ring";
79
+ const labelClasses = "min-w-0 font-sans font-medium text-muted-foreground";
80
+ const valueClasses = "font-heading font-semibold tracking-tight text-foreground";
81
+ const captionClasses = "font-sans text-xs text-muted-foreground mt-1";
82
+ const rowLabelClasses = {
83
+ node: "text-foreground",
84
+ other: "text-muted-foreground",
85
+ residual: "text-muted-foreground italic"
86
+ };
87
+ const treeRowClasses = "relative isolate rounded-md before:pointer-events-none before:absolute before:-inset-x-1.5 before:inset-y-0 before:-z-10 before:rounded-md before:bg-muted before:opacity-0 hover:before:opacity-100 focus-visible:outline-2 focus-visible:outline-offset-1 focus-visible:outline-ring";
88
+ const polarityBadgeVariant = {
89
+ positive: "success",
90
+ negative: "destructive",
91
+ neutral: "neutral"
92
+ };
93
+ const polarityTextClasses = {
94
+ positive: "text-chart-positive",
95
+ negative: "text-chart-negative",
96
+ neutral: "text-muted-foreground"
97
+ };
98
+ const headerControlClasses = "size-6 min-w-0 text-muted-foreground hover:text-foreground";
99
+ const tileStateClasses = "w-full max-w-none gap-2 px-2 py-2 [--artwork-box:3rem]";
100
+ const MISSING_NAME = "Breakdown";
101
+ const EMPTY_ROWS = [];
102
+ const EMPTY_KEYS = [];
103
+ /** The bar swatch — the exact hue a row's bar wears. */
104
+ function TreeSwatch({ color }) {
105
+ return /* @__PURE__ */ jsx("svg", {
106
+ viewBox: "0 0 12 12",
107
+ fill: "none",
108
+ "aria-hidden": "true",
109
+ className: "size-3 shrink-0",
110
+ style: { color },
111
+ children: /* @__PURE__ */ jsx("rect", {
112
+ x: "1",
113
+ y: "3",
114
+ width: "10",
115
+ height: "6",
116
+ rx: "1.5",
117
+ fill: "currentColor"
118
+ })
119
+ });
120
+ }
121
+ /** Match the plot's mark shape so a key's meaning never depends on color alone. */
122
+ function TreeKey({ label, color }) {
123
+ return /* @__PURE__ */ jsxs("span", {
124
+ className: "inline-flex items-center gap-2",
125
+ children: [/* @__PURE__ */ jsx("svg", {
126
+ width: "24",
127
+ height: "12",
128
+ viewBox: "0 0 24 12",
129
+ fill: "none",
130
+ "aria-hidden": "true",
131
+ className: "shrink-0",
132
+ style: { color },
133
+ children: /* @__PURE__ */ jsx("rect", {
134
+ x: "1",
135
+ y: "2",
136
+ width: "22",
137
+ height: "8",
138
+ rx: "1",
139
+ fill: "currentColor"
140
+ })
141
+ }), label]
142
+ });
143
+ }
144
+ function InfoIcon(props) {
145
+ return /* @__PURE__ */ jsxs("svg", {
146
+ viewBox: "0 0 24 24",
147
+ fill: "none",
148
+ stroke: "currentColor",
149
+ strokeWidth: 2,
150
+ strokeLinecap: "round",
151
+ strokeLinejoin: "round",
152
+ "aria-hidden": "true",
153
+ ...props,
154
+ children: [/* @__PURE__ */ jsx("circle", {
155
+ cx: "12",
156
+ cy: "12",
157
+ r: "9"
158
+ }), /* @__PURE__ */ jsx("path", { d: "M12 11v5M12 8h.01" })]
159
+ });
160
+ }
161
+ function ExpandAllIcon(props) {
162
+ return /* @__PURE__ */ jsx("svg", {
163
+ viewBox: "0 0 24 24",
164
+ fill: "none",
165
+ stroke: "currentColor",
166
+ strokeWidth: 2,
167
+ strokeLinecap: "round",
168
+ strokeLinejoin: "round",
169
+ "aria-hidden": "true",
170
+ ...props,
171
+ children: /* @__PURE__ */ jsx("path", { d: "m7 4 5 5 5-5M7 20l5-5 5 5" })
172
+ });
173
+ }
174
+ function CollapseAllIcon(props) {
175
+ return /* @__PURE__ */ jsx("svg", {
176
+ viewBox: "0 0 24 24",
177
+ fill: "none",
178
+ stroke: "currentColor",
179
+ strokeWidth: 2,
180
+ strokeLinecap: "round",
181
+ strokeLinejoin: "round",
182
+ "aria-hidden": "true",
183
+ ...props,
184
+ children: /* @__PURE__ */ jsx("path", { d: "m7 9 5-5 5 5M7 15l5 5 5-5" })
185
+ });
186
+ }
187
+ /** The disclosure triangle. Rotates, never animates — this package adds no motion. */
188
+ function TwistyIcon({ open, ...props }) {
189
+ return /* @__PURE__ */ jsx("svg", {
190
+ viewBox: "0 0 24 24",
191
+ fill: "currentColor",
192
+ "aria-hidden": "true",
193
+ ...props,
194
+ children: /* @__PURE__ */ jsx("path", { d: open ? "M5 9h14l-7 8z" : "M9 5v14l8-7z" })
195
+ });
196
+ }
197
+ function DeltaGlyph({ direction, ...props }) {
198
+ return /* @__PURE__ */ jsxs("svg", {
199
+ viewBox: "0 0 24 24",
200
+ fill: "none",
201
+ stroke: "currentColor",
202
+ strokeWidth: 2.5,
203
+ strokeLinecap: "round",
204
+ strokeLinejoin: "round",
205
+ "aria-hidden": "true",
206
+ ...props,
207
+ children: [
208
+ direction === "up" && /* @__PURE__ */ jsx("path", { d: "M7 17 17 7M7 7h10v10" }),
209
+ direction === "down" && /* @__PURE__ */ jsx("path", { d: "M7 7 17 17M7 17h10V7" }),
210
+ direction === "flat" && /* @__PURE__ */ jsx("path", { d: "M5 12h14M13 6l6 6-6 6" })
211
+ ]
212
+ });
213
+ }
214
+ function useMergedRef(forwarded) {
215
+ const inner = useRef(null);
216
+ return [inner, useCallback((node) => {
217
+ inner.current = node;
218
+ if (typeof forwarded === "function") forwarded(node);
219
+ else if (forwarded) forwarded.current = node;
220
+ }, [forwarded])];
221
+ }
222
+ function usePlotBox(cellRef, wrapperRef, enabled, resetKey) {
223
+ const [box, setBox] = useState({
224
+ left: 0,
225
+ width: 0
226
+ });
227
+ useLayoutEffect(() => {
228
+ if (!enabled) return;
229
+ const cell = cellRef.current;
230
+ const wrapper = wrapperRef.current;
231
+ if (!cell || !wrapper) return;
232
+ let frame = 0;
233
+ const measure = () => {
234
+ const cellRect = cell.getBoundingClientRect();
235
+ const wrapperRect = wrapper.getBoundingClientRect();
236
+ const next = {
237
+ left: cellRect.left - wrapperRect.left,
238
+ width: Math.round(cellRect.width)
239
+ };
240
+ setBox((prev) => prev.left === next.left && prev.width === next.width ? prev : next);
241
+ };
242
+ measure();
243
+ if (typeof ResizeObserver === "undefined") return;
244
+ const observer = new ResizeObserver(() => {
245
+ cancelAnimationFrame(frame);
246
+ frame = requestAnimationFrame(measure);
247
+ });
248
+ observer.observe(cell);
249
+ observer.observe(wrapper);
250
+ return () => {
251
+ cancelAnimationFrame(frame);
252
+ observer.disconnect();
253
+ };
254
+ }, [
255
+ cellRef,
256
+ wrapperRef,
257
+ enabled,
258
+ resetKey
259
+ ]);
260
+ return box;
261
+ }
262
+ /**
263
+ * A hierarchical breakdown tile — the decomposition subtype of the KPI card
264
+ * family beside `MetricCard` and `ComparisonCard`: one total broken into
265
+ * nested parts as indented, expandable rows whose bars share a single
266
+ * zero-based scale, so every bar length reads as its share of the whole.
267
+ * Depth rides the sequential ramp, each row carries its value and share as
268
+ * text, an optional `previous` per node turns it into a decomposition of
269
+ * change with the family's polarity badge, and every loading, empty, error
270
+ * and stale state is layout-stable inside one `Box` frame — with a generated
271
+ * text equivalent, full WAI-ARIA tree keyboard navigation, and a data table
272
+ * in an info popover.
273
+ */
274
+ const TreeCard = forwardRef(({ label, name: nameProp, hint, nodes, rollup = "sum", sort = "none", maxChildren, maxDepth, showResidual = true, residualTolerance, expanded, defaultExpanded, defaultExpandDepth = 1, onExpandedChange, selected, defaultSelected, onSelectedChange, selectable = true, headline = "total", totalLabel, headlineChip = true, colorBy = "depth", rowSecondary = "share", deltaMode = "percent", goodDirection = "up", format = "compact", formatValue, shareFormat = "percent:0", unit, expectedRows = 4, showExpandAll = 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) => {
275
+ const labels = useMemo(() => resolveTreeLabels(labelOverrides), [labelOverrides]);
276
+ const sizes = sizeStyles[size];
277
+ const name = nameProp ?? (typeof label === "string" ? label : MISSING_NAME);
278
+ const id = useId();
279
+ const headingId = `${id}-label`;
280
+ const descriptionId = `${id}-description`;
281
+ const hintId = `${id}-hint`;
282
+ const tableId = `${id}-table`;
283
+ const deltaExplanationId = `${id}-delta-explanation`;
284
+ const [rootRef, setRootRef] = useMergedRef(forwardedRef);
285
+ const treeRef = useRef(null);
286
+ const plotCellRef = useRef(null);
287
+ const rowRefs = useRef(/* @__PURE__ */ new Map());
288
+ const [tableOpen, setTableOpen] = useState(defaultTableOpen);
289
+ const [popoverContainer, setPopoverContainer] = useState(null);
290
+ const isExpandedControlled = expanded !== void 0;
291
+ const [innerExpanded, setInnerExpanded] = useState(defaultExpanded);
292
+ const expandedList = isExpandedControlled ? expanded : innerExpanded;
293
+ const expandedSet = useMemo(() => expandedList ? new Set(expandedList) : null, [expandedList]);
294
+ const isSelectedControlled = selected !== void 0;
295
+ const [innerSelected, setInnerSelected] = useState(defaultSelected ?? null);
296
+ const selectedKey = isSelectedControlled ? selected : innerSelected;
297
+ const pendingAnnounce = useRef(false);
298
+ const [announcement, setAnnouncement] = useState("");
299
+ const otherLabel = labels.other;
300
+ const residualLabel = labels.unallocated;
301
+ const summary = useMemo(() => buildTree(nodes, {
302
+ rollup,
303
+ sort,
304
+ maxChildren,
305
+ maxDepth,
306
+ expanded: expandedSet,
307
+ expandDepth: defaultExpandDepth,
308
+ showResidual,
309
+ residualTolerance,
310
+ otherLabel,
311
+ residualLabel
312
+ }), [
313
+ nodes,
314
+ rollup,
315
+ sort,
316
+ maxChildren,
317
+ maxDepth,
318
+ expandedSet,
319
+ defaultExpandDepth,
320
+ showResidual,
321
+ residualTolerance,
322
+ otherLabel,
323
+ residualLabel
324
+ ]);
325
+ const formatters = useMemo(() => createTreeFormatters({
326
+ format,
327
+ locale,
328
+ formatValue,
329
+ deltaMode,
330
+ shareFormat,
331
+ percentagePoints: labels.percentagePoints
332
+ }), [
333
+ format,
334
+ locale,
335
+ formatValue,
336
+ deltaMode,
337
+ shareFormat,
338
+ labels.percentagePoints
339
+ ]);
340
+ const rows = summary?.rows ?? EMPTY_ROWS;
341
+ const allRows = summary?.allRows ?? EMPTY_ROWS;
342
+ const hasValue = (summary?.count ?? 0) > 0;
343
+ const hasData = allRows.length > 0;
344
+ const state = deriveTreeState({
345
+ status,
346
+ hasValue,
347
+ hasData,
348
+ filtered
349
+ });
350
+ const showContent = state === "ready" || state === "refreshing";
351
+ const hasChart = showContent && rows.length > 0;
352
+ const selectedRow = useMemo(() => selectedKey == null ? null : allRows.find((row) => row.key === selectedKey) ?? null, [allRows, selectedKey]);
353
+ const rowsSelectable = selectable && (!isSelectedControlled || onSelectedChange != null);
354
+ const canExpand = (summary?.expandable ?? false) && (!isExpandedControlled || onExpandedChange != null);
355
+ const setExpanded = useCallback((next) => {
356
+ if (!isExpandedControlled) setInnerExpanded(next);
357
+ onExpandedChange?.(next);
358
+ }, [isExpandedControlled, onExpandedChange]);
359
+ const currentExpanded = summary?.expandedKeys ?? EMPTY_KEYS;
360
+ const currentExpandedRef = useRef(currentExpanded);
361
+ currentExpandedRef.current = currentExpanded;
362
+ const toggleExpanded = useCallback((key, open) => {
363
+ const next = new Set(currentExpandedRef.current);
364
+ if (open ?? !next.has(key)) next.add(key);
365
+ else next.delete(key);
366
+ setExpanded([...next]);
367
+ }, [setExpanded]);
368
+ const allExpandable = useMemo(() => allRows.filter((row) => row.hasChildren).map((row) => row.key), [allRows]);
369
+ const allOpen = allExpandable.length > 0 && allExpandable.every((key) => currentExpanded.includes(key));
370
+ const selectRow = useCallback((key) => {
371
+ pendingAnnounce.current = true;
372
+ if (!isSelectedControlled) setInnerSelected(key);
373
+ onSelectedChange?.(key);
374
+ }, [isSelectedControlled, onSelectedChange]);
375
+ const [activeKey, setActiveKey] = useState(null);
376
+ const activeRowKey = useMemo(() => {
377
+ if (activeKey && rows.some((row) => row.key === activeKey)) return activeKey;
378
+ if (selectedRow && rows.some((row) => row.key === selectedRow.key)) return selectedRow.key;
379
+ return rows[0]?.key ?? null;
380
+ }, [
381
+ activeKey,
382
+ rows,
383
+ selectedRow
384
+ ]);
385
+ const focusRow = useCallback((key) => {
386
+ setActiveKey(key);
387
+ rowRefs.current.get(key)?.focus();
388
+ }, []);
389
+ const handleRowKeyDown = (event, row) => {
390
+ const index = rows.indexOf(row);
391
+ if (index < 0) return;
392
+ switch (event.key) {
393
+ case "ArrowDown":
394
+ if (index < rows.length - 1) focusRow(rows[index + 1].key);
395
+ break;
396
+ case "ArrowUp":
397
+ if (index > 0) focusRow(rows[index - 1].key);
398
+ break;
399
+ case "ArrowRight":
400
+ if (row.hasChildren && !row.expanded) {
401
+ if (canExpand) toggleExpanded(row.key, true);
402
+ } else if (row.expanded && rows[index + 1]?.parentKey === row.key) focusRow(rows[index + 1].key);
403
+ break;
404
+ case "ArrowLeft":
405
+ if (row.expanded && canExpand) toggleExpanded(row.key, false);
406
+ else if (row.parentKey) {
407
+ const parent = rows.find((candidate) => candidate.key === row.parentKey);
408
+ if (parent) focusRow(parent.key);
409
+ }
410
+ break;
411
+ case "Home":
412
+ if (rows[0]) focusRow(rows[0].key);
413
+ break;
414
+ case "End":
415
+ if (rows[rows.length - 1]) focusRow(rows[rows.length - 1].key);
416
+ break;
417
+ case "Enter":
418
+ case " ":
419
+ if (rowsSelectable) selectRow(row.key === selectedRow?.key ? null : row.key);
420
+ else if (row.hasChildren && canExpand) toggleExpanded(row.key);
421
+ break;
422
+ default: return;
423
+ }
424
+ event.preventDefault();
425
+ };
426
+ const handlePlotClick = (event) => {
427
+ if (!rowsSelectable) return;
428
+ const top = event.currentTarget.getBoundingClientRect().top;
429
+ const row = rows[Math.floor((event.clientY - top) / sizes.step)];
430
+ if (row) {
431
+ selectRow(row.key === selectedRow?.key ? null : row.key);
432
+ setActiveKey(row.key);
433
+ }
434
+ };
435
+ const headlineRow = headline === "focus" ? selectedRow : null;
436
+ const headlineValue = headlineRow ? headlineRow.value : summary?.total ?? null;
437
+ const headlinePrevious = headlineRow ? headlineRow.previous : summary?.previousTotal ?? null;
438
+ const headlineName = headlineRow ? headlineRow.label : totalLabel ?? labels.total;
439
+ const delta = useMemo(() => deriveTreeDelta(headlineValue, headlinePrevious, {
440
+ deltaMode,
441
+ goodDirection,
442
+ format,
443
+ locale,
444
+ percentagePoints: labels.percentagePoints
445
+ }), [
446
+ headlineValue,
447
+ headlinePrevious,
448
+ deltaMode,
449
+ goodDirection,
450
+ format,
451
+ locale,
452
+ labels.percentagePoints
453
+ ]);
454
+ const valueText = headline === "none" ? null : formatters.formatValue(headlineValue);
455
+ const deltaExplanationRows = delta ? labels.deltaExplanation({
456
+ label: typeof headlineName === "string" ? headlineName : name,
457
+ valueText: formatters.formatValue(headlineValue),
458
+ previousText: formatters.formatValue(headlinePrevious),
459
+ changeText: delta.text,
460
+ secondaryText: delta.secondaryText,
461
+ renderedMode: delta.renderedMode,
462
+ direction: delta.direction
463
+ }) : [];
464
+ const deltaExplanation = deltaExplanationRows.map(([key, value]) => `${key}: ${value}`).join(". ");
465
+ const asOfDate = updatedAt == null ? null : updatedAt instanceof Date ? updatedAt : new Date(updatedAt);
466
+ const asOfValid = asOfDate != null && !Number.isNaN(asOfDate.getTime());
467
+ const asOfText = asOfValid ? typeof updatedAtFormat === "function" ? updatedAtFormat(asOfDate) : formatDate(asOfDate, {
468
+ granularity: updatedAtFormat,
469
+ locale,
470
+ timeZone
471
+ }) : void 0;
472
+ const cardDescription = description ?? buildTreeDescription({
473
+ name,
474
+ state,
475
+ headline,
476
+ summary,
477
+ focus: headlineRow,
478
+ delta,
479
+ formatters,
480
+ asOfText,
481
+ labels
482
+ });
483
+ useEffect(() => {
484
+ if (!pendingAnnounce.current) return;
485
+ pendingAnnounce.current = false;
486
+ setAnnouncement(cardDescription);
487
+ }, [cardDescription]);
488
+ const tokens = useChartTokens(rootRef);
489
+ const plotHeight = rows.length * sizes.step;
490
+ const plotBox = usePlotBox(plotCellRef, treeRef, hasChart, `${rows.length}:${rows[0]?.key ?? ""}`);
491
+ const inFocus = useCallback((key) => selectedRow != null && (key === selectedRow.key || key.startsWith(`${selectedRow.key}/`)), [selectedRow]);
492
+ const emphasize = selectedRow != null;
493
+ const barColor = useCallback((row) => tokens ? treeBarColor(tokens, {
494
+ depth: row.depth,
495
+ role: row.role,
496
+ inFocus: row.key ? inFocus(row.key) : false
497
+ }, colorBy, emphasize) : void 0, [
498
+ tokens,
499
+ colorBy,
500
+ emphasize,
501
+ inFocus
502
+ ]);
503
+ const tooltipContent = useCallback(({ item }) => {
504
+ const datum = item.datum;
505
+ if (!datum || !("key" in datum)) return null;
506
+ const tooltipRows = [[/* @__PURE__ */ jsx(TreeKey, {
507
+ label: datum.label,
508
+ color: barColor(datum)
509
+ }, "row"), formatters.formatValue(datum.value)]];
510
+ if (datum.share != null) tooltipRows.push([labels.tableShare, formatters.formatShare(datum.share)]);
511
+ if (datum.depth > 0 && datum.shareOfTotal != null) tooltipRows.push([labels.tableShareOfTotal, formatters.formatShare(datum.shareOfTotal)]);
512
+ if (datum.previous != null) tooltipRows.push([labels.tableChange, formatters.formatChange(datum.value, datum.previous)]);
513
+ return /* @__PURE__ */ jsx(ArcTooltipRows, { rows: tooltipRows });
514
+ }, [
515
+ barColor,
516
+ formatters,
517
+ labels
518
+ ]);
519
+ const { handler: tooltipHandler, tooltip } = useArcTooltipHandler({ content: tooltipContent });
520
+ const embedOptions = useMemo(() => arcEmbedOptions({ tooltip: tooltipHandler }), [tooltipHandler]);
521
+ const barHeight = sizes.barHeight;
522
+ const hasNegative = summary?.hasNegative ?? false;
523
+ const itemTitle = labels.tableItem;
524
+ const shareTitle = labels.tableShare;
525
+ const spec = useMemo(() => {
526
+ if (!tokens) return null;
527
+ const built = buildTreeBarsSpec({
528
+ barHeight,
529
+ height: 56,
530
+ zeroRule: hasNegative,
531
+ tooltip: true,
532
+ colorBy,
533
+ emphasize,
534
+ itemTitle,
535
+ valueTitle: name,
536
+ shareTitle,
537
+ valueFormat: locale ? `${format}@${locale}` : format,
538
+ shareFormat: locale ? `${shareFormat}@${locale}` : shareFormat,
539
+ locale
540
+ }, tokens);
541
+ return patchSpec ? patchSpec(built) : built;
542
+ }, [
543
+ tokens,
544
+ barHeight,
545
+ hasNegative,
546
+ colorBy,
547
+ emphasize,
548
+ itemTitle,
549
+ name,
550
+ shareTitle,
551
+ format,
552
+ shareFormat,
553
+ locale,
554
+ patchSpec
555
+ ]);
556
+ const plotRows = useMemo(() => rows.map((row) => ({
557
+ ...row,
558
+ inFocus: inFocus(row.key)
559
+ })), [rows, inFocus]);
560
+ const zeroRows = useMemo(() => hasNegative ? [{ value: 0 }] : [], [hasNegative]);
561
+ const datasets = useMemo(() => ({
562
+ [TREE_TABLE_DATASET]: plotRows,
563
+ [TREE_ZERO_DATASET]: zeroRows
564
+ }), [plotRows, zeroRows]);
565
+ const domainMax = useMemo(() => {
566
+ let top = summary?.total ?? 0;
567
+ for (const row of rows) if (row.value != null && row.value > top) top = row.value;
568
+ return top > 0 ? top : 1;
569
+ }, [rows, summary]);
570
+ const signals = useMemo(() => ({ [TREE_TOTAL_PARAM]: domainMax }), [domainMax]);
571
+ const chartDescription = summary && hasChart ? buildTreeChartDescription({
572
+ name,
573
+ summary,
574
+ formatters,
575
+ labels
576
+ }) : void 0;
577
+ const showInsight = insight != null && insight !== false;
578
+ const comparisonWanted = summary?.hasPrevious ?? false;
579
+ const reserveChip = headlineChip && headline !== "none";
580
+ const headlineMinHeight = Math.max(headline !== "none" ? sizes.valueLinePx + (reserveChip ? CHIP_GAP_PX + sizes.chipLinePx : 0) : 0, comparisonWanted ? sizes.deltaLinePx + 4 + sizes.comparisonLinePx : 0);
581
+ const reserveHeadline = headline !== "none" || comparisonWanted;
582
+ const rowCount = hasChart ? rows.length : Math.max(0, expectedRows);
583
+ const bodyMinHeight = (reserveHeadline ? headlineMinHeight : 0) + (rowCount > 0 ? (reserveHeadline ? sizes.gapPx : 0) + rowCount * sizes.step : 0) + (showInsight ? sizes.gapPx + sizes.comparisonLinePx : 0);
584
+ const secondary = rowSecondary === "delta" && !comparisonWanted ? "none" : rowSecondary;
585
+ const gridSpan = secondary === "none" ? "col-span-3" : "col-span-4";
586
+ const rowGridClasses = "grid gap-x-3";
587
+ const gridStyle = {
588
+ gridAutoRows: `${sizes.step}px`,
589
+ gridTemplateColumns: secondary === "none" ? `fit-content(40%) minmax(${sizes.plotMin}px, 1fr) auto` : `fit-content(40%) minmax(${sizes.plotMin}px, 1fr) auto auto`
590
+ };
591
+ const secondaryFor = (row) => {
592
+ if (secondary === "share") return {
593
+ text: formatters.formatShare(row.share),
594
+ polarity: "neutral"
595
+ };
596
+ if (secondary === "share-of-total") return {
597
+ text: formatters.formatShare(row.shareOfTotal),
598
+ polarity: "neutral"
599
+ };
600
+ const change = deriveTreeDelta(row.value, row.previous, {
601
+ deltaMode,
602
+ goodDirection,
603
+ format,
604
+ locale,
605
+ percentagePoints: labels.percentagePoints
606
+ });
607
+ return {
608
+ text: change?.text ?? "—",
609
+ polarity: change?.polarity ?? "neutral"
610
+ };
611
+ };
612
+ const HeadingTag = `h${titleLevel}`;
613
+ const busy = state === "loading" || state === "refreshing";
614
+ const directionWordFor = (direction) => direction === "up" ? labels.increase : direction === "down" ? labels.decrease : labels.unchanged;
615
+ const insufficient = showContent && !(summary?.expandable ?? false);
616
+ return /* @__PURE__ */ jsxs(Box, {
617
+ ref: setRootRef,
618
+ render: render ?? /* @__PURE__ */ jsx("figure", {}),
619
+ padding: sizes.padding,
620
+ "aria-labelledby": headingId,
621
+ "aria-describedby": descriptionId,
622
+ "aria-busy": busy || void 0,
623
+ "data-status": status,
624
+ "data-state": state,
625
+ "data-size": size,
626
+ "data-headline": headline,
627
+ "data-color-by": colorBy,
628
+ "data-row-secondary": secondary,
629
+ "data-rollup": rollup,
630
+ "data-sort": sort,
631
+ "data-levels": showContent ? summary?.levels : void 0,
632
+ "data-selectable": rowsSelectable ? "" : void 0,
633
+ "data-selected": showContent ? selectedRow?.key : void 0,
634
+ "data-empty": state === "empty" || state === "no-match" ? "" : void 0,
635
+ "data-stale": stale ? "" : void 0,
636
+ "data-insufficient": insufficient ? "" : void 0,
637
+ "data-folded": showContent && summary && summary.folded > 0 ? summary.folded : void 0,
638
+ "data-residual": showContent && summary?.hasResidual ? "" : void 0,
639
+ "data-polarity": showContent && delta ? delta.polarity : void 0,
640
+ ...rest,
641
+ className: cn("relative flex min-w-0 flex-col", sizes.gap, className),
642
+ children: [
643
+ /* @__PURE__ */ jsxs("div", {
644
+ className: "flex items-center justify-between gap-2",
645
+ children: [/* @__PURE__ */ jsxs("div", {
646
+ className: "flex min-w-0 items-center gap-1",
647
+ children: [/* @__PURE__ */ jsx(HeadingTag, {
648
+ id: headingId,
649
+ className: cn(labelClasses, sizes.label, "line-clamp-2"),
650
+ children: label
651
+ }), hint && /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsxs(Tooltip, { children: [/* @__PURE__ */ jsx(TooltipTrigger, {
652
+ render: /* @__PURE__ */ jsx(Button, {
653
+ variant: "ghost",
654
+ size: "sm",
655
+ iconOnly: true,
656
+ circular: true,
657
+ "aria-label": labels.about(name),
658
+ "aria-describedby": hintId,
659
+ className: headerControlClasses
660
+ }),
661
+ children: /* @__PURE__ */ jsx(InfoIcon, { className: "size-3.5" })
662
+ }), /* @__PURE__ */ jsx(TooltipPopup, { children: hint })] }), /* @__PURE__ */ jsx("span", {
663
+ id: hintId,
664
+ className: "sr-only",
665
+ children: hint
666
+ })] })]
667
+ }), /* @__PURE__ */ jsxs("div", {
668
+ className: "flex shrink-0 items-center gap-1",
669
+ children: [
670
+ stale && /* @__PURE__ */ jsx(Badge, {
671
+ variant: "warning",
672
+ appearance: "soft",
673
+ size: "sm",
674
+ children: labels.stale
675
+ }),
676
+ showExpandAll && hasChart && canExpand && /* @__PURE__ */ jsxs(Tooltip, { children: [/* @__PURE__ */ jsx(TooltipTrigger, {
677
+ render: /* @__PURE__ */ jsx(Button, {
678
+ variant: "ghost",
679
+ size: "sm",
680
+ iconOnly: true,
681
+ circular: true,
682
+ "aria-label": allOpen ? labels.collapseAll : labels.expandAll,
683
+ onClick: () => setExpanded(allOpen ? [] : allExpandable),
684
+ className: headerControlClasses,
685
+ "data-part": "expand-all"
686
+ }),
687
+ children: allOpen ? /* @__PURE__ */ jsx(CollapseAllIcon, { className: "size-3.5" }) : /* @__PURE__ */ jsx(ExpandAllIcon, { className: "size-3.5" })
688
+ }), /* @__PURE__ */ jsx(TooltipPopup, { children: allOpen ? labels.collapseAll : labels.expandAll })] }),
689
+ hasChart && summary && /* @__PURE__ */ jsxs(Popover, {
690
+ open: tableOpen,
691
+ onOpenChange: setTableOpen,
692
+ children: [!hideTableToggle && /* @__PURE__ */ jsx(PopoverTrigger, {
693
+ render: /* @__PURE__ */ jsx(Button, {
694
+ variant: "ghost",
695
+ size: "sm",
696
+ iconOnly: true,
697
+ circular: true,
698
+ "aria-label": tableOpen ? labels.hideTable(name) : labels.showTable(name),
699
+ className: headerControlClasses
700
+ }),
701
+ children: /* @__PURE__ */ jsx(InfoIcon, { className: "size-3.5" })
702
+ }), popoverContainer && /* @__PURE__ */ jsxs(PopoverPopup, {
703
+ id: tableId,
704
+ anchor: rootRef,
705
+ container: popoverContainer,
706
+ side: "right",
707
+ align: "start",
708
+ collisionPadding: 8,
709
+ className: "pointer-events-auto w-96 max-w-[min(24rem,var(--available-width))]",
710
+ "data-part": "data-popover",
711
+ children: [
712
+ /* @__PURE__ */ jsx(PopoverTitle, {
713
+ className: "mb-3",
714
+ children: name
715
+ }),
716
+ /* @__PURE__ */ jsxs("div", {
717
+ className: "mb-3 flex flex-wrap gap-x-3 gap-y-1 text-xs text-muted-foreground",
718
+ "data-part": "chart-legend",
719
+ children: [colorBy === "depth" ? Array.from({ length: Math.min(summary.levels, 3) }, (_, depth) => /* @__PURE__ */ jsx(TreeKey, {
720
+ label: `${labels.tableLevel} ${depth + 1}`,
721
+ color: barColor({ depth })
722
+ }, depth)) : selectedRow ? /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsx(TreeKey, {
723
+ label: selectedRow.label,
724
+ color: tokens?.emphasis
725
+ }), /* @__PURE__ */ jsx(TreeKey, {
726
+ label: labels.other,
727
+ color: tokens?.context
728
+ })] }) : /* @__PURE__ */ jsx(TreeKey, {
729
+ label: name,
730
+ color: tokens?.emphasis
731
+ }), summary.hasResidual && /* @__PURE__ */ jsx(TreeKey, {
732
+ label: labels.unallocated,
733
+ color: tokens?.context
734
+ })]
735
+ }),
736
+ /* @__PURE__ */ jsx(TableContainer, {
737
+ variant: "plain",
738
+ tabIndex: 0,
739
+ role: "region",
740
+ "aria-label": labels.tableCaption(name),
741
+ className: "max-h-[min(18rem,calc(var(--available-height)-4.5rem))] focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ring",
742
+ "data-part": "table",
743
+ children: /* @__PURE__ */ jsxs(Table, {
744
+ size: "sm",
745
+ stickyHeader: true,
746
+ hoverable: false,
747
+ children: [
748
+ /* @__PURE__ */ jsx(TableCaption, {
749
+ side: "top",
750
+ className: "sr-only",
751
+ children: labels.tableCaption(name)
752
+ }),
753
+ /* @__PURE__ */ jsx(TableHeader, { children: /* @__PURE__ */ jsxs(TableRow, { children: [
754
+ /* @__PURE__ */ jsx(TableHead, { children: labels.tableItem }),
755
+ /* @__PURE__ */ jsx(TableHead, {
756
+ className: "text-right",
757
+ children: labels.tableValue
758
+ }),
759
+ /* @__PURE__ */ jsx(TableHead, {
760
+ className: "text-right",
761
+ children: labels.tableShare
762
+ }),
763
+ /* @__PURE__ */ jsx(TableHead, {
764
+ className: "text-right",
765
+ children: labels.tableShareOfTotal
766
+ }),
767
+ comparisonWanted && /* @__PURE__ */ jsx(TableHead, {
768
+ className: "text-right",
769
+ children: labels.tableChange
770
+ })
771
+ ] }) }),
772
+ /* @__PURE__ */ jsx(TableBody, { children: allRows.map((row) => /* @__PURE__ */ jsxs(TableRow, {
773
+ "data-role": row.role,
774
+ "data-depth": row.depth,
775
+ children: [
776
+ /* @__PURE__ */ jsx(TableCell, { children: /* @__PURE__ */ jsxs("span", {
777
+ className: "inline-flex items-baseline gap-1.5",
778
+ style: { paddingInlineStart: row.depth * sizes.indent },
779
+ children: [/* @__PURE__ */ jsx("span", {
780
+ "aria-hidden": "true",
781
+ className: "inline-block size-1.5 shrink-0 self-center rounded-full",
782
+ style: { background: barColor(row) }
783
+ }), /* @__PURE__ */ jsxs("span", { children: [row.label, /* @__PURE__ */ jsxs("span", {
784
+ className: "sr-only",
785
+ children: [
786
+ " ",
787
+ "(",
788
+ labels.tableLevel,
789
+ " ",
790
+ row.depth + 1,
791
+ row.role === "other" ? `, ${labels.foldedItems(row.folded)}` : "",
792
+ row.key === selectedRow?.key ? `, ${labels.selected}` : "",
793
+ ")"
794
+ ]
795
+ })] })]
796
+ }) }),
797
+ /* @__PURE__ */ jsx(TableCell, {
798
+ className: "text-right tabular-nums",
799
+ children: formatters.formatValue(row.value)
800
+ }),
801
+ /* @__PURE__ */ jsx(TableCell, {
802
+ className: "text-right tabular-nums",
803
+ children: formatters.formatShare(row.share)
804
+ }),
805
+ /* @__PURE__ */ jsx(TableCell, {
806
+ className: "text-right tabular-nums",
807
+ children: formatters.formatShare(row.shareOfTotal)
808
+ }),
809
+ comparisonWanted && /* @__PURE__ */ jsx(TableCell, {
810
+ className: "text-right tabular-nums",
811
+ children: formatters.formatChange(row.value, row.previous)
812
+ })
813
+ ]
814
+ }, row.key)) })
815
+ ]
816
+ })
817
+ })
818
+ ]
819
+ })]
820
+ }),
821
+ action
822
+ ]
823
+ })]
824
+ }),
825
+ /* @__PURE__ */ jsx("p", {
826
+ id: descriptionId,
827
+ className: "sr-only",
828
+ children: cardDescription
829
+ }),
830
+ /* @__PURE__ */ jsx("p", {
831
+ role: "status",
832
+ "aria-live": "polite",
833
+ className: "sr-only",
834
+ "data-part": "announcement",
835
+ children: announcement
836
+ }),
837
+ /* @__PURE__ */ jsxs("div", {
838
+ className: "relative flex min-w-0 flex-col",
839
+ style: { minHeight: bodyMinHeight },
840
+ children: [
841
+ state === "loading" && /* @__PURE__ */ jsxs(SkeletonGroup, {
842
+ className: cn("flex flex-col", sizes.gap),
843
+ children: [
844
+ reserveHeadline && /* @__PURE__ */ jsxs("div", {
845
+ className: "flex min-w-0 items-center justify-between gap-2",
846
+ style: { minHeight: headlineMinHeight },
847
+ children: [headline !== "none" && /* @__PURE__ */ jsxs("div", {
848
+ className: "flex w-2/5 flex-col items-start gap-1",
849
+ children: [/* @__PURE__ */ jsx(Skeleton, {
850
+ variant: "rounded",
851
+ className: "w-full",
852
+ style: { height: sizes.valueLinePx }
853
+ }), reserveChip && /* @__PURE__ */ jsx(Skeleton, {
854
+ variant: "rounded",
855
+ className: "w-16 max-w-full",
856
+ style: { height: sizes.chipLinePx }
857
+ })]
858
+ }), comparisonWanted && /* @__PURE__ */ jsxs("div", {
859
+ className: cn("flex w-2/5 flex-col gap-1", headline !== "none" ? "items-end" : "items-start"),
860
+ children: [/* @__PURE__ */ jsx(Skeleton, {
861
+ variant: "rounded",
862
+ className: "w-20 max-w-full",
863
+ style: { height: sizes.deltaLinePx }
864
+ }), /* @__PURE__ */ jsx(Skeleton, {
865
+ variant: "rounded",
866
+ className: "w-full",
867
+ style: { height: sizes.comparisonLinePx }
868
+ })]
869
+ })]
870
+ }),
871
+ rowCount > 0 && /* @__PURE__ */ jsx("div", {
872
+ className: rowGridClasses,
873
+ style: gridStyle,
874
+ "data-part": "rows-skeleton",
875
+ children: Array.from({ length: rowCount }, (_, index) => /* @__PURE__ */ jsxs("div", {
876
+ className: cn(gridSpan, "grid grid-cols-subgrid items-center"),
877
+ children: [
878
+ /* @__PURE__ */ jsx(Skeleton, {
879
+ variant: "rounded",
880
+ className: "h-3",
881
+ style: {
882
+ width: 64,
883
+ marginInlineStart: index % 3 * sizes.indent
884
+ }
885
+ }),
886
+ /* @__PURE__ */ jsx(Skeleton, {
887
+ variant: "rounded",
888
+ style: {
889
+ height: sizes.barHeight,
890
+ width: `${Math.max(25, 90 - index * 18)}%`
891
+ }
892
+ }),
893
+ /* @__PURE__ */ jsx(Skeleton, {
894
+ variant: "rounded",
895
+ className: "h-3 w-10"
896
+ }),
897
+ secondary !== "none" && /* @__PURE__ */ jsx(Skeleton, {
898
+ variant: "rounded",
899
+ className: "h-3 w-8"
900
+ })
901
+ ]
902
+ }, index))
903
+ }),
904
+ showInsight && /* @__PURE__ */ jsxs("div", {
905
+ className: "flex items-start gap-2",
906
+ children: [/* @__PURE__ */ jsx(Skeleton, {
907
+ variant: "rounded",
908
+ className: "size-4 shrink-0"
909
+ }), /* @__PURE__ */ jsx(Skeleton, {
910
+ variant: "rounded",
911
+ className: "w-full",
912
+ style: { height: sizes.comparisonLinePx }
913
+ })]
914
+ })
915
+ ]
916
+ }),
917
+ state === "error" && /* @__PURE__ */ jsx(SystemState, {
918
+ variant: "server-error",
919
+ scope: "inline",
920
+ title: labels.error,
921
+ description: false,
922
+ action: onRetry ? /* @__PURE__ */ jsx(Button, {
923
+ size: "sm",
924
+ onClick: onRetry,
925
+ children: labels.retry
926
+ }) : void 0,
927
+ className: cn(tileStateClasses, "my-auto")
928
+ }),
929
+ state === "empty" && /* @__PURE__ */ jsx(SystemState, {
930
+ variant: "empty",
931
+ scope: "inline",
932
+ title: labels.noData,
933
+ description: false,
934
+ className: cn(tileStateClasses, "my-auto")
935
+ }),
936
+ state === "no-match" && /* @__PURE__ */ jsx(SystemState, {
937
+ variant: "no-results",
938
+ scope: "inline",
939
+ title: labels.noMatch,
940
+ description: false,
941
+ action: onClearFilters ? /* @__PURE__ */ jsx(Button, {
942
+ variant: "outline",
943
+ size: "sm",
944
+ onClick: onClearFilters,
945
+ children: labels.clearFilters
946
+ }) : void 0,
947
+ className: cn(tileStateClasses, "my-auto")
948
+ }),
949
+ showContent && /* @__PURE__ */ jsxs("div", {
950
+ className: cn("flex min-w-0 flex-col", sizes.gap),
951
+ children: [
952
+ (valueText != null || delta) && /* @__PURE__ */ jsxs("div", {
953
+ className: "flex min-w-0 items-center justify-between gap-2",
954
+ style: { minHeight: headlineMinHeight },
955
+ "data-part": "headline",
956
+ children: [valueText != null && /* @__PURE__ */ jsxs("div", {
957
+ className: "flex min-w-0 flex-col items-start gap-1",
958
+ "data-part": "value-stack",
959
+ children: [/* @__PURE__ */ jsxs("div", {
960
+ className: "flex min-w-0 flex-wrap items-baseline gap-x-1.5",
961
+ "data-part": "value",
962
+ children: [/* @__PURE__ */ jsx("span", {
963
+ className: cn(valueClasses, sizes.value, "min-w-0 wrap-break-word"),
964
+ children: valueText
965
+ }), unit != null && /* @__PURE__ */ jsx("span", {
966
+ className: cn("font-sans text-muted-foreground", sizes.unit),
967
+ children: unit
968
+ })]
969
+ }), reserveChip && /* @__PURE__ */ jsxs(Badge, {
970
+ variant: "primary",
971
+ appearance: "soft",
972
+ size: sizes.chip,
973
+ className: "max-w-full",
974
+ "data-part": "headline-chip",
975
+ children: [/* @__PURE__ */ jsx(TreeSwatch, { color: headlineRow ? barColor(headlineRow) : tokens?.emphasis }), /* @__PURE__ */ jsx("span", {
976
+ className: "truncate",
977
+ children: headlineName
978
+ })]
979
+ })]
980
+ }), delta && /* @__PURE__ */ jsxs(Tooltip, { children: [/* @__PURE__ */ jsxs(TooltipTrigger, {
981
+ delay: 150,
982
+ render: /* @__PURE__ */ jsx("p", {
983
+ tabIndex: 0,
984
+ role: "group",
985
+ "aria-label": labels.deltaComparison(name),
986
+ "aria-describedby": deltaExplanationId,
987
+ className: cn(deltaStackClasses, "rounded-md", valueText != null ? "ms-auto max-w-1/2 items-end text-right" : "items-start text-left", sizes.body),
988
+ "data-part": "delta"
989
+ }),
990
+ children: [
991
+ /* @__PURE__ */ jsx("span", {
992
+ id: deltaExplanationId,
993
+ className: "sr-only",
994
+ children: deltaExplanation
995
+ }),
996
+ /* @__PURE__ */ jsxs(Badge, {
997
+ variant: polarityBadgeVariant[delta.polarity],
998
+ appearance: "soft",
999
+ size: sizes.badge,
1000
+ "data-direction": delta.direction,
1001
+ children: [
1002
+ /* @__PURE__ */ jsx(DeltaGlyph, { direction: delta.direction }),
1003
+ /* @__PURE__ */ jsxs("span", {
1004
+ className: "sr-only",
1005
+ children: [directionWordFor(delta.direction), " "]
1006
+ }),
1007
+ delta.text
1008
+ ]
1009
+ }),
1010
+ /* @__PURE__ */ jsx("span", {
1011
+ className: "max-w-full text-muted-foreground wrap-break-word text-xs",
1012
+ children: labels.versusPrevious
1013
+ })
1014
+ ]
1015
+ }), /* @__PURE__ */ jsx(TooltipPopup, {
1016
+ arrow: false,
1017
+ side: "bottom",
1018
+ align: valueText != null ? "end" : "start",
1019
+ sideOffset: 8,
1020
+ collisionPadding: 8,
1021
+ container: popoverContainer,
1022
+ className: "text-left",
1023
+ "data-part": "delta-tooltip",
1024
+ children: /* @__PURE__ */ jsx(ArcTooltipRows, { rows: deltaExplanationRows })
1025
+ })] })]
1026
+ }),
1027
+ hasChart && /* @__PURE__ */ jsxs("div", {
1028
+ ref: treeRef,
1029
+ className: "relative w-full",
1030
+ "data-part": "tree-wrapper",
1031
+ children: [/* @__PURE__ */ jsx("ul", {
1032
+ role: "tree",
1033
+ "aria-label": labels.treeLabel(name),
1034
+ className: cn(rowGridClasses, "font-sans", sizes.rowText),
1035
+ style: gridStyle,
1036
+ "data-part": "tree",
1037
+ children: rows.map((row, index) => {
1038
+ const isSelected = row.key === selectedRow?.key;
1039
+ const isActive = row.key === activeRowKey;
1040
+ const { text: secondaryText, polarity } = secondary === "none" ? {
1041
+ text: "",
1042
+ polarity: "neutral"
1043
+ } : secondaryFor(row);
1044
+ return /* @__PURE__ */ jsxs("li", {
1045
+ ref: (node) => {
1046
+ if (node) rowRefs.current.set(row.key, node);
1047
+ else rowRefs.current.delete(row.key);
1048
+ },
1049
+ role: "treeitem",
1050
+ "aria-level": row.depth + 1,
1051
+ "aria-posinset": row.posInSet,
1052
+ "aria-setsize": row.setSize,
1053
+ "aria-expanded": row.hasChildren ? row.expanded : void 0,
1054
+ "aria-selected": rowsSelectable ? isSelected : void 0,
1055
+ tabIndex: isActive ? 0 : -1,
1056
+ onFocus: () => setActiveKey(row.key),
1057
+ onClick: rowsSelectable ? () => selectRow(isSelected ? null : row.key) : void 0,
1058
+ onKeyDown: (event) => handleRowKeyDown(event, row),
1059
+ className: cn(gridSpan, "grid grid-cols-subgrid items-center", treeRowClasses, rowsSelectable && "cursor-pointer"),
1060
+ "data-part": "row",
1061
+ "data-role": row.role,
1062
+ "data-depth": row.depth,
1063
+ "data-selected": isSelected ? "" : void 0,
1064
+ children: [
1065
+ /* @__PURE__ */ jsxs("span", {
1066
+ className: "flex min-w-0 items-center",
1067
+ "data-part": "row-label",
1068
+ children: [
1069
+ /* @__PURE__ */ jsx("span", {
1070
+ "aria-hidden": "true",
1071
+ onClick: row.hasChildren && canExpand ? (event) => {
1072
+ event.stopPropagation();
1073
+ toggleExpanded(row.key);
1074
+ } : void 0,
1075
+ className: cn("flex shrink-0 items-center justify-center self-stretch text-muted-foreground", row.hasChildren && canExpand && "cursor-pointer hover:text-foreground"),
1076
+ style: {
1077
+ marginInlineStart: row.depth * sizes.indent,
1078
+ width: sizes.twisty
1079
+ },
1080
+ "data-part": "twisty",
1081
+ children: row.hasChildren && /* @__PURE__ */ jsx(TwistyIcon, {
1082
+ open: row.expanded,
1083
+ className: "size-3"
1084
+ })
1085
+ }),
1086
+ /* @__PURE__ */ jsx("span", {
1087
+ className: cn("min-w-0 truncate", rowLabelClasses[row.role], isSelected && "font-semibold text-foreground"),
1088
+ children: row.label
1089
+ }),
1090
+ row.role === "other" && /* @__PURE__ */ jsxs("span", {
1091
+ className: "sr-only",
1092
+ children: [
1093
+ " (",
1094
+ labels.foldedItems(row.folded),
1095
+ ")"
1096
+ ]
1097
+ })
1098
+ ]
1099
+ }),
1100
+ /* @__PURE__ */ jsx("span", {
1101
+ ref: index === 0 ? plotCellRef : void 0,
1102
+ "aria-hidden": "true",
1103
+ className: "min-w-0 self-stretch",
1104
+ "data-part": "row-plot"
1105
+ }),
1106
+ /* @__PURE__ */ jsx("span", {
1107
+ className: cn("text-right tabular-nums", isSelected ? "font-semibold text-foreground" : "font-medium text-foreground"),
1108
+ "data-part": "row-value",
1109
+ children: formatters.formatValue(row.value)
1110
+ }),
1111
+ secondary !== "none" && /* @__PURE__ */ jsx("span", {
1112
+ className: cn("text-right tabular-nums", secondary === "delta" ? cn("font-medium", polarityTextClasses[polarity]) : "text-muted-foreground"),
1113
+ "data-part": "row-secondary",
1114
+ children: secondaryText
1115
+ })
1116
+ ]
1117
+ }, row.key);
1118
+ })
1119
+ }), plotBox.width > 0 && spec && /* @__PURE__ */ jsx(ArcVegaChart, {
1120
+ spec,
1121
+ options: embedOptions,
1122
+ datasets,
1123
+ signals,
1124
+ description: chartDescription,
1125
+ width: plotBox.width,
1126
+ height: plotHeight,
1127
+ onEmbed,
1128
+ onClick: rowsSelectable ? handlePlotClick : void 0,
1129
+ className: cn("absolute top-0 [&_svg]:block [&_.vega-bindings:empty]:hidden", rowsSelectable && "cursor-pointer"),
1130
+ style: {
1131
+ left: plotBox.left,
1132
+ width: plotBox.width,
1133
+ height: plotHeight
1134
+ },
1135
+ "data-part": "plot",
1136
+ fallback: /* @__PURE__ */ jsx(Skeleton, {
1137
+ variant: "rounded",
1138
+ className: "absolute top-0",
1139
+ style: {
1140
+ left: plotBox.left,
1141
+ width: plotBox.width,
1142
+ height: plotHeight
1143
+ }
1144
+ })
1145
+ })]
1146
+ }),
1147
+ showInsight && /* @__PURE__ */ jsxs("div", {
1148
+ className: cn("flex items-start gap-2 font-sans text-foreground", sizes.body),
1149
+ "data-part": "insight",
1150
+ children: [/* @__PURE__ */ jsx(AILoader, {
1151
+ size: "sm",
1152
+ state: "default",
1153
+ label: labels.insightMark,
1154
+ className: cn("shrink-0", size === "sm" ? "" : "mt-0.5")
1155
+ }), /* @__PURE__ */ jsx("p", {
1156
+ className: "min-w-0 flex-1 [&_strong]:font-semibold [&_strong]:text-foreground text-xs leading-[1.8]",
1157
+ children: insight
1158
+ })]
1159
+ })
1160
+ ]
1161
+ }),
1162
+ state === "refreshing" && /* @__PURE__ */ jsx("div", {
1163
+ "aria-hidden": "true",
1164
+ className: "absolute -inset-1 rounded-md bg-veil"
1165
+ })
1166
+ ]
1167
+ }),
1168
+ asOfValid && asOfDate && /* @__PURE__ */ jsx("p", {
1169
+ className: cn(captionClasses, "text-right"),
1170
+ "data-part": "as-of",
1171
+ children: /* @__PURE__ */ jsx("time", {
1172
+ dateTime: asOfDate.toISOString(),
1173
+ children: asOfText
1174
+ })
1175
+ }),
1176
+ /* @__PURE__ */ jsx("div", {
1177
+ ref: setPopoverContainer,
1178
+ className: "pointer-events-none absolute inset-0",
1179
+ "data-part": "popover-host"
1180
+ }),
1181
+ tooltip
1182
+ ]
1183
+ });
1184
+ });
1185
+ TreeCard.displayName = "TreeCard";
1186
+ //#endregion
1187
+ export { TreeCard };