@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.
- package/dist/_virtual/_rolldown/runtime.js +13 -0
- package/dist/components/AnomalyCard/AnomalyCard.chart.d.ts +64 -0
- package/dist/components/AnomalyCard/AnomalyCard.chart.js +341 -0
- package/dist/components/AnomalyCard/AnomalyCard.d.ts +169 -0
- package/dist/components/AnomalyCard/AnomalyCard.js +1019 -0
- package/dist/components/AnomalyCard/AnomalyCard.model.d.ts +508 -0
- package/dist/components/AnomalyCard/AnomalyCard.model.js +553 -0
- package/dist/components/AnomalyCard/index.d.ts +4 -0
- package/dist/components/AnomalyCard/index.js +4 -0
- package/dist/components/ChartCard/ChartCard.chart.d.ts +72 -0
- package/dist/components/ChartCard/ChartCard.chart.js +1333 -0
- package/dist/components/ChartCard/ChartCard.d.ts +206 -0
- package/dist/components/ChartCard/ChartCard.js +718 -0
- package/dist/components/ChartCard/ChartCard.model.d.ts +452 -0
- package/dist/components/ChartCard/ChartCard.model.js +1223 -0
- package/dist/components/ChartCard/index.d.ts +4 -0
- package/dist/components/ChartCard/index.js +4 -0
- package/dist/components/ComparisonCard/ComparisonCard.chart.d.ts +80 -0
- package/dist/components/ComparisonCard/ComparisonCard.chart.js +337 -0
- package/dist/components/ComparisonCard/ComparisonCard.d.ts +200 -0
- package/dist/components/ComparisonCard/ComparisonCard.js +1325 -0
- package/dist/components/ComparisonCard/ComparisonCard.model.d.ts +309 -0
- package/dist/components/ComparisonCard/ComparisonCard.model.js +263 -0
- package/dist/components/ComparisonCard/index.d.ts +4 -0
- package/dist/components/ComparisonCard/index.js +4 -0
- package/dist/components/ForecastCard/ForecastCard.chart.d.ts +82 -0
- package/dist/components/ForecastCard/ForecastCard.chart.js +416 -0
- package/dist/components/ForecastCard/ForecastCard.d.ts +148 -0
- package/dist/components/ForecastCard/ForecastCard.js +1029 -0
- package/dist/components/ForecastCard/ForecastCard.model.d.ts +497 -0
- package/dist/components/ForecastCard/ForecastCard.model.js +554 -0
- package/dist/components/ForecastCard/index.d.ts +4 -0
- package/dist/components/ForecastCard/index.js +4 -0
- package/dist/components/HighlightCard/HighlightCard.chart.d.ts +122 -0
- package/dist/components/HighlightCard/HighlightCard.chart.js +660 -0
- package/dist/components/HighlightCard/HighlightCard.d.ts +202 -0
- package/dist/components/HighlightCard/HighlightCard.js +1286 -0
- package/dist/components/HighlightCard/HighlightCard.model.d.ts +692 -0
- package/dist/components/HighlightCard/HighlightCard.model.js +875 -0
- package/dist/components/HighlightCard/index.d.ts +4 -0
- package/dist/components/HighlightCard/index.js +4 -0
- package/dist/components/InsightCard/InsightCard.chart.d.ts +91 -0
- package/dist/components/InsightCard/InsightCard.chart.js +471 -0
- package/dist/components/InsightCard/InsightCard.d.ts +161 -0
- package/dist/components/InsightCard/InsightCard.js +961 -0
- package/dist/components/InsightCard/InsightCard.model.d.ts +405 -0
- package/dist/components/InsightCard/InsightCard.model.js +421 -0
- package/dist/components/InsightCard/index.d.ts +4 -0
- package/dist/components/InsightCard/index.js +4 -0
- package/dist/components/MetricCard/MetricCard.chart.d.ts +79 -0
- package/dist/components/MetricCard/MetricCard.chart.js +435 -0
- package/dist/components/MetricCard/MetricCard.d.ts +227 -0
- package/dist/components/MetricCard/MetricCard.js +1293 -0
- package/dist/components/MetricCard/MetricCard.model.d.ts +521 -0
- package/dist/components/MetricCard/MetricCard.model.js +627 -0
- package/dist/components/MetricCard/index.d.ts +4 -0
- package/dist/components/MetricCard/index.js +4 -0
- package/dist/components/ProfileCard/ProfileCard.chart.d.ts +64 -0
- package/dist/components/ProfileCard/ProfileCard.chart.js +427 -0
- package/dist/components/ProfileCard/ProfileCard.d.ts +140 -0
- package/dist/components/ProfileCard/ProfileCard.js +735 -0
- package/dist/components/ProfileCard/ProfileCard.model.d.ts +279 -0
- package/dist/components/ProfileCard/ProfileCard.model.js +244 -0
- package/dist/components/ProfileCard/index.d.ts +4 -0
- package/dist/components/ProfileCard/index.js +4 -0
- package/dist/components/ProgressCard/ProgressCard.chart.d.ts +73 -0
- package/dist/components/ProgressCard/ProgressCard.chart.js +373 -0
- package/dist/components/ProgressCard/ProgressCard.d.ts +137 -0
- package/dist/components/ProgressCard/ProgressCard.js +908 -0
- package/dist/components/ProgressCard/ProgressCard.model.d.ts +385 -0
- package/dist/components/ProgressCard/ProgressCard.model.js +389 -0
- package/dist/components/ProgressCard/index.d.ts +4 -0
- package/dist/components/ProgressCard/index.js +4 -0
- package/dist/components/ReportCard/ReportCard.chart.d.ts +45 -0
- package/dist/components/ReportCard/ReportCard.chart.js +231 -0
- package/dist/components/ReportCard/ReportCard.d.ts +153 -0
- package/dist/components/ReportCard/ReportCard.js +1118 -0
- package/dist/components/ReportCard/ReportCard.model.d.ts +392 -0
- package/dist/components/ReportCard/ReportCard.model.js +399 -0
- package/dist/components/ReportCard/index.d.ts +4 -0
- package/dist/components/ReportCard/index.js +4 -0
- package/dist/components/SankeyCard/SankeyCard.chart.d.ts +75 -0
- package/dist/components/SankeyCard/SankeyCard.chart.js +249 -0
- package/dist/components/SankeyCard/SankeyCard.d.ts +185 -0
- package/dist/components/SankeyCard/SankeyCard.js +788 -0
- package/dist/components/SankeyCard/SankeyCard.model.d.ts +501 -0
- package/dist/components/SankeyCard/SankeyCard.model.js +789 -0
- package/dist/components/SankeyCard/index.d.ts +4 -0
- package/dist/components/SankeyCard/index.js +4 -0
- package/dist/components/StatusCard/StatusCard.chart.d.ts +60 -0
- package/dist/components/StatusCard/StatusCard.chart.js +188 -0
- package/dist/components/StatusCard/StatusCard.d.ts +176 -0
- package/dist/components/StatusCard/StatusCard.js +989 -0
- package/dist/components/StatusCard/StatusCard.model.d.ts +555 -0
- package/dist/components/StatusCard/StatusCard.model.js +776 -0
- package/dist/components/StatusCard/index.d.ts +4 -0
- package/dist/components/StatusCard/index.js +4 -0
- package/dist/components/TableCard/TableCard.chart.d.ts +53 -0
- package/dist/components/TableCard/TableCard.chart.js +192 -0
- package/dist/components/TableCard/TableCard.d.ts +140 -0
- package/dist/components/TableCard/TableCard.js +811 -0
- package/dist/components/TableCard/TableCard.model.d.ts +381 -0
- package/dist/components/TableCard/TableCard.model.js +475 -0
- package/dist/components/TableCard/index.d.ts +4 -0
- package/dist/components/TableCard/index.js +4 -0
- package/dist/components/TargetCard/TargetCard.chart.d.ts +52 -0
- package/dist/components/TargetCard/TargetCard.chart.js +273 -0
- package/dist/components/TargetCard/TargetCard.d.ts +143 -0
- package/dist/components/TargetCard/TargetCard.js +910 -0
- package/dist/components/TargetCard/TargetCard.model.d.ts +468 -0
- package/dist/components/TargetCard/TargetCard.model.js +581 -0
- package/dist/components/TargetCard/index.d.ts +4 -0
- package/dist/components/TargetCard/index.js +4 -0
- package/dist/components/TreeCard/TreeCard.chart.d.ts +80 -0
- package/dist/components/TreeCard/TreeCard.chart.js +178 -0
- package/dist/components/TreeCard/TreeCard.d.ts +182 -0
- package/dist/components/TreeCard/TreeCard.js +1187 -0
- package/dist/components/TreeCard/TreeCard.model.d.ts +330 -0
- package/dist/components/TreeCard/TreeCard.model.js +401 -0
- package/dist/components/TreeCard/index.d.ts +4 -0
- package/dist/components/TreeCard/index.js +4 -0
- package/dist/components/TrendCard/TrendCard.chart.d.ts +75 -0
- package/dist/components/TrendCard/TrendCard.chart.js +393 -0
- package/dist/components/TrendCard/TrendCard.d.ts +224 -0
- package/dist/components/TrendCard/TrendCard.js +1053 -0
- package/dist/components/TrendCard/TrendCard.model.d.ts +545 -0
- package/dist/components/TrendCard/TrendCard.model.js +623 -0
- package/dist/components/TrendCard/index.d.ts +4 -0
- package/dist/components/TrendCard/index.js +4 -0
- package/dist/index.d.ts +76 -0
- package/dist/index.js +57 -0
- package/dist/lib/vega/ArcVegaChart.d.ts +24 -0
- package/dist/lib/vega/ArcVegaChart.js +42 -0
- package/dist/lib/vega/ArcVegaSurface.d.ts +13 -0
- package/dist/lib/vega/ArcVegaSurface.js +38 -0
- package/dist/lib/vega/axisTicks.js +156 -0
- package/dist/lib/vega/config.d.ts +29 -0
- package/dist/lib/vega/config.js +113 -0
- package/dist/lib/vega/embed.d.ts +29 -0
- package/dist/lib/vega/embed.js +29 -0
- package/dist/lib/vega/engine.d.ts +10 -0
- package/dist/lib/vega/engine.js +6 -0
- package/dist/lib/vega/formatters.d.ts +125 -0
- package/dist/lib/vega/formatters.js +277 -0
- package/dist/lib/vega/index.d.ts +11 -0
- package/dist/lib/vega/load.d.ts +13 -0
- package/dist/lib/vega/load.js +21 -0
- package/dist/lib/vega/time.d.ts +45 -0
- package/dist/lib/vega/time.js +105 -0
- package/dist/lib/vega/tokens.d.ts +73 -0
- package/dist/lib/vega/tokens.js +137 -0
- package/dist/lib/vega/tooltip.d.ts +45 -0
- package/dist/lib/vega/tooltip.js +124 -0
- package/dist/lib/vega/useArcVega.d.ts +43 -0
- package/dist/lib/vega/useArcVega.js +121 -0
- package/package.json +60 -0
|
@@ -0,0 +1,279 @@
|
|
|
1
|
+
//#region src/components/ProfileCard/ProfileCard.model.d.ts
|
|
2
|
+
/** What the consumer's transport reports. */
|
|
3
|
+
type ProfileCardStatus = "loading" | "ready" | "error";
|
|
4
|
+
/**
|
|
5
|
+
* What the card actually shows, derived from `status`, the data and
|
|
6
|
+
* `filtered`. Stamped on the root as `data-state`.
|
|
7
|
+
*/
|
|
8
|
+
type ProfileCardState = "loading" | "refreshing" | "ready" | "empty" | "no-match" | "error";
|
|
9
|
+
type ProfileCardSize = "sm" | "md" | "lg";
|
|
10
|
+
/**
|
|
11
|
+
* The form the plot draws.
|
|
12
|
+
*
|
|
13
|
+
* - `radar` (default): each item is a closed polygon over the metric axes.
|
|
14
|
+
* The card exists for the question a radar answers — *what SHAPE is this
|
|
15
|
+
* item across these measures* — and the literature is otherwise hostile to
|
|
16
|
+
* the form, so the card enforces what makes it defensible: the axis order
|
|
17
|
+
* is the consumer's `metrics` order and never sorted, the readings are
|
|
18
|
+
* brought onto one radial scale, the polygon is a stroke with no area fill
|
|
19
|
+
* unless `fill` is asked for, and the caps below are stamped and warned
|
|
20
|
+
* about rather than silently exceeded.
|
|
21
|
+
* - `grouped`: one bar per item inside each metric's band. The accurate
|
|
22
|
+
* form — length on a shared baseline — and the right answer whenever the
|
|
23
|
+
* reader must compare VALUES rather than recognise a shape.
|
|
24
|
+
* - `parallel`: one path per item across parallel metric axes. Scales past
|
|
25
|
+
* the radar's series cap and never asks the reader to judge an area; the
|
|
26
|
+
* form the FT Visual Vocabulary names as radar's alternative.
|
|
27
|
+
*
|
|
28
|
+
* Stamped as `data-mark`.
|
|
29
|
+
*/
|
|
30
|
+
type ProfileMark = "radar" | "grouped" | "parallel";
|
|
31
|
+
/**
|
|
32
|
+
* How readings on different measures are brought onto one axis.
|
|
33
|
+
*
|
|
34
|
+
* - `shared` (default): one scale across every metric, from zero to the
|
|
35
|
+
* highest reading. Honest only when the metrics are COMMENSURABLE — five
|
|
36
|
+
* 0–100 scores, five 1–5 ratings, five rates. It is the only mode that
|
|
37
|
+
* keeps "further out means more" true in the same units on every axis.
|
|
38
|
+
* - `max`: each metric is divided by its own ceiling — `ProfileMetric.max`
|
|
39
|
+
* when declared, the highest observed reading otherwise. Use it when the
|
|
40
|
+
* measures have different units but each has a meaningful maximum.
|
|
41
|
+
* - `minmax`: each metric is rescaled from its own observed min to its own
|
|
42
|
+
* observed max. Readable, and the most misleading: the axes stop being
|
|
43
|
+
* comparable, the lowest item always sits at the centre however good it
|
|
44
|
+
* is, and adding or removing an item moves every other item's shape. Reach
|
|
45
|
+
* for it last, and never when a reader might read the enclosed area as a
|
|
46
|
+
* score.
|
|
47
|
+
*
|
|
48
|
+
* Stamped as `data-normalize`.
|
|
49
|
+
*/
|
|
50
|
+
type ProfileNormalize = "shared" | "max" | "minmax";
|
|
51
|
+
/** A row's part in the comparison — the subject, or another item. */
|
|
52
|
+
type ProfileRole = "focus" | "item";
|
|
53
|
+
/**
|
|
54
|
+
* The caps that keep a radar readable, from the form's own literature
|
|
55
|
+
* (Highcharts' "when they fail"; data-to-viz). Past them the card stamps
|
|
56
|
+
* `data-over-cap` and the docs point at `parallel` or `grouped`; it never
|
|
57
|
+
* silently drops data.
|
|
58
|
+
*/
|
|
59
|
+
declare const RADAR_MAX_AXES = 8;
|
|
60
|
+
declare const RADAR_MAX_SERIES = 3;
|
|
61
|
+
/** Below three axes a polygon is a line or a point — there is no shape to read. */
|
|
62
|
+
declare const RADAR_MIN_AXES = 3;
|
|
63
|
+
/**
|
|
64
|
+
* One measure — an axis. The ORDER of the `metrics` array is the axis order
|
|
65
|
+
* and the card never sorts it, because a radar's shape changes when the axes
|
|
66
|
+
* are reordered: the enclosed area is a sum of products of ADJACENT readings,
|
|
67
|
+
* so the same numbers in a different order enclose a different area. A stable,
|
|
68
|
+
* semantic order is what makes the shape mean anything.
|
|
69
|
+
*/
|
|
70
|
+
interface ProfileMetric {
|
|
71
|
+
key?: string;
|
|
72
|
+
label: string;
|
|
73
|
+
/** The measure's ceiling for `normalize="max"`. Falls back to the highest observed reading. */
|
|
74
|
+
max?: number;
|
|
75
|
+
/** The measure's floor for `normalize="minmax"`. Falls back to the lowest observed reading. */
|
|
76
|
+
min?: number;
|
|
77
|
+
/** `arcNumber` grammar for this measure alone, when it does not share the card's `format` (`percent:1`, `currency:THB`). */
|
|
78
|
+
format?: string;
|
|
79
|
+
}
|
|
80
|
+
/** One thing being profiled — a plan, a team, a vendor, a segment. */
|
|
81
|
+
interface ProfileItem {
|
|
82
|
+
key?: string;
|
|
83
|
+
label: string;
|
|
84
|
+
/** Reading per metric key. A metric this item has no reading for is a gap, not a zero — omit it or pass `null`. */
|
|
85
|
+
values: Readonly<Record<string, number | null>>;
|
|
86
|
+
}
|
|
87
|
+
/** A metric after normalisation, with the scaling the plot applies. */
|
|
88
|
+
interface ProfileMetricInfo {
|
|
89
|
+
key: string;
|
|
90
|
+
label: string;
|
|
91
|
+
/** Position on the axis ring / the parallel axes — the consumer's order, always. */
|
|
92
|
+
index: number;
|
|
93
|
+
format?: string;
|
|
94
|
+
/** The low and high this metric's readings are mapped from; equal for a metric with one distinct reading. */
|
|
95
|
+
low: number;
|
|
96
|
+
high: number;
|
|
97
|
+
/** Highest reading across the items, or `null` when nobody reported this metric. */
|
|
98
|
+
best: number | null;
|
|
99
|
+
/** The item key holding `best`; `null` when nobody reported it or two items tie. */
|
|
100
|
+
leader: string | null;
|
|
101
|
+
}
|
|
102
|
+
/** An item after normalisation, with its readings aligned to `metrics`. */
|
|
103
|
+
interface ProfileSeries {
|
|
104
|
+
key: string;
|
|
105
|
+
label: string;
|
|
106
|
+
/** Index in the consumer's order — the identity slot, never the rank, so a filter cannot repaint the survivors. */
|
|
107
|
+
index: number;
|
|
108
|
+
/** The paintable colour, baked in so the spec stays structural and hiding a series never repaints a survivor. */
|
|
109
|
+
color: string;
|
|
110
|
+
role: ProfileRole;
|
|
111
|
+
/** Readings aligned to `ProfileSummary.metrics`; `null` for a gap. */
|
|
112
|
+
values: (number | null)[];
|
|
113
|
+
/** The same readings mapped to 0–1 by `normalize`; `null` for a gap. */
|
|
114
|
+
normalized: (number | null)[];
|
|
115
|
+
/** Metrics with a finite reading. */
|
|
116
|
+
count: number;
|
|
117
|
+
/** Metrics where this item holds the highest reading. */
|
|
118
|
+
leads: number;
|
|
119
|
+
}
|
|
120
|
+
/** One long-form cell the plot reads: metric index, series key, and the colour the path paints with. */
|
|
121
|
+
interface ProfileCell {
|
|
122
|
+
/** Metric index — the ANGLE of a radar vertex and the path order of a parallel line. Numeric, because `order` is path order. */
|
|
123
|
+
i: number;
|
|
124
|
+
/** Metric key. */
|
|
125
|
+
m: string;
|
|
126
|
+
/** Metric label, for the tooltip and the axis ring. */
|
|
127
|
+
ml: string;
|
|
128
|
+
/** Series key. */
|
|
129
|
+
s: string;
|
|
130
|
+
/** Series label. */
|
|
131
|
+
sl: string;
|
|
132
|
+
/** The reading as it arrived. */
|
|
133
|
+
raw: number | null;
|
|
134
|
+
/** The reading mapped to 0–1 by `normalize` — what the plot positions. */
|
|
135
|
+
n: number | null;
|
|
136
|
+
/** The paintable colour for this series. */
|
|
137
|
+
c: string;
|
|
138
|
+
/** This cell belongs to the focused series. */
|
|
139
|
+
f: boolean;
|
|
140
|
+
}
|
|
141
|
+
interface ProfileSummary {
|
|
142
|
+
metrics: ProfileMetricInfo[];
|
|
143
|
+
series: ProfileSeries[];
|
|
144
|
+
/** Long-form cells for the visible series, in series × metric order. Only cells with a finite reading. */
|
|
145
|
+
cells: ProfileCell[];
|
|
146
|
+
/** The subject, when one resolved. */
|
|
147
|
+
focus: ProfileSeries | null;
|
|
148
|
+
/** Readings with a finite value across every visible series. */
|
|
149
|
+
count: number;
|
|
150
|
+
/** More axes than a radar can carry, or more series than it can overlay. */
|
|
151
|
+
overCap: boolean;
|
|
152
|
+
/** Fewer than three axes — a radar has no shape to draw. */
|
|
153
|
+
underCap: boolean;
|
|
154
|
+
}
|
|
155
|
+
/** Every string the card renders or announces, so a product can localise it. Sentences are functions. */
|
|
156
|
+
interface ProfileCardLabels {
|
|
157
|
+
noData: string;
|
|
158
|
+
noMatch: string;
|
|
159
|
+
clearFilters: string;
|
|
160
|
+
error: string;
|
|
161
|
+
retry: string;
|
|
162
|
+
stale: string;
|
|
163
|
+
asOf: string;
|
|
164
|
+
tableMetric: string;
|
|
165
|
+
/** Header of the popover table's leader column. */
|
|
166
|
+
tableLeader: string;
|
|
167
|
+
showTable: (name: string) => string;
|
|
168
|
+
hideTable: (name: string) => string;
|
|
169
|
+
about: (name: string) => string;
|
|
170
|
+
loading: (name: string) => string;
|
|
171
|
+
tableCaption: (name: string) => string;
|
|
172
|
+
/** Accessible name of the series legend — a group of toggles. */
|
|
173
|
+
seriesGroup: (name: string) => string;
|
|
174
|
+
/** Toggle a series in or out of the plot. */
|
|
175
|
+
hideSeries: (series: string) => string;
|
|
176
|
+
showSeries: (series: string) => string;
|
|
177
|
+
/** Read out after the focus item's label. */
|
|
178
|
+
highlighted: string;
|
|
179
|
+
/** How each mark names itself at the start of the plot's `aria-label`. */
|
|
180
|
+
markNames: Record<ProfileMark, string>;
|
|
181
|
+
/** Says how the readings were brought onto one axis, so a reader is never misled by a rescaled axis. */
|
|
182
|
+
normalizeNote: Record<ProfileNormalize, string>;
|
|
183
|
+
/** Sentence naming where the subject leads and where it trails. */
|
|
184
|
+
profileSentence: (input: {
|
|
185
|
+
name: string;
|
|
186
|
+
strongest: string;
|
|
187
|
+
strongestValue: string;
|
|
188
|
+
weakest: string;
|
|
189
|
+
weakestValue: string;
|
|
190
|
+
}) => string;
|
|
191
|
+
/** Lists the items on the plot. */
|
|
192
|
+
seriesSummary: (count: number, list: string) => string;
|
|
193
|
+
/** The plot's own `aria-label`. */
|
|
194
|
+
chartDescription: (input: {
|
|
195
|
+
mark: string;
|
|
196
|
+
name: string;
|
|
197
|
+
metrics: number;
|
|
198
|
+
series: number;
|
|
199
|
+
metricList: string;
|
|
200
|
+
focus: string | null;
|
|
201
|
+
normalize: string;
|
|
202
|
+
}) => string;
|
|
203
|
+
/** Accessible name of the AI mark beside the insight sentence. */
|
|
204
|
+
insightMark: string;
|
|
205
|
+
}
|
|
206
|
+
declare const DEFAULT_PROFILE_LABELS: ProfileCardLabels;
|
|
207
|
+
declare function resolveProfileLabels(overrides?: Partial<ProfileCardLabels>): ProfileCardLabels;
|
|
208
|
+
interface DeriveProfileStateInput {
|
|
209
|
+
status: ProfileCardStatus;
|
|
210
|
+
/** At least one item has a finite reading on at least one metric. */
|
|
211
|
+
hasValue: boolean;
|
|
212
|
+
/** Anything is on screen already, so a reload is a refresh rather than a first load. */
|
|
213
|
+
hasData: boolean;
|
|
214
|
+
filtered: boolean;
|
|
215
|
+
}
|
|
216
|
+
/**
|
|
217
|
+
* The state machine. Error wins; loading over existing data is a refresh (the
|
|
218
|
+
* previous render stays under the veil); no readings is `no-match` when the
|
|
219
|
+
* consumer's filter caused it and `empty` otherwise.
|
|
220
|
+
*/
|
|
221
|
+
declare function deriveProfileState({ status, hasValue, hasData, filtered }: DeriveProfileStateInput): ProfileCardState;
|
|
222
|
+
interface NormalizeProfileOptions {
|
|
223
|
+
/** Key of the subject. `undefined` takes the first item; `null` designates none. */
|
|
224
|
+
focus?: string | null;
|
|
225
|
+
normalize?: ProfileNormalize;
|
|
226
|
+
/** Series keys the reader has hidden through the legend. */
|
|
227
|
+
hidden?: readonly string[];
|
|
228
|
+
/** Identity slots in fixed order — a colour per item, assigned by position and never by rank. */
|
|
229
|
+
palette: readonly string[];
|
|
230
|
+
/** The one hue a single-item profile draws with. */
|
|
231
|
+
emphasis: string;
|
|
232
|
+
}
|
|
233
|
+
/**
|
|
234
|
+
* Normalises `metrics` and `items` for the plot, the legend, the table and
|
|
235
|
+
* the text equivalents: keys default to labels (a repeat is suffixed), the
|
|
236
|
+
* axis order is the consumer's order and is never sorted, hidden series drop
|
|
237
|
+
* out of the plot but keep their colour slot, every reading is mapped to 0–1
|
|
238
|
+
* by `normalize`, and the leader of each metric is resolved (`null` on a tie,
|
|
239
|
+
* because "joint best" is not a leader). Returns `null` when there is nothing
|
|
240
|
+
* to speak of.
|
|
241
|
+
*/
|
|
242
|
+
declare function normalizeProfile(metrics: readonly ProfileMetric[] | undefined, items: readonly ProfileItem[] | undefined, { focus, normalize, hidden, palette, emphasis }: NormalizeProfileOptions): ProfileSummary | null;
|
|
243
|
+
interface ProfileTextFormatters {
|
|
244
|
+
/** Formats a reading with its metric's own grammar when it has one. */
|
|
245
|
+
formatValue: (value: number | null | undefined, metric?: ProfileMetricInfo) => string;
|
|
246
|
+
}
|
|
247
|
+
declare function createProfileFormatters({ format, locale, formatValue }: {
|
|
248
|
+
format: string;
|
|
249
|
+
locale?: string;
|
|
250
|
+
formatValue?: (value: number, metric?: ProfileMetricInfo) => string;
|
|
251
|
+
}): ProfileTextFormatters;
|
|
252
|
+
/** Where the subject leads and where it trails — the takeaway a profile exists to deliver. */
|
|
253
|
+
declare function deriveProfileHighlights(summary: ProfileSummary | null, formatters: ProfileTextFormatters): {
|
|
254
|
+
strongest: ProfileMetricInfo;
|
|
255
|
+
strongestValue: string;
|
|
256
|
+
weakest: ProfileMetricInfo;
|
|
257
|
+
weakestValue: string;
|
|
258
|
+
} | null;
|
|
259
|
+
interface BuildProfileDescriptionInput {
|
|
260
|
+
name: string;
|
|
261
|
+
state: ProfileCardState;
|
|
262
|
+
summary: ProfileSummary | null;
|
|
263
|
+
formatters: ProfileTextFormatters;
|
|
264
|
+
normalize: ProfileNormalize;
|
|
265
|
+
asOfText?: string | null;
|
|
266
|
+
labels: ProfileCardLabels;
|
|
267
|
+
}
|
|
268
|
+
/** The figure's generated description: what is plotted, where the subject leads and trails, every item, and the as-of. */
|
|
269
|
+
declare function buildProfileDescription({ name, state, summary, formatters, normalize, asOfText, labels }: BuildProfileDescriptionInput): string;
|
|
270
|
+
/** The plot's own `aria-label` — the form, what is on it, and how the axes were scaled. */
|
|
271
|
+
declare function buildProfileChartDescription({ mark, name, summary, normalize, labels }: {
|
|
272
|
+
mark: ProfileMark;
|
|
273
|
+
name: string;
|
|
274
|
+
summary: ProfileSummary;
|
|
275
|
+
normalize: ProfileNormalize;
|
|
276
|
+
labels: ProfileCardLabels;
|
|
277
|
+
}): string;
|
|
278
|
+
//#endregion
|
|
279
|
+
export { BuildProfileDescriptionInput, DEFAULT_PROFILE_LABELS, DeriveProfileStateInput, NormalizeProfileOptions, ProfileCardLabels, ProfileCardSize, ProfileCardState, ProfileCardStatus, ProfileCell, ProfileItem, ProfileMark, ProfileMetric, ProfileMetricInfo, ProfileNormalize, ProfileRole, ProfileSeries, ProfileSummary, ProfileTextFormatters, RADAR_MAX_AXES, RADAR_MAX_SERIES, RADAR_MIN_AXES, buildProfileChartDescription, buildProfileDescription, createProfileFormatters, deriveProfileHighlights, deriveProfileState, normalizeProfile, resolveProfileLabels };
|
|
@@ -0,0 +1,244 @@
|
|
|
1
|
+
import { formatNumber } from "../../lib/vega/formatters.js";
|
|
2
|
+
//#region src/components/ProfileCard/ProfileCard.model.ts
|
|
3
|
+
/**
|
|
4
|
+
* The caps that keep a radar readable, from the form's own literature
|
|
5
|
+
* (Highcharts' "when they fail"; data-to-viz). Past them the card stamps
|
|
6
|
+
* `data-over-cap` and the docs point at `parallel` or `grouped`; it never
|
|
7
|
+
* silently drops data.
|
|
8
|
+
*/
|
|
9
|
+
const RADAR_MAX_AXES = 8;
|
|
10
|
+
const RADAR_MAX_SERIES = 3;
|
|
11
|
+
/** Below three axes a polygon is a line or a point — there is no shape to read. */
|
|
12
|
+
const RADAR_MIN_AXES = 3;
|
|
13
|
+
const DEFAULT_PROFILE_LABELS = {
|
|
14
|
+
noData: "No data",
|
|
15
|
+
noMatch: "No matching data",
|
|
16
|
+
clearFilters: "Clear filters",
|
|
17
|
+
error: "Couldn't load this profile",
|
|
18
|
+
retry: "Try again",
|
|
19
|
+
stale: "Stale",
|
|
20
|
+
asOf: "As of",
|
|
21
|
+
tableMetric: "Metric",
|
|
22
|
+
tableLeader: "Leader",
|
|
23
|
+
showTable: (name) => `Show the data behind ${name}`,
|
|
24
|
+
hideTable: (name) => `Hide the data behind ${name}`,
|
|
25
|
+
about: (name) => `About ${name}`,
|
|
26
|
+
loading: (name) => `Loading ${name}`,
|
|
27
|
+
tableCaption: (name) => `Data behind the ${name} profile`,
|
|
28
|
+
seriesGroup: (name) => `Choose which items ${name} plots`,
|
|
29
|
+
hideSeries: (series) => `Hide ${series}`,
|
|
30
|
+
showSeries: (series) => `Show ${series}`,
|
|
31
|
+
highlighted: "highlighted",
|
|
32
|
+
markNames: {
|
|
33
|
+
radar: "Radar",
|
|
34
|
+
grouped: "Grouped bar",
|
|
35
|
+
parallel: "Parallel coordinates"
|
|
36
|
+
},
|
|
37
|
+
normalizeNote: {
|
|
38
|
+
shared: "All metrics share one scale.",
|
|
39
|
+
max: "Each metric is scaled to its own maximum.",
|
|
40
|
+
minmax: "Each metric is scaled to its own range, so the axes are not comparable."
|
|
41
|
+
},
|
|
42
|
+
profileSentence: ({ name, strongest, strongestValue, weakest, weakestValue }) => `${name} is strongest on ${strongest} (${strongestValue}) and weakest on ${weakest} (${weakestValue}).`,
|
|
43
|
+
seriesSummary: (count, list) => `${count} ${count === 1 ? "item" : "items"}: ${list}.`,
|
|
44
|
+
chartDescription: ({ mark, name, metrics, series, metricList, focus, normalize }) => `${mark} profile of ${name}: ${series} ${series === 1 ? "item" : "items"} across ${metrics} ${metrics === 1 ? "metric" : "metrics"} — ${metricList}.` + (focus ? ` ${focus} highlighted.` : "") + ` ${normalize}`,
|
|
45
|
+
insightMark: "Insight"
|
|
46
|
+
};
|
|
47
|
+
function resolveProfileLabels(overrides) {
|
|
48
|
+
return overrides ? {
|
|
49
|
+
...DEFAULT_PROFILE_LABELS,
|
|
50
|
+
...overrides
|
|
51
|
+
} : DEFAULT_PROFILE_LABELS;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* The state machine. Error wins; loading over existing data is a refresh (the
|
|
55
|
+
* previous render stays under the veil); no readings is `no-match` when the
|
|
56
|
+
* consumer's filter caused it and `empty` otherwise.
|
|
57
|
+
*/
|
|
58
|
+
function deriveProfileState({ status, hasValue, hasData, filtered }) {
|
|
59
|
+
if (status === "error") return "error";
|
|
60
|
+
if (status === "loading") return hasData ? "refreshing" : "loading";
|
|
61
|
+
if (hasValue) return "ready";
|
|
62
|
+
return filtered ? "no-match" : "empty";
|
|
63
|
+
}
|
|
64
|
+
const isFinite = (value) => typeof value === "number" && Number.isFinite(value);
|
|
65
|
+
/** Suffix a repeated key so two entries never share an axis or a colour slot. */
|
|
66
|
+
function uniqueKeys(entries, read) {
|
|
67
|
+
const seen = /* @__PURE__ */ new Map();
|
|
68
|
+
return entries.map((entry) => {
|
|
69
|
+
const base = read(entry);
|
|
70
|
+
const repeats = seen.get(base) ?? 0;
|
|
71
|
+
seen.set(base, repeats + 1);
|
|
72
|
+
return repeats === 0 ? base : `${base}·${repeats + 1}`;
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Normalises `metrics` and `items` for the plot, the legend, the table and
|
|
77
|
+
* the text equivalents: keys default to labels (a repeat is suffixed), the
|
|
78
|
+
* axis order is the consumer's order and is never sorted, hidden series drop
|
|
79
|
+
* out of the plot but keep their colour slot, every reading is mapped to 0–1
|
|
80
|
+
* by `normalize`, and the leader of each metric is resolved (`null` on a tie,
|
|
81
|
+
* because "joint best" is not a leader). Returns `null` when there is nothing
|
|
82
|
+
* to speak of.
|
|
83
|
+
*/
|
|
84
|
+
function normalizeProfile(metrics, items, { focus, normalize = "shared", hidden, palette, emphasis }) {
|
|
85
|
+
if (!metrics || !items) return null;
|
|
86
|
+
const metricKeys = uniqueKeys(metrics, (metric) => metric.key ?? metric.label);
|
|
87
|
+
const itemKeys = uniqueKeys(items, (item) => item.key ?? item.label);
|
|
88
|
+
const hiddenSet = new Set(hidden ?? []);
|
|
89
|
+
const visible = items.map((_, index) => !hiddenSet.has(itemKeys[index]));
|
|
90
|
+
const readingsFor = (metricKey) => items.map((item, index) => visible[index] ? item.values[metricKey] : void 0).filter((value) => isFinite(value));
|
|
91
|
+
const metricInfos = metrics.map((metric, index) => {
|
|
92
|
+
const key = metricKeys[index];
|
|
93
|
+
const readings = readingsFor(key);
|
|
94
|
+
const observedHigh = readings.length > 0 ? Math.max(...readings) : 0;
|
|
95
|
+
const observedLow = readings.length > 0 ? Math.min(...readings) : 0;
|
|
96
|
+
const best = readings.length > 0 ? observedHigh : null;
|
|
97
|
+
const holders = items.map((item, i) => visible[i] && best != null && item.values[key] === best ? itemKeys[i] : null).filter((k) => k != null);
|
|
98
|
+
return {
|
|
99
|
+
key,
|
|
100
|
+
label: metric.label,
|
|
101
|
+
index,
|
|
102
|
+
format: metric.format,
|
|
103
|
+
low: normalize === "minmax" ? metric.min ?? observedLow : 0,
|
|
104
|
+
high: normalize === "shared" ? 0 : Math.max(metric.max ?? observedHigh, normalize === "minmax" ? metric.min ?? observedLow : 0),
|
|
105
|
+
best,
|
|
106
|
+
leader: holders.length === 1 ? holders[0] : null
|
|
107
|
+
};
|
|
108
|
+
});
|
|
109
|
+
if (normalize === "shared") {
|
|
110
|
+
const all = metricInfos.flatMap((metric) => readingsFor(metric.key));
|
|
111
|
+
const declared = metrics.map((metric) => metric.max).filter(isFinite);
|
|
112
|
+
const high = Math.max(0, ...all, ...declared);
|
|
113
|
+
for (const metric of metricInfos) {
|
|
114
|
+
metric.low = 0;
|
|
115
|
+
metric.high = high;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
const scaleOne = (metric, value) => {
|
|
119
|
+
const span = metric.high - metric.low;
|
|
120
|
+
if (span <= 0) return metric.high > 0 || value > 0 ? 1 : 0;
|
|
121
|
+
return Math.min(1, Math.max(0, (value - metric.low) / span));
|
|
122
|
+
};
|
|
123
|
+
const series = [];
|
|
124
|
+
const cells = [];
|
|
125
|
+
const focusKey = focus === null ? null : focus === void 0 ? itemKeys[0] ?? null : itemKeys.includes(focus) ? focus : null;
|
|
126
|
+
const single = items.length - hiddenSet.size <= 1;
|
|
127
|
+
items.forEach((item, index) => {
|
|
128
|
+
const key = itemKeys[index];
|
|
129
|
+
if (hiddenSet.has(key)) return;
|
|
130
|
+
const color = single ? emphasis : palette[index % Math.max(1, palette.length)] ?? emphasis;
|
|
131
|
+
const isFocus = key === focusKey;
|
|
132
|
+
const values = [];
|
|
133
|
+
const normalized = [];
|
|
134
|
+
let count = 0;
|
|
135
|
+
metricInfos.forEach((metric) => {
|
|
136
|
+
const raw = item.values[metric.key];
|
|
137
|
+
const value = isFinite(raw) ? raw : null;
|
|
138
|
+
values.push(value);
|
|
139
|
+
const n = value == null ? null : scaleOne(metric, value);
|
|
140
|
+
normalized.push(n);
|
|
141
|
+
if (value == null) return;
|
|
142
|
+
count += 1;
|
|
143
|
+
cells.push({
|
|
144
|
+
i: metric.index,
|
|
145
|
+
m: metric.key,
|
|
146
|
+
ml: metric.label,
|
|
147
|
+
s: key,
|
|
148
|
+
sl: item.label,
|
|
149
|
+
raw: value,
|
|
150
|
+
n,
|
|
151
|
+
c: color,
|
|
152
|
+
f: isFocus
|
|
153
|
+
});
|
|
154
|
+
});
|
|
155
|
+
series.push({
|
|
156
|
+
key,
|
|
157
|
+
label: item.label,
|
|
158
|
+
index,
|
|
159
|
+
color,
|
|
160
|
+
role: isFocus ? "focus" : "item",
|
|
161
|
+
values,
|
|
162
|
+
normalized,
|
|
163
|
+
count,
|
|
164
|
+
leads: metricInfos.filter((metric) => metric.leader === key).length
|
|
165
|
+
});
|
|
166
|
+
});
|
|
167
|
+
return {
|
|
168
|
+
metrics: metricInfos,
|
|
169
|
+
series,
|
|
170
|
+
cells,
|
|
171
|
+
focus: series.find((entry) => entry.role === "focus") ?? null,
|
|
172
|
+
count: cells.length,
|
|
173
|
+
overCap: metricInfos.length > 8 || series.length > 3,
|
|
174
|
+
underCap: metricInfos.length < 3
|
|
175
|
+
};
|
|
176
|
+
}
|
|
177
|
+
function createProfileFormatters({ format, locale, formatValue }) {
|
|
178
|
+
return { formatValue: (value, metric) => isFinite(value) ? formatValue ? formatValue(value, metric) : formatNumber(value, metric?.format ?? format, locale) : "—" };
|
|
179
|
+
}
|
|
180
|
+
/** Where the subject leads and where it trails — the takeaway a profile exists to deliver. */
|
|
181
|
+
function deriveProfileHighlights(summary, formatters) {
|
|
182
|
+
const focus = summary?.focus;
|
|
183
|
+
if (!summary || !focus || focus.count < 2) return null;
|
|
184
|
+
let strongest = null;
|
|
185
|
+
let weakest = null;
|
|
186
|
+
let high = -Infinity;
|
|
187
|
+
let low = Infinity;
|
|
188
|
+
summary.metrics.forEach((metric, index) => {
|
|
189
|
+
const n = focus.normalized[index];
|
|
190
|
+
if (n == null) return;
|
|
191
|
+
if (n > high) {
|
|
192
|
+
high = n;
|
|
193
|
+
strongest = metric;
|
|
194
|
+
}
|
|
195
|
+
if (n < low) {
|
|
196
|
+
low = n;
|
|
197
|
+
weakest = metric;
|
|
198
|
+
}
|
|
199
|
+
});
|
|
200
|
+
if (!strongest || !weakest || strongest === weakest) return null;
|
|
201
|
+
const s = strongest;
|
|
202
|
+
const w = weakest;
|
|
203
|
+
return {
|
|
204
|
+
strongest: s,
|
|
205
|
+
strongestValue: formatters.formatValue(focus.values[s.index], s),
|
|
206
|
+
weakest: w,
|
|
207
|
+
weakestValue: formatters.formatValue(focus.values[w.index], w)
|
|
208
|
+
};
|
|
209
|
+
}
|
|
210
|
+
/** The figure's generated description: what is plotted, where the subject leads and trails, every item, and the as-of. */
|
|
211
|
+
function buildProfileDescription({ name, state, summary, formatters, normalize, asOfText, labels }) {
|
|
212
|
+
if (state === "loading") return labels.loading(name);
|
|
213
|
+
if (state === "error") return `${name}: ${labels.error}`;
|
|
214
|
+
if (state === "empty") return `${name}: ${labels.noData}`;
|
|
215
|
+
if (state === "no-match") return `${name}: ${labels.noMatch}`;
|
|
216
|
+
if (!summary) return name;
|
|
217
|
+
const parts = [`${name}.`];
|
|
218
|
+
const highlights = deriveProfileHighlights(summary, formatters);
|
|
219
|
+
if (highlights && summary.focus) parts.push(labels.profileSentence({
|
|
220
|
+
name: summary.focus.label,
|
|
221
|
+
strongest: highlights.strongest.label,
|
|
222
|
+
strongestValue: highlights.strongestValue,
|
|
223
|
+
weakest: highlights.weakest.label,
|
|
224
|
+
weakestValue: highlights.weakestValue
|
|
225
|
+
}));
|
|
226
|
+
parts.push(labels.seriesSummary(summary.series.length, summary.series.map((entry) => entry.label).join(", ")));
|
|
227
|
+
parts.push(labels.normalizeNote[normalize]);
|
|
228
|
+
if (asOfText) parts.push(`${labels.asOf} ${asOfText}.`);
|
|
229
|
+
return parts.join(" ");
|
|
230
|
+
}
|
|
231
|
+
/** The plot's own `aria-label` — the form, what is on it, and how the axes were scaled. */
|
|
232
|
+
function buildProfileChartDescription({ mark, name, summary, normalize, labels }) {
|
|
233
|
+
return labels.chartDescription({
|
|
234
|
+
mark: labels.markNames[mark],
|
|
235
|
+
name,
|
|
236
|
+
metrics: summary.metrics.length,
|
|
237
|
+
series: summary.series.length,
|
|
238
|
+
metricList: summary.metrics.map((metric) => metric.label).join(", ") || "—",
|
|
239
|
+
focus: summary.focus?.label ?? null,
|
|
240
|
+
normalize: labels.normalizeNote[normalize]
|
|
241
|
+
});
|
|
242
|
+
}
|
|
243
|
+
//#endregion
|
|
244
|
+
export { DEFAULT_PROFILE_LABELS, RADAR_MAX_AXES, RADAR_MAX_SERIES, RADAR_MIN_AXES, buildProfileChartDescription, buildProfileDescription, createProfileFormatters, deriveProfileHighlights, deriveProfileState, normalizeProfile, resolveProfileLabels };
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import { BuildProfileDescriptionInput, DEFAULT_PROFILE_LABELS, DeriveProfileStateInput, NormalizeProfileOptions, ProfileCardLabels, ProfileCardSize, ProfileCardState, ProfileCardStatus, ProfileCell, ProfileItem, ProfileMark, ProfileMetric, ProfileMetricInfo, ProfileNormalize, ProfileRole, ProfileSeries, ProfileSummary, ProfileTextFormatters, RADAR_MAX_AXES, RADAR_MAX_SERIES, RADAR_MIN_AXES, buildProfileChartDescription, buildProfileDescription, createProfileFormatters, deriveProfileHighlights, deriveProfileState, normalizeProfile, resolveProfileLabels } from "./ProfileCard.model.js";
|
|
2
|
+
import { ProfileCard, ProfileCardProps } from "./ProfileCard.js";
|
|
3
|
+
import { PROFILE_AXES_DATASET, PROFILE_CELLS_DATASET, PROFILE_HOVER_PARAM, PROFILE_RINGS_DATASET, ProfilePlotSpecOptions, buildProfilePlotSpec, profileNeedsRings } from "./ProfileCard.chart.js";
|
|
4
|
+
export { type BuildProfileDescriptionInput, DEFAULT_PROFILE_LABELS, type DeriveProfileStateInput, type NormalizeProfileOptions, PROFILE_AXES_DATASET, PROFILE_CELLS_DATASET, PROFILE_HOVER_PARAM, PROFILE_RINGS_DATASET, ProfileCard, type ProfileCardLabels, type ProfileCardProps, type ProfileCardSize, type ProfileCardState, type ProfileCardStatus, type ProfileCell, type ProfileItem, type ProfileMark, type ProfileMetric, type ProfileMetricInfo, type ProfileNormalize, type ProfilePlotSpecOptions, type ProfileRole, type ProfileSeries, type ProfileSummary, type ProfileTextFormatters, RADAR_MAX_AXES, RADAR_MAX_SERIES, RADAR_MIN_AXES, buildProfileChartDescription, buildProfileDescription, buildProfilePlotSpec, createProfileFormatters, deriveProfileHighlights, deriveProfileState, normalizeProfile, profileNeedsRings, resolveProfileLabels };
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import { PROFILE_AXES_DATASET, PROFILE_CELLS_DATASET, PROFILE_HOVER_PARAM, PROFILE_RINGS_DATASET, buildProfilePlotSpec, profileNeedsRings } from "./ProfileCard.chart.js";
|
|
2
|
+
import { DEFAULT_PROFILE_LABELS, RADAR_MAX_AXES, RADAR_MAX_SERIES, RADAR_MIN_AXES, buildProfileChartDescription, buildProfileDescription, createProfileFormatters, deriveProfileHighlights, deriveProfileState, normalizeProfile, resolveProfileLabels } from "./ProfileCard.model.js";
|
|
3
|
+
import { ProfileCard } from "./ProfileCard.js";
|
|
4
|
+
export { DEFAULT_PROFILE_LABELS, PROFILE_AXES_DATASET, PROFILE_CELLS_DATASET, PROFILE_HOVER_PARAM, PROFILE_RINGS_DATASET, ProfileCard, RADAR_MAX_AXES, RADAR_MAX_SERIES, RADAR_MIN_AXES, buildProfileChartDescription, buildProfileDescription, buildProfilePlotSpec, createProfileFormatters, deriveProfileHighlights, deriveProfileState, normalizeProfile, profileNeedsRings, resolveProfileLabels };
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { ChartTokens } from "../../lib/vega/tokens.js";
|
|
2
|
+
import { TopLevelSpec } from "vega-lite";
|
|
3
|
+
//#region src/components/ProgressCard/ProgressCard.chart.d.ts
|
|
4
|
+
/** The completion series: `{ t, y }` per reading, `y` null for a missed one. */
|
|
5
|
+
declare const PROGRESS_COMPLETED_DATASET = "table";
|
|
6
|
+
/** The scope at each reading: `{ t, y }`, drawn `step-after` — scope moves at moments, not gradually. */
|
|
7
|
+
declare const PROGRESS_SCOPE_DATASET = "scope";
|
|
8
|
+
/** The remaining band: `{ t, y, y2 }` per reading — completion up to the scope above it. */
|
|
9
|
+
declare const PROGRESS_GAP_DATASET = "gap";
|
|
10
|
+
/** The required pace: two rows `{ t, y }` from the run's start to the deadline. */
|
|
11
|
+
declare const PROGRESS_IDEAL_DATASET = "ideal";
|
|
12
|
+
/** The projection: two rows `{ t, y, tone }` from the last reading to where the rate crosses the scope. */
|
|
13
|
+
declare const PROGRESS_PROJECTION_DATASET = "projection";
|
|
14
|
+
/** Zero or one row `{ t }` — the deadline's vertical rule. */
|
|
15
|
+
declare const PROGRESS_DUE_DATASET = "due";
|
|
16
|
+
/** One row `{ t, y }` — the last reading, dotted. */
|
|
17
|
+
declare const PROGRESS_LATEST_DATASET = "latest";
|
|
18
|
+
/** Zero or one row `{ t, y, tone }` — where the projection meets the scope. */
|
|
19
|
+
declare const PROGRESS_FINISH_DATASET = "finish";
|
|
20
|
+
/** Responsive x tick values, recomputed by Vega when the domain or width changes. */
|
|
21
|
+
declare const PROGRESS_X_TICKS_PARAM = "progress_x_ticks";
|
|
22
|
+
/** The nearest-reading hover selection. */
|
|
23
|
+
declare const PROGRESS_HOVER_PARAM = "hover";
|
|
24
|
+
interface ProgressBurnUpSpecOptions {
|
|
25
|
+
/** Plot height in pixels, including the x-axis labels. */
|
|
26
|
+
height: number;
|
|
27
|
+
/** Draw the x and y axes. `false` for a sparkline-scale tile — the table still carries the figures. */
|
|
28
|
+
axes: boolean;
|
|
29
|
+
/** Draw the required-pace line (the `ideal` dataset carries its two ends). */
|
|
30
|
+
ideal: boolean;
|
|
31
|
+
/**
|
|
32
|
+
* Draw the projection to the crossing (the `projection` and `finish`
|
|
33
|
+
* datasets carry it). The card passes `false` when the run has no
|
|
34
|
+
* projection to draw: with the x domain unioned from the layers, an empty
|
|
35
|
+
* dataset would put an infinite extent into the shared scale and Vega would
|
|
36
|
+
* warn — so a series the run does not have is not declared. A run that
|
|
37
|
+
* stalls or recovers therefore re-embeds once, which is a change of
|
|
38
|
+
* encoding and not of data.
|
|
39
|
+
*/
|
|
40
|
+
projection: boolean;
|
|
41
|
+
/** Draw the deadline's vertical rule. `false` when there is no due date, for the same reason as `projection`. */
|
|
42
|
+
due: boolean;
|
|
43
|
+
/** The word written at the deadline's rule — a direct label, so the rule needs no legend key of its own. */
|
|
44
|
+
dueLabel: string;
|
|
45
|
+
/** Shade the gap between completion and scope as the work remaining. */
|
|
46
|
+
gap: boolean;
|
|
47
|
+
/** Wire the nearest-reading hover selection and the tooltip channel. */
|
|
48
|
+
tooltip: boolean;
|
|
49
|
+
/** Localised tooltip titles. */
|
|
50
|
+
dateTitle: string;
|
|
51
|
+
completedTitle: string;
|
|
52
|
+
scopeTitle: string;
|
|
53
|
+
/** `arcNumber` grammar for counts (`integer@en-US`). */
|
|
54
|
+
valueFormat: string;
|
|
55
|
+
/** `arcTime` grammar for the tooltip's dates (`day@en-US@Asia/Bangkok`). */
|
|
56
|
+
timeFormat: string;
|
|
57
|
+
/** `arcTime` grammar for x-axis tick labels — the axis-length form (`day-short@…`). */
|
|
58
|
+
axisTimeFormat: string;
|
|
59
|
+
locale?: string;
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Builds the burn-up: the work remaining as a quiet band between completion
|
|
63
|
+
* and scope, the scope as a stepped context line, the required pace as a
|
|
64
|
+
* dashed reference from the run's start to the deadline, cumulative completion
|
|
65
|
+
* as an area and a 2px line from zero (a count, so zero is meaningful and the
|
|
66
|
+
* ink stays proportional), the deadline as a vertical rule, the projection as
|
|
67
|
+
* a dashed continuation in its own polarity hue ending in a hollow dot at the
|
|
68
|
+
* crossing, and a nearest-reading hover carrying the tooltip. Gaps stay gaps:
|
|
69
|
+
* Vega-Lite's default `invalid` handling breaks the path at a null reading.
|
|
70
|
+
*/
|
|
71
|
+
declare function buildProgressBurnUpSpec(options: ProgressBurnUpSpecOptions, tokens: ChartTokens): TopLevelSpec;
|
|
72
|
+
//#endregion
|
|
73
|
+
export { PROGRESS_COMPLETED_DATASET, PROGRESS_DUE_DATASET, PROGRESS_FINISH_DATASET, PROGRESS_GAP_DATASET, PROGRESS_HOVER_PARAM, PROGRESS_IDEAL_DATASET, PROGRESS_LATEST_DATASET, PROGRESS_PROJECTION_DATASET, PROGRESS_SCOPE_DATASET, PROGRESS_X_TICKS_PARAM, ProgressBurnUpSpecOptions, buildProgressBurnUpSpec };
|