@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,64 @@
1
+ import { ChartTokens } from "../../lib/vega/tokens.js";
2
+ import { ProfileMark } from "./ProfileCard.model.js";
3
+ import { TopLevelSpec } from "vega-lite";
4
+ //#region src/components/ProfileCard/ProfileCard.chart.d.ts
5
+ /** Long-form readings: one row per series × metric with a finite value (`ProfileCell`). */
6
+ declare const PROFILE_CELLS_DATASET = "cells";
7
+ /** One row per metric — `{ i, a, m, ml }` — the axis ring, the spokes, the band domain and the labels. */
8
+ declare const PROFILE_AXES_DATASET = "axes";
9
+ /** The web: one row per metric × ring level — `{ i, a, r }`. Empty for the non-radar marks. */
10
+ declare const PROFILE_RINGS_DATASET = "rings";
11
+ /** The hovered series' point selection. */
12
+ declare const PROFILE_HOVER_PARAM = "hover";
13
+ /** Which geometry the mark draws in. `radar` is cartesian-from-polar; the rest are ordinary plots. */
14
+ declare function profileNeedsRings(mark: ProfileMark): boolean;
15
+ interface ProfilePlotSpecOptions {
16
+ /** Which form the plot draws. */
17
+ mark: ProfileMark;
18
+ /** Plot height in pixels. The width rides the View API. */
19
+ height: number;
20
+ /**
21
+ * Which field the value channel reads. `raw` when every metric already
22
+ * shares one scale (`normalize="shared"`), so the axis speaks real units;
23
+ * `n` when each metric was scaled to its own range and only the 0–1 form
24
+ * is comparable. A radar and a parallel plot always read `n`.
25
+ */
26
+ valueField: "raw" | "n";
27
+ /** Draw the web rings and the spokes (radar only). */
28
+ rings: boolean;
29
+ /** Fill each radar polygon faintly. Off by default — a filled area invites an area comparison the geometry cannot support. */
30
+ fill: boolean;
31
+ /** Draw a dot on each vertex, so a reading is a mark and not only a bend in a path. */
32
+ vertices: boolean;
33
+ /** Wire the hover selection and the tooltip channel. */
34
+ tooltip: boolean;
35
+ /** Dim every series but the focused one. Only meaningful while a focus exists. */
36
+ emphasize: boolean;
37
+ /** Name the metrics on the plot. `false` leaves the axis to the popover table (a tile too small for labels). */
38
+ axisLabels: boolean;
39
+ /** Tooltip titles. */
40
+ itemTitle: string;
41
+ metricTitle: string;
42
+ valueTitle: string;
43
+ /** `arcNumber` grammar for the value axis and the tooltip. */
44
+ valueFormat: string;
45
+ locale?: string;
46
+ }
47
+ /**
48
+ * Builds the plot.
49
+ *
50
+ * `radar` draws a web of rings and spokes, then one closed polygon per item
51
+ * over the metric axes — the shape IS the reading, so the card fixes the axis
52
+ * order, keeps the fill off by default and never lets a rescaled axis go
53
+ * unannounced. `grouped` draws one bar per item inside each metric's band,
54
+ * which is the accurate form whenever values rather than shapes are being
55
+ * compared. `parallel` draws one path per item across parallel axes, which
56
+ * carries more items than a radar can without turning into a knot.
57
+ *
58
+ * Colour is identity here — several items on one plot — so it comes baked
59
+ * into every pushed row (`c`) with `scale: null`: hiding a series through the
60
+ * legend can then never repaint the survivors.
61
+ */
62
+ declare function buildProfilePlotSpec(options: ProfilePlotSpecOptions, tokens: ChartTokens): TopLevelSpec;
63
+ //#endregion
64
+ export { PROFILE_AXES_DATASET, PROFILE_CELLS_DATASET, PROFILE_HOVER_PARAM, PROFILE_RINGS_DATASET, ProfilePlotSpecOptions, buildProfilePlotSpec, profileNeedsRings };
@@ -0,0 +1,427 @@
1
+ import { arcVegaConfig } from "../../lib/vega/config.js";
2
+ //#region src/components/ProfileCard/ProfileCard.chart.ts
3
+ /** Long-form readings: one row per series × metric with a finite value (`ProfileCell`). */
4
+ const PROFILE_CELLS_DATASET = "cells";
5
+ /** One row per metric — `{ i, a, m, ml }` — the axis ring, the spokes, the band domain and the labels. */
6
+ const PROFILE_AXES_DATASET = "axes";
7
+ /** The web: one row per metric × ring level — `{ i, a, r }`. Empty for the non-radar marks. */
8
+ const PROFILE_RINGS_DATASET = "rings";
9
+ /** The hovered series' point selection. */
10
+ const PROFILE_HOVER_PARAM = "hover";
11
+ const PATH_WIDTH = 2;
12
+ const VERTEX_SIZE = 48;
13
+ const RESTING_OPACITY = .55;
14
+ const FILL_OPACITY = .12;
15
+ const LABEL_RADIUS = 1.08;
16
+ const RADAR_DOMAIN = 1.72;
17
+ const LABEL_LIMIT_EXPR = `max(24, width / 2 * ${(.6399999999999999 / RADAR_DOMAIN).toFixed(4)} - 2)`;
18
+ const MAX_GROUP_BAR = 18;
19
+ const PARALLEL_PADDING = .14;
20
+ /** Which geometry the mark draws in. `radar` is cartesian-from-polar; the rest are ordinary plots. */
21
+ function profileNeedsRings(mark) {
22
+ return mark === "radar";
23
+ }
24
+ /** The band domain and the axis-label lookup both read the axes dataset, so a metric nobody reported keeps its place. */
25
+ const AXIS_DOMAIN_EXPR = `pluck(data('${PROFILE_AXES_DATASET}'), 'm')`;
26
+ const AXIS_INDEX_DOMAIN_EXPR = `pluck(data('${PROFILE_AXES_DATASET}'), 'i')`;
27
+ const AXIS_LABEL_EXPR = `((data('${PROFILE_AXES_DATASET}')[indexof(pluck(data('${PROFILE_AXES_DATASET}'), 'm'), datum.value)] || {}).ml)`;
28
+ const AXIS_LABEL_BY_INDEX_EXPR = `((data('${PROFILE_AXES_DATASET}')[indexof(pluck(data('${PROFILE_AXES_DATASET}'), 'i'), datum.value)] || {}).ml)`;
29
+ const RADAR_X = (radius) => `sin(datum.a) * ${radius}`;
30
+ const RADAR_Y = (radius) => `cos(datum.a) * ${radius}`;
31
+ /**
32
+ * Builds the plot.
33
+ *
34
+ * `radar` draws a web of rings and spokes, then one closed polygon per item
35
+ * over the metric axes — the shape IS the reading, so the card fixes the axis
36
+ * order, keeps the fill off by default and never lets a rescaled axis go
37
+ * unannounced. `grouped` draws one bar per item inside each metric's band,
38
+ * which is the accurate form whenever values rather than shapes are being
39
+ * compared. `parallel` draws one path per item across parallel axes, which
40
+ * carries more items than a radar can without turning into a knot.
41
+ *
42
+ * Colour is identity here — several items on one plot — so it comes baked
43
+ * into every pushed row (`c`) with `scale: null`: hiding a series through the
44
+ * legend can then never repaint the survivors.
45
+ */
46
+ function buildProfilePlotSpec(options, tokens) {
47
+ const { mark, height, valueField, rings, fill, vertices, tooltip, emphasize, axisLabels, itemTitle, metricTitle, valueTitle, valueFormat, locale } = options;
48
+ const colorChannel = {
49
+ field: "c",
50
+ type: "nominal",
51
+ scale: null,
52
+ legend: null
53
+ };
54
+ const seriesDetail = {
55
+ field: "s",
56
+ type: "nominal"
57
+ };
58
+ const tooltipChannel = [
59
+ {
60
+ field: "sl",
61
+ type: "nominal",
62
+ title: itemTitle
63
+ },
64
+ {
65
+ field: "ml",
66
+ type: "nominal",
67
+ title: metricTitle
68
+ },
69
+ {
70
+ field: "raw",
71
+ type: "quantitative",
72
+ title: valueTitle,
73
+ format: valueFormat,
74
+ formatType: "arcNumber"
75
+ }
76
+ ];
77
+ const solid = { value: 1 };
78
+ const hovered = {
79
+ param: PROFILE_HOVER_PARAM,
80
+ ...emphasize ? { empty: false } : {},
81
+ ...solid
82
+ };
83
+ const focused = {
84
+ test: "datum.f",
85
+ ...solid
86
+ };
87
+ const opacityChannel = tooltip && emphasize ? { opacity: {
88
+ condition: [hovered, focused],
89
+ value: RESTING_OPACITY
90
+ } } : tooltip ? { opacity: {
91
+ condition: hovered,
92
+ value: RESTING_OPACITY
93
+ } } : emphasize ? { opacity: {
94
+ condition: focused,
95
+ value: RESTING_OPACITY
96
+ } } : {};
97
+ const hoverParam = tooltip ? { params: [{
98
+ name: PROFILE_HOVER_PARAM,
99
+ select: {
100
+ type: "point",
101
+ on: "pointerover",
102
+ clear: "pointerout",
103
+ fields: ["s"]
104
+ }
105
+ }] } : {};
106
+ const layers = [];
107
+ if (mark === "radar") {
108
+ const cartesian = (radius) => [{
109
+ calculate: RADAR_X(radius),
110
+ as: "px"
111
+ }, {
112
+ calculate: RADAR_Y(radius),
113
+ as: "py"
114
+ }];
115
+ const px = {
116
+ field: "px",
117
+ type: "quantitative",
118
+ axis: null,
119
+ scale: {
120
+ domain: [-1.72, RADAR_DOMAIN],
121
+ nice: false
122
+ },
123
+ title: null
124
+ };
125
+ const py = {
126
+ field: "py",
127
+ type: "quantitative",
128
+ axis: null,
129
+ scale: {
130
+ domain: [-1.72, RADAR_DOMAIN],
131
+ nice: false
132
+ },
133
+ title: null
134
+ };
135
+ if (rings) {
136
+ layers.push({
137
+ data: { name: PROFILE_RINGS_DATASET },
138
+ transform: cartesian("datum.r"),
139
+ mark: {
140
+ type: "line",
141
+ interpolate: "linear-closed",
142
+ stroke: tokens.grid,
143
+ strokeWidth: 1,
144
+ aria: false
145
+ },
146
+ encoding: {
147
+ x: px,
148
+ y: py,
149
+ detail: {
150
+ field: "r",
151
+ type: "nominal"
152
+ },
153
+ order: {
154
+ field: "i",
155
+ type: "quantitative"
156
+ }
157
+ }
158
+ });
159
+ layers.push({
160
+ data: { name: PROFILE_AXES_DATASET },
161
+ transform: cartesian("1"),
162
+ mark: {
163
+ type: "rule",
164
+ stroke: tokens.grid,
165
+ strokeWidth: 1,
166
+ aria: false
167
+ },
168
+ encoding: {
169
+ x: px,
170
+ y: py,
171
+ x2: { datum: 0 },
172
+ y2: { datum: 0 }
173
+ }
174
+ });
175
+ }
176
+ layers.push({
177
+ transform: cartesian("datum.n"),
178
+ mark: {
179
+ type: "line",
180
+ interpolate: "linear-closed",
181
+ strokeWidth: PATH_WIDTH,
182
+ strokeJoin: "round",
183
+ ...fill ? {
184
+ filled: true,
185
+ fillOpacity: FILL_OPACITY
186
+ } : { fill: null },
187
+ aria: false
188
+ },
189
+ encoding: {
190
+ x: px,
191
+ y: py,
192
+ stroke: colorChannel,
193
+ ...fill ? { fill: colorChannel } : {},
194
+ detail: seriesDetail,
195
+ order: {
196
+ field: "i",
197
+ type: "quantitative"
198
+ },
199
+ ...opacityChannel
200
+ }
201
+ });
202
+ if (vertices) layers.push({
203
+ ...hoverParam,
204
+ transform: cartesian("datum.n"),
205
+ mark: {
206
+ type: "point",
207
+ filled: true,
208
+ size: VERTEX_SIZE,
209
+ aria: false
210
+ },
211
+ encoding: {
212
+ x: px,
213
+ y: py,
214
+ fill: colorChannel,
215
+ ...opacityChannel,
216
+ ...tooltip ? { tooltip: tooltipChannel } : {}
217
+ }
218
+ });
219
+ if (axisLabels) layers.push({
220
+ data: { name: PROFILE_AXES_DATASET },
221
+ transform: cartesian(String(LABEL_RADIUS)),
222
+ mark: {
223
+ type: "text",
224
+ fontSize: 10,
225
+ baseline: "middle",
226
+ fill: tokens.axisLabel,
227
+ limit: { expr: LABEL_LIMIT_EXPR },
228
+ align: { expr: "abs(datum.px) < 0.08 ? 'center' : (datum.px > 0 ? 'left' : 'right')" },
229
+ aria: false
230
+ },
231
+ encoding: {
232
+ x: px,
233
+ y: py,
234
+ text: {
235
+ field: "ml",
236
+ type: "nominal"
237
+ }
238
+ }
239
+ });
240
+ }
241
+ if (mark === "grouped") {
242
+ const catAxis = (owner) => !axisLabels ? { axis: null } : owner ? { axis: {
243
+ title: null,
244
+ labelExpr: AXIS_LABEL_EXPR,
245
+ labelLimit: 96,
246
+ domain: false,
247
+ ticks: false,
248
+ grid: false,
249
+ aria: false
250
+ } } : {};
251
+ const band = (owner) => ({
252
+ field: "m",
253
+ type: "nominal",
254
+ sort: null,
255
+ scale: {
256
+ type: "band",
257
+ domain: { expr: AXIS_DOMAIN_EXPR },
258
+ paddingInner: .3,
259
+ paddingOuter: .15
260
+ },
261
+ ...catAxis(owner)
262
+ });
263
+ layers.push({
264
+ data: { name: PROFILE_AXES_DATASET },
265
+ mark: {
266
+ type: "rule",
267
+ stroke: tokens.grid,
268
+ strokeWidth: 1,
269
+ aria: false
270
+ },
271
+ encoding: { y: band(false) }
272
+ });
273
+ layers.push({
274
+ ...hoverParam,
275
+ mark: {
276
+ type: "bar",
277
+ cornerRadiusEnd: 2,
278
+ aria: false
279
+ },
280
+ encoding: {
281
+ y: band(true),
282
+ yOffset: {
283
+ field: "s",
284
+ type: "nominal",
285
+ sort: null,
286
+ scale: { paddingInner: .1 }
287
+ },
288
+ x: {
289
+ field: valueField,
290
+ type: "quantitative",
291
+ scale: {
292
+ zero: true,
293
+ nice: true
294
+ },
295
+ axis: axisLabels ? {
296
+ title: null,
297
+ tickCount: 3,
298
+ format: valueFormat,
299
+ formatType: "arcNumber",
300
+ domain: false,
301
+ ticks: false,
302
+ grid: true,
303
+ aria: false
304
+ } : null
305
+ },
306
+ fill: colorChannel,
307
+ ...opacityChannel,
308
+ ...tooltip ? { tooltip: tooltipChannel } : {}
309
+ }
310
+ });
311
+ }
312
+ if (mark === "parallel") {
313
+ const axisChannel = (owner) => ({
314
+ field: "i",
315
+ type: "ordinal",
316
+ sort: null,
317
+ scale: {
318
+ type: "point",
319
+ domain: { expr: AXIS_INDEX_DOMAIN_EXPR },
320
+ padding: PARALLEL_PADDING
321
+ },
322
+ ...!axisLabels ? { axis: null } : owner ? { axis: {
323
+ title: null,
324
+ labelAngle: 0,
325
+ labelExpr: AXIS_LABEL_BY_INDEX_EXPR,
326
+ labelLimit: 72,
327
+ domain: false,
328
+ ticks: false,
329
+ grid: false,
330
+ aria: false
331
+ } } : {}
332
+ });
333
+ const valueChannel = {
334
+ field: "n",
335
+ type: "quantitative",
336
+ axis: null,
337
+ scale: {
338
+ domain: [0, 1],
339
+ nice: false
340
+ },
341
+ title: null
342
+ };
343
+ layers.push({
344
+ data: { name: PROFILE_AXES_DATASET },
345
+ mark: {
346
+ type: "rule",
347
+ stroke: tokens.grid,
348
+ strokeWidth: 1,
349
+ aria: false
350
+ },
351
+ encoding: {
352
+ x: axisChannel(true),
353
+ y: {
354
+ datum: 0,
355
+ type: "quantitative",
356
+ scale: {
357
+ domain: [0, 1],
358
+ nice: false
359
+ },
360
+ axis: null,
361
+ title: null
362
+ },
363
+ y2: { datum: 1 }
364
+ }
365
+ });
366
+ layers.push({
367
+ mark: {
368
+ type: "line",
369
+ strokeWidth: PATH_WIDTH,
370
+ strokeJoin: "round",
371
+ aria: false
372
+ },
373
+ encoding: {
374
+ x: axisChannel(false),
375
+ y: valueChannel,
376
+ stroke: colorChannel,
377
+ detail: seriesDetail,
378
+ order: {
379
+ field: "i",
380
+ type: "quantitative"
381
+ },
382
+ ...opacityChannel
383
+ }
384
+ });
385
+ if (vertices) layers.push({
386
+ ...hoverParam,
387
+ mark: {
388
+ type: "point",
389
+ filled: true,
390
+ size: VERTEX_SIZE,
391
+ aria: false
392
+ },
393
+ encoding: {
394
+ x: axisChannel(false),
395
+ y: valueChannel,
396
+ fill: colorChannel,
397
+ ...opacityChannel,
398
+ ...tooltip ? { tooltip: tooltipChannel } : {}
399
+ }
400
+ });
401
+ }
402
+ return {
403
+ $schema: "https://vega.github.io/schema/vega-lite/v6.json",
404
+ width: 280,
405
+ height,
406
+ autosize: mark === "radar" ? { type: "none" } : {
407
+ type: "fit",
408
+ contains: "padding"
409
+ },
410
+ padding: 0,
411
+ background: "transparent",
412
+ data: { name: PROFILE_CELLS_DATASET },
413
+ layer: layers,
414
+ config: (() => {
415
+ const base = arcVegaConfig(tokens, { locale });
416
+ return {
417
+ ...base,
418
+ bar: {
419
+ ...base.bar,
420
+ maxBandSize: MAX_GROUP_BAR
421
+ }
422
+ };
423
+ })()
424
+ };
425
+ }
426
+ //#endregion
427
+ export { PROFILE_AXES_DATASET, PROFILE_CELLS_DATASET, PROFILE_HOVER_PARAM, PROFILE_RINGS_DATASET, buildProfilePlotSpec, profileNeedsRings };
@@ -0,0 +1,140 @@
1
+ import { ArcTimeGranularity } from "../../lib/vega/formatters.js";
2
+ import { ProfileCardLabels, ProfileCardSize, ProfileCardStatus, ProfileItem, ProfileMark, ProfileMetric, ProfileMetricInfo, ProfileNormalize, RADAR_MAX_AXES, RADAR_MAX_SERIES } from "./ProfileCard.model.js";
3
+ import { ReactNode } from "react";
4
+ import { BoxProps } from "@arcfusionz/arc-primitive-ui";
5
+ import { TopLevelSpec } from "vega-lite";
6
+ import { Result } from "vega-embed";
7
+ //#region src/components/ProfileCard/ProfileCard.d.ts
8
+ interface ProfileCardProps extends Omit<BoxProps, "children" | "padding" | "title"> {
9
+ /** The comparison's name — the panel's heading. Pass `name` too when this is not plain text. */
10
+ label: ReactNode;
11
+ /** Plain-text name used in the generated descriptions and control labels. Defaults to `label` when it is a string. */
12
+ name?: string;
13
+ /** What the profile means, shown in a tooltip behind an info control beside the label and exposed to assistive technology as its description. */
14
+ hint?: string;
15
+ /**
16
+ * The measures — one axis each, **in the order they should be read**. The
17
+ * card never sorts them, because a radar's shape depends on the axis order:
18
+ * pick an order the reader recognises and keep it stable across renders.
19
+ * `max` gives a measure its own ceiling for `normalize="max"`, `format` its
20
+ * own number grammar. Pass `[]` while loading.
21
+ */
22
+ metrics: readonly ProfileMetric[];
23
+ /**
24
+ * The things being profiled. `values` is keyed by metric key; a metric an
25
+ * item has no reading for is a gap, not a zero — omit it or pass `null`.
26
+ * Pass `[]` while loading.
27
+ */
28
+ items: readonly ProfileItem[];
29
+ /**
30
+ * The form the plot draws. `radar` (default) for the shape of a profile;
31
+ * `grouped` when values rather than shapes are being compared — the
32
+ * accurate form; `parallel` when there are more items than a radar can
33
+ * overlay. Stamped as `data-mark`.
34
+ */
35
+ mark?: ProfileMark;
36
+ /**
37
+ * How readings on different measures are brought onto one axis. `shared`
38
+ * (default) keeps one scale across every metric — honest only when they
39
+ * are commensurable; `max` scales each metric to its own ceiling; `minmax`
40
+ * scales each to its own observed range, which is readable and the most
41
+ * misleading. Whichever is chosen is announced in the description and
42
+ * printed under the plot. Stamped as `data-normalize`.
43
+ */
44
+ normalize?: ProfileNormalize;
45
+ /**
46
+ * Fill each radar polygon faintly. **Off by default**: a polygon's area is
47
+ * a sum of products of adjacent readings, so it changes when the axes are
48
+ * reordered and must never be read as a score. Turn it on only for a
49
+ * single-item profile, where there is no area to compare against.
50
+ */
51
+ fill?: boolean;
52
+ /** Draw a dot at every reading, so a value is a mark and not only a bend in a path. Default `true`. */
53
+ vertices?: boolean;
54
+ /** Name the metrics on the plot. `false` on a panel too small for them — the popover table still carries every name. Default `true`. */
55
+ axisLabels?: boolean;
56
+ /**
57
+ * Key of the subject, controlled — pair with `onFocusChange`. Its path
58
+ * stays solid while the others recede, and the description says where it
59
+ * leads and trails. `null` designates none: every item reads equally.
60
+ */
61
+ focus?: string | null;
62
+ /** The subject on first render when `focus` is uncontrolled. Default: the first item. */
63
+ defaultFocus?: string | null;
64
+ /** Called with the chosen item's key when the reader picks another subject from the legend. */
65
+ onFocusChange?: (key: string) => void;
66
+ /** Keys of the items on the plot, controlled — pair with `onVisibleSeriesChange`. Omit for the uncontrolled legend. */
67
+ visibleSeries?: readonly string[];
68
+ /** The items on the plot at first render when `visibleSeries` is uncontrolled. Default: all of them. */
69
+ defaultVisibleSeries?: readonly string[];
70
+ /** Called with the new visible set when the reader toggles an item in the legend. */
71
+ onVisibleSeriesChange?: (keys: string[]) => void;
72
+ /** Show the legend. `false` only when another surface names every item. Default `true`. */
73
+ showLegend?: boolean;
74
+ /** One grammar for every reading that has no `ProfileMetric.format` of its own: `number[:digits]`, `integer`, `compact[:digits]`, `percent[:digits]`, `currency:CODE[:digits|compact]`. */
75
+ format?: string;
76
+ /** Custom formatter for readings. Takes precedence over `format` and over a metric's own `format`. */
77
+ formatValue?: (value: number, metric?: ProfileMetricInfo) => string;
78
+ /** Metrics the skeleton reserves before `metrics` arrive, so the panel is already its final height. Default 5. */
79
+ expectedMetrics?: number;
80
+ /** Items the skeleton reserves in the legend before they arrive. Default 2. */
81
+ expectedItems?: number;
82
+ /** The takeaway in a sentence, rendered beside the AI mark under the plot. Wrap values in `<strong>`. */
83
+ insight?: ReactNode;
84
+ /** The transport's status. `loading` with data already present is a refresh: the previous render stays under the veil. Default `ready`. */
85
+ status?: ProfileCardStatus;
86
+ /** The consumer's filter is active — no readings then reads as no-match rather than empty. */
87
+ filtered?: boolean;
88
+ /** The data is older than the product's freshness rule (TanStack Query's `isStale`). Shows a badge and stamps `data-stale`. */
89
+ stale?: boolean;
90
+ /** When the data was last true. Rendered as an "as of" line and included in the description. */
91
+ updatedAt?: Date | string | number;
92
+ /** How `updatedAt` is written, in the card's `locale` and `timeZone`. Default `day`. */
93
+ updatedAtFormat?: ArcTimeGranularity | ((date: Date) => string);
94
+ /** Recovery action for the error state; renders a retry button when set. */
95
+ onRetry?: () => void;
96
+ /** Recovery action for the no-match state; renders a clear-filters button when set. */
97
+ onClearFilters?: () => void;
98
+ /** Density: `sm` for a dense grid, `md` (default), `lg` for a hero panel. */
99
+ size?: ProfileCardSize;
100
+ /** Heading level of the label for the document outline. Default 3. */
101
+ titleLevel?: 2 | 3 | 4 | 5 | 6;
102
+ /** BCP 47 locale for every number and date. Thai products on the Gregorian calendar pass `th-TH-u-ca-gregory`. */
103
+ locale?: string;
104
+ /** IANA time zone for the as-of date. Defaults to the runtime's — pass it for deterministic output. */
105
+ timeZone?: string;
106
+ /** Overrides for every rendered or announced string. */
107
+ labels?: Partial<ProfileCardLabels>;
108
+ /** Replaces the generated accessible description of the whole card. */
109
+ description?: string;
110
+ /** Trailing header slot — a ghost `Button` or `Menu`. */
111
+ action?: ReactNode;
112
+ /** Open the data popover on first render. */
113
+ defaultTableOpen?: boolean;
114
+ /** Hide the header info button. Only when another surface exposes the same values to keyboard and screen-reader users. */
115
+ hideTableToggle?: boolean;
116
+ /** Called with vega-embed's result once the plot mounts — for `view` access in advanced compositions. */
117
+ onEmbed?: (result: Result) => void;
118
+ /** Escape hatch: transform the spec before it renders. Voids the token and accessibility guarantees for whatever it changes. */
119
+ patchSpec?: (spec: TopLevelSpec) => TopLevelSpec;
120
+ }
121
+ /**
122
+ * A profile panel — the ninth member of the analytics card family, and the
123
+ * only one that compares **several items across several measures at once**.
124
+ * MetricCard compares one metric with its own past; ComparisonCard compares
125
+ * one metric across items; TrendCard asks what shape a metric is in over
126
+ * time. ProfileCard answers the question none of them can: *what shape is
127
+ * each of these things across the same set of measures, and where does one
128
+ * lead or lag?*
129
+ *
130
+ * `mark="radar"` (default) draws the profile as a polygon per item — with the
131
+ * axis order fixed, the fill off and the scaling announced, which is what
132
+ * makes a radar honest. `mark="grouped"` is the accurate alternative for
133
+ * comparing values, and `mark="parallel"` carries more items than a radar
134
+ * can. Every reading is also in the popover table, the legend is real
135
+ * toggles, and every loading, empty, error and stale state is layout-stable
136
+ * inside one `Box` frame.
137
+ */
138
+ declare const ProfileCard: import("react").ForwardRefExoticComponent<ProfileCardProps & import("react").RefAttributes<HTMLElement>>;
139
+ //#endregion
140
+ export { ProfileCard, ProfileCardProps };