@motionscript/charts 0.1.0 → 0.1.1-alpha.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/CHANGELOG.md +19 -0
- package/dist/bar-chart/bar-chart.d.ts +22 -93
- package/dist/bar-chart/bar-chart.d.ts.map +1 -1
- package/dist/bar-chart/bar-chart.js +48 -121
- package/dist/bar-chart/bar-chart.js.map +1 -1
- package/dist/bar-chart/category-axis.d.ts +11 -38
- package/dist/bar-chart/category-axis.d.ts.map +1 -1
- package/dist/bar-chart/category-axis.js +15 -38
- package/dist/bar-chart/category-axis.js.map +1 -1
- package/dist/bar-chart/chart-bars.d.ts +43 -74
- package/dist/bar-chart/chart-bars.d.ts.map +1 -1
- package/dist/bar-chart/chart-bars.js +31 -59
- package/dist/bar-chart/chart-bars.js.map +1 -1
- package/dist/bar-chart/index.d.ts +2 -10
- package/dist/bar-chart/index.d.ts.map +1 -1
- package/dist/bar-chart/index.js +0 -7
- package/dist/bar-chart/index.js.map +1 -1
- package/dist/bar-chart/shared.d.ts +27 -90
- package/dist/bar-chart/shared.d.ts.map +1 -1
- package/dist/bar-chart/shared.js +15 -54
- package/dist/bar-chart/shared.js.map +1 -1
- package/dist/browser/chunks/chunk-4SMRCU24.js +2 -0
- package/dist/browser/chunks/chunk-4SMRCU24.js.map +7 -0
- package/dist/browser/index.js +1 -1
- package/dist/browser/index.js.map +3 -3
- package/dist/browser/kit.js +1 -1
- package/dist/browser/manifest.json +1 -1
- package/dist/engine.d.ts +4 -16
- package/dist/engine.d.ts.map +1 -1
- package/dist/engine.js +4 -16
- package/dist/engine.js.map +1 -1
- package/dist/kit/cartesian.d.ts +8 -28
- package/dist/kit/cartesian.d.ts.map +1 -1
- package/dist/kit/cartesian.js +7 -19
- package/dist/kit/cartesian.js.map +1 -1
- package/dist/kit/chart-border.d.ts +9 -18
- package/dist/kit/chart-border.d.ts.map +1 -1
- package/dist/kit/chart-border.js +4 -9
- package/dist/kit/chart-border.js.map +1 -1
- package/dist/kit/chart-canvas.d.ts +25 -36
- package/dist/kit/chart-canvas.d.ts.map +1 -1
- package/dist/kit/chart-canvas.js +13 -13
- package/dist/kit/chart-canvas.js.map +1 -1
- package/dist/kit/chart-legend.d.ts +14 -61
- package/dist/kit/chart-legend.d.ts.map +1 -1
- package/dist/kit/chart-legend.js +17 -65
- package/dist/kit/chart-legend.js.map +1 -1
- package/dist/kit/entries.d.ts +2 -0
- package/dist/kit/entries.d.ts.map +1 -1
- package/dist/kit/entries.js +2 -0
- package/dist/kit/entries.js.map +1 -1
- package/dist/kit/motion.d.ts +11 -3
- package/dist/kit/motion.d.ts.map +1 -1
- package/dist/kit/motion.js +7 -1
- package/dist/kit/motion.js.map +1 -1
- package/dist/kit/shared.d.ts +60 -175
- package/dist/kit/shared.d.ts.map +1 -1
- package/dist/kit/shared.js +40 -157
- package/dist/kit/shared.js.map +1 -1
- package/dist/kit/vertical-axis.d.ts +16 -45
- package/dist/kit/vertical-axis.d.ts.map +1 -1
- package/dist/kit/vertical-axis.js +17 -42
- package/dist/kit/vertical-axis.js.map +1 -1
- package/dist/line-chart/chart-line.d.ts +16 -41
- package/dist/line-chart/chart-line.d.ts.map +1 -1
- package/dist/line-chart/chart-line.js +14 -43
- package/dist/line-chart/chart-line.js.map +1 -1
- package/dist/line-chart/chart-region.d.ts +14 -27
- package/dist/line-chart/chart-region.d.ts.map +1 -1
- package/dist/line-chart/chart-region.js +8 -22
- package/dist/line-chart/chart-region.js.map +1 -1
- package/dist/line-chart/horizontal-axis.d.ts +16 -43
- package/dist/line-chart/horizontal-axis.d.ts.map +1 -1
- package/dist/line-chart/horizontal-axis.js +13 -42
- package/dist/line-chart/horizontal-axis.js.map +1 -1
- package/dist/line-chart/index.d.ts +1 -13
- package/dist/line-chart/index.d.ts.map +1 -1
- package/dist/line-chart/index.js +0 -12
- package/dist/line-chart/index.js.map +1 -1
- package/dist/line-chart/line-chart.d.ts +23 -109
- package/dist/line-chart/line-chart.d.ts.map +1 -1
- package/dist/line-chart/line-chart.js +45 -141
- package/dist/line-chart/line-chart.js.map +1 -1
- package/dist/line-chart/shared.d.ts +33 -107
- package/dist/line-chart/shared.d.ts.map +1 -1
- package/dist/line-chart/shared.js +28 -81
- package/dist/line-chart/shared.js.map +1 -1
- package/dist/line-chart/x-scale.d.ts +14 -55
- package/dist/line-chart/x-scale.d.ts.map +1 -1
- package/dist/line-chart/x-scale.js +29 -111
- package/dist/line-chart/x-scale.js.map +1 -1
- package/dist/nodes.d.ts +0 -12
- package/dist/nodes.d.ts.map +1 -1
- package/dist/nodes.js +0 -12
- package/dist/nodes.js.map +1 -1
- package/dist/pie-chart/index.d.ts +1 -12
- package/dist/pie-chart/index.d.ts.map +1 -1
- package/dist/pie-chart/index.js +0 -9
- package/dist/pie-chart/index.js.map +1 -1
- package/dist/pie-chart/pie-chart.d.ts +37 -71
- package/dist/pie-chart/pie-chart.d.ts.map +1 -1
- package/dist/pie-chart/pie-chart.js +31 -65
- package/dist/pie-chart/pie-chart.js.map +1 -1
- package/dist/pie-chart/pie-legend.d.ts +11 -26
- package/dist/pie-chart/pie-legend.d.ts.map +1 -1
- package/dist/pie-chart/pie-legend.js +14 -39
- package/dist/pie-chart/pie-legend.js.map +1 -1
- package/dist/pie-chart/pie-ring.d.ts +50 -192
- package/dist/pie-chart/pie-ring.d.ts.map +1 -1
- package/dist/pie-chart/pie-ring.js +75 -247
- package/dist/pie-chart/pie-ring.js.map +1 -1
- package/dist/pie-chart/shared.d.ts +45 -131
- package/dist/pie-chart/shared.d.ts.map +1 -1
- package/dist/pie-chart/shared.js +29 -72
- package/dist/pie-chart/shared.js.map +1 -1
- package/package.json +4 -4
- package/src/bar-chart/bar-chart.ts +51 -165
- package/src/bar-chart/category-axis.ts +16 -57
- package/src/bar-chart/chart-bars.ts +43 -120
- package/src/bar-chart/index.ts +2 -10
- package/src/bar-chart/shared.ts +29 -97
- package/src/engine.ts +4 -16
- package/src/kit/cartesian.ts +11 -26
- package/src/kit/chart-border.ts +6 -20
- package/src/kit/chart-canvas.ts +15 -40
- package/src/kit/chart-legend.ts +17 -86
- package/src/kit/entries.ts +4 -1
- package/src/kit/motion.ts +11 -2
- package/src/kit/shared.ts +58 -219
- package/src/kit/vertical-axis.ts +18 -71
- package/src/line-chart/chart-line.ts +16 -44
- package/src/line-chart/chart-region.ts +10 -23
- package/src/line-chart/horizontal-axis.ts +13 -42
- package/src/line-chart/index.ts +1 -13
- package/src/line-chart/line-chart.ts +50 -184
- package/src/line-chart/shared.ts +51 -145
- package/src/line-chart/x-scale.ts +31 -121
- package/src/nodes.ts +0 -12
- package/src/pie-chart/index.ts +1 -12
- package/src/pie-chart/pie-chart.ts +34 -102
- package/src/pie-chart/pie-legend.ts +14 -55
- package/src/pie-chart/pie-ring.ts +80 -307
- package/src/pie-chart/shared.ts +50 -139
- package/dist/browser/chunks/chunk-TJNHLQGV.js +0 -2
- package/dist/browser/chunks/chunk-TJNHLQGV.js.map +0 -7
package/src/pie-chart/shared.ts
CHANGED
|
@@ -1,20 +1,5 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Types and pure helpers specific to the {@link PieChart} family: how rows
|
|
3
|
-
* become slices, how slices become arcs on a ring, where the labels around it
|
|
4
|
-
* end up, and the theme all of that is drawn from.
|
|
5
|
-
*
|
|
6
|
-
* The one chart in the family that draws **one mark per row** rather than one
|
|
7
|
-
* per column, which is why almost nothing about its geometry is shared: there is
|
|
8
|
-
* no scale, no axis and no baseline, only a circle divided by share. What it
|
|
9
|
-
* *does* share — the legend, the theme leaf resolvers, the palette, the
|
|
10
|
-
* percentage format — comes from `../kit/shared.ts`.
|
|
11
|
-
*
|
|
12
|
-
* Kept renderer-agnostic (no `Node` imports) so a host's inspector mapping
|
|
13
|
-
* can build a partial {@link PieChartTheme} without
|
|
14
|
-
* pulling the node classes in.
|
|
15
|
-
*/
|
|
16
|
-
|
|
17
1
|
import {
|
|
2
|
+
clamp,
|
|
18
3
|
lerpNumber,
|
|
19
4
|
shadowOps,
|
|
20
5
|
strokeOps,
|
|
@@ -43,33 +28,20 @@ import {
|
|
|
43
28
|
type LegendTheme,
|
|
44
29
|
} from "../kit/shared"
|
|
45
30
|
|
|
46
|
-
/**
|
|
47
|
-
* One **resolved** slice of a {@link PieChart}: its caption, its magnitude, and
|
|
48
|
-
* the paint its wedge and legend swatch take.
|
|
49
|
-
*
|
|
50
|
-
* This is what the ring and the legend consume, not what a scene authors: the
|
|
51
|
-
* chart takes tabular `data` plus two field names and resolves it into these, so
|
|
52
|
-
* the parts stay unaware of where a slice's caption, magnitude and paint came
|
|
53
|
-
* from.
|
|
54
|
-
*/
|
|
31
|
+
/** One resolved slice, which the ring and legend consume; the chart resolves it from `data` and two field names. */
|
|
55
32
|
export interface PieSlice {
|
|
56
|
-
/**
|
|
33
|
+
/** Label and legend caption. */
|
|
57
34
|
category: string
|
|
58
|
-
/**
|
|
35
|
+
/** The wedge covers `value / (sum of all values)`. */
|
|
59
36
|
value: number
|
|
60
37
|
/** Wedge and swatch paint. */
|
|
61
38
|
fill: Fill
|
|
62
39
|
}
|
|
63
40
|
|
|
64
41
|
/**
|
|
65
|
-
*
|
|
66
|
-
*
|
|
67
|
-
*
|
|
68
|
-
*
|
|
69
|
-
* Paint comes from the palette rather than from the rows because a pie draws one
|
|
70
|
-
* mark per *row* — unlike the line and bar charts, where a colour belongs to a
|
|
71
|
-
* series (a whole column) and rides on the series entry instead. Keeping it out
|
|
72
|
-
* of the rows is what lets a pie be fed straight from a CSV of plain numbers.
|
|
42
|
+
* One slice per row, painted from `palette` by row order (cycling). Paint comes
|
|
43
|
+
* from the palette, not the rows, because a pie's mark is a row where a line's
|
|
44
|
+
* colour belongs to a column; that lets a pie be fed a CSV of plain numbers.
|
|
73
45
|
*/
|
|
74
46
|
export function resolvePieSlices(
|
|
75
47
|
data: DataRecord[],
|
|
@@ -87,51 +59,41 @@ export function resolvePieSlices(
|
|
|
87
59
|
|
|
88
60
|
/** What a {@link PieChart}'s labels say. */
|
|
89
61
|
export type PieLabelMode =
|
|
90
|
-
/**
|
|
91
|
-
* The category and its percentage, two lines. Their order flips with the
|
|
92
|
-
* placement: a **leader** label reads category over percentage, an **inside**
|
|
93
|
-
* label reads the percentage big over a small category caption — inside a
|
|
94
|
-
* wedge the reader already knows which slice they're on, so the magnitude is
|
|
95
|
-
* the thing being read.
|
|
96
|
-
*/
|
|
62
|
+
/** Two lines, whose order flips with placement: a leader label reads category over percentage, an inside label reads the percentage big over a small category. */
|
|
97
63
|
| "both"
|
|
98
|
-
/** The category only. */
|
|
99
64
|
| "category"
|
|
100
|
-
/** The percentage only — the pick when a legend already names the categories. */
|
|
101
65
|
| "percent"
|
|
102
|
-
/** No labels and no leader lines
|
|
66
|
+
/** No labels and no leader lines. */
|
|
103
67
|
| "none"
|
|
104
68
|
|
|
105
|
-
/**
|
|
106
|
-
* Where a slice's label sits.
|
|
107
|
-
*
|
|
108
|
-
* `"inside"` drops the leader line entirely and centres the label in the wedge's
|
|
109
|
-
* own band, which costs no gutter — so the ring draws bigger — and costs the eye
|
|
110
|
-
* no round trip. `"leader"` is the classic treatment, and what a sliver too
|
|
111
|
-
* narrow to hold text needs.
|
|
112
|
-
*/
|
|
69
|
+
/** `"inside"` centres the label in the wedge's band with no leader or gutter, so the ring draws bigger; `"leader"` is what a sliver too narrow for text needs. */
|
|
113
70
|
export type PieLabelPlacement = "inside" | "leader" | "none"
|
|
114
71
|
|
|
115
|
-
/** Degrees → radians. */
|
|
116
72
|
export const DEG = Math.PI / 180
|
|
117
73
|
|
|
74
|
+
/**
|
|
75
|
+
* Opacity of a slice under the selection: 1 while nothing is out (`focus` is the
|
|
76
|
+
* most-grown pop) and for the selected slice itself, falling toward `dimOpacity`
|
|
77
|
+
* for the rest as the selection grows.
|
|
78
|
+
*/
|
|
79
|
+
export function selectionFade(focus: number, dimOpacity: number, pop: number): number {
|
|
80
|
+
if (focus <= 0) return 1
|
|
81
|
+
return 1 - (1 - clamp(dimOpacity, 0, 1)) * focus * (1 - pop)
|
|
82
|
+
}
|
|
83
|
+
|
|
118
84
|
/** A slice's plotted value: negatives and non-finite values count as 0. */
|
|
119
85
|
export function sliceValue(slice: PieSlice): number {
|
|
120
86
|
return slice.value > 0 && Number.isFinite(slice.value) ? slice.value : 0
|
|
121
87
|
}
|
|
122
88
|
|
|
123
|
-
/** Sum of every slice's plotted value
|
|
89
|
+
/** Sum of every slice's plotted value, the denominator of every share. */
|
|
124
90
|
export function sliceTotal(slices: PieSlice[]): number {
|
|
125
91
|
let sum = 0
|
|
126
92
|
for (const slice of slices) sum += sliceValue(slice)
|
|
127
93
|
return sum
|
|
128
94
|
}
|
|
129
95
|
|
|
130
|
-
/**
|
|
131
|
-
* The point at radius `r` and angle `a` on a ring centred on the origin, in the
|
|
132
|
-
* node's centred, **y-up** space. Angles run **clockwise from 12 o'clock**:
|
|
133
|
-
* `a = 0` points straight up (+y), and increasing `a` sweeps toward +x then −y.
|
|
134
|
-
*/
|
|
96
|
+
/** The point at radius `r`, angle `a` on a ring about the origin in centred y-up space. Angles run clockwise from 12 o'clock. */
|
|
135
97
|
export function ringPoint(r: number, a: number): Vector2 {
|
|
136
98
|
return { x: r * Math.sin(a), y: r * Math.cos(a) }
|
|
137
99
|
}
|
|
@@ -140,27 +102,20 @@ export function ringPoint(r: number, a: number): Vector2 {
|
|
|
140
102
|
export interface PieArc {
|
|
141
103
|
/** Index of the slice in the source list. */
|
|
142
104
|
index: number
|
|
143
|
-
/**
|
|
105
|
+
/** Radians clockwise from 12 o'clock. */
|
|
144
106
|
a0: number
|
|
145
|
-
/** End angle, radians clockwise from 12 o'clock. */
|
|
146
107
|
a1: number
|
|
147
|
-
/**
|
|
108
|
+
/** Where the slice's label and pop-out point. */
|
|
148
109
|
mid: number
|
|
149
|
-
/**
|
|
110
|
+
/** Share of the total, `[0, 1]`. */
|
|
150
111
|
fraction: number
|
|
151
112
|
}
|
|
152
113
|
|
|
153
114
|
/**
|
|
154
|
-
*
|
|
155
|
-
*
|
|
156
|
-
*
|
|
157
|
-
*
|
|
158
|
-
*
|
|
159
|
-
* Arcs are the *undivided* shares — the gap between neighbours is not carved out
|
|
160
|
-
* here, because a fixed angular gap reads as a wedge: wide at the rim, pinched
|
|
161
|
-
* shut at the hole. It's applied when the wedge is drawn instead, as a
|
|
162
|
-
* constant-width inset off both ends of every arc, so a slice still covers its
|
|
163
|
-
* true percentage of the circle.
|
|
115
|
+
* One arc per positive slice, walking clockwise from `startDegrees` and abutting.
|
|
116
|
+
* Arcs are the undivided shares: a fixed angular gap would pinch shut at the
|
|
117
|
+
* hole, so the gap is applied when the wedge is drawn, as a constant-width inset
|
|
118
|
+
* off both ends.
|
|
164
119
|
*/
|
|
165
120
|
export function pieArcs(slices: PieSlice[], startDegrees: number): PieArc[] {
|
|
166
121
|
const total = sliceTotal(slices)
|
|
@@ -180,25 +135,19 @@ export function pieArcs(slices: PieSlice[], startDegrees: number): PieArc[] {
|
|
|
180
135
|
return arcs
|
|
181
136
|
}
|
|
182
137
|
|
|
183
|
-
|
|
184
|
-
export interface LabelSlot {
|
|
138
|
+
interface LabelSlot {
|
|
185
139
|
/** Where the label would sit if nothing else were in the way. */
|
|
186
140
|
ideal: number
|
|
187
|
-
/**
|
|
141
|
+
/** Written by {@link relaxLabels}. */
|
|
188
142
|
y: number
|
|
189
143
|
}
|
|
190
144
|
|
|
191
145
|
/**
|
|
192
|
-
* Spread one column of labels so
|
|
193
|
-
* `
|
|
194
|
-
*
|
|
195
|
-
*
|
|
196
|
-
*
|
|
197
|
-
* `minGap`; if that pushes the tail past `bottom`, a reverse pass lifts it back
|
|
198
|
-
* up. When there are more labels than the column can hold, the reverse pass wins
|
|
199
|
-
* and the crowding is shared evenly rather than dumped off the bottom edge.
|
|
200
|
-
*
|
|
201
|
-
* Mutates each slot's `y` and returns `slots` re-ordered top-to-bottom.
|
|
146
|
+
* Spread one column of labels so none overlap, each near its `ideal` y and all
|
|
147
|
+
* inside `[bottom, top]`. A top-down pass clears each label from the one above
|
|
148
|
+
* by `minGap`; a reverse pass lifts a tail pushed past `bottom`. When the column
|
|
149
|
+
* cannot hold them all, the reverse pass wins and the crowding is shared evenly.
|
|
150
|
+
* Mutates each `y` and returns `slots` ordered top to bottom.
|
|
202
151
|
*/
|
|
203
152
|
export function relaxLabels<T extends LabelSlot>(
|
|
204
153
|
slots: T[],
|
|
@@ -223,50 +172,29 @@ export function relaxLabels<T extends LabelSlot>(
|
|
|
223
172
|
return slots
|
|
224
173
|
}
|
|
225
174
|
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
/** The ring's geometry and paint — everything about the wedges but their colour. */
|
|
175
|
+
/** The ring's geometry and paint: everything about the wedges but their colour. */
|
|
229
176
|
export interface RingConfig {
|
|
230
|
-
/**
|
|
231
|
-
* Width of the channel between adjacent slices, in px, held constant all the
|
|
232
|
-
* way across the band — the same clearance at the hole as at the rim. (An
|
|
233
|
-
* angular gap would splay open toward the rim instead.)
|
|
234
|
-
*/
|
|
177
|
+
/** Channel between adjacent slices in px, constant across the band (an angular gap would splay open toward the rim). */
|
|
235
178
|
gap: number
|
|
236
|
-
/**
|
|
237
|
-
* Radius of the hole punched through the middle. Below 1 it reads as a
|
|
238
|
-
* fraction of the ring's outer radius (`0.6` leaves a band 40% of the radius
|
|
239
|
-
* thick); 1 and up as an explicit pixel radius; `0` fills the middle in,
|
|
240
|
-
* drawing solid pie wedges.
|
|
241
|
-
*/
|
|
179
|
+
/** Below 1 a fraction of the outer radius (`0.6` leaves a band 40% thick); 1 and up a pixel radius; `0` draws solid wedges. */
|
|
242
180
|
holeRadius: number
|
|
243
|
-
/**
|
|
181
|
+
/** Degrees clockwise from 12 o'clock where the first slice begins. */
|
|
244
182
|
startAngle: number
|
|
245
|
-
/**
|
|
246
|
-
* How far out a selected slice's rim reaches, as a **multiple of the ring's
|
|
247
|
-
* outer radius**. Being relative, the emphasis scales with the chart instead
|
|
248
|
-
* of shrinking on a big one and swamping a small one. Clamped to `[1, 1.6]`.
|
|
249
|
-
*/
|
|
183
|
+
/** How far a selected slice's rim reaches, as a multiple of the outer radius so the emphasis scales with the chart. Clamped to `[1, 1.6]`. */
|
|
250
184
|
selectedGrow: number
|
|
251
|
-
/** Optional outline on every wedge. */
|
|
252
185
|
stroke: Stroke
|
|
253
|
-
/** Optional drop shadow on every wedge. */
|
|
254
186
|
shadow: Shadow
|
|
255
187
|
}
|
|
256
188
|
|
|
257
189
|
/** How the ring's labels are set, and the leader lines that point at them. */
|
|
258
190
|
export interface LabelConfig {
|
|
259
|
-
/** The category caption
|
|
191
|
+
/** The category caption. */
|
|
260
192
|
textStyle: TextStyle
|
|
261
|
-
/** The percentage
|
|
193
|
+
/** The percentage. */
|
|
262
194
|
percentStyle: TextStyle
|
|
263
|
-
/**
|
|
264
|
-
* Ink for a label set **inside** its wedge, both lines. Separate because an
|
|
265
|
-
* inside label sits on the wedge's own paint rather than on the background, so
|
|
266
|
-
* it usually wants the opposite end of the value range from everything else.
|
|
267
|
-
*/
|
|
195
|
+
/** Both lines of a label inside its wedge: it sits on the wedge's paint, so it usually wants the opposite end of the value range from everything else. */
|
|
268
196
|
insideStyle: TextStyle
|
|
269
|
-
/**
|
|
197
|
+
/** Runs from a wedge out to its label. */
|
|
270
198
|
leaderStroke: Stroke
|
|
271
199
|
}
|
|
272
200
|
|
|
@@ -288,7 +216,7 @@ export interface PieChartConfig {
|
|
|
288
216
|
spotlight: { dimOpacity: number }
|
|
289
217
|
}
|
|
290
218
|
|
|
291
|
-
/**
|
|
219
|
+
/** The author's partial theme, deep-merged onto {@link DEFAULT_PIE_CHART_THEME}. */
|
|
292
220
|
export type PieChartTheme = {
|
|
293
221
|
sliceFills?: Fill[]
|
|
294
222
|
ring?: Partial<RingConfig>
|
|
@@ -305,21 +233,14 @@ const LABEL_TEXT_STYLE: TextStyle = {
|
|
|
305
233
|
fill: CHART_PALETTE.markerColor,
|
|
306
234
|
}
|
|
307
235
|
|
|
308
|
-
/**
|
|
309
|
-
* Opacity the other slices fade to under a selection. Fainter than the line and
|
|
310
|
-
* bar charts' `SPOTLIGHT_DIM`: a ring's slices touch, so at that a selected
|
|
311
|
-
* slice barely separates from its neighbours.
|
|
312
|
-
*/
|
|
236
|
+
/** Fainter than `SPOTLIGHT_DIM`: a ring's slices touch, so at that a selected slice barely separates. */
|
|
313
237
|
export const PIE_SPOTLIGHT_DIM = 0.1
|
|
314
238
|
|
|
315
|
-
/** The pie chart's out-of-the-box look. */
|
|
316
239
|
export const DEFAULT_PIE_CHART_THEME: PieChartConfig = {
|
|
317
240
|
sliceFills: DEFAULT_SERIES_FILLS,
|
|
318
241
|
ring: {
|
|
319
242
|
gap: 6,
|
|
320
|
-
// A donut
|
|
321
|
-
// uninterrupted area the chart owns and the one place a conclusion can be
|
|
322
|
-
// set. `0` here would give that away before anyone had asked for it.
|
|
243
|
+
// A donut by default: the hole is the one place a conclusion can be set.
|
|
323
244
|
holeRadius: 0.6,
|
|
324
245
|
startAngle: 0,
|
|
325
246
|
selectedGrow: 1.08,
|
|
@@ -329,8 +250,7 @@ export const DEFAULT_PIE_CHART_THEME: PieChartConfig = {
|
|
|
329
250
|
label: {
|
|
330
251
|
textStyle: LABEL_TEXT_STYLE,
|
|
331
252
|
percentStyle: { ...LABEL_TEXT_STYLE, fontWeight: 700 },
|
|
332
|
-
//
|
|
333
|
-
// palette — so they lead with the panel colour rather than the axis ink.
|
|
253
|
+
// On a mid-tone wedge, so the panel colour rather than the axis ink.
|
|
334
254
|
insideStyle: { ...LABEL_TEXT_STYLE, fontWeight: 800, fill: "#15130e" },
|
|
335
255
|
leaderStroke: { weight: 2, fill: CHART_PALETTE.markerColor },
|
|
336
256
|
},
|
|
@@ -447,12 +367,7 @@ function lerpCenterConfig(
|
|
|
447
367
|
}
|
|
448
368
|
}
|
|
449
369
|
|
|
450
|
-
/**
|
|
451
|
-
* {@link PieChart}'s `theme` property mapper: merges the author's partial
|
|
452
|
-
* {@link PieChartTheme} onto `previous` (falling back to
|
|
453
|
-
* {@link DEFAULT_PIE_CHART_THEME} on first resolution), fully resolving every
|
|
454
|
-
* leaf so the tween below always has resolved data to interpolate.
|
|
455
|
-
*/
|
|
370
|
+
/** The `theme` entry's resolve: merges a partial {@link PieChartTheme} onto `previous` (first resolution: the default). */
|
|
456
371
|
export function resolvePieChartTheme(
|
|
457
372
|
theme: PieChartTheme | undefined,
|
|
458
373
|
previous?: PieChartConfig
|
|
@@ -470,16 +385,12 @@ export function resolvePieChartTheme(
|
|
|
470
385
|
}
|
|
471
386
|
}
|
|
472
387
|
|
|
473
|
-
/** {@link PieChart}'s `theme` property tween — leaf by leaf. */
|
|
474
388
|
export function lerpPieChartTheme(
|
|
475
389
|
from: PieChartConfig,
|
|
476
390
|
to: PieChartConfig,
|
|
477
391
|
t: number
|
|
478
392
|
): PieChartConfig {
|
|
479
393
|
return {
|
|
480
|
-
// A palette is a list of independent paints rather than one value, so it
|
|
481
|
-
// interpolates pairwise by index; one that grows or shrinks holds the longer
|
|
482
|
-
// side's tail, exactly as the cartesian charts' palettes do.
|
|
483
394
|
sliceFills: lerpSeriesFills(from.sliceFills, to.sliceFills, t),
|
|
484
395
|
ring: lerpRingConfig(from.ring, to.ring, t),
|
|
485
396
|
label: lerpLabelConfig(from.label, to.label, t),
|
|
@@ -1,2 +0,0 @@
|
|
|
1
|
-
import{fillOps as b,lerpNumber as y,shadowOps as M,strokeOps as g,insetsOps as w,textOps as S}from"@motionscript/sdk";import{niceStep as ft,enterBudget as Z}from"@motionscript/sdk/component";function ge(e,t){return{label:e?.label??t?.label??"",range:e?.range??t?.range,ticks:e?.ticks??t?.ticks,format:e?.format??t?.format}}function D(e,t,r){return{label:r<.5?e.label:t.label,range:xe(e.range,t.range,r),ticks:me(e.ticks,t.ticks,r),format:r<.5?e.format:t.format}}function xe(e,t,r){return!e||!t?r<.5?e:t:[y(e[0],t[0],r),y(e[1],t[1],r)]}function me(e,t,r){if(!e||!t)return r<.5?e:t;let n=Math.max(e.length,t.length),i=[];for(let o=0;o<n;o++)i.push(y(e[o]??t[o],t[o]??e[o],r));return i}function Be(e,t){if(t?.range===void 0&&t?.ticks===void 0)return;let r=ge(t,e);if(t.ticks===void 0&&t.range!==void 0){let n=Math.max(2,e.ticks?.length??2);r.ticks=Q(t.range,n-1)}return r}function B(e){return e.blend}function Ye(e,t,r){let n=t.range!==e.range,i=n&&t.range&&t.ticks&&t.ticks===e.ticks?{...t,ticks:Q(t.range,Math.max(2,t.ticks.length)-1)}:t,o=B(e)!==void 0,l=o||n||i.ticks!==e.ticks,s=o||e.range===void 0||e.ticks===void 0||i.range===void 0||i.ticks===void 0;return!l||!s?D(e,i,r):r===0?e:r===1?i:{...D(e,i,r),blend:{from:e,to:i,t:r}}}function X(e,t){let r=B(e);return r?D(X(r.from,t),X(r.to,t),r.t):t(e)}var E={plotFill:"#15130e",borderColor:"#4a4036",gridColor:"#2c2820",markerColor:"#8a7f70",regionColor:"#d99a3a"},Y=["#d9603b","#8194ad","#e0a93b","#5e9d6b","#9ec85a"],$=.2;function $e(e){return e<0?0:e>1?1:e}function He(e,t){if(t===void 0)return 0;let r=e[t];return typeof r=="number"&&Number.isFinite(r)?r:0}function Ve(e,t){if(t===void 0||t==="")return"";let r=e[t];return r==null?"":String(r)}function We(e,t=[]){if(e.length===0)return[];let r=new Set(t),n=new Set,i=[];for(let o of e)for(let l of Object.keys(o))!r.has(l)&&!n.has(l)&&(n.add(l),i.push(l));return i.filter(o=>{let l=!1;for(let s of e){let a=s[o];if(!(a===void 0||a==="")){if(typeof a!="number")return!1;l=!0}}return l})}var H={fontSize:24,fontWeight:700,textAlign:"center",fill:E.markerColor},V={fontSize:30,fontWeight:600,textAlign:"center",fill:E.markerColor},W={weight:2,fill:E.gridColor},j={labelStyle:H,titleStyle:V,lineStyle:W,tickPadding:20,titlePadding:4},O={labelStyle:{...H,fontWeight:500,textAlign:"right"},titleStyle:V,lineStyle:W,tickPadding:40,titlePadding:24},v={fill:[],stroke:{weight:6,fill:E.borderColor},shadow:[]},K={padding:0,textStyle:{fontSize:30,fontWeight:600,textAlign:"left",fill:E.markerColor},itemGap:44,markerStyle:{width:60,height:24,borderRadius:6,stroke:[],shadow:[]}},je=Y.map(e=>({weight:4,fill:e})),Ke=Y.map(e=>e);function Je(e,t){return{labelStyle:S.resolveStyle(e?.labelStyle,t.labelStyle),titleStyle:S.resolveStyle(e?.titleStyle,t.titleStyle),lineStyle:g.resolve(e?.lineStyle??t.lineStyle),tickPadding:w.resolve(e?.tickPadding??t.tickPadding,t.tickPadding),titlePadding:w.resolve(e?.titlePadding??t.titlePadding,t.titlePadding)}}function Qe(e,t,r){return{labelStyle:S.lerpStyle(e.labelStyle,t.labelStyle,r),titleStyle:S.lerpStyle(e.titleStyle,t.titleStyle,r),lineStyle:g.lerp(e.lineStyle,t.lineStyle,r),tickPadding:w.lerp(e.tickPadding,t.tickPadding,r),titlePadding:w.lerp(e.titlePadding,t.titlePadding,r)}}function Ze(e,t){return{fill:b.resolve(e?.fill??t.fill),stroke:g.resolve(e?.stroke??t.stroke),shadow:M.resolve(e?.shadow??t.shadow)}}function qe(e,t,r){return{fill:b.lerp(e.fill,t.fill,r),stroke:g.lerp(e.stroke,t.stroke,r),shadow:M.lerp(e.shadow,t.shadow,r)}}function ye(e,t){return{width:e?.width??t.width,height:e?.height??t.height,borderRadius:e?.borderRadius??t.borderRadius,stroke:g.resolve(e?.stroke??t.stroke),shadow:M.resolve(e?.shadow??t.shadow)}}function ke(e,t,r){return{width:y(e.width,t.width,r),height:y(e.height,t.height,r),borderRadius:y(e.borderRadius,t.borderRadius,r),stroke:g.lerp(e.stroke,t.stroke,r),shadow:M.lerp(e.shadow,t.shadow,r)}}function et(e,t){return{padding:w.resolve(e?.padding??t.padding,t.padding),textStyle:S.resolveStyle(e?.textStyle,t.textStyle),itemGap:e?.itemGap??t.itemGap,markerStyle:ye(e?.markerStyle,t.markerStyle)}}function tt(e,t,r){return{padding:w.lerp(e.padding,t.padding,r),textStyle:S.lerpStyle(e.textStyle,t.textStyle,r),itemGap:y(e.itemGap,t.itemGap,r),markerStyle:ke(e.markerStyle,t.markerStyle,r)}}function rt(e,t){return(e??t).map(r=>g.resolve(r))}function nt(e,t,r){let n=Math.max(e.length,t.length),i=[];for(let o=0;o<n;o++){let l=e[o]??t[o],s=t[o]??e[o];i.push(g.lerp(l,s,r))}return i}function ot(e,t){return(e??t).map(r=>b.resolve(r))}function it(e,t,r){let n=Math.max(e.length,t.length),i=[];for(let o=0;o<n;o++){let l=e[o]??t[o],s=t[o]??e[o];i.push(b.lerp(l,s,r))}return i}function we(e,t,r){let n=e.length>0?e[r%e.length]:{weight:4,fill:"white"};if(t.fill==null)return n;let i=g.resolve(n);if(i.length===0)return{weight:4,fill:t.fill};let o=[...i];return o[o.length-1]={...o[o.length-1],fill:b.resolve(t.fill)},o}function lt(e,t,r){let n=g.resolve(we(e,t,r));return n[n.length-1]?.fill??"white"}function st(e,t,r){return t??(e.length===0?"white":e[r%e.length])}function J(e,t){if(Number.isInteger(e))return String(e);let r=Math.max(1,t===void 0?1:Se(t));return e.toFixed(r).replace(/(\.\d*?)0+$/,"$1").replace(/\.$/,"")}function Se(e){if(!(e>0)||!Number.isFinite(e))return 1;for(let t=0;t<=6;t++){let r=e*10**t;if(Math.abs(r-Math.round(r))<1e-6*Math.max(1,r))return t}return Math.min(6,Math.max(1,Math.ceil(-Math.log10(e))+1))}function at(e){let t=[...e].filter(Number.isFinite).sort((n,i)=>n-i),r;for(let n=1;n<t.length;n++){let i=t[n]-t[n-1];i>1e-12&&(r===void 0||i<r)&&(r=i)}return r}function ct(e){let t=e*100;return Number.isInteger(t)?`${t}%`:`${t.toFixed(1).replace(/\.0$/,"")}%`}function Q([e,t],r){let n=Math.max(2,Math.round(r));return Array.from({length:n+1},(i,o)=>e+(t-e)/n*o)}import{Rect as T}from"@motionscript/sdk";import{Rect as re,RotatedBox as Te,Text as ne,Types as G,component2d as Le,insetsOps as oe,textOps as ie}from"@motionscript/sdk";import{Types as be}from"@motionscript/sdk";function h(e,t){return be.custom(e,t)}function Ee(e){return e}import{Types as ve,easeOut as Ae}from"@motionscript/sdk";var Pe=e=>e;function A(e){return e.easing?Pe:Ae()}function q(e,t){let r=e.duration,n=t.reduce((o,l)=>Math.max(o,l.at+l.duration),0),i=n>r&&n>0?r/n:1;return e.together(t.map(({at:o,duration:l,piece:s})=>{let a=o*i,f=l*i;return e.sequence([e.wait(a),s,e.wait(Math.max(0,r-a-f))])}))}var R={opacity:1,shift:0};function ee(){return ve.custom({},{private:!0,resolve:(e,t)=>({...t,...e})})}function P(e,t){return e[t]??R}function te(e,t){let r={};for(let n of e)r[n]=t;return r}function C(e,t,r,n){return e.animate(i=>{let o=n(i);return{arrivals:{[t]:{opacity:r.opacity+(R.opacity-r.opacity)*o,shift:r.shift+(R.shift-r.shift)*o}}}})}var _e=24;function Me(e,t){let r=e.markers.indexOf(t);return(r>=0?e.captions[r]:void 0)??J(t)}var le=Le({key:"verticalAxis",state:{range:h(()=>[0,1]),markers:h(()=>[]),captions:h(()=>[]),axis:G.custom(O),label:"",arrivals:ee()},defaults:{width:"fill",height:"fill"},children:e=>{let t=oe.resolve(e.axis.tickPadding).right,r=oe.resolve(e.axis.titlePadding).right,n=[],i=()=>n[0]?.parent?.layoutBounds??{width:0,height:0},o=()=>[...e.markers].sort((f,c)=>c-f),l=f=>{if(f===void 0)return 0;let[c,p]=e.range;return p<=c?0:(f-c)/(p-c)},s=e.markers.map((f,c)=>{let p=new ne({...ie.styleProps(e.axis.labelStyle),text:()=>{let u=o()[c];return u===void 0?"":Me(e,u)},y:()=>{let u=i().height,d=(l(o()[c])-.5)*u,m=u/2-p.layoutBounds.height/2;return Math.min(d,m)},opacity:()=>P(e.arrivals,c).opacity,x:()=>P(e.arrivals,c).shift});return p}),a=[];return e.label!==""&&a.push(new Te({angle:-90,width:"hug",height:"hug",children:new ne({...ie.styleProps(e.axis.titleStyle),text:e.label,opacity:()=>P(e.arrivals,"label").opacity,x:()=>P(e.arrivals,"label").shift})})),a.push(new re({height:"fill",width:"hug",padding:{right:t},children:s})),n.push(new re({flow:"horizontal",gap:r,children:a})),n},commands:{enter:{args:{stagger:G.custom(void 0),slide:G.number(_e)},run:(e,t)=>{let{stagger:r,slide:n}=e.args,i=t.markers.length,o=t.label!=="",l={opacity:0,shift:n},s=[...t.markers.keys(),...o?["label"]:[]],a=e.animate(()=>({arrivals:te(s,l)}));if(i===0)return a;let{step:f,per:c,caption:p}=Z(e.duration,i,o,r),u=A(e),d=t.markers.map((k,N)=>({at:(i-1-N)*f,duration:c,piece:C(e,N,l,u)})),m=o?[{at:(i-1)*f+c,duration:p,piece:C(e,"label",l,u)}]:[];return e.together([a,q(e,[...d,...m])])}}}});var Oe=24;function vt(e){return new T({flow:"vertical",width:"fill",height:"fill",padding:{top:Oe},children:[new T({flow:"horizontal",height:"fill",width:"fill",children:[e.axis,e.plot]}),new T({flow:"horizontal",width:"fill",children:[e.gutter,new T({flow:"vertical",width:"fill",gap:20,children:[...e.below]})]})]})}function At(e){for(let t=e.length-1;t>=0;t--){let r=e[t];if(!T.is(r))continue;let[n,i]=r.children,o=n?.children[0],l=n?.children[1],s=i?.children[1];if(!(!o||!le.is(o)||!l||!s))return{axis:o,plot:l.children,below:s.children}}return null}function Pt(e){let t=e?.layoutBounds;return{width:t?.width??0,height:t?.height??0}}import{Graphics2D as Fe,Types as De,component2d as Re,strokeOps as se}from"@motionscript/sdk";function F(e,t){for(let r of t.fills??[])e.fill(r);for(let r of t.strokes??[])for(let n of r)e.fill(n.fill);for(let r of t.shadows??[])for(let n of r)e.fill(n.fill)}var Ft=Re({key:"chartBorder",state:{stroke:De.custom(v.stroke,{resolve:se.resolve,lerp:se.lerp})},load:(e,t)=>F(e,{strokes:[t.stroke]}),render:(e,t)=>{let{width:r,height:n}=e.size;r<=0||n<=0||e.draw(new Fe().rect({x:0,y:0,width:r,height:n}).stroke(t.stroke))}});import{Graphics2D as I,Types as z,component2d as Ce,fillOps as ae,shadowOps as ce,strokeOps as de}from"@motionscript/sdk";var he=e=>z.custom(e,{resolve:de.resolve,lerp:de.lerp}),zt=Ce({key:"chartCanvas",state:{fill:z.custom(v.fill,{resolve:ae.resolve,lerp:ae.lerp}),shadow:z.custom(v.shadow,{resolve:ce.resolve,lerp:ce.lerp}),xGridStroke:he(j.lineStyle),yGridStroke:he(O.lineStyle),xMarkers:h(()=>[]),yMarkers:h(()=>[]),xExtent:h(()=>[0,1]),yExtent:h(()=>[0,1])},load:(e,t)=>F(e,{fills:[t.fill],shadows:[t.shadow],strokes:[t.xGridStroke,t.yGridStroke]}),render:(e,t)=>{let{width:r,height:n}=e.size;if(r<=0||n<=0)return;e.draw(new I().rect({x:0,y:0,width:r,height:n}).shadow(t.shadow).fill(t.fill));let[i,o]=t.xExtent,[l,s]=t.yExtent,a=d=>o<=i?0:(d-i)/(o-i),f=d=>s<=l?0:(d-l)/(s-l),c=d=>d>1e-6&&d<1-1e-6,p=t.xGridStroke[0];if(p){let d=new I;for(let m of t.xMarkers){let k=a(m);c(k)&&d.rect({x:-r/2+k*r,y:0,width:p.weight,height:n})}e.draw(d.fill(p.fill))}let u=t.yGridStroke[0];if(u){let d=new I;for(let m of t.yMarkers){let k=f(m);c(k)&&d.rect({x:0,y:-n/2+k*n,width:r,height:u.weight})}e.draw(d.fill(u.fill))}}});import{Flow as _,Paint as x,Rect as Ge,Shapes as U,Text as Ie,Types as L,component2d as ue,textOps as ze}from"@motionscript/sdk";var pe={...x.state,..._.state,...U.corners},Ue=ue({key:"legendEntry",state:{...pe,shown:L.number(1)},commands:{...x.commands},measure:(e,t,r)=>{let n=_.measure(e,t,r,x.inset(t));return t.shown>=1?n:{...n,width:t.shown*n.width}},layout:(e,t,r)=>_.layout(e,t,r,x.inset(t)),render:(e,t)=>x.draw(e,t,U.rect(e.size,t))});function Ne(e,t){if(Array.isArray(e))return e;let r=[...t??[]];for(let[n,i]of Object.entries(e))r[Number(n)]=i;return r}var $t=ue({key:"chartLegend",state:{...pe,series:h(()=>[]),swatches:h(()=>[]),legend:L.custom(K),reveal:h(()=>[],{resolve:Ne})},defaults:{flow:"horizontal",width:"hug",height:"hug",gap:e=>e.legend.itemGap,padding:e=>e.legend.padding,reveal:e=>e.series.map(t=>t.enabled??!0?1:0)},children:e=>{let t=e.legend.markerStyle;return e.series.map((r,n)=>Ue({flow:"horizontal",gap:12,align:"center",width:"hug",height:"hug",opacity:()=>e.reveal[n]??0,gapScale:()=>e.reveal[n]??0,shown:()=>e.reveal[n]??0,children:[Ie({...ze.styleProps(e.legend.textStyle),text:r.label}),Ge({width:t.width,height:t.height,cornerRadius:t.borderRadius,fill:e.swatches[n]??"white",stroke:t.stroke,shadow:t.shadow})]}))},commands:{...x.commands,showSeries:{args:{index:L.number(0,{min:0,step:1})},run:(e,t)=>e.args.index in t.series?e.animate(fe(t,e.args.index,1,A(e))):e.wait(e.duration)},hideSeries:{args:{index:L.number(0,{min:0,step:1})},run:(e,t)=>e.args.index in t.series?e.animate(fe(t,e.args.index,0,A(e))):e.wait(e.duration)},spotlightSeries:{args:{index:L.number(0,{min:0,step:1})},run:(e,t,r)=>e.together(r.flatMap((n,i)=>(t.reveal[i]??0)>0?[n.to({opacity:i===e.args.index?1:$})]:[]))}},measure:(e,t,r)=>_.measure(e,t,r,x.inset(t)),layout:(e,t,r)=>_.layout(e,t,r,x.inset(t)),render:(e,t)=>x.draw(e,t,U.rect(e.size,t))});function fe(e,t,r,n){let i=e.reveal[t]??0;return o=>({reveal:{[t]:i+(r-i)*n(o)}})}function Ht(e,t){return(e.reveal[t]??0)>0}export*from"@motionscript/sdk/component";export{h as a,Ee as b,A as c,q as d,R as e,ee as f,P as g,te as h,C as i,ge as j,D as k,Be as l,Ye as m,X as n,E as o,Y as p,$ as q,$e as r,He as s,Ve as t,We as u,j as v,O as w,v as x,K as y,je as z,Ke as A,Je as B,Qe as C,Ze as D,qe as E,et as F,tt as G,rt as H,nt as I,ot as J,it as K,we as L,lt as M,st as N,J as O,at as P,ct as Q,Q as R,ft as S,Z as T,Me as U,le as V,Oe as W,vt as X,At as Y,Pt as Z,F as _,Ft as $,zt as aa,$t as ba,Ht as ca};
|
|
2
|
-
//# sourceMappingURL=chunk-TJNHLQGV.js.map
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"version": 3,
|
|
3
|
-
"sources": ["../../../src/kit/shared.ts", "../../../src/kit/cartesian.ts", "../../../src/kit/vertical-axis.ts", "../../../src/kit/entries.ts", "../../../src/kit/motion.ts", "../../../src/kit/chart-border.ts", "../../../src/kit/paints.ts", "../../../src/kit/chart-canvas.ts", "../../../src/kit/chart-legend.ts", "../../../src/engine.ts"],
|
|
4
|
-
"sourcesContent": ["/**\n * The vocabulary every chart node shares: what a *series* is, how an axis is\n * scaled and formatted, and the theme leaves each chart's own theme is assembled\n * from.\n *\n * This module exists because the three chart types are three ways of drawing the\n * same table. A line chart, a bar chart and a pie chart disagree about almost\n * everything visible — polylines against columns against wedges — and agree\n * about almost everything underneath: a series is a column with a caption and a\n * paint, a marker is a number formatted one way, a legend is a swatch beside a\n * label, and a theme is a tree of paints with one mapper and one tween. Writing\n * that three times would mean three answers to \"how does a chart round its axis\"\n * within one app.\n *\n * Kept renderer-agnostic (no `Node` imports) so a host's own inspector\n * mappings can build a partial theme without pulling the components in.\n *\n * **Two conventions run through every resolver and lerp below**, inherited from\n * the shape system's `resolveStroke`/`resolveShadow`:\n *\n * - A **mapper** merges the author's loose, partial input onto a `previous`\n * resolved value and resolves every leaf, so the tween always has resolved\n * data to interpolate. It is idempotent.\n * - A **tween** interpolates two resolved values leaf by leaf. Categorical\n * fields (`fontFamily`, `textAlign`) and fields that are `undefined` on either\n * side **step at the midpoint**, because there is no meaningful in-between and\n * defaulting the missing side to 0 would animate a font weight down to nothing\n * on the way to \"unset\".\n */\n\nimport {\n fillOps,\n lerpNumber,\n shadowOps,\n strokeOps,\n insetsOps,\n textOps,\n type DataRecord,\n type Fill,\n type FillResolved,\n type Insets,\n type InsetsResolved,\n type Shadow,\n type ShadowResolved,\n type Stroke,\n type StrokeResolved,\n type TextStyle,\n} from \"@motionscript/sdk\"\n\n/**\n * One series of a chart: a column of the table, captioned and painted.\n *\n * `field` names the column carrying this series' values. What that becomes is\n * the chart's business — a traced polyline, a column of bars, a wedge — but the\n * three questions asked of it are the same everywhere: which column, what to\n * call it, what to paint it, and whether it is currently drawn.\n *\n * `fill` is an **override**: omit it and the series takes the theme palette's\n * entry for its own index, which is what makes a chart nobody has themed come\n * out in five distinct colours rather than five identical ones.\n */\nexport interface ChartSeries {\n /** Key into each data row for this series' value. */\n field: string\n /** Legend caption for this series. */\n label: string\n /**\n * Paint override for this series' mark and its legend swatch. Omit to take\n * the theme palette's entry for this series' index.\n */\n fill?: Fill\n /** Whether the series shows by default. Defaults to `true`. */\n enabled?: boolean\n}\n\n/** A `[min, max]` numeric range in data units. */\nexport type Range = [min: number, max: number]\n\n/**\n * One axis's *data* domain — what it shows — as opposed to {@link AxisConfig},\n * which is how it's *styled*. Every field is optional: `range`/`ticks` derive\n * from the plotted data when omitted, and `label` defaults to `\"\"`.\n */\nexport interface AxisDomainConfig {\n /** Caption drawn alongside the axis. Pass `\"\"` (the default) to hide it. */\n label?: string\n /** `[min, max]` of the scale. Derived from the data bounds when omitted. */\n range?: Range\n /** Tick values (in data units). Derived (five evenly-spaced) when omitted. */\n ticks?: number[]\n /**\n * How a tick value is written down — a `number-format` pattern such as\n * `#,##0.00` or `$#,##0`.\n *\n * Here rather than in {@link AxisConfig} because it is a statement about the\n * *numbers*, not about the ink they are set in: `$` on a revenue axis says\n * what the column holds, and it has to survive a re-theme. It sits beside\n * `range` and `ticks` for the same reason those do.\n */\n format?: string\n}\n\n/**\n * An axis domain entry's resolve: merges the author's partial input onto\n * `previous`, so a later partial `.set({ yAxis: {...} })` patches\n * `label`/`range`/`ticks` independently. `range`/`ticks` staying `undefined`\n * after the merge is the auto-derive signal the scale getters read.\n */\nexport function resolveAxisDomain(\n domain: AxisDomainConfig | undefined,\n previous?: AxisDomainConfig\n): AxisDomainConfig {\n return {\n label: domain?.label ?? previous?.label ?? \"\",\n range: domain?.range ?? previous?.range,\n ticks: domain?.ticks ?? previous?.ticks,\n format: domain?.format ?? previous?.format,\n }\n}\n\n/**\n * An axis domain entry's lerp: interpolates `range`/`ticks` numerically\n * (pairwise by index for `ticks`), so a `to` command that restates a range\n * rescales the plot smoothly. `label`, and either field when one side is in\n * auto-derive mode, step at the midpoint.\n */\nexport function lerpAxisDomain(\n from: AxisDomainConfig,\n to: AxisDomainConfig,\n t: number\n): AxisDomainConfig {\n return {\n label: t < 0.5 ? from.label : to.label,\n range: lerpOptionalRange(from.range, to.range, t),\n ticks: lerpOptionalTicks(from.ticks, to.ticks, t),\n // Steps at the midpoint with the caption, for the same reason: there is no\n // halfway between `$#,##0` and `0.0%`.\n format: t < 0.5 ? from.format : to.format,\n }\n}\n\nfunction lerpOptionalRange(\n from: Range | undefined,\n to: Range | undefined,\n t: number\n): Range | undefined {\n if (!from || !to) return t < 0.5 ? from : to\n return [lerpNumber(from[0], to[0], t), lerpNumber(from[1], to[1], t)]\n}\n\nfunction lerpOptionalTicks(\n from: number[] | undefined,\n to: number[] | undefined,\n t: number\n): number[] | undefined {\n if (!from || !to) return t < 0.5 ? from : to\n const n = Math.max(from.length, to.length)\n const out: number[] = []\n for (let i = 0; i < n; i++) {\n out.push(lerpNumber(from[i] ?? to[i], to[i] ?? from[i], t))\n }\n return out\n}\n\n/**\n * Where a generic `to()` of an axis domain should land, given the domain as it\n * is *drawn* (`drawn`, with `range` and `ticks` resolved off the data), or\n * `undefined` when the request states neither a `range` nor `ticks` and the\n * plain property tween will do.\n *\n * The stored domain of an auto axis has no `range` and no `ticks`, and\n * {@link lerpAxisDomain} steps an unset side at the midpoint, so tweening the\n * stored value from an auto axis is a cut. The charts therefore tween between\n * resolved domains, as their zoom does; this builds the destination. A `range`\n * given without `ticks` keeps the tick *count* the axis already draws, so each\n * marker slides from its old value to its new one instead of the row popping.\n */\nexport function axisTweenTarget(\n drawn: AxisDomainConfig,\n requested: AxisDomainConfig | undefined\n): AxisDomainConfig | undefined {\n if (requested?.range === undefined && requested?.ticks === undefined) return undefined\n const target = resolveAxisDomain(requested, drawn)\n if (requested.ticks === undefined && requested.range !== undefined) {\n const count = Math.max(2, drawn.ticks?.length ?? 2)\n target.ticks = markerValues(requested.range, count - 1)\n }\n return target\n}\n\n/**\n * An axis domain part-way through a tween that leaves or reaches a `range` or\n * `ticks` left to the data: both ends as stored, and how far along. Only the\n * chart knows what an auto side draws as, so {@link settleAxisDomain} resolves\n * it there. Its own fields are the plain {@link lerpAxisDomain} of its ends, so\n * a mapper merging onto one still reads a domain.\n */\nexport interface AxisDomainBlend extends AxisDomainConfig {\n readonly blend: { readonly from: AxisDomainConfig; readonly to: AxisDomainConfig; readonly t: number }\n}\n\nfunction blendOf(domain: AxisDomainConfig): AxisDomainBlend[\"blend\"] | undefined {\n return (domain as Partial<AxisDomainBlend>).blend\n}\n\n/**\n * A chart's axis entry tween: what `to({ yAxis: { range } })` moves through.\n * Between two explicit domains it is {@link lerpAxisDomain}, and a restated\n * `range` without `ticks` spreads explicit ticks across it, keeping their count.\n * When either end leaves `range` or `ticks` to the data, the value between is an\n * {@link AxisDomainBlend}, which the chart draws as the blend of what each end\n * draws: a rescale from an auto axis is continuous, not a cut at the midpoint.\n */\nexport function lerpAxisEntry(from: AxisDomainConfig, to: AxisDomainConfig, t: number): AxisDomainConfig {\n // The mapper merged the request onto `from`, so a field it left unstated is `from`'s own, by identity.\n const rangeMoved = to.range !== from.range\n const target = rangeMoved && to.range && to.ticks && to.ticks === from.ticks\n ? { ...to, ticks: markerValues(to.range, Math.max(2, to.ticks.length) - 1) }\n : to\n const blended = blendOf(from) !== undefined\n const moved = blended || rangeMoved || target.ticks !== from.ticks\n const auto = blended || from.range === undefined || from.ticks === undefined || target.range === undefined || target.ticks === undefined\n if (!moved || !auto) return lerpAxisDomain(from, target, t)\n if (t === 0) return from\n if (t === 1) return target\n const blend: AxisDomainBlend = { ...lerpAxisDomain(from, target, t), blend: { from, to: target, t } }\n return blend\n}\n\n/**\n * `domain` as the chart draws it: `drawn` resolves a plain domain's auto `range`\n * and `ticks` off the data, and a blend is the lerp of what its two ends draw.\n */\nexport function settleAxisDomain(domain: AxisDomainConfig, drawn: (domain: AxisDomainConfig) => AxisDomainConfig): AxisDomainConfig {\n const blend = blendOf(domain)\n if (!blend) return drawn(domain)\n return lerpAxisDomain(settleAxisDomain(blend.from, drawn), settleAxisDomain(blend.to, drawn), blend.t)\n}\n\n/**\n * The low-level colour tokens the default themes are built from — a dark plot\n * panel with warm-grey axis furniture.\n */\nexport const CHART_PALETTE = {\n /** Fill of the plot panel behind the marks. */\n plotFill: \"#15130e\",\n /** Colour of the plot's thick border. */\n borderColor: \"#4a4036\",\n /** Colour of the faint grid lines spanning the plot. */\n gridColor: \"#2c2820\",\n /** Colour of the numeric axis markers, axis captions, and legend labels. */\n markerColor: \"#8a7f70\",\n /** Colour of a spotlit region's dashed rules and its wash. */\n regionColor: \"#d99a3a\",\n} as const\n\n/** The out-of-the-box series palette, cycled by series index. */\nexport const DEFAULT_SERIES_COLORS = [\n \"#d9603b\",\n \"#8194ad\",\n \"#e0a93b\",\n \"#5e9d6b\",\n \"#9ec85a\",\n] as const\n\n/**\n * Opacity an *un*-spotlighted series fades to — its mark and its legend entry\n * alike, so the two dim together.\n */\nexport const SPOTLIGHT_DIM = 0.2\n\n/** Clamp a number into `[0, 1]` — the range every reveal signal here runs in. */\nexport function clampUnit(value: number): number {\n return value < 0 ? 0 : value > 1 ? 1 : value\n}\n\n// ---- Row readers ---------------------------------------------------------\n\n/**\n * Read a numeric field off a row, defaulting missing, non-numeric and non-finite\n * values to 0.\n *\n * A column left as strings silently plots as 0, which is why the CSV parser's\n * numeric auto-coercion matters — a quoted or unit-suffixed column won't plot\n * until it's coerced.\n */\nexport function num(row: DataRecord, field: string | undefined): number {\n if (field === undefined) return 0\n const v = row[field]\n // A `NaN` or infinite cell is no more a measurement than a missing one: read\n // as 0, it cannot poison the scale and turn every point into `NaN`.\n return typeof v === \"number\" && Number.isFinite(v) ? v : 0\n}\n\n/**\n * Read a field off a row as its caption — the counterpart to {@link num} for\n * the columns a chart *names* things by rather than measures them by (a bar's\n * category, a wedge's slice). A missing cell reads as empty rather than as the\n * string `\"undefined\"`.\n */\nexport function text(row: DataRecord, field: string | undefined): string {\n if (field === undefined || field === \"\") return \"\"\n const v = row[field]\n return v === undefined || v === null ? \"\" : String(v)\n}\n\n/**\n * Every column that holds numbers across `data`, in header order, skipping the\n * names in `exclude` (typically the category column).\n *\n * A column counts as numeric only if every non-empty cell in it is a number, so\n * a stray text cell leaves the column out rather than plotting zeros. This is\n * what lets a chart plot \"every other column\" without the author naming twenty\n * fields.\n */\nexport function numericFields(\n data: DataRecord[],\n exclude: string[] = []\n): string[] {\n if (data.length === 0) return []\n const skip = new Set(exclude)\n const seen = new Set<string>()\n const order: string[] = []\n for (const row of data)\n for (const key of Object.keys(row))\n if (!skip.has(key) && !seen.has(key)) {\n seen.add(key)\n order.push(key)\n }\n\n return order.filter((key) => {\n let sawValue = false\n for (const row of data) {\n const v = row[key]\n if (v === undefined || v === \"\") continue\n if (typeof v !== \"number\") return false\n sawValue = true\n }\n return sawValue\n })\n}\n\n// ---- Theme branches ------------------------------------------------------\n\n/** One axis's (x or y) style vocabulary. */\nexport interface AxisConfig {\n /** Style of each marker along the axis. */\n labelStyle: TextStyle\n /** Style of the axis caption. */\n titleStyle: TextStyle\n /**\n * This axis's direction of the plot's internal grid — weight+fill honoured,\n * dash/cap/join ignored (grid lines are drawn as filled rects).\n */\n lineStyle: Stroke\n /**\n * Gap between the plot and the marker row. Only the side facing the plot is\n * read — `.top` for a horizontal axis, `.right` for a vertical one.\n */\n tickPadding: Insets\n /** Gap between the marker row and the caption, read the same side. */\n titlePadding: Insets\n}\n\n/** A legend's per-series colour swatch — a rounded chip, not the caption. */\nexport interface LegendMarkerConfig {\n width: number\n height: number\n borderRadius: number\n stroke: Stroke\n shadow: Shadow\n}\n\n/** A legend's visual theme. */\nexport interface LegendConfig {\n padding: Insets\n /** Caption style — only the swatch carries the series colour. */\n textStyle: TextStyle\n /** Gap between legend entries. */\n itemGap: number\n markerStyle: LegendMarkerConfig\n}\n\n/** The plot panel a cartesian chart draws its marks over. */\nexport interface PlotAreaConfig {\n stroke: Stroke\n fill: Fill\n shadow: Shadow\n}\n\nconst AXIS_LABEL_STYLE: TextStyle = {\n fontSize: 24,\n fontWeight: 700,\n textAlign: \"center\",\n fill: CHART_PALETTE.markerColor,\n}\nconst AXIS_TITLE_STYLE: TextStyle = {\n fontSize: 30,\n fontWeight: 600,\n textAlign: \"center\",\n fill: CHART_PALETTE.markerColor,\n}\nconst AXIS_LINE_STYLE: Stroke = { weight: 2, fill: CHART_PALETTE.gridColor }\n\n/** The default styling of a horizontal (marker row beneath the plot) axis. */\nexport const DEFAULT_X_AXIS: AxisConfig = {\n labelStyle: AXIS_LABEL_STYLE,\n titleStyle: AXIS_TITLE_STYLE,\n lineStyle: AXIS_LINE_STYLE,\n tickPadding: 20,\n titlePadding: 4,\n}\n\n/** The default styling of a vertical (marker column beside the plot) axis. */\nexport const DEFAULT_Y_AXIS: AxisConfig = {\n labelStyle: { ...AXIS_LABEL_STYLE, fontWeight: 500, textAlign: \"right\" },\n titleStyle: AXIS_TITLE_STYLE,\n lineStyle: AXIS_LINE_STYLE,\n tickPadding: 40,\n titlePadding: 24,\n}\n\n/**\n * The default plot panel: **unpainted**, framed. A chart reads against the\n * stage rather than punching a panel into it, which is the look the family was\n * designed at.\n */\nexport const DEFAULT_PLOT_AREA: PlotAreaConfig = {\n fill: [],\n stroke: { weight: 6, fill: CHART_PALETTE.borderColor },\n shadow: [],\n}\n\n/** The default legend styling, shared by every chart that draws one. */\nexport const DEFAULT_LEGEND: LegendConfig = {\n padding: 0,\n textStyle: {\n fontSize: 30,\n fontWeight: 600,\n textAlign: \"left\",\n fill: CHART_PALETTE.markerColor,\n },\n itemGap: 44,\n markerStyle: {\n width: 60,\n height: 24,\n borderRadius: 6,\n stroke: [],\n shadow: [],\n },\n}\n\n/** The default per-series stroke palette — {@link DEFAULT_SERIES_COLORS}, drawn 4px. */\nexport const DEFAULT_SERIES_STROKES: Stroke[] = DEFAULT_SERIES_COLORS.map(\n (fill) => ({ weight: 4, fill })\n)\n\n/** The default per-series fill palette — the same colours, as solid paint. */\nexport const DEFAULT_SERIES_FILLS: Fill[] = DEFAULT_SERIES_COLORS.map(\n (fill) => fill\n)\n\n// ---- Theme branch resolvers ----------------------------------------------\n// Each chart type assembles its own theme out of these, so \"the axis furniture\"\n// means one thing across the family and each chart still declares only the\n// branches it actually draws.\n\nexport function resolveAxisConfig(\n axis: Partial<AxisConfig> | undefined,\n previous: AxisConfig\n): AxisConfig {\n return {\n labelStyle: textOps.resolveStyle(axis?.labelStyle, previous.labelStyle),\n titleStyle: textOps.resolveStyle(axis?.titleStyle, previous.titleStyle),\n lineStyle: strokeOps.resolve(axis?.lineStyle ?? previous.lineStyle),\n tickPadding: insetsOps.resolve(\n axis?.tickPadding ?? previous.tickPadding,\n previous.tickPadding as InsetsResolved\n ),\n titlePadding: insetsOps.resolve(\n axis?.titlePadding ?? previous.titlePadding,\n previous.titlePadding as InsetsResolved\n ),\n }\n}\n\nexport function lerpAxisConfig(\n from: AxisConfig,\n to: AxisConfig,\n t: number\n): AxisConfig {\n return {\n labelStyle: textOps.lerpStyle(from.labelStyle, to.labelStyle, t),\n titleStyle: textOps.lerpStyle(from.titleStyle, to.titleStyle, t),\n lineStyle: strokeOps.lerp(\n from.lineStyle as StrokeResolved[],\n to.lineStyle as StrokeResolved[],\n t\n ),\n tickPadding: insetsOps.lerp(\n from.tickPadding as InsetsResolved,\n to.tickPadding as InsetsResolved,\n t\n ),\n titlePadding: insetsOps.lerp(\n from.titlePadding as InsetsResolved,\n to.titlePadding as InsetsResolved,\n t\n ),\n }\n}\n\nexport function resolvePlotArea(\n plotArea: Partial<PlotAreaConfig> | undefined,\n previous: PlotAreaConfig\n): PlotAreaConfig {\n return {\n fill: fillOps.resolve(plotArea?.fill ?? previous.fill),\n stroke: strokeOps.resolve(plotArea?.stroke ?? previous.stroke),\n shadow: shadowOps.resolve(plotArea?.shadow ?? previous.shadow),\n }\n}\n\nexport function lerpPlotArea(\n from: PlotAreaConfig,\n to: PlotAreaConfig,\n t: number\n): PlotAreaConfig {\n return {\n fill: fillOps.lerp(\n from.fill as FillResolved[],\n to.fill as FillResolved[],\n t\n ),\n stroke: strokeOps.lerp(\n from.stroke as StrokeResolved[],\n to.stroke as StrokeResolved[],\n t\n ),\n shadow: shadowOps.lerp(\n from.shadow as ShadowResolved[],\n to.shadow as ShadowResolved[],\n t\n ),\n }\n}\n\nfunction resolveLegendMarkerConfig(\n marker: Partial<LegendMarkerConfig> | undefined,\n previous: LegendMarkerConfig\n): LegendMarkerConfig {\n return {\n width: marker?.width ?? previous.width,\n height: marker?.height ?? previous.height,\n borderRadius: marker?.borderRadius ?? previous.borderRadius,\n stroke: strokeOps.resolve(marker?.stroke ?? previous.stroke),\n shadow: shadowOps.resolve(marker?.shadow ?? previous.shadow),\n }\n}\n\nfunction lerpLegendMarkerConfig(\n from: LegendMarkerConfig,\n to: LegendMarkerConfig,\n t: number\n): LegendMarkerConfig {\n return {\n width: lerpNumber(from.width, to.width, t),\n height: lerpNumber(from.height, to.height, t),\n borderRadius: lerpNumber(from.borderRadius, to.borderRadius, t),\n stroke: strokeOps.lerp(\n from.stroke as StrokeResolved[],\n to.stroke as StrokeResolved[],\n t\n ),\n shadow: shadowOps.lerp(\n from.shadow as ShadowResolved[],\n to.shadow as ShadowResolved[],\n t\n ),\n }\n}\n\n/** A legend theme as the author may state it — every branch optional. */\nexport type LegendTheme = Partial<Omit<LegendConfig, \"markerStyle\">> & {\n markerStyle?: Partial<LegendMarkerConfig>\n}\n\nexport function resolveLegendConfig(\n legend: LegendTheme | undefined,\n previous: LegendConfig\n): LegendConfig {\n return {\n padding: insetsOps.resolve(\n legend?.padding ?? previous.padding,\n previous.padding as InsetsResolved\n ),\n textStyle: textOps.resolveStyle(legend?.textStyle, previous.textStyle),\n itemGap: legend?.itemGap ?? previous.itemGap,\n markerStyle: resolveLegendMarkerConfig(\n legend?.markerStyle,\n previous.markerStyle\n ),\n }\n}\n\nexport function lerpLegendConfig(\n from: LegendConfig,\n to: LegendConfig,\n t: number\n): LegendConfig {\n return {\n padding: insetsOps.lerp(\n from.padding as InsetsResolved,\n to.padding as InsetsResolved,\n t\n ),\n textStyle: textOps.lerpStyle(from.textStyle, to.textStyle, t),\n itemGap: lerpNumber(from.itemGap, to.itemGap, t),\n markerStyle: lerpLegendMarkerConfig(from.markerStyle, to.markerStyle, t),\n }\n}\n\n/** A whole-palette override replaces the previous palette outright. */\nexport function resolveSeriesStrokes(\n styles: Stroke[] | undefined,\n previous: Stroke[]\n): Stroke[] {\n return (styles ?? previous).map((s) => strokeOps.resolve(s))\n}\n\n/** Pairwise by index; a palette that grows/shrinks holds the longer side's tail. */\nexport function lerpSeriesStrokes(\n from: Stroke[],\n to: Stroke[],\n t: number\n): Stroke[] {\n const n = Math.max(from.length, to.length)\n const out: Stroke[] = []\n for (let i = 0; i < n; i++) {\n const a = (from[i] ?? to[i]) as StrokeResolved[]\n const b = (to[i] ?? from[i]) as StrokeResolved[]\n out.push(strokeOps.lerp(a, b, t))\n }\n return out\n}\n\n/** The fill-palette twin of {@link resolveSeriesStrokes}, for filled marks. */\nexport function resolveSeriesFills(\n fills: Fill[] | undefined,\n previous: Fill[]\n): Fill[] {\n return (fills ?? previous).map((f) => fillOps.resolve(f))\n}\n\n/** The fill-palette twin of {@link lerpSeriesStrokes}. */\nexport function lerpSeriesFills(from: Fill[], to: Fill[], t: number): Fill[] {\n const n = Math.max(from.length, to.length)\n const out: Fill[] = []\n for (let i = 0; i < n; i++) {\n const a = (from[i] ?? to[i]) as FillResolved[]\n const b = (to[i] ?? from[i]) as FillResolved[]\n out.push(fillOps.lerp(a, b, t))\n }\n return out\n}\n\n// ---- Per-series paint ----------------------------------------------------\n\n/**\n * The effective stroke for series `index`: the palette's entry at\n * `index % palette.length`, with the series' own `fill`, if set, overriding just\n * the last (topmost) layer's colour.\n */\nexport function resolveSeriesStroke(\n palette: Stroke[],\n series: ChartSeries,\n index: number\n): Stroke {\n const entry: Stroke =\n palette.length > 0\n ? palette[index % palette.length]\n : { weight: 4, fill: \"white\" }\n if (series.fill == null) return entry\n const layers = strokeOps.resolve(entry)\n if (layers.length === 0) return { weight: 4, fill: series.fill }\n const next = [...layers]\n next[next.length - 1] = {\n ...next[next.length - 1],\n fill: fillOps.resolve(series.fill),\n }\n return next\n}\n\n/**\n * The effective *colour* for series `index` — the same colour\n * {@link resolveSeriesStroke} draws its line in — for contexts (a legend\n * swatch) that only need the fill.\n */\nexport function resolveSeriesFill(\n palette: Stroke[],\n series: ChartSeries,\n index: number\n): Fill {\n const layers = strokeOps.resolve(resolveSeriesStroke(palette, series, index))\n return layers[layers.length - 1]?.fill ?? \"white\"\n}\n\n/**\n * The effective paint for mark `index` of a **filled** chart (a bar, a wedge):\n * the series' own `fill` when it carries one, else the palette's entry for its\n * index, cycled.\n *\n * The stroke twin above has to reach inside a stroke to swap one layer's colour;\n * a filled mark has no such geometry wrapped around its paint, so an override is\n * simply the whole answer.\n */\nexport function resolvePalettePaint(\n palette: Fill[],\n override: Fill | undefined,\n index: number\n): Fill {\n if (override != null) return override\n if (palette.length === 0) return \"white\"\n return palette[index % palette.length]\n}\n\n// ---- Scales and formatting -----------------------------------------------\n\n\n/**\n * Format a marker value: integers plain, otherwise trimmed to one decimal, or to\n * as many as the axis's tick `step` needs to tell neighbouring ticks apart.\n *\n * One decimal is the floor, so a step of 0.1 or more reads exactly as it always\n * has; a finer step (a unit axis ticked every 0.05) gets two, instead of five\n * ticks captioned `0.1 0.1 0.2 0.2 0.3`. Trailing zeros are dropped either way.\n */\nexport function formatMarker(value: number, step?: number): string {\n if (Number.isInteger(value)) return String(value)\n const decimals = Math.max(1, step === undefined ? 1 : decimalsFor(step))\n return value.toFixed(decimals).replace(/(\\.\\d*?)0+$/, \"$1\").replace(/\\.$/, \"\")\n}\n\n/** Decimal places needed to write `step` exactly, capped at six. */\nfunction decimalsFor(step: number): number {\n if (!(step > 0) || !Number.isFinite(step)) return 1\n for (let d = 0; d <= 6; d++) {\n const scaled = step * 10 ** d\n if (Math.abs(scaled - Math.round(scaled)) < 1e-6 * Math.max(1, scaled)) return d\n }\n // An irrational-looking step (a third): as many places as separate the ticks.\n return Math.min(6, Math.max(1, Math.ceil(-Math.log10(step)) + 1))\n}\n\n/** The gap between neighbouring tick values, or `undefined` for fewer than two distinct ones. */\nexport function markerStep(markers: readonly number[]): number | undefined {\n const sorted = [...markers].filter(Number.isFinite).sort((a, b) => a - b)\n let step: number | undefined\n for (let i = 1; i < sorted.length; i++) {\n const gap = sorted[i] - sorted[i - 1]\n if (gap > 1e-12 && (step === undefined || gap < step)) step = gap\n }\n return step\n}\n\n/** Format a fraction in `[0, 1]` as a percentage: integers plain, else one decimal. */\nexport function formatPercent(fraction: number): string {\n const pct = fraction * 100\n if (Number.isInteger(pct)) return `${pct}%`\n return `${pct.toFixed(1).replace(/\\.0$/, \"\")}%`\n}\n\n/** `markerCount` evenly-spaced tick values across `[min, max]`, ends inclusive. */\nexport function markerValues([min, max]: Range, markerCount: number): number[] {\n const n = Math.max(2, Math.round(markerCount))\n return Array.from({ length: n + 1 }, (_, i) => min + ((max - min) / n) * i)\n}\n\n// Lifted into the engine, because a graph and a diagram want them too — see\n// `@motionscript/sdk/component`. Re-exported so a chart, and anything that\n// copied one, still reaches them where it always did.\nexport { niceStep, enterBudget } from \"@motionscript/sdk/component\"\n", "import { Rect, type ChildView, type Node2D, type Size2D } from \"@motionscript/sdk\"\n\nimport { VerticalAxis } from \"./vertical-axis\"\n\n/**\n * Headroom above the plot, in px.\n *\n * Two jobs, and the second is why it is real padding on the composition rather\n * than a number the point-mapping math imagines: a series peaking at the top of\n * the scale isn't flush to the top edge, **and** the top marker, which is\n * centred on that top grid line, has somewhere to be. Without it the marker's\n * upper half is drawn above the chart's own box.\n */\nexport const TOP_HEADROOM = 24\n\n/** The parts of a cartesian chart's frame, as {@link cartesianFrame} arranges them. */\nexport interface CartesianParts {\n /** The value axis, left of the plot. */\n readonly axis: Node2D\n /** The plot panel: the box the grid and the marks share. */\n readonly plot: Node2D\n /** An invisible twin of the value axis, reserving its gutter under the plot. */\n readonly gutter: Node2D\n /** What sits under the plot, top to bottom: the other axis, then the legend. */\n readonly below: readonly Node2D[]\n}\n\n/**\n * The frame the line and bar charts share: the value axis beside the plot, and\n * under them the gutter beside the rows below the plot, so those rows start at\n * the plot's left edge rather than the chart's.\n */\nexport function cartesianFrame(parts: CartesianParts): Node2D {\n return new Rect({\n flow: \"vertical\",\n width: \"fill\",\n height: \"fill\",\n padding: { top: TOP_HEADROOM },\n children: [\n new Rect({ flow: \"horizontal\", height: \"fill\", width: \"fill\", children: [parts.axis, parts.plot] }),\n new Rect({\n flow: \"horizontal\",\n width: \"fill\",\n children: [parts.gutter, new Rect({ flow: \"vertical\", width: \"fill\", gap: 20, children: [...parts.below] })],\n }),\n ],\n }) as unknown as Node2D\n}\n\n/** A cartesian frame's parts, as a command reaches them through its chart's children. */\nexport interface CartesianViews {\n readonly axis: ChildView\n /** The plot panel's own children: the grid, the marks, the border. */\n readonly plot: readonly ChildView[]\n readonly below: readonly ChildView[]\n}\n\n/**\n * The frame among a chart's children: the last child shaped as\n * {@link cartesianFrame} builds it. The chart builds it after the children it\n * was given, and a document adds its own later still, so it is found by shape.\n */\nexport function cartesianViews(children: readonly ChildView[]): CartesianViews | null {\n for (let i = children.length - 1; i >= 0; i--) {\n const frame = children[i]\n if (!Rect.is(frame)) continue\n const [top, bottom] = frame.children\n const axis = top?.children[0]\n const plot = top?.children[1]\n const below = bottom?.children[1]\n if (!axis || !VerticalAxis.is(axis) || !plot || !below) continue\n return { axis, plot: plot.children, below: below.children }\n }\n return null\n}\n\n/** A part's laid-out size, or nothing before it is built or laid out. */\nexport function laidOutSize(node: Node2D | undefined): Size2D {\n const bounds = node?.layoutBounds\n return { width: bounds?.width ?? 0, height: bounds?.height ?? 0 }\n}\n", "import {\n Rect,\n RotatedBox,\n Text,\n Types,\n component2d,\n insetsOps,\n textOps,\n type NodeOf,\n type Node2D,\n type Node2DProps,\n} from \"@motionscript/sdk\"\n\nimport { asParts, fresh } from \"./entries\"\nimport { armed, arrival, arrivalOf, arrivalsEntry, beats, frameEase, type Arrival } from \"./motion\"\nimport {\n DEFAULT_Y_AXIS,\n enterBudget,\n formatMarker,\n type AxisConfig,\n type Range,\n} from \"./shared\"\n\nexport interface VerticalAxisProps extends Node2DProps {\n /** `[min, max]` of the vertical scale the markers are positioned within. */\n range: Range\n /**\n * Tick values (in data units) to draw up the axis, each placed by its fraction\n * across {@link VerticalAxisProps.range}. Need not be evenly spaced or include\n * the ends.\n */\n markers: number[]\n /**\n * How each marker is captioned, one per entry of `markers` and in its order.\n * Empty (the default), or short, captions a marker with its bare numeric\n * reading; a chart with a number format on this axis writes them through it.\n */\n captions: string[]\n /** Marker/caption text style, grid stroke, and gutters — see {@link AxisConfig}. */\n axis: AxisConfig\n /**\n * Rotated caption drawn in the gutter to the left of the marker column. Empty\n * string (the default) hides it — no caption node is built and no label gutter\n * is reserved.\n */\n label: string\n}\n\n/** How far a marker starts to the right of its slot when it enters, in px. */\nconst SLIDE = 24\n\n/** How a tick value is captioned — see {@link VerticalAxisProps.captions}. */\nexport type MarkerLabel = (value: number) => string\n\n/** What a marker's caption reads: the one listed for its value, else its bare reading. */\nexport function markerCaption(s: { readonly markers: readonly number[]; readonly captions: readonly string[] }, value: number): string {\n const index = s.markers.indexOf(value)\n return (index >= 0 ? s.captions[index] : undefined) ?? formatMarker(value)\n}\n\n/**\n * The numeric vertical axis shared by the cartesian charts: a **column of\n * {@link Text} nodes**, one per marker value, running top-to-bottom (highest\n * value at the top). Pure layout — no `Graphics` — so the markers are real text\n * nodes the scene can measure, restyle, or animate individually.\n *\n * Each marker is positioned so its centre lands exactly on its value's fraction\n * across `range` within the axis's filled height, so unevenly-spaced markers\n * land where their values fall. Both the caption and the position are *bound*,\n * not snapshotted: a chart that rescales its axes (see `LineChart.zoomTo`)\n * tweens `range`/`markers` live. Only the marker **count** is fixed at\n * construction.\n *\n * `enter` staggers them in **bottom-to-top**: each marker starts pushed a\n * little to the right and transparent, then fades and slides left into place.\n * The resting state is the landed one, so a chart that is never told to enter\n * still reads as a finished chart.\n */\nexport const VerticalAxis = component2d({\n key: \"verticalAxis\",\n state: {\n range: fresh<Range>(() => [0, 1]),\n markers: fresh<number[]>(() => []),\n captions: fresh<string[]>(() => []),\n axis: Types.custom<AxisConfig>(DEFAULT_Y_AXIS),\n label: \"\",\n arrivals: arrivalsEntry(),\n },\n // A plain node filled its parent; one made of its markers would hug them.\n defaults: { width: \"fill\", height: \"fill\" },\n children: (state) => {\n // Composed from plain entries (range/markers/label), so it's built once here.\n const tickPad = insetsOps.resolve(state.axis.tickPadding).right\n const titlePad = insetsOps.resolve(state.axis.titlePadding).right\n\n const row: Node2D[] = []\n // The row's parent is the axis: its box is what the markers are placed in.\n const box = () => (row[0]?.parent as Node2D | null | undefined)?.layoutBounds ?? { width: 0, height: 0 }\n /** The live tick values, top → bottom (highest first) — re-read every frame. */\n const sorted = () => [...state.markers].sort((a, b) => b - a)\n /** Tick `i`'s fraction across the live `range`, 0 at the bottom edge. */\n const fracAt = (value: number | undefined) => {\n if (value === undefined) return 0\n const [min, max] = state.range\n return max <= min ? 0 : (value - min) / (max - min)\n }\n\n // One text node per marker, ordered top → bottom. A slot with no value (the\n // live list is shorter than the column built for it) renders as empty text\n // rather than repeating its neighbour.\n const markers = state.markers.map((_, i) => {\n const marker: Node2D = new Text({\n ...textOps.styleProps(state.axis.labelStyle),\n text: () => {\n const value = sorted()[i]\n return value === undefined ? \"\" : markerCaption(state, value)\n },\n y: () => {\n // Node2D space is y-up, so the fraction runs from the bottom.\n const height = box().height\n const centred = (fracAt(sorted()[i]) - 0.5) * height\n // Held inside the axis's top edge — the backstop for a chart whose\n // headroom is smaller than its marker text is tall, which a theme with\n // much larger markers can arrange. Only the top, because what lies\n // below the axis is the chart's own horizontal axis and legend.\n const limit = height / 2 - marker.layoutBounds.height / 2\n return Math.min(centred, limit)\n },\n opacity: () => arrivalOf(state.arrivals, i).opacity,\n x: () => arrivalOf(state.arrivals, i).shift,\n })\n return marker\n })\n\n const children: Node2D[] = []\n if (state.label !== \"\") {\n // Node2D space is y-up and rotation is clockwise, so -90° reads bottom-to-top.\n children.push(\n new RotatedBox({\n angle: -90,\n width: \"hug\",\n height: \"hug\",\n children: new Text({\n ...textOps.styleProps(state.axis.titleStyle),\n text: state.label,\n opacity: () => arrivalOf(state.arrivals, \"label\").opacity,\n x: () => arrivalOf(state.arrivals, \"label\").shift,\n }),\n })\n )\n }\n children.push(new Rect({ height: \"fill\", width: \"hug\", padding: { right: tickPad }, children: markers }))\n row.push(new Rect({ flow: \"horizontal\", gap: titlePad, children }))\n return asParts(row)\n },\n commands: {\n /**\n * Stagger the markers in bottom to top, then slide the caption in from the\n * right after them. Its first frame arms them all: shifted right by `slide`\n * and transparent.\n *\n * The timeline's duration is the **whole** entrance, not one marker's slide:\n * the stagger and the caption are carved out of it rather than added on top.\n * See {@link enterBudget} for why that distinction matters here.\n *\n * `stagger` is seconds between successive markers (derived when omitted);\n * `slide` is the starting rightward offset in px.\n */\n enter: {\n args: {\n stagger: Types.custom<number | undefined>(undefined),\n slide: Types.number(SLIDE),\n },\n run: (timeline, state) => {\n const { stagger, slide } = timeline.args\n const count = state.markers.length\n const captioned = state.label !== \"\"\n const from: Arrival = { opacity: 0, shift: slide }\n const keys = [...state.markers.keys(), ...(captioned ? [\"label\"] : [])]\n const arm = timeline.animate(() => ({ arrivals: armed(keys, from) }))\n if (count === 0) return arm\n const { step, per, caption } = enterBudget(timeline.duration, count, captioned, stagger)\n const ease = frameEase(timeline)\n // Bottom marker leads: the column is top → bottom, so walk it in reverse.\n const markers = state.markers.map((_, i) => ({ at: (count - 1 - i) * step, duration: per, piece: arrival(timeline, i, from, ease) }))\n // Once every marker has landed, the caption slides in from the right.\n const label = captioned ? [{ at: (count - 1) * step + per, duration: caption, piece: arrival(timeline, \"label\", from, ease) }] : []\n return timeline.together([arm, beats(timeline, [...markers, ...label])])\n },\n },\n },\n})\n\nexport type VerticalAxis = NodeOf<typeof VerticalAxis>\n", "import { Types, type Element, type Node2D, type Type } from \"@motionscript/sdk\"\n\n/**\n * An entry made fresh for every node: each node gets its own array or box\n * rather than one shared literal, and a host is told no default for it.\n */\nexport function fresh<T, In = T>(make: () => T, options?: { readonly resolve?: (value: In) => T; readonly kind?: string }): Type<In, T> {\n // A function default is a factory: the component calls it once per node.\n return Types.custom<In, T>(make as unknown as In, options)\n}\n\n/**\n * Nodes a `children` builder made with `new`, as the elements it returns. A\n * part built that way can be read in a binding — its own box, or its parent's,\n * which is the component it was built for — where a call would hand back only\n * the element.\n */\nexport function asParts(nodes: readonly Node2D[]): readonly Element[] {\n return nodes as unknown as readonly Element[]\n}\n", "import { Types, easeOut, type EasingFunction, type Piece, type Timeline } from \"@motionscript/sdk\"\n\nconst identity: EasingFunction = (t) => t\n\n/** Any component's timeline: these helpers only build pieces, which every timeline builds alike. */\ntype AnyTimeline = Timeline<any, any>\n\n/**\n * The ease a part's own frames apply to `t`: none when its command was given\n * one, which already eases every frame the command builds, and otherwise the\n * ease-out every chart command defaults to.\n */\nexport function frameEase(timeline: { readonly easing: EasingFunction | undefined }): EasingFunction {\n return timeline.easing ? identity : easeOut()\n}\n\n/** A piece of a command placed at its own time: from `at` seconds, for `duration` seconds. */\nexport interface Beat {\n readonly at: number\n readonly duration: number\n readonly piece: Piece\n}\n\n/**\n * `beats` at their own offsets over the command, each eased on its own and held\n * at its last frame once it is done. A beat ahead of the playhead writes nothing,\n * so whatever stands before it is written by a piece ahead of these. Beats that\n * run past the command are played faster to end with it.\n */\nexport function beats(timeline: AnyTimeline, parts: readonly Beat[]): Piece {\n const total = timeline.duration\n const natural = parts.reduce((end, part) => Math.max(end, part.at + part.duration), 0)\n const k = natural > total && natural > 0 ? total / natural : 1\n return timeline.together(\n parts.map(({ at, duration, piece }) => {\n const start = at * k\n const length = duration * k\n return timeline.sequence([timeline.wait(start), piece, timeline.wait(Math.max(0, total - start - length))])\n })\n )\n}\n\n/** How far one piece of a staggered entrance has come: its opacity, and how far it still sits off its place. */\nexport interface Arrival {\n readonly opacity: number\n readonly shift: number\n}\n\n/** Each piece's arrival, by key. An unlisted piece has landed. */\nexport type Arrivals = Readonly<Record<string, Arrival>>\n\nexport const LANDED: Arrival = { opacity: 1, shift: 0 }\n\n/**\n * A part's private record of its pieces' arrivals. A write merges into it, so\n * beats placed apart each write their own piece and leave the others as the\n * frame left them.\n */\nexport function arrivalsEntry() {\n return Types.custom<Arrivals>({}, { private: true, resolve: (next: Arrivals, previous?: Arrivals) => ({ ...previous, ...next }) })\n}\n\nexport function arrivalOf(arrivals: Arrivals, key: string | number): Arrival {\n return arrivals[key] ?? LANDED\n}\n\n/** Every piece in `keys` armed at `from`: the first frame of their entrance. */\nexport function armed(keys: readonly (string | number)[], from: Arrival): Arrivals {\n const record: Record<string, Arrival> = {}\n for (const key of keys) record[key] = from\n return record\n}\n\n/**\n * A piece arriving from `from` over its beat: a part armed at `from` tweened to\n * `{ opacity: 1, [axis]: 0 }`, eased by `ease`.\n */\nexport function arrival(timeline: AnyTimeline, key: string | number, from: Arrival, ease: EasingFunction): Piece {\n return timeline.animate((t) => {\n const e = ease(t)\n return {\n arrivals: {\n [key]: {\n opacity: from.opacity + (LANDED.opacity - from.opacity) * e,\n shift: from.shift + (LANDED.shift - from.shift) * e,\n },\n },\n }\n })\n}\n", "import {\n Graphics2D,\n Types,\n component2d,\n strokeOps,\n type NodeOf,\n // Named so this component's emitted type reaches `Node2D` through the sdk, not core.\n type Node2D as _Node2D,\n type Node2DProps,\n type Stroke,\n type StrokeResolved,\n} from \"@motionscript/sdk\"\n\nimport { loadPaints } from \"./paints\"\nimport { DEFAULT_PLOT_AREA } from \"./shared\"\n\nexport interface ChartBorderProps extends Node2DProps {\n /**\n * The plot's thick frame stroke. The stroke resolver's default `align` is\n * `'inside'`, so the whole weight sits within the layout box unless the theme\n * overrides it.\n */\n stroke: Stroke\n}\n\n/**\n * The thick **frame** around a cartesian chart's plot: an empty rect wearing an\n * inside stroke, sized from its box.\n *\n * Split out of {@link ChartCanvas} (the filled + gridded backdrop) so it can be\n * placed *after* the marks in the tree — the box then frames the plot **over**\n * them, so a line or a column running to the edge tucks cleanly under the border\n * rather than sitting on top of it.\n */\nexport const ChartBorder = component2d({\n key: \"chartBorder\",\n state: {\n stroke: Types.custom<Stroke, StrokeResolved[]>(DEFAULT_PLOT_AREA.stroke, { resolve: strokeOps.resolve, lerp: strokeOps.lerp }),\n },\n load: (loader, state) => loadPaints(loader, { strokes: [state.stroke] }),\n render: (ctx, state) => {\n const { width, height } = ctx.size\n if (width <= 0 || height <= 0) return\n // Empty rect (no fill) with the themed stroke, so the frame sits over the\n // marks but the box interior stays clear.\n ctx.draw(new Graphics2D().rect({ x: 0, y: 0, width, height }).stroke(state.stroke))\n },\n})\n\nexport type ChartBorder = NodeOf<typeof ChartBorder>\n", "import type { FillResolved, Loader, ShadowResolved, StrokeResolved } from \"@motionscript/sdk\"\n\n/** Paints a chart part holds in entries of its own, which no paint entry loads for it. */\nexport interface PartPaints {\n readonly fills?: readonly (readonly FillResolved[])[]\n readonly strokes?: readonly (readonly StrokeResolved[])[]\n readonly shadows?: readonly (readonly ShadowResolved[])[]\n}\n\n/** Declare `paints` from a component's `load`. */\nexport function loadPaints(loader: Loader, paints: PartPaints): void {\n for (const fill of paints.fills ?? []) loader.fill(fill)\n for (const strokes of paints.strokes ?? []) for (const stroke of strokes) loader.fill(stroke.fill)\n for (const shadows of paints.shadows ?? []) for (const shadow of shadows) loader.fill(shadow.fill)\n}\n", "import {\n Graphics2D,\n Types,\n component2d,\n fillOps,\n shadowOps,\n strokeOps,\n type Fill,\n type FillResolved,\n type NodeOf,\n // Named so this component's emitted type reaches `Node2D` through the sdk, not core.\n type Node2D as _Node2D,\n type Node2DProps,\n type Shadow,\n type ShadowResolved,\n type Stroke,\n type StrokeResolved,\n} from \"@motionscript/sdk\"\n\nimport { fresh } from \"./entries\"\nimport { loadPaints } from \"./paints\"\nimport {\n DEFAULT_PLOT_AREA,\n DEFAULT_X_AXIS,\n DEFAULT_Y_AXIS,\n type Range,\n} from \"./shared\"\n\nexport interface ChartCanvasProps extends Node2DProps {\n /** Fill of the plot panel behind the marks. */\n fill: Fill\n /** Drop shadow of the panel. Default none. */\n shadow: Shadow\n /** Stroke of the vertical grid lines (one per {@link xMarkers}). */\n xGridStroke: Stroke\n /** Stroke of the horizontal grid lines (one per {@link yMarkers}). */\n yGridStroke: Stroke\n /**\n * Data-unit x positions to draw a vertical grid line at — one per horizontal\n * axis marker, so a line lands under every marker regardless of spacing.\n * Positions at (or past) an {@link xExtent} edge coincide with the border and\n * are skipped. Pass an empty list for a chart whose horizontal axis is\n * categorical and so has no numeric position to rule at.\n */\n xMarkers: number[]\n /** Data-unit y positions to draw a horizontal grid line at. */\n yMarkers: number[]\n /** `[min, max]` of the horizontal scale — maps {@link xMarkers} into the panel. */\n xExtent: Range\n /** `[min, max]` of the vertical scale — maps {@link yMarkers} into the panel. */\n yExtent: Range\n}\n\nconst strokeEntry = (initial: Stroke) =>\n Types.custom<Stroke, StrokeResolved[]>(initial, { resolve: strokeOps.resolve, lerp: strokeOps.lerp })\n\n/**\n * The plot **backdrop** of a cartesian chart: a filled panel with a grid of\n * faint lines spanning both directions across it, sized from its box and centred\n * on its origin.\n *\n * The thick frame is a **separate** {@link ChartBorder} node so it can be drawn\n * *over* the marks while this panel sits under them. Grid lines are placed from\n * the axis marker positions (in data units) mapped into the panel through\n * {@link ChartCanvasProps.xExtent}/`yExtent` — so each line lands under its\n * marker whether or not the markers are evenly spaced.\n */\nexport const ChartCanvas = component2d({\n key: \"chartCanvas\",\n state: {\n fill: Types.custom<Fill, FillResolved[]>(DEFAULT_PLOT_AREA.fill, { resolve: fillOps.resolve, lerp: fillOps.lerp }),\n shadow: Types.custom<Shadow, ShadowResolved[]>(DEFAULT_PLOT_AREA.shadow, { resolve: shadowOps.resolve, lerp: shadowOps.lerp }),\n xGridStroke: strokeEntry(DEFAULT_X_AXIS.lineStyle),\n yGridStroke: strokeEntry(DEFAULT_Y_AXIS.lineStyle),\n xMarkers: fresh<number[]>(() => []),\n yMarkers: fresh<number[]>(() => []),\n xExtent: fresh<Range>(() => [0, 1]),\n yExtent: fresh<Range>(() => [0, 1]),\n },\n // The grid strokes are named past the conventional slots, so they are named here.\n load: (loader, state) =>\n loadPaints(loader, { fills: [state.fill], shadows: [state.shadow], strokes: [state.xGridStroke, state.yGridStroke] }),\n render: (ctx, state) => {\n const { width: W, height: H } = ctx.size\n if (W <= 0 || H <= 0) return\n\n // Plot panel, centred on the origin (node space is y-up, origin centre).\n ctx.draw(new Graphics2D().rect({ x: 0, y: 0, width: W, height: H }).shadow(state.shadow).fill(state.fill))\n\n const [xMin, xMax] = state.xExtent\n const [yMin, yMax] = state.yExtent\n // Fraction of a marker across its extent; guard a zero-width extent.\n const xFrac = (v: number) => (xMax <= xMin ? 0 : (v - xMin) / (xMax - xMin))\n const yFrac = (v: number) => (yMax <= yMin ? 0 : (v - yMin) / (yMax - yMin))\n // Skip markers landing on (or outside) the edges — they'd sit under the border.\n const interior = (f: number) => f > 1e-6 && f < 1 - 1e-6\n\n const xStroke = state.xGridStroke[0]\n if (xStroke) {\n const xGrid = new Graphics2D()\n for (const v of state.xMarkers) {\n const f = xFrac(v)\n if (!interior(f)) continue\n xGrid.rect({ x: -W / 2 + f * W, y: 0, width: xStroke.weight, height: H })\n }\n ctx.draw(xGrid.fill(xStroke.fill))\n }\n\n const yStroke = state.yGridStroke[0]\n if (yStroke) {\n const yGrid = new Graphics2D()\n for (const v of state.yMarkers) {\n const f = yFrac(v)\n if (!interior(f)) continue\n yGrid.rect({ x: 0, y: -H / 2 + f * H, width: W, height: yStroke.weight })\n }\n ctx.draw(yGrid.fill(yStroke.fill))\n }\n },\n})\n\nexport type ChartCanvas = NodeOf<typeof ChartCanvas>\n", "import {\n Flow,\n Paint,\n Rect,\n Shapes,\n Text,\n Types,\n component2d,\n textOps,\n type Fill,\n type NodeOf,\n type RectProps,\n // Named so the row's emitted type reaches the Rect surface it spreads through the sdk, not core.\n type Alignment as _Alignment,\n type CornerRadiusResolved as _CornerRadiusResolved,\n type CornerStyleResolved as _CornerStyleResolved,\n type FillResolved as _FillResolved,\n type FlowMode as _FlowMode,\n type FlowResolved as _FlowResolved,\n type GapSize as _GapSize,\n type Node2D as _Node2D,\n type Node2DProps as _Node2DProps,\n type RectCornerRadius as _RectCornerRadius,\n type RectCornerStyle as _RectCornerStyle,\n type Shadow as _Shadow,\n type ShadowResolved as _ShadowResolved,\n type Stroke as _Stroke,\n type StrokeResolved as _StrokeResolved,\n type Vector2 as _Vector2,\n} from \"@motionscript/sdk\"\n\nimport { fresh } from \"./entries\"\nimport { frameEase } from \"./motion\"\nimport {\n DEFAULT_LEGEND,\n SPOTLIGHT_DIM,\n type ChartSeries,\n type LegendConfig,\n} from \"./shared\"\n\nexport interface ChartLegendProps extends RectProps {\n /**\n * One entry per series: legend `label`, its `enabled` default, and — through\n * {@link ChartLegendProps.swatches} — the colour its chip is painted. A series\n * that starts enabled (the default) is added to the row up front; one that\n * starts `false` is hidden until a later `showSeries`.\n */\n series: ChartSeries[]\n /**\n * The swatch colour for each series, by index — resolved by the chart, whose\n * business it is whether a series' colour comes out of a stroke palette (a\n * line) or a fill palette (a bar). Shorter than the series list means the\n * missing entries fall back to white.\n */\n swatches: Fill[]\n /** Padding, caption style, gaps, and swatch geometry — see {@link LegendConfig}. */\n legend: LegendConfig\n}\n\n/** A row's entries are a `Rect`'s: paint, the flow of its children, its corners. */\nconst row = { ...Paint.state, ...Flow.state, ...Shapes.corners }\n\n/**\n * One legend entry. It hugs its caption and swatch; `shown` scales that width\n * from nothing to the whole of it.\n *\n * The width is taken from the content on every measure, at any reveal, so a frame\n * never depends on whether the entry was once seen fully shown. A collapse needs a\n * number to scale, and `\"hug\"` is not one until something measures it — this is\n * the something.\n */\nconst LegendEntry = component2d({\n key: \"legendEntry\",\n state: { ...row, shown: Types.number(1) },\n commands: { ...Paint.commands },\n measure: (measurer, state, children) => {\n const size = Flow.measure(measurer, state, children, Paint.inset(state))\n return state.shown >= 1 ? size : { ...size, width: state.shown * size.width }\n },\n layout: (box, state, children) => Flow.layout(box, state, children, Paint.inset(state)),\n render: (ctx, state) => Paint.draw(ctx, state, Shapes.rect(ctx.size, state)),\n})\n\n/**\n * How far each entry is shown, written whole (a document, a `set`) or one\n * entry at a time (a command, as `{ [index]: reveal }`), which leaves the others\n * as the frame left them — so two series moving at once each move their own.\n */\nfunction revealOf(value: readonly number[] | Readonly<Record<number, number>>, previous?: number[]): number[] {\n if (Array.isArray(value)) return value as number[]\n const next = [...(previous ?? [])]\n for (const [index, reveal] of Object.entries(value)) next[Number(index)] = reveal\n return next\n}\n\n/**\n * The legend of a chart: a **horizontal-flow row of entries**, one per series, laid out\n * left-to-right with the theme's `itemGap`. Each entry pairs the series label\n * with a rounded colour swatch; only the swatch carries the series colour, the\n * caption reads in the legend's neutral ink.\n *\n * `showSeries`/`hideSeries` flip a series on or off — showing one grows its\n * entry back in at its stable series position, hiding one shrinks it away. So\n * the row only ever shows the currently-enabled series, in series order, and\n * closes the gap when one leaves.\n *\n * This node *is* the row, so the parent places it (centred below the chart) and\n * it hugs its entries.\n */\nexport const ChartLegend = component2d({\n key: \"chartLegend\",\n state: {\n ...row,\n series: fresh<ChartSeries[]>(() => []),\n swatches: fresh<Fill[]>(() => []),\n legend: Types.custom<LegendConfig>(DEFAULT_LEGEND),\n /**\n * How far each entry is shown, `0`–`1`. Drives its width, opacity and gap\n * together, so `0` is an entry that takes no room and `1` is one at its\n * natural size.\n *\n * This replaces adding and removing the entry from the row: a command is\n * asked what it looks like at a time, and \"is this node a child\" is not a\n * value a time can produce. Collapsed-but-present is the same picture reached\n * by a route that *is* a function of `t`.\n */\n reveal: fresh<number[], number[] | Readonly<Record<number, number>>>(() => [], { resolve: revealOf }),\n },\n // This node *is* the row, so the direction is its own answer. It has to be\n // stated — a `Rect` given no flow lays its children out `freeform`, which\n // stacks every entry on the same spot. The gap and padding are the theme's.\n defaults: {\n flow: \"horizontal\",\n width: \"hug\",\n height: \"hug\",\n gap: (state) => state.legend.itemGap,\n padding: (state) => state.legend.padding,\n reveal: (state) => state.series.map((s) => ((s.enabled ?? true) ? 1 : 0)),\n },\n // One entry per series, in series order, every one a child for good: order is\n // fixed rather than depending on which siblings happen to be shown, so an entry\n // always comes back where it belongs.\n children: (state) => {\n const marker = state.legend.markerStyle\n return state.series.map((series, index) =>\n LegendEntry({\n flow: \"horizontal\",\n gap: 12,\n align: \"center\",\n width: \"hug\",\n height: \"hug\",\n // Bound to the reveal, so showing and hiding is a number moving rather\n // than a child arriving and leaving. The width follows it in `measure`,\n // and the flanking gap closes with the entry rather than snapping.\n opacity: () => state.reveal[index] ?? 0,\n gapScale: () => state.reveal[index] ?? 0,\n shown: () => state.reveal[index] ?? 0,\n children: [\n Text({ ...textOps.styleProps(state.legend.textStyle), text: series.label }),\n Rect({\n width: marker.width,\n height: marker.height,\n cornerRadius: marker.borderRadius,\n fill: state.swatches[index] ?? \"white\",\n stroke: marker.stroke,\n shadow: marker.shadow,\n }),\n ],\n })\n )\n },\n commands: {\n ...Paint.commands,\n /**\n * Grow series `index`'s entry back in at its series-order position, the\n * siblings sliding over to make room. A reveal tween rather than an animated\n * `addChildAt`: the entry never left, so the whole thing is a function of `t`.\n */\n showSeries: {\n args: { index: Types.number(0, { min: 0, step: 1 }) },\n // An index that is not a series takes its slot and moves nothing.\n run: (timeline, state) => (timeline.args.index in state.series\n ? timeline.animate(revealFrame(state, timeline.args.index, 1, frameEase(timeline)))\n : timeline.wait(timeline.duration)),\n },\n /** Shrink series `index`'s entry away, the gap closing behind it — the mirror of `showSeries`. */\n hideSeries: {\n args: { index: Types.number(0, { min: 0, step: 1 }) },\n run: (timeline, state) => (timeline.args.index in state.series\n ? timeline.animate(revealFrame(state, timeline.args.index, 0, frameEase(timeline)))\n : timeline.wait(timeline.duration)),\n },\n /**\n * Dim every entry but series `index` to {@link SPOTLIGHT_DIM} and bring that\n * one back to full — the legend half of a chart's `spotlightSeries`. Entries\n * stay in place; only their opacity moves, so the row doesn't reflow. Only\n * the entries shown when it is built are touched: a hidden series' entry is\n * left as `hideSeries` left it.\n */\n spotlightSeries: {\n args: { index: Types.number(0, { min: 0, step: 1 }) },\n run: (timeline, state, children) =>\n timeline.together(\n children.flatMap((entry, i) =>\n (state.reveal[i] ?? 0) > 0 ? [entry.to({ opacity: i === timeline.args.index ? 1 : SPOTLIGHT_DIM })] : []\n )\n ),\n },\n },\n measure: (measurer, state, children) => Flow.measure(measurer, state, children, Paint.inset(state)),\n layout: (box, state, children) => Flow.layout(box, state, children, Paint.inset(state)),\n render: (ctx, state) => Paint.draw(ctx, state, Shapes.rect(ctx.size, state)),\n})\n\n/**\n * One entry's reveal ramping to `target` from where the command finds it — so a\n * hide that follows a show starts where the show left it.\n */\nfunction revealFrame(state: { readonly reveal: readonly number[] }, index: number, target: number, ease: (t: number) => number) {\n const from = state.reveal[index] ?? 0\n return (t: number) => ({ reveal: { [index]: from + (target - from) * ease(t) } })\n}\n\n/** Whether series `index` is showing at all on `legend`. */\nexport function legendShows(legend: { readonly reveal: readonly number[] }, index: number): boolean {\n return (legend.reveal[index] ?? 0) > 0\n}\n\nexport type ChartLegend = NodeOf<typeof ChartLegend>\n", "/**\n * The chart **kit**: everything the three charts share, and the one thing a\n * copied chart does not copy.\n *\n * A copy of the line chart takes `line-chart/*` into your project and rewrites its\n * reach for these to `@motionscript/charts`, so the plot panel, the value\n * axis, the legend and the number formatting stay a real dependency. That is\n * not a size decision — it is the only arrangement that works.\n *\n * A chart identifies its own parts by component identity (`VerticalAxis.is`).\n * Copy `VerticalAxis` alongside your fork and there are now two of it — two\n * components answering one name, and whichever one your chart did not get is\n * a part it never finds, with no error. Keeping the kit external makes that\n * impossible rather than unlikely.\n *\n * It also means a fix to the axis reaches a forked chart without a re-add,\n * which is the half of the bargain you get back for it.\n */\nexport * from \"./kit/shared\"\nexport * from \"./kit/cartesian\"\nexport * from \"./kit/chart-border\"\nexport * from \"./kit/chart-canvas\"\nexport * from \"./kit/chart-legend\"\nexport * from \"./kit/entries\"\nexport * from \"./kit/motion\"\nexport * from \"./kit/paints\"\nexport * from \"./kit/vertical-axis\"\nexport * from \"@motionscript/sdk/component\"\n"],
|
|
5
|
-
"mappings": "AA8BA,OACE,WAAAA,EACA,cAAAC,EACA,aAAAC,EACA,aAAAC,EACA,aAAAC,EACA,WAAAC,MAWK,oBA8tBP,OAAS,YAAAC,GAAU,eAAAC,MAAmB,8BAjqBhC,SAAUC,GACdC,EACAC,EAA2B,CAE3B,MAAO,CACL,MAAOD,GAAQ,OAASC,GAAU,OAAS,GAC3C,MAAOD,GAAQ,OAASC,GAAU,MAClC,MAAOD,GAAQ,OAASC,GAAU,MAClC,OAAQD,GAAQ,QAAUC,GAAU,OAExC,CAQM,SAAUC,EACdC,EACAC,EACAC,EAAS,CAET,MAAO,CACL,MAAOA,EAAI,GAAMF,EAAK,MAAQC,EAAG,MACjC,MAAOE,GAAkBH,EAAK,MAAOC,EAAG,MAAOC,CAAC,EAChD,MAAOE,GAAkBJ,EAAK,MAAOC,EAAG,MAAOC,CAAC,EAGhD,OAAQA,EAAI,GAAMF,EAAK,OAASC,EAAG,OAEvC,CAEA,SAASE,GACPH,EACAC,EACAC,EAAS,CAET,MAAI,CAACF,GAAQ,CAACC,EAAWC,EAAI,GAAMF,EAAOC,EACnC,CAACZ,EAAWW,EAAK,CAAC,EAAGC,EAAG,CAAC,EAAGC,CAAC,EAAGb,EAAWW,EAAK,CAAC,EAAGC,EAAG,CAAC,EAAGC,CAAC,CAAC,CACtE,CAEA,SAASE,GACPJ,EACAC,EACAC,EAAS,CAET,GAAI,CAACF,GAAQ,CAACC,EAAI,OAAOC,EAAI,GAAMF,EAAOC,EAC1C,IAAM,EAAI,KAAK,IAAID,EAAK,OAAQC,EAAG,MAAM,EACnCI,EAAgB,CAAA,EACtB,QAASC,EAAI,EAAGA,EAAI,EAAGA,IACrBD,EAAI,KAAKhB,EAAWW,EAAKM,CAAC,GAAKL,EAAGK,CAAC,EAAGL,EAAGK,CAAC,GAAKN,EAAKM,CAAC,EAAGJ,CAAC,CAAC,EAE5D,OAAOG,CACT,CAeM,SAAUE,GACdC,EACAC,EAAuC,CAEvC,GAAIA,GAAW,QAAU,QAAaA,GAAW,QAAU,OAAW,OACtE,IAAMC,EAASd,GAAkBa,EAAWD,CAAK,EACjD,GAAIC,EAAU,QAAU,QAAaA,EAAU,QAAU,OAAW,CAClE,IAAME,EAAQ,KAAK,IAAI,EAAGH,EAAM,OAAO,QAAU,CAAC,EAClDE,EAAO,MAAQE,EAAaH,EAAU,MAAOE,EAAQ,CAAC,CACxD,CACA,OAAOD,CACT,CAaA,SAASG,EAAQhB,EAAwB,CACvC,OAAQA,EAAoC,KAC9C,CAUM,SAAUiB,GAAcd,EAAwBC,EAAsBC,EAAS,CAEnF,IAAMa,EAAad,EAAG,QAAUD,EAAK,MAC/BU,EAASK,GAAcd,EAAG,OAASA,EAAG,OAASA,EAAG,QAAUD,EAAK,MACnE,CAAE,GAAGC,EAAI,MAAOW,EAAaX,EAAG,MAAO,KAAK,IAAI,EAAGA,EAAG,MAAM,MAAM,EAAI,CAAC,CAAC,EACxEA,EACEe,EAAUH,EAAQb,CAAI,IAAM,OAC5BiB,EAAQD,GAAWD,GAAcL,EAAO,QAAUV,EAAK,MACvDkB,EAAOF,GAAWhB,EAAK,QAAU,QAAaA,EAAK,QAAU,QAAaU,EAAO,QAAU,QAAaA,EAAO,QAAU,OAC/H,MAAI,CAACO,GAAS,CAACC,EAAanB,EAAeC,EAAMU,EAAQR,CAAC,EACtDA,IAAM,EAAUF,EAChBE,IAAM,EAAUQ,EACW,CAAE,GAAGX,EAAeC,EAAMU,EAAQR,CAAC,EAAG,MAAO,CAAE,KAAAF,EAAM,GAAIU,EAAQ,EAAAR,CAAC,CAAE,CAErG,CAMM,SAAUiB,EAAiBtB,EAA0BW,EAAqD,CAC9G,IAAMY,EAAQP,EAAQhB,CAAM,EAC5B,OAAKuB,EACErB,EAAeoB,EAAiBC,EAAM,KAAMZ,CAAK,EAAGW,EAAiBC,EAAM,GAAIZ,CAAK,EAAGY,EAAM,CAAC,EADlFZ,EAAMX,CAAM,CAEjC,CAMO,IAAMwB,EAAgB,CAE3B,SAAU,UAEV,YAAa,UAEb,UAAW,UAEX,YAAa,UAEb,YAAa,WAIFC,EAAwB,CACnC,UACA,UACA,UACA,UACA,WAOWC,EAAgB,GAGvB,SAAUC,GAAUC,EAAa,CACrC,OAAOA,EAAQ,EAAI,EAAIA,EAAQ,EAAI,EAAIA,CACzC,CAYM,SAAUC,GAAIC,EAAiBC,EAAyB,CAC5D,GAAIA,IAAU,OAAW,MAAO,GAChC,IAAMC,EAAIF,EAAIC,CAAK,EAGnB,OAAO,OAAOC,GAAM,UAAY,OAAO,SAASA,CAAC,EAAIA,EAAI,CAC3D,CAQM,SAAUC,GAAKH,EAAiBC,EAAyB,CAC7D,GAAIA,IAAU,QAAaA,IAAU,GAAI,MAAO,GAChD,IAAMC,EAAIF,EAAIC,CAAK,EACnB,OAA0BC,GAAM,KAAO,GAAK,OAAOA,CAAC,CACtD,CAWM,SAAUE,GACdC,EACAC,EAAoB,CAAA,EAAE,CAEtB,GAAID,EAAK,SAAW,EAAG,MAAO,CAAA,EAC9B,IAAME,EAAO,IAAI,IAAID,CAAO,EACtBE,EAAO,IAAI,IACXC,EAAkB,CAAA,EACxB,QAAWT,KAAOK,EAChB,QAAWK,KAAO,OAAO,KAAKV,CAAG,EAC3B,CAACO,EAAK,IAAIG,CAAG,GAAK,CAACF,EAAK,IAAIE,CAAG,IACjCF,EAAK,IAAIE,CAAG,EACZD,EAAM,KAAKC,CAAG,GAGpB,OAAOD,EAAM,OAAQC,GAAO,CAC1B,IAAIC,EAAW,GACf,QAAWX,KAAOK,EAAM,CACtB,IAAMH,EAAIF,EAAIU,CAAG,EACjB,GAAI,EAAAR,IAAM,QAAaA,IAAM,IAC7B,IAAI,OAAOA,GAAM,SAAU,MAAO,GAClCS,EAAW,GACb,CACA,OAAOA,CACT,CAAC,CACH,CAkDA,IAAMC,EAA8B,CAClC,SAAU,GACV,WAAY,IACZ,UAAW,SACX,KAAMlB,EAAc,aAEhBmB,EAA8B,CAClC,SAAU,GACV,WAAY,IACZ,UAAW,SACX,KAAMnB,EAAc,aAEhBoB,EAA0B,CAAE,OAAQ,EAAG,KAAMpB,EAAc,SAAS,EAG7DqB,EAA6B,CACxC,WAAYH,EACZ,WAAYC,EACZ,UAAWC,EACX,YAAa,GACb,aAAc,GAIHE,EAA6B,CACxC,WAAY,CAAE,GAAGJ,EAAkB,WAAY,IAAK,UAAW,OAAO,EACtE,WAAYC,EACZ,UAAWC,EACX,YAAa,GACb,aAAc,IAQHG,EAAoC,CAC/C,KAAM,CAAA,EACN,OAAQ,CAAE,OAAQ,EAAG,KAAMvB,EAAc,WAAW,EACpD,OAAQ,CAAA,GAIGwB,EAA+B,CAC1C,QAAS,EACT,UAAW,CACT,SAAU,GACV,WAAY,IACZ,UAAW,OACX,KAAMxB,EAAc,aAEtB,QAAS,GACT,YAAa,CACX,MAAO,GACP,OAAQ,GACR,aAAc,EACd,OAAQ,CAAA,EACR,OAAQ,CAAA,IAKCyB,GAAmCxB,EAAsB,IACnEyB,IAAU,CAAE,OAAQ,EAAG,KAAAA,CAAI,EAAG,EAIpBC,GAA+B1B,EAAsB,IAC/DyB,GAASA,CAAI,EAQV,SAAUE,GACdC,EACApD,EAAoB,CAEpB,MAAO,CACL,WAAYL,EAAQ,aAAayD,GAAM,WAAYpD,EAAS,UAAU,EACtE,WAAYL,EAAQ,aAAayD,GAAM,WAAYpD,EAAS,UAAU,EACtE,UAAWP,EAAU,QAAQ2D,GAAM,WAAapD,EAAS,SAAS,EAClE,YAAaN,EAAU,QACrB0D,GAAM,aAAepD,EAAS,YAC9BA,EAAS,WAA6B,EAExC,aAAcN,EAAU,QACtB0D,GAAM,cAAgBpD,EAAS,aAC/BA,EAAS,YAA8B,EAG7C,CAEM,SAAUqD,GACdnD,EACAC,EACAC,EAAS,CAET,MAAO,CACL,WAAYT,EAAQ,UAAUO,EAAK,WAAYC,EAAG,WAAYC,CAAC,EAC/D,WAAYT,EAAQ,UAAUO,EAAK,WAAYC,EAAG,WAAYC,CAAC,EAC/D,UAAWX,EAAU,KACnBS,EAAK,UACLC,EAAG,UACHC,CAAC,EAEH,YAAaV,EAAU,KACrBQ,EAAK,YACLC,EAAG,YACHC,CAAC,EAEH,aAAcV,EAAU,KACtBQ,EAAK,aACLC,EAAG,aACHC,CAAC,EAGP,CAEM,SAAUkD,GACdC,EACAvD,EAAwB,CAExB,MAAO,CACL,KAAMV,EAAQ,QAAQiE,GAAU,MAAQvD,EAAS,IAAI,EACrD,OAAQP,EAAU,QAAQ8D,GAAU,QAAUvD,EAAS,MAAM,EAC7D,OAAQR,EAAU,QAAQ+D,GAAU,QAAUvD,EAAS,MAAM,EAEjE,CAEM,SAAUwD,GACdtD,EACAC,EACAC,EAAS,CAET,MAAO,CACL,KAAMd,EAAQ,KACZY,EAAK,KACLC,EAAG,KACHC,CAAC,EAEH,OAAQX,EAAU,KAChBS,EAAK,OACLC,EAAG,OACHC,CAAC,EAEH,OAAQZ,EAAU,KAChBU,EAAK,OACLC,EAAG,OACHC,CAAC,EAGP,CAEA,SAASqD,GACPC,EACA1D,EAA4B,CAE5B,MAAO,CACL,MAAO0D,GAAQ,OAAS1D,EAAS,MACjC,OAAQ0D,GAAQ,QAAU1D,EAAS,OACnC,aAAc0D,GAAQ,cAAgB1D,EAAS,aAC/C,OAAQP,EAAU,QAAQiE,GAAQ,QAAU1D,EAAS,MAAM,EAC3D,OAAQR,EAAU,QAAQkE,GAAQ,QAAU1D,EAAS,MAAM,EAE/D,CAEA,SAAS2D,GACPzD,EACAC,EACAC,EAAS,CAET,MAAO,CACL,MAAOb,EAAWW,EAAK,MAAOC,EAAG,MAAOC,CAAC,EACzC,OAAQb,EAAWW,EAAK,OAAQC,EAAG,OAAQC,CAAC,EAC5C,aAAcb,EAAWW,EAAK,aAAcC,EAAG,aAAcC,CAAC,EAC9D,OAAQX,EAAU,KAChBS,EAAK,OACLC,EAAG,OACHC,CAAC,EAEH,OAAQZ,EAAU,KAChBU,EAAK,OACLC,EAAG,OACHC,CAAC,EAGP,CAOM,SAAUwD,GACdC,EACA7D,EAAsB,CAEtB,MAAO,CACL,QAASN,EAAU,QACjBmE,GAAQ,SAAW7D,EAAS,QAC5BA,EAAS,OAAyB,EAEpC,UAAWL,EAAQ,aAAakE,GAAQ,UAAW7D,EAAS,SAAS,EACrE,QAAS6D,GAAQ,SAAW7D,EAAS,QACrC,YAAayD,GACXI,GAAQ,YACR7D,EAAS,WAAW,EAG1B,CAEM,SAAU8D,GACd5D,EACAC,EACAC,EAAS,CAET,MAAO,CACL,QAASV,EAAU,KACjBQ,EAAK,QACLC,EAAG,QACHC,CAAC,EAEH,UAAWT,EAAQ,UAAUO,EAAK,UAAWC,EAAG,UAAWC,CAAC,EAC5D,QAASb,EAAWW,EAAK,QAASC,EAAG,QAASC,CAAC,EAC/C,YAAauD,GAAuBzD,EAAK,YAAaC,EAAG,YAAaC,CAAC,EAE3E,CAGM,SAAU2D,GACdC,EACAhE,EAAkB,CAElB,OAAQgE,GAAUhE,GAAU,IAAKiE,GAAMxE,EAAU,QAAQwE,CAAC,CAAC,CAC7D,CAGM,SAAUC,GACdhE,EACAC,EACAC,EAAS,CAET,IAAM,EAAI,KAAK,IAAIF,EAAK,OAAQC,EAAG,MAAM,EACnCI,EAAgB,CAAA,EACtB,QAASC,EAAI,EAAGA,EAAI,EAAGA,IAAK,CAC1B,IAAM2D,EAAKjE,EAAKM,CAAC,GAAKL,EAAGK,CAAC,EACpB4D,EAAKjE,EAAGK,CAAC,GAAKN,EAAKM,CAAC,EAC1BD,EAAI,KAAKd,EAAU,KAAK0E,EAAGC,EAAGhE,CAAC,CAAC,CAClC,CACA,OAAOG,CACT,CAGM,SAAU8D,GACdC,EACAtE,EAAgB,CAEhB,OAAQsE,GAAStE,GAAU,IAAKuE,GAAMjF,EAAQ,QAAQiF,CAAC,CAAC,CAC1D,CAGM,SAAUC,GAAgBtE,EAAcC,EAAYC,EAAS,CACjE,IAAM,EAAI,KAAK,IAAIF,EAAK,OAAQC,EAAG,MAAM,EACnCI,EAAc,CAAA,EACpB,QAASC,EAAI,EAAGA,EAAI,EAAGA,IAAK,CAC1B,IAAM2D,EAAKjE,EAAKM,CAAC,GAAKL,EAAGK,CAAC,EACpB4D,EAAKjE,EAAGK,CAAC,GAAKN,EAAKM,CAAC,EAC1BD,EAAI,KAAKjB,EAAQ,KAAK6E,EAAGC,EAAGhE,CAAC,CAAC,CAChC,CACA,OAAOG,CACT,CASM,SAAUkE,GACdC,EACAC,EACAC,EAAa,CAEb,IAAMC,EACJH,EAAQ,OAAS,EACbA,EAAQE,EAAQF,EAAQ,MAAM,EAC9B,CAAE,OAAQ,EAAG,KAAM,OAAO,EAChC,GAAIC,EAAO,MAAQ,KAAM,OAAOE,EAChC,IAAMC,EAASrF,EAAU,QAAQoF,CAAK,EACtC,GAAIC,EAAO,SAAW,EAAG,MAAO,CAAE,OAAQ,EAAG,KAAMH,EAAO,IAAI,EAC9D,IAAMI,EAAO,CAAC,GAAGD,CAAM,EACvB,OAAAC,EAAKA,EAAK,OAAS,CAAC,EAAI,CACtB,GAAGA,EAAKA,EAAK,OAAS,CAAC,EACvB,KAAMzF,EAAQ,QAAQqF,EAAO,IAAI,GAE5BI,CACT,CAOM,SAAUC,GACdN,EACAC,EACAC,EAAa,CAEb,IAAME,EAASrF,EAAU,QAAQgF,GAAoBC,EAASC,EAAQC,CAAK,CAAC,EAC5E,OAAOE,EAAOA,EAAO,OAAS,CAAC,GAAG,MAAQ,OAC5C,CAWM,SAAUG,GACdP,EACAQ,EACAN,EAAa,CAEb,OAAIM,IACAR,EAAQ,SAAW,EAAU,QAC1BA,EAAQE,EAAQF,EAAQ,MAAM,EACvC,CAaM,SAAUS,EAAaxD,EAAeyD,EAAa,CACvD,GAAI,OAAO,UAAUzD,CAAK,EAAG,OAAO,OAAOA,CAAK,EAChD,IAAM0D,EAAW,KAAK,IAAI,EAAGD,IAAS,OAAY,EAAIE,GAAYF,CAAI,CAAC,EACvE,OAAOzD,EAAM,QAAQ0D,CAAQ,EAAE,QAAQ,cAAe,IAAI,EAAE,QAAQ,MAAO,EAAE,CAC/E,CAGA,SAASC,GAAYF,EAAY,CAC/B,GAAI,EAAEA,EAAO,IAAM,CAAC,OAAO,SAASA,CAAI,EAAG,MAAO,GAClD,QAASG,EAAI,EAAGA,GAAK,EAAGA,IAAK,CAC3B,IAAMC,EAASJ,EAAO,IAAMG,EAC5B,GAAI,KAAK,IAAIC,EAAS,KAAK,MAAMA,CAAM,CAAC,EAAI,KAAO,KAAK,IAAI,EAAGA,CAAM,EAAG,OAAOD,CACjF,CAEA,OAAO,KAAK,IAAI,EAAG,KAAK,IAAI,EAAG,KAAK,KAAK,CAAC,KAAK,MAAMH,CAAI,CAAC,EAAI,CAAC,CAAC,CAClE,CAGM,SAAUK,GAAWC,EAA0B,CACnD,IAAMC,EAAS,CAAC,GAAGD,CAAO,EAAE,OAAO,OAAO,QAAQ,EAAE,KAAK,CAACvB,EAAGC,IAAMD,EAAIC,CAAC,EACpEgB,EACJ,QAAS5E,EAAI,EAAGA,EAAImF,EAAO,OAAQnF,IAAK,CACtC,IAAMoF,EAAMD,EAAOnF,CAAC,EAAImF,EAAOnF,EAAI,CAAC,EAChCoF,EAAM,QAAUR,IAAS,QAAaQ,EAAMR,KAAOA,EAAOQ,EAChE,CACA,OAAOR,CACT,CAGM,SAAUS,GAAcC,EAAgB,CAC5C,IAAMC,EAAMD,EAAW,IACvB,OAAI,OAAO,UAAUC,CAAG,EAAU,GAAGA,CAAG,IACjC,GAAGA,EAAI,QAAQ,CAAC,EAAE,QAAQ,OAAQ,EAAE,CAAC,GAC9C,CAGM,SAAUjF,EAAa,CAACkF,EAAKC,CAAG,EAAUC,EAAmB,CACjE,IAAM,EAAI,KAAK,IAAI,EAAG,KAAK,MAAMA,CAAW,CAAC,EAC7C,OAAO,MAAM,KAAK,CAAE,OAAQ,EAAI,CAAC,EAAI,CAACC,EAAG3F,IAAMwF,GAAQC,EAAMD,GAAO,EAAKxF,CAAC,CAC5E,CCxwBA,OAAS,QAAA4F,MAAsD,oBCA/D,OACE,QAAAC,GACA,cAAAC,GACA,QAAAC,GACA,SAAAC,EACA,eAAAC,GACA,aAAAC,GACA,WAAAC,OAIK,oBCXP,OAAS,SAAAC,OAAmD,oBAMtD,SAAUC,EAAiBC,EAAeC,EAAyE,CAEvH,OAAOH,GAAM,OAAcE,EAAuBC,CAAO,CAC3D,CAQM,SAAUC,GAAQC,EAAwB,CAC9C,OAAOA,CACT,CCnBA,OAAS,SAAAC,GAAO,WAAAC,OAA+D,oBAE/E,IAAMC,GAA4BC,GAAMA,EAUlC,SAAUC,EAAUC,EAAyD,CACjF,OAAOA,EAAS,OAASH,GAAWD,GAAO,CAC7C,CAeM,SAAUK,EAAMD,EAAuBE,EAAsB,CACjE,IAAMC,EAAQH,EAAS,SACjBI,EAAUF,EAAM,OAAO,CAACG,EAAKC,IAAS,KAAK,IAAID,EAAKC,EAAK,GAAKA,EAAK,QAAQ,EAAG,CAAC,EAC/EC,EAAIH,EAAUD,GAASC,EAAU,EAAID,EAAQC,EAAU,EAC7D,OAAOJ,EAAS,SACdE,EAAM,IAAI,CAAC,CAAE,GAAAM,EAAI,SAAAC,EAAU,MAAAC,CAAK,IAAM,CACpC,IAAMC,EAAQH,EAAKD,EACbK,EAASH,EAAWF,EAC1B,OAAOP,EAAS,SAAS,CAACA,EAAS,KAAKW,CAAK,EAAGD,EAAOV,EAAS,KAAK,KAAK,IAAI,EAAGG,EAAQQ,EAAQC,CAAM,CAAC,CAAC,CAAC,CAC5G,CAAC,CAAC,CAEN,CAWO,IAAMC,EAAkB,CAAE,QAAS,EAAG,MAAO,CAAC,EAO/C,SAAUC,IAAa,CAC3B,OAAOnB,GAAM,OAAiB,CAAA,EAAI,CAAE,QAAS,GAAM,QAAS,CAACoB,EAAgBC,KAAyB,CAAE,GAAGA,EAAU,GAAGD,CAAI,EAAG,CAAE,CACnI,CAEM,SAAUE,EAAUC,EAAoBC,EAAoB,CAChE,OAAOD,EAASC,CAAG,GAAKN,CAC1B,CAGM,SAAUO,GAAMC,EAAoCC,EAAa,CACrE,IAAMC,EAAkC,CAAA,EACxC,QAAWJ,KAAOE,EAAME,EAAOJ,CAAG,EAAIG,EACtC,OAAOC,CACT,CAMM,SAAUC,EAAQxB,EAAuBmB,EAAsBG,EAAeG,EAAoB,CACtG,OAAOzB,EAAS,QAASF,GAAK,CAC5B,IAAM4B,EAAID,EAAK3B,CAAC,EAChB,MAAO,CACL,SAAU,CACR,CAACqB,CAAG,EAAG,CACL,QAASG,EAAK,SAAWT,EAAO,QAAUS,EAAK,SAAWI,EAC1D,MAAOJ,EAAK,OAAST,EAAO,MAAQS,EAAK,OAASI,IAI1D,CAAC,CACH,CFxCA,IAAMC,GAAQ,GAMR,SAAUC,GAAcC,EAAkFC,EAAa,CAC3H,IAAMC,EAAQF,EAAE,QAAQ,QAAQC,CAAK,EACrC,OAAQC,GAAS,EAAIF,EAAE,SAASE,CAAK,EAAI,SAAcC,EAAaF,CAAK,CAC3E,CAoBO,IAAMG,GAAeC,GAAY,CACtC,IAAK,eACL,MAAO,CACL,MAAOC,EAAa,IAAM,CAAC,EAAG,CAAC,CAAC,EAChC,QAASA,EAAgB,IAAM,CAAA,CAAE,EACjC,SAAUA,EAAgB,IAAM,CAAA,CAAE,EAClC,KAAMC,EAAM,OAAmBC,CAAc,EAC7C,MAAO,GACP,SAAUC,GAAa,GAGzB,SAAU,CAAE,MAAO,OAAQ,OAAQ,MAAM,EACzC,SAAWC,GAAS,CAElB,IAAMC,EAAUC,GAAU,QAAQF,EAAM,KAAK,WAAW,EAAE,MACpDG,EAAWD,GAAU,QAAQF,EAAM,KAAK,YAAY,EAAE,MAEtDI,EAAgB,CAAA,EAEhBC,EAAM,IAAOD,EAAI,CAAC,GAAG,QAAsC,cAAgB,CAAE,MAAO,EAAG,OAAQ,CAAC,EAEhGE,EAAS,IAAM,CAAC,GAAGN,EAAM,OAAO,EAAE,KAAK,CAACO,EAAGC,IAAMA,EAAID,CAAC,EAEtDE,EAAUlB,GAA6B,CAC3C,GAAIA,IAAU,OAAW,MAAO,GAChC,GAAM,CAACmB,EAAKC,CAAG,EAAIX,EAAM,MACzB,OAAOW,GAAOD,EAAM,GAAKnB,EAAQmB,IAAQC,EAAMD,EACjD,EAKME,EAAUZ,EAAM,QAAQ,IAAI,CAACa,EAAGC,IAAK,CACzC,IAAMC,EAAiB,IAAIC,GAAK,CAC9B,GAAGC,GAAQ,WAAWjB,EAAM,KAAK,UAAU,EAC3C,KAAM,IAAK,CACT,IAAMT,EAAQe,EAAM,EAAGQ,CAAC,EACxB,OAAOvB,IAAU,OAAY,GAAKF,GAAcW,EAAOT,CAAK,CAC9D,EACA,EAAG,IAAK,CAEN,IAAM2B,EAASb,EAAG,EAAG,OACfc,GAAWV,EAAOH,EAAM,EAAGQ,CAAC,CAAC,EAAI,IAAOI,EAKxCE,EAAQF,EAAS,EAAIH,EAAO,aAAa,OAAS,EACxD,OAAO,KAAK,IAAII,EAASC,CAAK,CAChC,EACA,QAAS,IAAMC,EAAUrB,EAAM,SAAUc,CAAC,EAAE,QAC5C,EAAG,IAAMO,EAAUrB,EAAM,SAAUc,CAAC,EAAE,MACvC,EACD,OAAOC,CACT,CAAC,EAEKO,EAAqB,CAAA,EAC3B,OAAItB,EAAM,QAAU,IAElBsB,EAAS,KACP,IAAIC,GAAW,CACb,MAAO,IACP,MAAO,MACP,OAAQ,MACR,SAAU,IAAIP,GAAK,CACjB,GAAGC,GAAQ,WAAWjB,EAAM,KAAK,UAAU,EAC3C,KAAMA,EAAM,MACZ,QAAS,IAAMqB,EAAUrB,EAAM,SAAU,OAAO,EAAE,QAClD,EAAG,IAAMqB,EAAUrB,EAAM,SAAU,OAAO,EAAE,MAC7C,EACF,CAAC,EAGNsB,EAAS,KAAK,IAAIE,GAAK,CAAE,OAAQ,OAAQ,MAAO,MAAO,QAAS,CAAE,MAAOvB,CAAO,EAAI,SAAUW,CAAO,CAAE,CAAC,EACxGR,EAAI,KAAK,IAAIoB,GAAK,CAAE,KAAM,aAAc,IAAKrB,EAAU,SAAAmB,CAAQ,CAAE,CAAC,EACnDlB,CACjB,EACA,SAAU,CAaR,MAAO,CACL,KAAM,CACJ,QAASP,EAAM,OAA2B,MAAS,EACnD,MAAOA,EAAM,OAAOT,EAAK,GAE3B,IAAK,CAACqC,EAAUzB,IAAS,CACvB,GAAM,CAAE,QAAA0B,EAAS,MAAAC,CAAK,EAAKF,EAAS,KAC9BG,EAAQ5B,EAAM,QAAQ,OACtB6B,EAAY7B,EAAM,QAAU,GAC5B8B,EAAgB,CAAE,QAAS,EAAG,MAAOH,CAAK,EAC1CI,EAAO,CAAC,GAAG/B,EAAM,QAAQ,KAAI,EAAI,GAAI6B,EAAY,CAAC,OAAO,EAAI,CAAA,CAAG,EAChEG,EAAMP,EAAS,QAAQ,KAAO,CAAE,SAAUQ,GAAMF,EAAMD,CAAI,CAAC,EAAG,EACpE,GAAIF,IAAU,EAAG,OAAOI,EACxB,GAAM,CAAE,KAAAE,EAAM,IAAAC,EAAK,QAAAC,CAAO,EAAKC,EAAYZ,EAAS,SAAUG,EAAOC,EAAWH,CAAO,EACjFY,EAAOC,EAAUd,CAAQ,EAEzBb,EAAUZ,EAAM,QAAQ,IAAI,CAACa,EAAGC,KAAO,CAAE,IAAKc,EAAQ,EAAId,GAAKoB,EAAM,SAAUC,EAAK,MAAOK,EAAQf,EAAUX,EAAGgB,EAAMQ,CAAI,CAAC,EAAG,EAE9HG,EAAQZ,EAAY,CAAC,CAAE,IAAKD,EAAQ,GAAKM,EAAOC,EAAK,SAAUC,EAAS,MAAOI,EAAQf,EAAU,QAASK,EAAMQ,CAAI,CAAC,CAAE,EAAI,CAAA,EACjI,OAAOb,EAAS,SAAS,CAACO,EAAKU,EAAMjB,EAAU,CAAC,GAAGb,EAAS,GAAG6B,CAAK,CAAC,CAAC,CAAC,CACzE,IAGL,EDlLM,IAAME,GAAe,GAmBtB,SAAUC,GAAeC,EAAqB,CAClD,OAAO,IAAIC,EAAK,CACd,KAAM,WACN,MAAO,OACP,OAAQ,OACR,QAAS,CAAE,IAAKH,EAAY,EAC5B,SAAU,CACR,IAAIG,EAAK,CAAE,KAAM,aAAc,OAAQ,OAAQ,MAAO,OAAQ,SAAU,CAACD,EAAM,KAAMA,EAAM,IAAI,CAAC,CAAE,EAClG,IAAIC,EAAK,CACP,KAAM,aACN,MAAO,OACP,SAAU,CAACD,EAAM,OAAQ,IAAIC,EAAK,CAAE,KAAM,WAAY,MAAO,OAAQ,IAAK,GAAI,SAAU,CAAC,GAAGD,EAAM,KAAK,CAAC,CAAE,CAAC,EAC5G,GAEJ,CACH,CAeM,SAAUE,GAAeC,EAA8B,CAC3D,QAASC,EAAID,EAAS,OAAS,EAAGC,GAAK,EAAGA,IAAK,CAC7C,IAAMC,EAAQF,EAASC,CAAC,EACxB,GAAI,CAACH,EAAK,GAAGI,CAAK,EAAG,SACrB,GAAM,CAACC,EAAKC,CAAM,EAAIF,EAAM,SACtBG,EAAOF,GAAK,SAAS,CAAC,EACtBG,EAAOH,GAAK,SAAS,CAAC,EACtBI,EAAQH,GAAQ,SAAS,CAAC,EAChC,GAAI,GAACC,GAAQ,CAACG,GAAa,GAAGH,CAAI,GAAK,CAACC,GAAQ,CAACC,GACjD,MAAO,CAAE,KAAAF,EAAM,KAAMC,EAAK,SAAU,MAAOC,EAAM,QAAQ,CAC3D,CACA,OAAO,IACT,CAGM,SAAUE,GAAYC,EAAwB,CAClD,IAAMC,EAASD,GAAM,aACrB,MAAO,CAAE,MAAOC,GAAQ,OAAS,EAAG,OAAQA,GAAQ,QAAU,CAAC,CACjE,CIhFA,OACE,cAAAC,GACA,SAAAC,GACA,eAAAC,GACA,aAAAC,OAOK,oBCDD,SAAUC,EAAWC,EAAgBC,EAAkB,CAC3D,QAAWC,KAAQD,EAAO,OAAS,CAAA,EAAID,EAAO,KAAKE,CAAI,EACvD,QAAWC,KAAWF,EAAO,SAAW,CAAA,EAAI,QAAWG,KAAUD,EAASH,EAAO,KAAKI,EAAO,IAAI,EACjG,QAAWC,KAAWJ,EAAO,SAAW,CAAA,EAAI,QAAWK,KAAUD,EAASL,EAAO,KAAKM,EAAO,IAAI,CACnG,CDoBO,IAAMC,GAAcC,GAAY,CACrC,IAAK,cACL,MAAO,CACL,OAAQC,GAAM,OAAiCC,EAAkB,OAAQ,CAAE,QAASC,GAAU,QAAS,KAAMA,GAAU,IAAI,CAAE,GAE/H,KAAM,CAACC,EAAQC,IAAUC,EAAWF,EAAQ,CAAE,QAAS,CAACC,EAAM,MAAM,CAAC,CAAE,EACvE,OAAQ,CAACE,EAAKF,IAAS,CACrB,GAAM,CAAE,MAAAG,EAAO,OAAAC,CAAM,EAAKF,EAAI,KAC1BC,GAAS,GAAKC,GAAU,GAG5BF,EAAI,KAAK,IAAIG,GAAU,EAAG,KAAK,CAAE,EAAG,EAAG,EAAG,EAAG,MAAAF,EAAO,OAAAC,CAAM,CAAE,EAAE,OAAOJ,EAAM,MAAM,CAAC,CACpF,EACD,EE/CD,OACE,cAAAM,EACA,SAAAC,EACA,eAAAC,GACA,WAAAC,GACA,aAAAC,GACA,aAAAC,OAWK,oBAoCP,IAAMC,GAAeC,GACnBC,EAAM,OAAiCD,EAAS,CAAE,QAASE,GAAU,QAAS,KAAMA,GAAU,IAAI,CAAE,EAazFC,GAAcC,GAAY,CACrC,IAAK,cACL,MAAO,CACL,KAAMH,EAAM,OAA6BI,EAAkB,KAAM,CAAE,QAASC,GAAQ,QAAS,KAAMA,GAAQ,IAAI,CAAE,EACjH,OAAQL,EAAM,OAAiCI,EAAkB,OAAQ,CAAE,QAASE,GAAU,QAAS,KAAMA,GAAU,IAAI,CAAE,EAC7H,YAAaR,GAAYS,EAAe,SAAS,EACjD,YAAaT,GAAYU,EAAe,SAAS,EACjD,SAAUC,EAAgB,IAAM,CAAA,CAAE,EAClC,SAAUA,EAAgB,IAAM,CAAA,CAAE,EAClC,QAASA,EAAa,IAAM,CAAC,EAAG,CAAC,CAAC,EAClC,QAASA,EAAa,IAAM,CAAC,EAAG,CAAC,CAAC,GAGpC,KAAM,CAACC,EAAQC,IACbC,EAAWF,EAAQ,CAAE,MAAO,CAACC,EAAM,IAAI,EAAG,QAAS,CAACA,EAAM,MAAM,EAAG,QAAS,CAACA,EAAM,YAAaA,EAAM,WAAW,CAAC,CAAE,EACtH,OAAQ,CAACE,EAAKF,IAAS,CACrB,GAAM,CAAE,MAAOG,EAAG,OAAQC,CAAC,EAAKF,EAAI,KACpC,GAAIC,GAAK,GAAKC,GAAK,EAAG,OAGtBF,EAAI,KAAK,IAAIG,EAAU,EAAG,KAAK,CAAE,EAAG,EAAG,EAAG,EAAG,MAAOF,EAAG,OAAQC,CAAC,CAAE,EAAE,OAAOJ,EAAM,MAAM,EAAE,KAAKA,EAAM,IAAI,CAAC,EAEzG,GAAM,CAACM,EAAMC,CAAI,EAAIP,EAAM,QACrB,CAACQ,EAAMC,CAAI,EAAIT,EAAM,QAErBU,EAASC,GAAeJ,GAAQD,EAAO,GAAKK,EAAIL,IAASC,EAAOD,GAChEM,EAASD,GAAeF,GAAQD,EAAO,GAAKG,EAAIH,IAASC,EAAOD,GAEhEK,EAAYC,GAAcA,EAAI,MAAQA,EAAI,EAAI,KAE9CC,EAAUf,EAAM,YAAY,CAAC,EACnC,GAAIe,EAAS,CACX,IAAMC,EAAQ,IAAIX,EAClB,QAAWM,KAAKX,EAAM,SAAU,CAC9B,IAAMc,EAAIJ,EAAMC,CAAC,EACZE,EAASC,CAAC,GACfE,EAAM,KAAK,CAAE,EAAG,CAACb,EAAI,EAAIW,EAAIX,EAAG,EAAG,EAAG,MAAOY,EAAQ,OAAQ,OAAQX,CAAC,CAAE,CAC1E,CACAF,EAAI,KAAKc,EAAM,KAAKD,EAAQ,IAAI,CAAC,CACnC,CAEA,IAAME,EAAUjB,EAAM,YAAY,CAAC,EACnC,GAAIiB,EAAS,CACX,IAAMC,EAAQ,IAAIb,EAClB,QAAWM,KAAKX,EAAM,SAAU,CAC9B,IAAMc,EAAIF,EAAMD,CAAC,EACZE,EAASC,CAAC,GACfI,EAAM,KAAK,CAAE,EAAG,EAAG,EAAG,CAACd,EAAI,EAAIU,EAAIV,EAAG,MAAOD,EAAG,OAAQc,EAAQ,MAAM,CAAE,CAC1E,CACAf,EAAI,KAAKgB,EAAM,KAAKD,EAAQ,IAAI,CAAC,CACnC,CACF,EACD,ECvHD,OACE,QAAAE,EACA,SAAAC,EACA,QAAAC,GACA,UAAAC,EACA,QAAAC,GACA,SAAAC,EACA,eAAAC,GACA,WAAAC,OAqBK,oBA+BP,IAAMC,GAAM,CAAE,GAAGC,EAAM,MAAO,GAAGC,EAAK,MAAO,GAAGC,EAAO,OAAO,EAWxDC,GAAcC,GAAY,CAC9B,IAAK,cACL,MAAO,CAAE,GAAGL,GAAK,MAAOM,EAAM,OAAO,CAAC,CAAC,EACvC,SAAU,CAAE,GAAGL,EAAM,QAAQ,EAC7B,QAAS,CAACM,EAAUC,EAAOC,IAAY,CACrC,IAAMC,EAAOR,EAAK,QAAQK,EAAUC,EAAOC,EAAUR,EAAM,MAAMO,CAAK,CAAC,EACvE,OAAOA,EAAM,OAAS,EAAIE,EAAO,CAAE,GAAGA,EAAM,MAAOF,EAAM,MAAQE,EAAK,KAAK,CAC7E,EACA,OAAQ,CAACC,EAAKH,EAAOC,IAAaP,EAAK,OAAOS,EAAKH,EAAOC,EAAUR,EAAM,MAAMO,CAAK,CAAC,EACtF,OAAQ,CAACI,EAAKJ,IAAUP,EAAM,KAAKW,EAAKJ,EAAOL,EAAO,KAAKS,EAAI,KAAMJ,CAAK,CAAC,EAC5E,EAOD,SAASK,GAASC,EAA6DC,EAAmB,CAChG,GAAI,MAAM,QAAQD,CAAK,EAAG,OAAOA,EACjC,IAAME,EAAO,CAAC,GAAID,GAAY,CAAA,CAAG,EACjC,OAAW,CAACE,EAAOC,CAAM,IAAK,OAAO,QAAQJ,CAAK,EAAGE,EAAK,OAAOC,CAAK,CAAC,EAAIC,EAC3E,OAAOF,CACT,CAgBO,IAAMG,GAAcd,GAAY,CACrC,IAAK,cACL,MAAO,CACL,GAAGL,GACH,OAAQoB,EAAqB,IAAM,CAAA,CAAE,EACrC,SAAUA,EAAc,IAAM,CAAA,CAAE,EAChC,OAAQd,EAAM,OAAqBe,CAAc,EAWjD,OAAQD,EAA6D,IAAM,CAAA,EAAI,CAAE,QAASP,EAAQ,CAAE,GAKtG,SAAU,CACR,KAAM,aACN,MAAO,MACP,OAAQ,MACR,IAAML,GAAUA,EAAM,OAAO,QAC7B,QAAUA,GAAUA,EAAM,OAAO,QACjC,OAASA,GAAUA,EAAM,OAAO,IAAKc,GAAQA,EAAE,SAAW,GAAQ,EAAI,CAAE,GAK1E,SAAWd,GAAS,CAClB,IAAMe,EAASf,EAAM,OAAO,YAC5B,OAAOA,EAAM,OAAO,IAAI,CAACgB,EAAQP,IAC/Bb,GAAY,CACV,KAAM,aACN,IAAK,GACL,MAAO,SACP,MAAO,MACP,OAAQ,MAIR,QAAS,IAAMI,EAAM,OAAOS,CAAK,GAAK,EACtC,SAAU,IAAMT,EAAM,OAAOS,CAAK,GAAK,EACvC,MAAO,IAAMT,EAAM,OAAOS,CAAK,GAAK,EACpC,SAAU,CACRQ,GAAK,CAAE,GAAGC,GAAQ,WAAWlB,EAAM,OAAO,SAAS,EAAG,KAAMgB,EAAO,KAAK,CAAE,EAC1EG,GAAK,CACH,MAAOJ,EAAO,MACd,OAAQA,EAAO,OACf,aAAcA,EAAO,aACrB,KAAMf,EAAM,SAASS,CAAK,GAAK,QAC/B,OAAQM,EAAO,OACf,OAAQA,EAAO,OAChB,GAEJ,CAAC,CAEN,EACA,SAAU,CACR,GAAGtB,EAAM,SAMT,WAAY,CACV,KAAM,CAAE,MAAOK,EAAM,OAAO,EAAG,CAAE,IAAK,EAAG,KAAM,CAAC,CAAE,CAAC,EAEnD,IAAK,CAACsB,EAAUpB,IAAWoB,EAAS,KAAK,SAASpB,EAAM,OACpDoB,EAAS,QAAQC,GAAYrB,EAAOoB,EAAS,KAAK,MAAO,EAAGE,EAAUF,CAAQ,CAAC,CAAC,EAChFA,EAAS,KAAKA,EAAS,QAAQ,GAGrC,WAAY,CACV,KAAM,CAAE,MAAOtB,EAAM,OAAO,EAAG,CAAE,IAAK,EAAG,KAAM,CAAC,CAAE,CAAC,EACnD,IAAK,CAACsB,EAAUpB,IAAWoB,EAAS,KAAK,SAASpB,EAAM,OACpDoB,EAAS,QAAQC,GAAYrB,EAAOoB,EAAS,KAAK,MAAO,EAAGE,EAAUF,CAAQ,CAAC,CAAC,EAChFA,EAAS,KAAKA,EAAS,QAAQ,GASrC,gBAAiB,CACf,KAAM,CAAE,MAAOtB,EAAM,OAAO,EAAG,CAAE,IAAK,EAAG,KAAM,CAAC,CAAE,CAAC,EACnD,IAAK,CAACsB,EAAUpB,EAAOC,IACrBmB,EAAS,SACPnB,EAAS,QAAQ,CAACsB,EAAO,KACtBvB,EAAM,OAAO,CAAC,GAAK,GAAK,EAAI,CAACuB,EAAM,GAAG,CAAE,QAAS,IAAMH,EAAS,KAAK,MAAQ,EAAII,CAAa,CAAE,CAAC,EAAI,CAAA,CAAE,CACzG,IAIT,QAAS,CAACzB,EAAUC,EAAOC,IAAaP,EAAK,QAAQK,EAAUC,EAAOC,EAAUR,EAAM,MAAMO,CAAK,CAAC,EAClG,OAAQ,CAACG,EAAKH,EAAOC,IAAaP,EAAK,OAAOS,EAAKH,EAAOC,EAAUR,EAAM,MAAMO,CAAK,CAAC,EACtF,OAAQ,CAACI,EAAKJ,IAAUP,EAAM,KAAKW,EAAKJ,EAAOL,EAAO,KAAKS,EAAI,KAAMJ,CAAK,CAAC,EAC5E,EAMD,SAASqB,GAAYrB,EAA+CS,EAAegB,EAAgBC,EAA2B,CAC5H,IAAMC,EAAO3B,EAAM,OAAOS,CAAK,GAAK,EACpC,OAAQmB,IAAe,CAAE,OAAQ,CAAE,CAACnB,CAAK,EAAGkB,GAAQF,EAASE,GAAQD,EAAKE,CAAC,CAAC,CAAE,EAChF,CAGM,SAAUC,GAAYC,EAAgDrB,EAAa,CACvF,OAAQqB,EAAO,OAAOrB,CAAK,GAAK,GAAK,CACvC,CCvMA,WAAc",
|
|
6
|
-
"names": ["fillOps", "lerpNumber", "shadowOps", "strokeOps", "insetsOps", "textOps", "niceStep", "enterBudget", "resolveAxisDomain", "domain", "previous", "lerpAxisDomain", "from", "to", "t", "lerpOptionalRange", "lerpOptionalTicks", "out", "i", "axisTweenTarget", "drawn", "requested", "target", "count", "markerValues", "blendOf", "lerpAxisEntry", "rangeMoved", "blended", "moved", "auto", "settleAxisDomain", "blend", "CHART_PALETTE", "DEFAULT_SERIES_COLORS", "SPOTLIGHT_DIM", "clampUnit", "value", "num", "row", "field", "v", "text", "numericFields", "data", "exclude", "skip", "seen", "order", "key", "sawValue", "AXIS_LABEL_STYLE", "AXIS_TITLE_STYLE", "AXIS_LINE_STYLE", "DEFAULT_X_AXIS", "DEFAULT_Y_AXIS", "DEFAULT_PLOT_AREA", "DEFAULT_LEGEND", "DEFAULT_SERIES_STROKES", "fill", "DEFAULT_SERIES_FILLS", "resolveAxisConfig", "axis", "lerpAxisConfig", "resolvePlotArea", "plotArea", "lerpPlotArea", "resolveLegendMarkerConfig", "marker", "lerpLegendMarkerConfig", "resolveLegendConfig", "legend", "lerpLegendConfig", "resolveSeriesStrokes", "styles", "s", "lerpSeriesStrokes", "a", "b", "resolveSeriesFills", "fills", "f", "lerpSeriesFills", "resolveSeriesStroke", "palette", "series", "index", "entry", "layers", "next", "resolveSeriesFill", "resolvePalettePaint", "override", "formatMarker", "step", "decimals", "decimalsFor", "d", "scaled", "markerStep", "markers", "sorted", "gap", "formatPercent", "fraction", "pct", "min", "max", "markerCount", "_", "Rect", "Rect", "RotatedBox", "Text", "Types", "component2d", "insetsOps", "textOps", "Types", "fresh", "make", "options", "asParts", "nodes", "Types", "easeOut", "identity", "t", "frameEase", "timeline", "beats", "parts", "total", "natural", "end", "part", "k", "at", "duration", "piece", "start", "length", "LANDED", "arrivalsEntry", "next", "previous", "arrivalOf", "arrivals", "key", "armed", "keys", "from", "record", "arrival", "ease", "e", "SLIDE", "markerCaption", "s", "value", "index", "formatMarker", "VerticalAxis", "component2d", "fresh", "Types", "DEFAULT_Y_AXIS", "arrivalsEntry", "state", "tickPad", "insetsOps", "titlePad", "row", "box", "sorted", "a", "b", "fracAt", "min", "max", "markers", "_", "i", "marker", "Text", "textOps", "height", "centred", "limit", "arrivalOf", "children", "RotatedBox", "Rect", "timeline", "stagger", "slide", "count", "captioned", "from", "keys", "arm", "armed", "step", "per", "caption", "enterBudget", "ease", "frameEase", "arrival", "label", "beats", "TOP_HEADROOM", "cartesianFrame", "parts", "Rect", "cartesianViews", "children", "i", "frame", "top", "bottom", "axis", "plot", "below", "VerticalAxis", "laidOutSize", "node", "bounds", "Graphics2D", "Types", "component2d", "strokeOps", "loadPaints", "loader", "paints", "fill", "strokes", "stroke", "shadows", "shadow", "ChartBorder", "component2d", "Types", "DEFAULT_PLOT_AREA", "strokeOps", "loader", "state", "loadPaints", "ctx", "width", "height", "Graphics2D", "Graphics2D", "Types", "component2d", "fillOps", "shadowOps", "strokeOps", "strokeEntry", "initial", "Types", "strokeOps", "ChartCanvas", "component2d", "DEFAULT_PLOT_AREA", "fillOps", "shadowOps", "DEFAULT_X_AXIS", "DEFAULT_Y_AXIS", "fresh", "loader", "state", "loadPaints", "ctx", "W", "H", "Graphics2D", "xMin", "xMax", "yMin", "yMax", "xFrac", "v", "yFrac", "interior", "f", "xStroke", "xGrid", "yStroke", "yGrid", "Flow", "Paint", "Rect", "Shapes", "Text", "Types", "component2d", "textOps", "row", "Paint", "Flow", "Shapes", "LegendEntry", "component2d", "Types", "measurer", "state", "children", "size", "box", "ctx", "revealOf", "value", "previous", "next", "index", "reveal", "ChartLegend", "fresh", "DEFAULT_LEGEND", "s", "marker", "series", "Text", "textOps", "Rect", "timeline", "revealFrame", "frameEase", "entry", "SPOTLIGHT_DIM", "target", "ease", "from", "t", "legendShows", "legend"]
|
|
7
|
-
}
|