@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,401 @@
1
+ import { formatDelta, formatNumber } from "../../lib/vega/formatters.js";
2
+ //#region src/components/TreeCard/TreeCard.model.ts
3
+ const DEFAULT_TREE_LABELS = {
4
+ total: "Total",
5
+ versusPrevious: "vs previous period",
6
+ increase: "Up",
7
+ decrease: "Down",
8
+ unchanged: "No change",
9
+ percentagePoints: " pp",
10
+ noData: "No data",
11
+ noMatch: "No matching data",
12
+ clearFilters: "Clear filters",
13
+ error: "Couldn't load this breakdown",
14
+ retry: "Try again",
15
+ stale: "Stale",
16
+ asOf: "As of",
17
+ other: "Other",
18
+ unallocated: "Unallocated",
19
+ expand: (label) => `Expand ${label}`,
20
+ collapse: (label) => `Collapse ${label}`,
21
+ expandAll: "Expand all",
22
+ collapseAll: "Collapse all",
23
+ tableItem: "Item",
24
+ tableValue: "Value",
25
+ tableShare: "Share of parent",
26
+ tableShareOfTotal: "Share of total",
27
+ tableLevel: "Level",
28
+ tableChange: "Change",
29
+ selected: "selected",
30
+ foldedItems: (count) => `${count} items`,
31
+ showTable: (name) => `Show the data behind ${name}`,
32
+ hideTable: (name) => `Hide the data behind ${name}`,
33
+ about: (name) => `About ${name}`,
34
+ loading: (name) => `Loading ${name}`,
35
+ tableCaption: (name) => `Data behind the ${name} breakdown`,
36
+ treeLabel: (name) => `Breakdown of ${name}`,
37
+ deltaComparison: (name) => `${name} comparison`,
38
+ deltaExplanation: ({ label, valueText, changeText, secondaryText, renderedMode, direction }) => {
39
+ const rows = [[label, valueText], [direction === "up" ? "Increase" : direction === "down" ? "Decrease" : "No change", changeText]];
40
+ if (secondaryText) rows.push([renderedMode === "percent" ? "Change" : "Change %", secondaryText]);
41
+ return rows;
42
+ },
43
+ comparisonSentence: ({ word, primary, secondary, previousText }) => primary ? `${word} ${primary}${secondary ? ` (${secondary})` : ""} from ${previousText}.` : `${word} from ${previousText}.`,
44
+ moverSentence: ({ label, changeText }) => `${label} moved furthest, ${changeText}.`,
45
+ shapeSummary: ({ nodes, levels }) => `${nodes} ${nodes === 1 ? "part" : "parts"} across ${levels} ${levels === 1 ? "level" : "levels"}.`,
46
+ partsSummary: (count, list) => `${count === 1 ? "One part" : `${count} parts`}: ${list}.`,
47
+ foldedSummary: (count) => `${count} smaller parts folded into Other.`,
48
+ residualSummary: "Some parents report more than their parts account for; the remainder is shown as Unallocated.",
49
+ chartDescription: ({ name, count, levels, total, top, topValue }) => `Indented bar breakdown of ${name}: ${count} ${count === 1 ? "row" : "rows"} across ${levels} ${levels === 1 ? "level" : "levels"}, total ${total}, largest part ${top} ${topValue}. Bars share one zero-based scale, so each length reads as its share of the total.`,
50
+ insightMark: "Insight"
51
+ };
52
+ function resolveTreeLabels(overrides) {
53
+ return overrides ? {
54
+ ...DEFAULT_TREE_LABELS,
55
+ ...overrides
56
+ } : DEFAULT_TREE_LABELS;
57
+ }
58
+ /**
59
+ * The state machine. Error wins; loading over existing nodes is a refresh (the
60
+ * previous render stays under the veil); no values is `no-match` when the
61
+ * consumer's filter caused it and `empty` otherwise.
62
+ */
63
+ function deriveTreeState({ status, hasValue, hasData, filtered }) {
64
+ if (status === "error") return "error";
65
+ if (status === "loading") return hasData ? "refreshing" : "loading";
66
+ if (hasValue) return "ready";
67
+ return filtered ? "no-match" : "empty";
68
+ }
69
+ const isFinite = (value) => typeof value === "number" && Number.isFinite(value);
70
+ /** Local key segment of the row a parent's long tail folds into. */
71
+ const TREE_OTHER_KEY = "__other__";
72
+ /** Local key segment of the row carrying what a parent's children do not account for. */
73
+ const TREE_RESIDUAL_KEY = "__residual__";
74
+ /** Default share of a parent's own reading below which a mismatch with its children is rounding, not a gap. */
75
+ const DEFAULT_RESIDUAL_TOLERANCE = 5e-4;
76
+ function sumFinite(values) {
77
+ let total = 0;
78
+ let any = false;
79
+ for (const value of values) if (isFinite(value)) {
80
+ total += value;
81
+ any = true;
82
+ }
83
+ return any ? total : null;
84
+ }
85
+ /**
86
+ * Walks the source nodes into work nodes with keys unique across the whole
87
+ * tree: a node's key is its parent's key, a slash and its own key (defaulting
88
+ * to its label), and a collision anywhere is suffixed — so two siblings named
89
+ * the same never share a band, and neither do two cousins.
90
+ */
91
+ function walk(nodes, parentKey, depth, seen) {
92
+ return nodes.map((node) => {
93
+ const local = node.key ?? node.label;
94
+ const base = parentKey == null ? local : `${parentKey}/${local}`;
95
+ const repeats = seen.get(base) ?? 0;
96
+ seen.set(base, repeats + 1);
97
+ const key = repeats === 0 ? base : `${base}·${repeats + 1}`;
98
+ return {
99
+ key,
100
+ label: node.label,
101
+ depth,
102
+ ownValue: isFinite(node.value) ? node.value : null,
103
+ ownPrevious: isFinite(node.previous) ? node.previous : null,
104
+ value: null,
105
+ previous: null,
106
+ children: node.children?.length ? walk(node.children, key, depth + 1, seen) : [],
107
+ role: "node",
108
+ folded: 0
109
+ };
110
+ });
111
+ }
112
+ /** Bottom-up rollup over the full tree, before any pruning, so a capped parent still totals everything beneath it. */
113
+ function rollupTree(nodes, rollup) {
114
+ for (const node of nodes) {
115
+ rollupTree(node.children, rollup);
116
+ if (node.children.length === 0) {
117
+ node.value = node.ownValue;
118
+ node.previous = node.ownPrevious;
119
+ continue;
120
+ }
121
+ const childValue = sumFinite(node.children.map((child) => child.value));
122
+ const childPrevious = sumFinite(node.children.map((child) => child.previous));
123
+ node.value = rollup === "own" ? node.ownValue ?? childValue : childValue ?? node.ownValue;
124
+ node.previous = rollup === "own" ? node.ownPrevious ?? childPrevious : childPrevious ?? node.ownPrevious;
125
+ }
126
+ }
127
+ function prune(nodes, maxDepth) {
128
+ for (const node of nodes) if (node.depth >= maxDepth) node.children = [];
129
+ else prune(node.children, maxDepth);
130
+ }
131
+ function sortSiblings(nodes, sort) {
132
+ if (sort === "none") return nodes;
133
+ const direction = sort === "asc" ? 1 : -1;
134
+ return nodes.map((node, index) => ({
135
+ node,
136
+ index
137
+ })).sort((a, b) => {
138
+ const av = a.node.value;
139
+ const bv = b.node.value;
140
+ if (av == null && bv == null) return a.index - b.index;
141
+ if (av == null) return 1;
142
+ if (bv == null) return -1;
143
+ return av === bv ? a.index - b.index : (av - bv) * direction;
144
+ }).map(({ node }) => node);
145
+ }
146
+ /** Sorts, folds the tail and appends the residual, recursively. Reports what it changed so the card can say so. */
147
+ function shape(nodes, parentKey, parent, options) {
148
+ const { rollup, sort, maxChildren, showResidual, residualTolerance, otherLabel, residualLabel } = options;
149
+ let folded = 0;
150
+ let residual = false;
151
+ let ordered = sortSiblings(nodes, sort);
152
+ if (maxChildren != null && maxChildren >= 1 && ordered.length > maxChildren) {
153
+ const keep = ordered.slice(0, Math.max(0, maxChildren - 1));
154
+ const rest = ordered.slice(Math.max(0, maxChildren - 1));
155
+ ordered = [...keep, {
156
+ key: parentKey == null ? TREE_OTHER_KEY : `${parentKey}/${TREE_OTHER_KEY}`,
157
+ label: otherLabel,
158
+ depth: rest[0].depth,
159
+ ownValue: null,
160
+ ownPrevious: null,
161
+ value: sumFinite(rest.map((node) => node.value)),
162
+ previous: sumFinite(rest.map((node) => node.previous)),
163
+ children: [],
164
+ role: "other",
165
+ folded: rest.length
166
+ }];
167
+ folded += rest.length;
168
+ }
169
+ for (const node of ordered) {
170
+ if (node.children.length === 0) continue;
171
+ const inner = shape(node.children, node.key, node, options);
172
+ node.children = inner.nodes;
173
+ folded += inner.folded;
174
+ residual ||= inner.residual;
175
+ }
176
+ if (showResidual && rollup === "own" && parent != null && isFinite(parent.ownValue) && ordered.length > 0 && !ordered.some((node) => node.role === "residual")) {
177
+ const childTotal = sumFinite(ordered.map((node) => node.value));
178
+ if (isFinite(childTotal)) {
179
+ const gap = parent.ownValue - childTotal;
180
+ const floor = Math.abs(parent.ownValue) * residualTolerance;
181
+ if (Math.abs(gap) > floor && gap !== 0) {
182
+ const childPrevious = sumFinite(ordered.map((node) => node.previous));
183
+ ordered = [...ordered, {
184
+ key: `${parent.key}/${TREE_RESIDUAL_KEY}`,
185
+ label: residualLabel,
186
+ depth: ordered[0].depth,
187
+ ownValue: gap,
188
+ ownPrevious: null,
189
+ value: gap,
190
+ previous: isFinite(parent.ownPrevious) && isFinite(childPrevious) ? parent.ownPrevious - childPrevious : null,
191
+ children: [],
192
+ role: "residual",
193
+ folded: 0
194
+ }];
195
+ residual = true;
196
+ }
197
+ }
198
+ }
199
+ return {
200
+ nodes: ordered,
201
+ folded,
202
+ residual
203
+ };
204
+ }
205
+ /**
206
+ * Normalises the hierarchy into the rows the plot, the tree and the table all
207
+ * read from: unique path keys, a bottom-up rollup, the depth cap, each
208
+ * parent's long tail folded into `Other`, the unallocated remainder made
209
+ * explicit, shares against the parent and against the total, and the ARIA
210
+ * bookkeeping every `treeitem` needs. Returns `null` when there is nothing to
211
+ * speak of.
212
+ */
213
+ function buildTree(nodes, options) {
214
+ if (!nodes) return null;
215
+ const { rollup = "sum", sort = "none", maxChildren, maxDepth, expanded = null, expandDepth = 1, showResidual = true, residualTolerance = DEFAULT_RESIDUAL_TOLERANCE, otherLabel, residualLabel } = options;
216
+ const tree = walk(nodes, null, 0, /* @__PURE__ */ new Map());
217
+ rollupTree(tree, rollup);
218
+ if (maxDepth != null && maxDepth >= 0) prune(tree, maxDepth);
219
+ const shaped = shape(tree, null, null, {
220
+ sort,
221
+ maxChildren,
222
+ showResidual,
223
+ residualTolerance,
224
+ otherLabel,
225
+ residualLabel,
226
+ rollup
227
+ });
228
+ const total = sumFinite(shaped.nodes.map((node) => node.value));
229
+ const previousTotal = sumFinite(shaped.nodes.map((node) => node.previous));
230
+ const partsDepth = shaped.nodes.length >= 2 ? 0 : 1;
231
+ const allRows = [];
232
+ const rows = [];
233
+ const expandedKeys = [];
234
+ let levels = 0;
235
+ let hasPrevious = false;
236
+ let expandable = false;
237
+ const isOpen = (node) => node.children.length > 0 && (expanded ? expanded.has(node.key) : node.depth < expandDepth);
238
+ const visit = (siblings, parent, parentVisible) => {
239
+ siblings.forEach((node, index) => {
240
+ const parentValue = parent ? parent.value : total;
241
+ const open = isOpen(node);
242
+ const row = {
243
+ key: node.key,
244
+ label: node.label,
245
+ depth: node.depth,
246
+ value: node.value,
247
+ previous: node.previous,
248
+ parentKey: parent?.key ?? null,
249
+ parentValue,
250
+ share: isFinite(node.value) && isFinite(parentValue) && parentValue !== 0 ? node.value / parentValue : null,
251
+ shareOfTotal: isFinite(node.value) && isFinite(total) && total !== 0 ? node.value / total : null,
252
+ hasChildren: node.children.length > 0,
253
+ expanded: open,
254
+ posInSet: index + 1,
255
+ setSize: siblings.length,
256
+ role: node.role,
257
+ folded: node.folded
258
+ };
259
+ allRows.push(row);
260
+ if (parentVisible) rows.push(row);
261
+ if (node.children.length > 0) expandable = true;
262
+ if (open) expandedKeys.push(node.key);
263
+ if (isFinite(node.previous)) hasPrevious = true;
264
+ levels = Math.max(levels, node.depth + 1);
265
+ if (node.children.length > 0) visit(node.children, node, parentVisible && open);
266
+ });
267
+ };
268
+ visit(shaped.nodes, null, true);
269
+ if (allRows.length === 0) return null;
270
+ const parts = allRows.filter((row) => row.depth === partsDepth);
271
+ let largest = null;
272
+ let topMover = null;
273
+ for (const row of parts) {
274
+ if (isFinite(row.value) && (!largest || row.value > largest.value)) largest = row;
275
+ if (isFinite(row.value) && isFinite(row.previous)) {
276
+ if (Math.abs(row.value - row.previous) > (topMover ? Math.abs(topMover.value - topMover.previous) : -1)) topMover = row;
277
+ }
278
+ }
279
+ return {
280
+ rows,
281
+ allRows,
282
+ expandedKeys,
283
+ total,
284
+ previousTotal,
285
+ count: rows.filter((row) => row.value != null).length,
286
+ nodeCount: allRows.length,
287
+ levels,
288
+ partsDepth,
289
+ largest,
290
+ topMover,
291
+ hasNegative: rows.some((row) => isFinite(row.value) && row.value < 0),
292
+ hasPrevious,
293
+ hasResidual: shaped.residual,
294
+ folded: shaped.folded,
295
+ expandable
296
+ };
297
+ }
298
+ /** A reading against its comparison period, with polarity resolved through `goodDirection`. `null` unless both are finite. */
299
+ function deriveTreeDelta(value, previous, { deltaMode, goodDirection, format, locale, percentagePoints }) {
300
+ if (!isFinite(value) || !isFinite(previous)) return null;
301
+ const delta = formatDelta(value, previous, {
302
+ mode: deltaMode,
303
+ format,
304
+ locale,
305
+ pointsSuffix: percentagePoints
306
+ });
307
+ const other = formatDelta(value, previous, {
308
+ mode: delta.renderedMode === "percent" ? "absolute" : "percent",
309
+ format,
310
+ locale,
311
+ pointsSuffix: percentagePoints
312
+ });
313
+ let polarity = "neutral";
314
+ if (delta.direction !== "flat" && goodDirection !== "none") polarity = delta.direction === "up" === (goodDirection === "up") ? "positive" : "negative";
315
+ return {
316
+ direction: delta.direction,
317
+ polarity,
318
+ text: delta.text,
319
+ renderedMode: delta.renderedMode,
320
+ secondaryText: other.renderedMode === delta.renderedMode ? null : other.text
321
+ };
322
+ }
323
+ /** The formatters every text surface shares — headline, rows, delta, table, tooltip, descriptions. */
324
+ function createTreeFormatters({ format, locale, formatValue, deltaMode = "percent", shareFormat = "percent:0", percentagePoints = DEFAULT_TREE_LABELS.percentagePoints }) {
325
+ return {
326
+ formatValue: (value) => isFinite(value) ? formatValue ? formatValue(value) : formatNumber(value, format, locale) : "—",
327
+ formatShare: (share) => isFinite(share) ? formatNumber(share, shareFormat, locale) : "—",
328
+ formatChange: (value, previous) => isFinite(value) && isFinite(previous) ? formatDelta(value, previous, {
329
+ mode: deltaMode,
330
+ format,
331
+ locale,
332
+ pointsSuffix: percentagePoints
333
+ }).text : "—"
334
+ };
335
+ }
336
+ const stripSign = (text) => text.replace(/^[+\-−]/, "");
337
+ /**
338
+ * The card's text equivalent (Amy Cesal's alt-text formula: what it is, what
339
+ * it shows, why it is here, when it was true) — the figure's accessible
340
+ * description.
341
+ *
342
+ * It names the shape, the total's direct parts with their values and shares,
343
+ * and the branch that moved furthest — rather than enumerating every visible
344
+ * row the way a flat card must. The rows here are real `treeitem`s carrying
345
+ * their own label, value, share and `aria-level`, so a screen reader already
346
+ * reaches each one in place; repeating all of them in one sentence would bury
347
+ * the answer this description exists to give.
348
+ */
349
+ function buildTreeDescription({ name, state, headline, summary, focus, delta, formatters, asOfText, labels }) {
350
+ if (state === "loading") return `${labels.loading(name)}.`;
351
+ if (state === "error") return `${name}: ${labels.error}.`;
352
+ if (state === "empty") return `${name}: ${labels.noData}.`;
353
+ if (state === "no-match") return `${name}: ${labels.noMatch}.`;
354
+ const sentences = [];
355
+ if (headline === "focus" && focus) sentences.push(`${name}: ${focus.label} ${formatters.formatValue(focus.value)}.`);
356
+ else if (headline !== "none" && summary) sentences.push(`${name}: ${labels.total} ${formatters.formatValue(summary.total)}.`);
357
+ else sentences.push(`${name}.`);
358
+ if (delta) {
359
+ const word = delta.direction === "up" ? labels.increase : delta.direction === "down" ? labels.decrease : labels.unchanged;
360
+ const flat = delta.direction === "flat";
361
+ const previous = headline === "focus" && focus ? focus.previous : summary?.previousTotal;
362
+ sentences.push(labels.comparisonSentence({
363
+ word,
364
+ primary: flat ? "" : stripSign(delta.text),
365
+ secondary: flat || !delta.secondaryText ? null : stripSign(delta.secondaryText),
366
+ previousText: formatters.formatValue(previous)
367
+ }));
368
+ }
369
+ if (summary) {
370
+ sentences.push(labels.shapeSummary({
371
+ nodes: summary.nodeCount,
372
+ levels: summary.levels
373
+ }));
374
+ const parts = summary.allRows.filter((row) => row.depth === summary.partsDepth);
375
+ if (parts.length > 0) {
376
+ const list = parts.map((row) => `${row.label} ${formatters.formatValue(row.value)} (${formatters.formatShare(row.shareOfTotal)})`).join(", ");
377
+ sentences.push(labels.partsSummary(parts.length, list));
378
+ }
379
+ if (summary.topMover && summary.topMover.key !== focus?.key) sentences.push(labels.moverSentence({
380
+ label: summary.topMover.label,
381
+ changeText: formatters.formatChange(summary.topMover.value, summary.topMover.previous)
382
+ }));
383
+ if (summary.folded > 0) sentences.push(labels.foldedSummary(summary.folded));
384
+ if (summary.hasResidual) sentences.push(labels.residualSummary);
385
+ }
386
+ if (asOfText) sentences.push(`${labels.asOf} ${asOfText}.`);
387
+ return sentences.join(" ");
388
+ }
389
+ /** The plot's own `description` — the `aria-label` Vega stamps on the `graphics-document`. */
390
+ function buildTreeChartDescription({ name, summary, formatters, labels }) {
391
+ return labels.chartDescription({
392
+ name,
393
+ count: summary.rows.length,
394
+ levels: summary.levels,
395
+ total: formatters.formatValue(summary.total),
396
+ top: summary.largest?.label ?? "—",
397
+ topValue: formatters.formatValue(summary.largest?.value)
398
+ });
399
+ }
400
+ //#endregion
401
+ export { DEFAULT_RESIDUAL_TOLERANCE, DEFAULT_TREE_LABELS, TREE_OTHER_KEY, TREE_RESIDUAL_KEY, buildTree, buildTreeChartDescription, buildTreeDescription, createTreeFormatters, deriveTreeDelta, deriveTreeState, resolveTreeLabels };
@@ -0,0 +1,4 @@
1
+ import { BuildTreeDescriptionInput, BuildTreeOptions, DEFAULT_RESIDUAL_TOLERANCE, DEFAULT_TREE_LABELS, DeriveTreeDeltaOptions, DeriveTreeStateInput, TREE_OTHER_KEY, TREE_RESIDUAL_KEY, TreeCardLabels, TreeCardSize, TreeCardState, TreeCardStatus, TreeColorBy, TreeDelta, TreeDeltaMode, TreeGoodDirection, TreeHeadline, TreeNode, TreeNodeRole, TreePolarity, TreeRollup, TreeRow, TreeRowSecondary, TreeSort, TreeSummary, TreeTextFormatters, buildTree, buildTreeChartDescription, buildTreeDescription, createTreeFormatters, deriveTreeDelta, deriveTreeState, resolveTreeLabels } from "./TreeCard.model.js";
2
+ import { TreeCard, TreeCardProps } from "./TreeCard.js";
3
+ import { TREE_DEPTH_STEPS, TREE_HOVER_PARAM, TREE_MAX_BAR_HEIGHT, TREE_TABLE_DATASET, TREE_TOTAL_PARAM, TREE_ZERO_DATASET, TreeBarsSpecOptions, buildTreeBarsSpec, treeBarColor } from "./TreeCard.chart.js";
4
+ export { type BuildTreeDescriptionInput, type BuildTreeOptions, DEFAULT_RESIDUAL_TOLERANCE, DEFAULT_TREE_LABELS, type DeriveTreeDeltaOptions, type DeriveTreeStateInput, TREE_DEPTH_STEPS, TREE_HOVER_PARAM, TREE_MAX_BAR_HEIGHT, TREE_OTHER_KEY, TREE_RESIDUAL_KEY, TREE_TABLE_DATASET, TREE_TOTAL_PARAM, TREE_ZERO_DATASET, type TreeBarsSpecOptions, TreeCard, type TreeCardLabels, type TreeCardProps, type TreeCardSize, type TreeCardState, type TreeCardStatus, type TreeColorBy, type TreeDelta, type TreeDeltaMode, type TreeGoodDirection, type TreeHeadline, type TreeNode, type TreeNodeRole, type TreePolarity, type TreeRollup, type TreeRow, type TreeRowSecondary, type TreeSort, type TreeSummary, type TreeTextFormatters, buildTree, buildTreeBarsSpec, buildTreeChartDescription, buildTreeDescription, createTreeFormatters, deriveTreeDelta, deriveTreeState, resolveTreeLabels, treeBarColor };
@@ -0,0 +1,4 @@
1
+ import { TREE_DEPTH_STEPS, TREE_HOVER_PARAM, TREE_MAX_BAR_HEIGHT, TREE_TABLE_DATASET, TREE_TOTAL_PARAM, TREE_ZERO_DATASET, buildTreeBarsSpec, treeBarColor } from "./TreeCard.chart.js";
2
+ import { DEFAULT_RESIDUAL_TOLERANCE, DEFAULT_TREE_LABELS, TREE_OTHER_KEY, TREE_RESIDUAL_KEY, buildTree, buildTreeChartDescription, buildTreeDescription, createTreeFormatters, deriveTreeDelta, deriveTreeState, resolveTreeLabels } from "./TreeCard.model.js";
3
+ import { TreeCard } from "./TreeCard.js";
4
+ export { DEFAULT_RESIDUAL_TOLERANCE, DEFAULT_TREE_LABELS, TREE_DEPTH_STEPS, TREE_HOVER_PARAM, TREE_MAX_BAR_HEIGHT, TREE_OTHER_KEY, TREE_RESIDUAL_KEY, TREE_TABLE_DATASET, TREE_TOTAL_PARAM, TREE_ZERO_DATASET, TreeCard, buildTree, buildTreeBarsSpec, buildTreeChartDescription, buildTreeDescription, createTreeFormatters, deriveTreeDelta, deriveTreeState, resolveTreeLabels, treeBarColor };
@@ -0,0 +1,75 @@
1
+ import { ChartTokens } from "../../lib/vega/tokens.js";
2
+ import { TrendMark, TrendScale, TrendStack, TrendXType } from "./TrendCard.model.js";
3
+ import { TopLevelSpec } from "vega-lite";
4
+ //#region src/components/TrendCard/TrendCard.chart.d.ts
5
+ /** Long-form readings: `{ x, s, y, raw, i, c, n }` per `TrendRow`, for the visible series only. */
6
+ declare const TREND_TABLE_DATASET = "table";
7
+ /** One row per period, `{ x }` — the crosshair, filtered by the hover selection. */
8
+ declare const TREND_PERIODS_DATASET = "periods";
9
+ /** Zero or one row `{ value }` — the target reference rule. */
10
+ declare const TREND_REFERENCE_DATASET = "reference";
11
+ /** Resolved bands `{ start, end, label? }` — the washed period annotations. */
12
+ declare const TREND_BANDS_DATASET = "bands";
13
+ /** One row per visible series — its last finite reading, for the endpoint labels. */
14
+ declare const TREND_LATEST_DATASET = "latest";
15
+ /** The shared-x hover selection: projected onto `x`, so every series lights up at the pointed period. */
16
+ declare const TREND_HOVER_PARAM = "hover";
17
+ /** Responsive x tick values, recomputed by Vega when the domain or the width changes. */
18
+ declare const TREND_X_TICKS_PARAM = "trend_x_ticks";
19
+ interface TrendPlotSpecOptions {
20
+ mark: TrendMark;
21
+ /** `auto` lets a line crop to its data range; `zero` includes zero. Areas and bars always include zero. */
22
+ scale: TrendScale;
23
+ xType: TrendXType;
24
+ /** How several area or bar series sit together. Lines never stack. */
25
+ stack: TrendStack;
26
+ /** Plot height in pixels, axes included. */
27
+ height: number;
28
+ /** Draw both axes. `false` shrinks the plot toward a sparkline; the table still carries every value. */
29
+ axes: boolean;
30
+ /** Number of y ticks — the caller scales it to the plot's height. */
31
+ yTicks: number;
32
+ /** Draw the target reference rule (the `reference` dataset carries the value). */
33
+ reference: boolean;
34
+ /** Wash the `bands` dataset behind the marks. */
35
+ bands: boolean;
36
+ /** Wire the shared-x hover selection, the crosshair and the tooltip channel. */
37
+ tooltip: boolean;
38
+ /** Label each series' last reading in a right-hand gutter, so a reader need not travel to the legend. */
39
+ labelSeries: boolean;
40
+ /**
41
+ * Dim every series but the focused one. Only meaningful while colour is doing
42
+ * emphasis — with identity slots or a ramp, dimming would fight the hue.
43
+ * The focused rows carry `f: true`.
44
+ */
45
+ emphasize: boolean;
46
+ /** Tooltip titles. */
47
+ xTitle: string;
48
+ yTitle: string;
49
+ /** Localised title for the target reference rule's hover value. */
50
+ targetTitle?: string;
51
+ /** `arcNumber` grammar for the y axis and the tooltip — the PLOTTED units (index points or a share when the card transforms them). */
52
+ plotFormat: string;
53
+ /** `arcTime` grammar for temporal x in tooltips (`day@en-US@Asia/Bangkok`). */
54
+ timeFormat: string;
55
+ /** `arcTime` grammar for x-axis tick labels — the axis-length form (`day-short@en-US@Asia/Bangkok`). */
56
+ axisTimeFormat: string;
57
+ locale?: string;
58
+ timeZone?: string;
59
+ }
60
+ /**
61
+ * Builds the plot: one mark per series on a SHARED y scale (never a second
62
+ * axis — series in different units are indexed to a common base instead, which
63
+ * the card does before it gets here), quiet labelled axes, an optional dotted
64
+ * target rule, washed period bands behind everything, and the documented
65
+ * multi-series hover: a transparent `point` layer carrying a `nearest`
66
+ * selection projected onto `x`, a crosshair rule filtered by that selection,
67
+ * and dots on every series at the pointed period.
68
+ *
69
+ * Gaps stay gaps — a `null` reading breaks the path rather than dropping it to
70
+ * zero. Lines may crop (`scale: "auto"`); areas and bars keep a zero baseline
71
+ * whatever `scale` says, because proportional ink is not negotiable.
72
+ */
73
+ declare function buildTrendPlotSpec(options: TrendPlotSpecOptions, tokens: ChartTokens): TopLevelSpec;
74
+ //#endregion
75
+ export { TREND_BANDS_DATASET, TREND_HOVER_PARAM, TREND_LATEST_DATASET, TREND_PERIODS_DATASET, TREND_REFERENCE_DATASET, TREND_TABLE_DATASET, TREND_X_TICKS_PARAM, TrendPlotSpecOptions, buildTrendPlotSpec };