@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,581 @@
1
+ import { formatDate, formatDateRange, formatDelta, formatNumber } from "../../lib/vega/formatters.js";
2
+ import { zonedMidnight, zonedParts } from "../../lib/vega/time.js";
3
+ //#region src/components/TargetCard/TargetCard.model.ts
4
+ const DEFAULT_TARGET_LABELS = {
5
+ ofTarget: "of target",
6
+ pairSeparator: "/",
7
+ readingLine: (value, target) => `${value} of ${target}`,
8
+ attainmentLine: (percent, target) => `${percent} of ${target}`,
9
+ target: "Target",
10
+ reached: "Reached",
11
+ remaining: "Remaining",
12
+ overBy: "Over by",
13
+ expectedByNow: "Expected by now",
14
+ aheadOfPace: (direction) => direction === "down" ? "under pace" : "ahead of pace",
15
+ behindPace: (direction) => direction === "down" ? "over pace" : "behind pace",
16
+ onPace: "on pace",
17
+ vsTarget: "vs target",
18
+ aboveTarget: "Above target",
19
+ belowTarget: "Below target",
20
+ onTarget: "On target",
21
+ projected: "Projected",
22
+ period: "Period",
23
+ week: (weekNumber) => `Week ${weekNumber}`,
24
+ quarter: (quarter, year) => `Q${quarter} ${year}`,
25
+ elapsed: (percent) => `${percent} elapsed`,
26
+ elapsedRow: "Elapsed",
27
+ daysRow: "Days elapsed",
28
+ days: (elapsed, total) => `${elapsed} of ${total}`,
29
+ trackNames: {
30
+ achieved: "Target met",
31
+ "on-track": "On track",
32
+ "at-risk": "At risk",
33
+ "off-track": "Off track",
34
+ missed: "Target missed"
35
+ },
36
+ trackRow: "Status",
37
+ increase: "Up",
38
+ decrease: "Down",
39
+ unchanged: "No change",
40
+ percentagePoints: " pp",
41
+ noData: "No data",
42
+ noMatch: "No matching data",
43
+ clearFilters: "Clear filters",
44
+ error: "Couldn't load this target",
45
+ retry: "Try again",
46
+ noTarget: "No target set",
47
+ stale: "Stale",
48
+ asOf: "As of",
49
+ tableItem: "Item",
50
+ tableValue: "Value",
51
+ changePercent: "Change %",
52
+ changeAmount: "Change",
53
+ markNames: {
54
+ actual: "Reached",
55
+ pace: "Pace",
56
+ target: "Target",
57
+ projection: "Projected"
58
+ },
59
+ showTable: (name) => `Show the details behind ${name}`,
60
+ hideTable: (name) => `Hide the details behind ${name}`,
61
+ about: (name) => `About ${name}`,
62
+ loading: (name) => `Loading ${name}`,
63
+ tableCaption: (name) => `Details of ${name} against its target`,
64
+ progressGroup: (name) => `${name} against its target`,
65
+ trackGroup: (name) => `${name} status`,
66
+ insightMark: "Insight",
67
+ explanation: ({ kind, direction, valueText, percentText, targetText, gapKind, gapText, expectedText, expectedPercentText, delta, paceStatus }) => {
68
+ const rows = [[DEFAULT_TARGET_LABELS.reached, `${valueText} (${percentText})`]];
69
+ if (kind === "pace" && expectedText) {
70
+ rows.push([DEFAULT_TARGET_LABELS.expectedByNow, expectedPercentText ? `${expectedText} (${expectedPercentText})` : expectedText]);
71
+ const word = paceStatus === "ahead" ? DEFAULT_TARGET_LABELS.aheadOfPace(direction) : paceStatus === "behind" ? DEFAULT_TARGET_LABELS.behindPace(direction) : DEFAULT_TARGET_LABELS.onPace;
72
+ rows.push([word.charAt(0).toUpperCase() + word.slice(1), delta.text]);
73
+ } else {
74
+ rows.push([DEFAULT_TARGET_LABELS.target, targetText]);
75
+ rows.push([gapKind === "over" ? DEFAULT_TARGET_LABELS.overBy : DEFAULT_TARGET_LABELS.remaining, gapText]);
76
+ rows.push([delta.direction === "up" ? DEFAULT_TARGET_LABELS.aboveTarget : delta.direction === "down" ? DEFAULT_TARGET_LABELS.belowTarget : DEFAULT_TARGET_LABELS.onTarget, delta.text]);
77
+ }
78
+ if (delta.secondaryText) rows.push([delta.renderedMode === "percent" ? DEFAULT_TARGET_LABELS.changeAmount : DEFAULT_TARGET_LABELS.changePercent, delta.secondaryText]);
79
+ return rows;
80
+ },
81
+ attainmentSentence: ({ name, percent, value, target }) => `${name}: ${percent} of target (${value} of ${target}).`,
82
+ gapSentence: ({ kind, amount }) => kind === "over" ? `Over by ${amount}.` : `Remaining ${amount}.`,
83
+ paceSentence: ({ word, primary, secondary, expectedPercent, elapsedPercent }) => {
84
+ const capitalised = word.charAt(0).toUpperCase() + word.slice(1);
85
+ const context = elapsedPercent ? `${expectedPercent} expected by now, ${elapsedPercent} of the period elapsed` : `${expectedPercent} expected by now`;
86
+ return primary ? `${capitalised} by ${primary}${secondary ? ` (${secondary})` : ""}: ${context}.` : `${capitalised}: ${context}.`;
87
+ },
88
+ projectionSentence: ({ value, percent }) => `Projected ${value} (${percent} of target).`,
89
+ periodSentence: ({ name, range }) => name === range ? `Period ${range}.` : `Period ${name} (${range}).`,
90
+ chartDescription: ({ name, value, percent, target, expected, expectedPercent, projected, projectedPercent }) => `Bullet graph of ${name}: reached ${value}, ${percent} of the ${target} target.` + (expected ? ` Pace marker at ${expected}${expectedPercent ? ` (${expectedPercent})` : ""}.` : "") + (projected ? ` Projected ${projected}${projectedPercent ? ` (${projectedPercent})` : ""}.` : "")
91
+ };
92
+ function resolveTargetLabels(overrides) {
93
+ return overrides ? {
94
+ ...DEFAULT_TARGET_LABELS,
95
+ ...overrides
96
+ } : DEFAULT_TARGET_LABELS;
97
+ }
98
+ /**
99
+ * The state machine. Error wins; loading over existing data is a refresh (the
100
+ * previous render stays under the veil); a missing reading is `no-match` when
101
+ * the consumer's filter caused it and `empty` otherwise. A reading without a
102
+ * target is `ready` — the card then shows the reading and says no target is
103
+ * set (`data-insufficient`).
104
+ */
105
+ function deriveTargetState({ status, hasValue, hasData, filtered }) {
106
+ if (status === "error") return "error";
107
+ if (status === "loading") return hasData ? "refreshing" : "loading";
108
+ if (hasValue) return "ready";
109
+ return filtered ? "no-match" : "empty";
110
+ }
111
+ const isFinite = (value) => typeof value === "number" && Number.isFinite(value);
112
+ const DAY_MS = 864e5;
113
+ const DATE_ONLY = /^(\d{4})-(\d{2})-(\d{2})$/;
114
+ function toInstant(value) {
115
+ return value instanceof Date ? value.getTime() : new Date(value).getTime();
116
+ }
117
+ /**
118
+ * The period as instants in the card's time zone. A date-only bound is a
119
+ * whole calendar day: `start` opens at its midnight and `end` closes at the
120
+ * midnight after it, so `"2026-09-01"` – `"2026-09-30"` is all of September
121
+ * (30 days, not 29). Any other form is the exact instant. `null` when a bound
122
+ * cannot be parsed or the period has no length.
123
+ */
124
+ function resolvePeriod(period, timeZone) {
125
+ if (!period) return null;
126
+ const resolve = (bound, inclusiveEnd) => {
127
+ if (typeof bound === "string") {
128
+ const match = DATE_ONLY.exec(bound);
129
+ if (match) {
130
+ const [, year, month, day] = match;
131
+ return zonedMidnight(Number(year), Number(month) - 1, Number(day) + (inclusiveEnd ? 1 : 0), timeZone);
132
+ }
133
+ }
134
+ const time = toInstant(bound);
135
+ return Number.isNaN(time) ? null : time;
136
+ };
137
+ const start = resolve(period.start, false);
138
+ const end = resolve(period.end, true);
139
+ if (start == null || end == null || end <= start) return null;
140
+ const dateOnly = typeof period.start === "string" && DATE_ONLY.test(period.start) && typeof period.end === "string" && DATE_ONLY.test(period.end);
141
+ return {
142
+ start,
143
+ end,
144
+ label: period.label,
145
+ dateOnly
146
+ };
147
+ }
148
+ /**
149
+ * How far the period has run at `asOf`. The fraction is clamped for the
150
+ * plan's expectation (nothing is expected before the start, everything by the
151
+ * end); `rawFraction` keeps the overshoot so a reading after the end knows
152
+ * the period is over. `null` when `asOf` is missing or unparseable.
153
+ */
154
+ function deriveElapsed(bounds, asOf, locale) {
155
+ if (!bounds || asOf == null) return null;
156
+ const now = toInstant(asOf);
157
+ if (Number.isNaN(now)) return null;
158
+ const rawFraction = (now - bounds.start) / (bounds.end - bounds.start);
159
+ const fraction = Math.min(1, Math.max(0, rawFraction));
160
+ return {
161
+ fraction,
162
+ rawFraction,
163
+ started: rawFraction >= 0,
164
+ ended: rawFraction >= 1,
165
+ percentText: formatNumber(fraction, "percent:0", locale)
166
+ };
167
+ }
168
+ /** The whole days a date-only period spans. */
169
+ function periodDays(bounds) {
170
+ return Math.round((bounds.end - bounds.start) / DAY_MS);
171
+ }
172
+ /**
173
+ * The period counted in days — `18 of 30` on 19 September. Only for a period
174
+ * whose bounds are whole calendar days: an intraday window has no honest day
175
+ * count, and `null` keeps the row out of the table rather than rounding one
176
+ * into existence. Counted between calendar dates in the card's zone, so a
177
+ * daylight-saving change never shifts the count by a day.
178
+ */
179
+ function deriveDays(bounds, asOf, timeZone) {
180
+ if (!bounds || !bounds.dateOnly || asOf == null) return null;
181
+ const now = toInstant(asOf);
182
+ if (Number.isNaN(now)) return null;
183
+ const total = periodDays(bounds);
184
+ const start = zonedParts(bounds.start, timeZone);
185
+ const current = zonedParts(now, timeZone);
186
+ const startDay = Date.UTC(start.year, start.month - 1, start.day);
187
+ const currentDay = Date.UTC(current.year, current.month - 1, current.day);
188
+ const elapsed = Math.round((currentDay - startDay) / DAY_MS);
189
+ return {
190
+ elapsed: Math.min(total, Math.max(0, elapsed)),
191
+ total
192
+ };
193
+ }
194
+ /** The formatters every text surface shares — headline, badge, plot tooltip, table, descriptions. */
195
+ function createTargetFormatters({ format, locale, formatValue }) {
196
+ return {
197
+ formatValue: (value) => isFinite(value) ? formatValue ? formatValue(value) : formatNumber(value, format, locale) : "—",
198
+ formatPercent: (ratio) => isFinite(ratio) ? formatNumber(ratio, "percent:0", locale) : "—"
199
+ };
200
+ }
201
+ const NUMERIC_CORE = /^[\d.,'\s]+$/;
202
+ /**
203
+ * Joins a reading and its target into one compact pair — `฿2.6M` and `฿4M`
204
+ * become `฿2.6/4M`, `187 ms` and `200 ms` become `187/200 ms`, `2.7%` and `3%`
205
+ * become `2.7/3%` — by lifting out the symbol they both start with and the
206
+ * magnitude they both end with. A leading sign stays on the reading
207
+ * (`-฿120/500K`).
208
+ *
209
+ * Returns `null` whenever the two cannot share their parts — `฿850K` against
210
+ * `฿4M` is not `฿850/4M`, and a locale or formatter this heuristic does not
211
+ * recognise must not be guessed at — so the caller falls back to the full
212
+ * `X of Y` line rather than printing something untrue.
213
+ */
214
+ function formatValuePair(valueText, targetText, separator = "/") {
215
+ const sign = /^[-−+]/.exec(valueText)?.[0] ?? "";
216
+ const value = valueText.slice(sign.length);
217
+ if (!value || !targetText) return null;
218
+ const isDigit = (char) => char >= "0" && char <= "9";
219
+ let prefix = 0;
220
+ while (prefix < value.length && prefix < targetText.length && value[prefix] === targetText[prefix] && !isDigit(value[prefix])) prefix += 1;
221
+ let suffix = 0;
222
+ while (suffix < value.length - prefix && suffix < targetText.length - prefix && value[value.length - 1 - suffix] === targetText[targetText.length - 1 - suffix] && !isDigit(value[value.length - 1 - suffix])) suffix += 1;
223
+ const head = value.slice(0, prefix);
224
+ const tail = suffix > 0 ? value.slice(value.length - suffix) : "";
225
+ const valueCore = value.slice(prefix, value.length - suffix);
226
+ const targetCore = targetText.slice(prefix, targetText.length - suffix);
227
+ if (!NUMERIC_CORE.test(valueCore) || !NUMERIC_CORE.test(targetCore)) return null;
228
+ return `${sign}${head}${valueCore}${separator}${targetCore}${tail}`;
229
+ }
230
+ /** The reading against a reference (the target, or the plan's expectation), with polarity resolved through `goodDirection`. */
231
+ function deriveTargetDelta(value, reference, { deltaMode, goodDirection, format, locale, percentagePoints }) {
232
+ const delta = formatDelta(value, reference, {
233
+ mode: deltaMode,
234
+ format,
235
+ locale,
236
+ pointsSuffix: percentagePoints
237
+ });
238
+ const other = formatDelta(value, reference, {
239
+ mode: delta.renderedMode === "percent" ? "absolute" : "percent",
240
+ format,
241
+ locale,
242
+ pointsSuffix: percentagePoints
243
+ });
244
+ let polarity = "neutral";
245
+ if (delta.direction !== "flat") polarity = delta.direction === "up" === (goodDirection === "up") ? "positive" : "negative";
246
+ return {
247
+ direction: delta.direction,
248
+ polarity,
249
+ text: delta.text,
250
+ renderedMode: delta.renderedMode,
251
+ secondaryText: other.renderedMode === delta.renderedMode ? null : other.text
252
+ };
253
+ }
254
+ /**
255
+ * Attainment, pace, projection and verdict — `null` without a finite reading
256
+ * and a finite, non-zero target (a zero target has no ratio).
257
+ *
258
+ * Pace is `target × elapsed` unless the product supplies its own `expected`
259
+ * (a seasonal plan curve); the projection is the linear run rate
260
+ * `value / elapsed` unless the product supplies `projected` — both are
261
+ * declared as such in `source`, and a run rate is only projected while the
262
+ * period is still running with some of it elapsed. The verdict: an `up`
263
+ * target is achieved the moment the reading reaches it and missed once the
264
+ * period ends short of it; a `down` target is missed the moment the reading
265
+ * exceeds it and achieved once the period ends within it. While the period
266
+ * runs, the gap to the plan's expectation in points of the target decides
267
+ * between on track, at risk (behind by no more than `tolerance`) and off
268
+ * track. Without a period or an `expected` there is no pace and no verdict
269
+ * short of achieved or missed.
270
+ */
271
+ function deriveTargetProgress({ value, target, formatters, tolerance, elapsed, expected, projected, ...deltaOptions }) {
272
+ if (!isFinite(value) || !isFinite(target) || target === 0) return null;
273
+ const { goodDirection, locale, percentagePoints } = deltaOptions;
274
+ const ratio = value / target;
275
+ const good = (reading, reference) => goodDirection === "up" ? reading - reference : reference - reading;
276
+ const withinTarget = goodDirection === "up" ? value >= target : value <= target;
277
+ const gapKind = value > target ? "over" : "remaining";
278
+ const targetDelta = deriveTargetDelta(value, target, deltaOptions);
279
+ let pace = null;
280
+ const expectedValue = expected === null ? null : isFinite(expected) ? expected : elapsed ? target * elapsed.fraction : null;
281
+ if (expectedValue != null) {
282
+ const expectedRatio = expectedValue / target;
283
+ const delta = deriveTargetDelta(value, expectedValue, deltaOptions);
284
+ const points = formatDelta(ratio, expectedRatio, {
285
+ mode: "absolute",
286
+ format: "percent:1",
287
+ locale,
288
+ pointsSuffix: percentagePoints
289
+ });
290
+ const advantage = good(value, expectedValue);
291
+ pace = {
292
+ expected: expectedValue,
293
+ expectedRatio,
294
+ expectedText: formatters.formatValue(expectedValue),
295
+ expectedPercentText: formatters.formatPercent(expectedRatio),
296
+ delta,
297
+ pointsText: points.text,
298
+ status: advantage > 0 ? "ahead" : advantage < 0 ? "behind" : "on",
299
+ source: isFinite(expected) ? "explicit" : "linear"
300
+ };
301
+ }
302
+ let projection = null;
303
+ const projectedValue = projected === null ? null : isFinite(projected) ? projected : elapsed && elapsed.fraction > 0 && !elapsed.ended ? value / elapsed.fraction : null;
304
+ if (projectedValue != null) projection = {
305
+ value: projectedValue,
306
+ ratio: projectedValue / target,
307
+ text: formatters.formatValue(projectedValue),
308
+ percentText: formatters.formatPercent(projectedValue / target),
309
+ source: isFinite(projected) ? "explicit" : "linear",
310
+ extends: Math.sign(projectedValue) === Math.sign(value) && Math.abs(projectedValue) > Math.abs(value)
311
+ };
312
+ let track = null;
313
+ const ended = elapsed?.ended ?? false;
314
+ if (goodDirection === "up") {
315
+ if (value >= target) track = "achieved";
316
+ else if (ended) track = "missed";
317
+ } else if (value > target) track = "missed";
318
+ else if (ended) track = "achieved";
319
+ if (track == null && pace) {
320
+ const gap = good(ratio, pace.expectedRatio);
321
+ track = gap >= 0 ? "on-track" : gap >= -tolerance ? "at-risk" : "off-track";
322
+ }
323
+ const tone = track === "achieved" ? "positive" : track === "missed" ? "negative" : "primary";
324
+ const markers = [
325
+ 0,
326
+ target,
327
+ value,
328
+ pace?.expected ?? 0,
329
+ projection?.value ?? 0
330
+ ];
331
+ return {
332
+ value,
333
+ target,
334
+ ratio,
335
+ valueText: formatters.formatValue(value),
336
+ targetText: formatters.formatValue(target),
337
+ percentText: formatters.formatPercent(ratio),
338
+ withinTarget,
339
+ gapKind,
340
+ gapText: formatters.formatValue(Math.abs(target - value)),
341
+ targetDelta,
342
+ elapsed,
343
+ pace,
344
+ projection,
345
+ track,
346
+ tone,
347
+ bounds: {
348
+ min: Math.min(...markers),
349
+ max: Math.max(...markers)
350
+ }
351
+ };
352
+ }
353
+ /** The word the pace badge's comparison line and explanation use for the reading's standing against the plan. */
354
+ function paceWord(pace, direction, labels) {
355
+ return pace.status === "ahead" ? labels.aheadOfPace(direction) : pace.status === "behind" ? labels.behindPace(direction) : labels.onPace;
356
+ }
357
+ /** The rows behind the badge: the reading against the plan's expectation when there is a pace, else against the target. */
358
+ function buildTargetExplanation(progress, direction, labels) {
359
+ const { pace } = progress;
360
+ return labels.explanation({
361
+ kind: pace ? "pace" : "target",
362
+ direction,
363
+ valueText: progress.valueText,
364
+ percentText: progress.percentText,
365
+ targetText: progress.targetText,
366
+ gapKind: progress.gapKind,
367
+ gapText: progress.gapText,
368
+ expectedText: pace?.expectedText ?? null,
369
+ expectedPercentText: pace?.expectedPercentText ?? null,
370
+ delta: pace ? pace.delta : progress.targetDelta,
371
+ paceStatus: pace?.status ?? null
372
+ });
373
+ }
374
+ /**
375
+ * Every value the card shows, as label/value rows: target, reading, the gap,
376
+ * the plan's expectation and the pace against it, the elapsed share, the
377
+ * projection, the period and the verdict — the popover's table, so nothing
378
+ * lives only in the plot or on hover.
379
+ */
380
+ function buildTargetDetailRows({ progress, direction, periodText, days, labels }) {
381
+ const rows = [
382
+ {
383
+ key: "target",
384
+ label: labels.target,
385
+ value: progress.targetText,
386
+ mark: "target"
387
+ },
388
+ {
389
+ key: "reached",
390
+ label: labels.reached,
391
+ value: `${progress.valueText} (${progress.percentText})`,
392
+ mark: "actual"
393
+ },
394
+ {
395
+ key: "gap",
396
+ label: progress.gapKind === "over" ? labels.overBy : labels.remaining,
397
+ value: progress.gapText
398
+ }
399
+ ];
400
+ if (progress.pace) {
401
+ const word = paceWord(progress.pace, direction, labels);
402
+ rows.push({
403
+ key: "expected",
404
+ label: labels.expectedByNow,
405
+ value: `${progress.pace.expectedText} (${progress.pace.expectedPercentText})`,
406
+ mark: "pace"
407
+ }, {
408
+ key: "pace",
409
+ label: word.charAt(0).toUpperCase() + word.slice(1),
410
+ value: progress.pace.delta.secondaryText ? `${progress.pace.delta.text} (${progress.pace.delta.secondaryText})` : progress.pace.delta.text,
411
+ tone: progress.pace.delta.polarity
412
+ });
413
+ }
414
+ if (progress.projection) rows.push({
415
+ key: "projected",
416
+ label: labels.projected,
417
+ value: `${progress.projection.text} (${progress.projection.percentText})`,
418
+ mark: "projection"
419
+ });
420
+ if (periodText) rows.push({
421
+ key: "period",
422
+ label: labels.period,
423
+ value: periodText
424
+ });
425
+ if (progress.elapsed) rows.push({
426
+ key: "elapsed",
427
+ label: labels.elapsedRow,
428
+ value: progress.elapsed.percentText
429
+ });
430
+ if (days) rows.push({
431
+ key: "days",
432
+ label: labels.daysRow,
433
+ value: labels.days(days.elapsed, days.total)
434
+ });
435
+ if (progress.track) rows.push({
436
+ key: "track",
437
+ label: labels.trackRow,
438
+ value: labels.trackNames[progress.track]
439
+ });
440
+ return rows;
441
+ }
442
+ /** The ISO-8601 week number of a Gregorian date (weeks run Monday to Sunday; week 1 holds the first Thursday). */
443
+ function isoWeekNumber(year, month, day) {
444
+ const date = new Date(Date.UTC(year, month - 1, day));
445
+ date.setUTCDate(date.getUTCDate() - (date.getUTCDay() + 6) % 7 + 3);
446
+ const firstThursday = new Date(Date.UTC(date.getUTCFullYear(), 0, 4));
447
+ firstThursday.setUTCDate(firstThursday.getUTCDate() - (firstThursday.getUTCDay() + 6) % 7 + 3);
448
+ return 1 + Math.round((date.getTime() - firstThursday.getTime()) / (7 * DAY_MS));
449
+ }
450
+ /**
451
+ * Which calendar unit the bounds describe. A period qualifies only when it
452
+ * opens on a zoned midnight and closes exactly one week, month, quarter or
453
+ * year later in the same zone — so a DST weekend still measures seven days,
454
+ * and a range that merely looks month-sized (`Sep 2` to `Oct 1`) stays
455
+ * `custom` rather than being renamed after a month it is not.
456
+ */
457
+ function describePeriodShape(bounds, timeZone) {
458
+ if (!bounds.dateOnly) return "custom";
459
+ const start = zonedParts(bounds.start, timeZone);
460
+ if (start.hour !== 0 || start.minute !== 0 || start.second !== 0) return "custom";
461
+ const closes = (years, months, days) => zonedMidnight(start.year + years, start.month - 1 + months, start.day + days, timeZone) === bounds.end;
462
+ if (start.day === 1) {
463
+ if (start.month === 1 && closes(1, 0, 0)) return "year";
464
+ if ((start.month - 1) % 3 === 0 && closes(0, 3, 0)) return "quarter";
465
+ if (closes(0, 1, 0)) return "month";
466
+ }
467
+ if (closes(0, 0, 7)) return "week";
468
+ return "custom";
469
+ }
470
+ /**
471
+ * Names the period and spells out its bounds. The name is the consumer's
472
+ * `label` when it gave one; otherwise the period's own shape supplies it
473
+ * (`Week 32`, `Sep 2026`, `Q3 2026`, `2026`), and a shape the calendar has no
474
+ * word for falls back to the range itself. Every part goes through `Intl` in
475
+ * the card's locale and zone, so a Thai card on the Buddhist calendar reads
476
+ * `ก.ย. 2569` while the arithmetic stays Gregorian.
477
+ */
478
+ function formatPeriod(bounds, { locale, timeZone, labels = DEFAULT_TARGET_LABELS }) {
479
+ if (!bounds) return null;
480
+ const lastInstant = bounds.dateOnly ? bounds.end - DAY_MS / 2 : bounds.end;
481
+ const granularity = bounds.dateOnly ? "day" : "datetime";
482
+ const range = formatDateRange(bounds.start, lastInstant, {
483
+ granularity,
484
+ locale,
485
+ timeZone
486
+ });
487
+ const shape = describePeriodShape(bounds, timeZone);
488
+ let derived = range;
489
+ if (shape === "year") derived = formatDate(bounds.start, {
490
+ granularity: "year",
491
+ locale,
492
+ timeZone
493
+ });
494
+ else if (shape === "month") derived = formatDate(bounds.start, {
495
+ granularity: "month",
496
+ locale,
497
+ timeZone
498
+ });
499
+ else if (shape === "quarter") {
500
+ const start = zonedParts(bounds.start, timeZone);
501
+ derived = labels.quarter(Math.floor((start.month - 1) / 3) + 1, formatDate(bounds.start, {
502
+ granularity: "year",
503
+ locale,
504
+ timeZone
505
+ }));
506
+ } else if (shape === "week") {
507
+ const midweek = zonedParts(bounds.start + 3 * DAY_MS, timeZone);
508
+ derived = labels.week(isoWeekNumber(midweek.year, midweek.month, midweek.day), formatDate(bounds.start, {
509
+ granularity: "year",
510
+ locale,
511
+ timeZone
512
+ }));
513
+ }
514
+ return {
515
+ name: bounds.label ?? derived,
516
+ shape,
517
+ labelled: bounds.label != null,
518
+ range
519
+ };
520
+ }
521
+ const stripSign = (text) => text.replace(/^[+\-−]/, "");
522
+ /**
523
+ * The card's text equivalent (Amy Cesal's alt-text formula: what it is, what
524
+ * it shows, why it is here, when it was true) — the figure's accessible
525
+ * description, and the summary a screen reader gets before the parts.
526
+ */
527
+ function buildTargetDescription({ name, state, valueText, progress, direction, periodText, asOfText, labels }) {
528
+ if (state === "loading") return `${labels.loading(name)}.`;
529
+ if (state === "error") return `${name}: ${labels.error}.`;
530
+ if (state === "empty") return `${name}: ${labels.noData}.`;
531
+ if (state === "no-match") return `${name}: ${labels.noMatch}.`;
532
+ const sentences = [];
533
+ if (!progress) sentences.push(`${name}: ${valueText}.`, `${labels.noTarget}.`);
534
+ else {
535
+ sentences.push(labels.attainmentSentence({
536
+ name,
537
+ percent: progress.percentText,
538
+ value: progress.valueText,
539
+ target: progress.targetText
540
+ }), labels.gapSentence({
541
+ kind: progress.gapKind,
542
+ amount: progress.gapText
543
+ }));
544
+ if (progress.pace) {
545
+ const flat = progress.pace.delta.direction === "flat";
546
+ sentences.push(labels.paceSentence({
547
+ word: paceWord(progress.pace, direction, labels),
548
+ primary: flat ? "" : stripSign(progress.pace.delta.text),
549
+ secondary: flat || !progress.pace.delta.secondaryText ? null : stripSign(progress.pace.delta.secondaryText),
550
+ expectedPercent: progress.pace.expectedPercentText,
551
+ elapsedPercent: progress.elapsed?.percentText ?? null
552
+ }));
553
+ }
554
+ if (progress.track) sentences.push(`${labels.trackNames[progress.track]}.`);
555
+ if (progress.projection) sentences.push(labels.projectionSentence({
556
+ value: progress.projection.text,
557
+ percent: progress.projection.percentText
558
+ }));
559
+ }
560
+ if (periodText) sentences.push(labels.periodSentence({
561
+ name: periodText.name,
562
+ range: periodText.range
563
+ }));
564
+ if (asOfText) sentences.push(`${labels.asOf} ${asOfText}.`);
565
+ return sentences.join(" ");
566
+ }
567
+ /** The bullet graph's own `description` — the `aria-label` Vega stamps on the `graphics-document`. */
568
+ function buildTargetChartDescription({ name, progress, labels }) {
569
+ return labels.chartDescription({
570
+ name,
571
+ value: progress.valueText,
572
+ percent: progress.percentText,
573
+ target: progress.targetText,
574
+ expected: progress.pace?.expectedText ?? null,
575
+ expectedPercent: progress.pace?.expectedPercentText ?? null,
576
+ projected: progress.projection?.text ?? null,
577
+ projectedPercent: progress.projection?.percentText ?? null
578
+ });
579
+ }
580
+ //#endregion
581
+ export { DEFAULT_TARGET_LABELS, buildTargetChartDescription, buildTargetDescription, buildTargetDetailRows, buildTargetExplanation, createTargetFormatters, deriveDays, deriveElapsed, deriveTargetDelta, deriveTargetProgress, deriveTargetState, describePeriodShape, formatPeriod, formatValuePair, isoWeekNumber, paceWord, periodDays, resolvePeriod, resolveTargetLabels };
@@ -0,0 +1,4 @@
1
+ import { BuildTargetDescriptionInput, BuildTargetDetailRowsInput, DEFAULT_TARGET_LABELS, DeriveTargetDeltaOptions, DeriveTargetProgressInput, DeriveTargetStateInput, TargetBounds, TargetCardLabels, TargetCardSize, TargetCardState, TargetCardStatus, TargetDelta, TargetDeltaMode, TargetDetailRow, TargetElapsed, TargetExplanationInput, TargetGoodDirection, TargetHeadline, TargetPace, TargetPaceStatus, TargetPeriod, TargetPeriodBounds, TargetPolarity, TargetProgress, TargetProjection, TargetTextFormatters, TargetTone, TargetTrack, buildTargetChartDescription, buildTargetDescription, buildTargetDetailRows, buildTargetExplanation, createTargetFormatters, deriveElapsed, deriveTargetDelta, deriveTargetProgress, deriveTargetState, formatPeriod, formatValuePair, paceWord, periodDays, resolvePeriod, resolveTargetLabels } from "./TargetCard.model.js";
2
+ import { TargetCard, TargetCardProps } from "./TargetCard.js";
3
+ import { TARGET_ACTUAL_DATASET, TARGET_BOUNDS_DATASET, TARGET_LABEL_ROW_PX, TARGET_MARKERS_DATASET, TARGET_MARKER_PARAM, TARGET_MAX_PARAM, TARGET_MIN_PARAM, TARGET_PROJECTION_DATASET, TargetBulletSpecOptions, buildTargetBulletSpec } from "./TargetCard.chart.js";
4
+ export { type BuildTargetDescriptionInput, type BuildTargetDetailRowsInput, DEFAULT_TARGET_LABELS, type DeriveTargetDeltaOptions, type DeriveTargetProgressInput, type DeriveTargetStateInput, TARGET_ACTUAL_DATASET, TARGET_BOUNDS_DATASET, TARGET_LABEL_ROW_PX, TARGET_MARKERS_DATASET, TARGET_MARKER_PARAM, TARGET_MAX_PARAM, TARGET_MIN_PARAM, TARGET_PROJECTION_DATASET, type TargetBounds, type TargetBulletSpecOptions, TargetCard, type TargetCardLabels, type TargetCardProps, type TargetCardSize, type TargetCardState, type TargetCardStatus, type TargetDelta, type TargetDeltaMode, type TargetDetailRow, type TargetElapsed, type TargetExplanationInput, type TargetGoodDirection, type TargetHeadline, type TargetPace, type TargetPaceStatus, type TargetPeriod, type TargetPeriodBounds, type TargetPolarity, type TargetProgress, type TargetProjection, type TargetTextFormatters, type TargetTone, type TargetTrack, buildTargetBulletSpec, buildTargetChartDescription, buildTargetDescription, buildTargetDetailRows, buildTargetExplanation, createTargetFormatters, deriveElapsed, deriveTargetDelta, deriveTargetProgress, deriveTargetState, formatPeriod, formatValuePair, paceWord, periodDays, resolvePeriod, resolveTargetLabels };
@@ -0,0 +1,4 @@
1
+ import { TARGET_ACTUAL_DATASET, TARGET_BOUNDS_DATASET, TARGET_LABEL_ROW_PX, TARGET_MARKERS_DATASET, TARGET_MARKER_PARAM, TARGET_MAX_PARAM, TARGET_MIN_PARAM, TARGET_PROJECTION_DATASET, buildTargetBulletSpec } from "./TargetCard.chart.js";
2
+ import { DEFAULT_TARGET_LABELS, buildTargetChartDescription, buildTargetDescription, buildTargetDetailRows, buildTargetExplanation, createTargetFormatters, deriveElapsed, deriveTargetDelta, deriveTargetProgress, deriveTargetState, formatPeriod, formatValuePair, paceWord, periodDays, resolvePeriod, resolveTargetLabels } from "./TargetCard.model.js";
3
+ import { TargetCard } from "./TargetCard.js";
4
+ export { DEFAULT_TARGET_LABELS, TARGET_ACTUAL_DATASET, TARGET_BOUNDS_DATASET, TARGET_LABEL_ROW_PX, TARGET_MARKERS_DATASET, TARGET_MARKER_PARAM, TARGET_MAX_PARAM, TARGET_MIN_PARAM, TARGET_PROJECTION_DATASET, TargetCard, buildTargetBulletSpec, buildTargetChartDescription, buildTargetDescription, buildTargetDetailRows, buildTargetExplanation, createTargetFormatters, deriveElapsed, deriveTargetDelta, deriveTargetProgress, deriveTargetState, formatPeriod, formatValuePair, paceWord, periodDays, resolvePeriod, resolveTargetLabels };
@@ -0,0 +1,80 @@
1
+ import { ChartTokens } from "../../lib/vega/tokens.js";
2
+ import { TopLevelSpec } from "vega-lite";
3
+ //#region src/components/TreeCard/TreeCard.chart.d.ts
4
+ /** The visible rows: `{ key, label, depth, value, share, role, inFocus }` per `TreeRow`, in display order. */
5
+ declare const TREE_TABLE_DATASET = "table";
6
+ /** Zero or one row `{ value: 0 }` — a hairline at zero, pushed only when a visible value is negative. */
7
+ declare const TREE_ZERO_DATASET = "zero";
8
+ /** The hovered row's point selection. */
9
+ declare const TREE_HOVER_PARAM = "hover";
10
+ /**
11
+ * The upper bound of the shared value scale — the tree's total, pushed as a
12
+ * signal.
13
+ *
14
+ * Pinning the domain to the whole rather than to the tallest visible bar is
15
+ * what makes a bar's length read as its share of the total at every depth,
16
+ * whether or not the total is itself a row. It rides a signal, not the spec,
17
+ * because the total is data: baking it into the spec would re-embed the view
18
+ * on every refresh.
19
+ */
20
+ declare const TREE_TOTAL_PARAM = "treeTotal";
21
+ declare const TREE_MAX_BAR_HEIGHT = 24;
22
+ /**
23
+ * Depth reads through the sequential ramp's top steps, darkest at the root.
24
+ *
25
+ * Only three: the ramp's lighter steps (`chart-sequential-1…4`) do not clear
26
+ * WCAG 1.4.11's 3:1 against `background` — `primary-400` measures 2.7:1 on
27
+ * white — so a fourth shade would buy a depth cue by making the deepest bars
28
+ * unreadable. Depth past the third level therefore repeats the third step;
29
+ * indentation, the disclosure control and `aria-level` carry the rest, so the
30
+ * hierarchy never depends on the ramp alone.
31
+ */
32
+ declare const TREE_DEPTH_STEPS: readonly [6, 5, 4];
33
+ /** The exact hue a row's bar wears — the swatches in the legend, chips and tooltip read from this too. */
34
+ declare function treeBarColor(tokens: ChartTokens, { depth, role, inFocus }: {
35
+ depth: number;
36
+ role?: string;
37
+ inFocus?: boolean;
38
+ }, colorBy: "depth" | "emphasis", emphasize: boolean): string;
39
+ interface TreeBarsSpecOptions {
40
+ /** Bar thickness in pixels, capped at 24. Centered in each row's band. */
41
+ barHeight: number;
42
+ /** Initial plot height — rows × step. The live value rides the View API. */
43
+ height: number;
44
+ /**
45
+ * Draw a hairline at zero (the `zero` dataset carries the one row) — only
46
+ * when a visible value is negative: for all-positive bars the left edge is
47
+ * the baseline already, and a hairline there would read as a border.
48
+ * Structural rather than data-driven because Vega warns about the infinite
49
+ * extent of an empty dataset that feeds a scale.
50
+ */
51
+ zeroRule: boolean;
52
+ /** Wire the hover selection and the tooltip channel. */
53
+ tooltip: boolean;
54
+ /** `depth` ramps the sequential steps by level; `emphasis` paints the selected branch and grays the rest. */
55
+ colorBy: "depth" | "emphasis";
56
+ /** A row is selected, so `emphasis` has a branch to paint. Without one every bar takes the emphasis hue. */
57
+ emphasize: boolean;
58
+ /** Tooltip titles. */
59
+ itemTitle: string;
60
+ valueTitle: string;
61
+ shareTitle: string;
62
+ /** `arcNumber` grammar for values (`currency:THB:compact@en-US`). */
63
+ valueFormat: string;
64
+ /** `arcNumber` grammar for shares (`percent:0@en-US`). */
65
+ shareFormat: string;
66
+ locale?: string;
67
+ }
68
+ /**
69
+ * Builds the bars: one horizontal bar per visible row on a shared zero-based
70
+ * scale (length compares magnitude — Cleveland & McGill's second-best channel
71
+ * — and, because the whole is on the same scale, reads as share of it), the
72
+ * fill carrying depth through the sequential ramp or the selected branch
73
+ * through the emphasis hue, a hairline at zero once a value is negative, and
74
+ * a hover selection that keeps the pointed bar solid while its siblings
75
+ * recede. Every band has no padding, so the bar centers line up with the DOM
76
+ * rows that carry the names, values and shares.
77
+ */
78
+ declare function buildTreeBarsSpec(options: TreeBarsSpecOptions, tokens: ChartTokens): TopLevelSpec;
79
+ //#endregion
80
+ export { TREE_DEPTH_STEPS, TREE_HOVER_PARAM, TREE_MAX_BAR_HEIGHT, TREE_TABLE_DATASET, TREE_TOTAL_PARAM, TREE_ZERO_DATASET, TreeBarsSpecOptions, buildTreeBarsSpec, treeBarColor };