@motionscript/charts 0.1.0 → 0.1.1-alpha.1
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 +38 -0
- package/README.md +98 -28
- package/dist/bar-chart/bar-chart.d.ts +242 -119
- package/dist/bar-chart/bar-chart.d.ts.map +1 -1
- package/dist/bar-chart/bar-chart.js +231 -260
- package/dist/bar-chart/bar-chart.js.map +1 -1
- package/dist/bar-chart/category-axis.d.ts +16 -38
- package/dist/bar-chart/category-axis.d.ts.map +1 -1
- package/dist/bar-chart/category-axis.js +59 -68
- package/dist/bar-chart/category-axis.js.map +1 -1
- package/dist/bar-chart/chart-bars.d.ts +60 -91
- package/dist/bar-chart/chart-bars.d.ts.map +1 -1
- package/dist/bar-chart/chart-bars.js +88 -125
- package/dist/bar-chart/chart-bars.js.map +1 -1
- package/dist/bar-chart/index.d.ts +5 -13
- package/dist/bar-chart/index.d.ts.map +1 -1
- package/dist/bar-chart/index.js +2 -9
- package/dist/bar-chart/index.js.map +1 -1
- package/dist/bar-chart/shared.d.ts +40 -132
- package/dist/bar-chart/shared.d.ts.map +1 -1
- package/dist/bar-chart/shared.js +70 -158
- package/dist/bar-chart/shared.js.map +1 -1
- package/dist/browser/chunks/chunk-R3Q5EE6E.js +2 -0
- package/dist/browser/chunks/chunk-R3Q5EE6E.js.map +7 -0
- package/dist/browser/index.js +1 -1
- package/dist/browser/index.js.map +4 -4
- package/dist/browser/kit.js +1 -1
- package/dist/browser/manifest.json +1 -1
- package/dist/engine.d.ts +8 -17
- package/dist/engine.d.ts.map +1 -1
- package/dist/engine.js +8 -17
- package/dist/engine.js.map +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/kit/cartesian.d.ts +49 -35
- package/dist/kit/cartesian.d.ts.map +1 -1
- package/dist/kit/cartesian.js +80 -42
- package/dist/kit/cartesian.js.map +1 -1
- package/dist/kit/chart-canvas.d.ts +37 -40
- package/dist/kit/chart-canvas.d.ts.map +1 -1
- package/dist/kit/chart-canvas.js +53 -47
- package/dist/kit/chart-canvas.js.map +1 -1
- package/dist/kit/chart-legend.d.ts +16 -59
- package/dist/kit/chart-legend.d.ts.map +1 -1
- package/dist/kit/chart-legend.js +38 -76
- 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/horizontal-axis.d.ts +77 -0
- package/dist/kit/horizontal-axis.d.ts.map +1 -0
- package/dist/kit/horizontal-axis.js +99 -0
- package/dist/kit/horizontal-axis.js.map +1 -0
- package/dist/kit/motion.d.ts +16 -3
- package/dist/kit/motion.d.ts.map +1 -1
- package/dist/kit/motion.js +15 -1
- package/dist/kit/motion.js.map +1 -1
- package/dist/{pie-chart → kit}/pie-legend.d.ts +14 -29
- package/dist/kit/pie-legend.d.ts.map +1 -0
- package/dist/kit/pie-legend.js +93 -0
- package/dist/kit/pie-legend.js.map +1 -0
- package/dist/kit/polar.d.ts +31 -0
- package/dist/kit/polar.d.ts.map +1 -0
- package/dist/kit/polar.js +33 -0
- package/dist/kit/polar.js.map +1 -0
- package/dist/kit/shared.d.ts +60 -214
- package/dist/kit/shared.d.ts.map +1 -1
- package/dist/kit/shared.js +91 -291
- package/dist/kit/shared.js.map +1 -1
- package/dist/kit/style.d.ts +254 -0
- package/dist/kit/style.d.ts.map +1 -0
- package/dist/kit/style.js +197 -0
- package/dist/kit/style.js.map +1 -0
- package/dist/kit/vertical-axis.d.ts +36 -45
- package/dist/kit/vertical-axis.d.ts.map +1 -1
- package/dist/kit/vertical-axis.js +69 -81
- package/dist/kit/vertical-axis.js.map +1 -1
- package/dist/line-chart/chart-line.d.ts +107 -40
- package/dist/line-chart/chart-line.d.ts.map +1 -1
- package/dist/line-chart/chart-line.js +172 -89
- 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 +13 -28
- package/dist/line-chart/chart-region.js.map +1 -1
- package/dist/line-chart/index.d.ts +5 -18
- package/dist/line-chart/index.d.ts.map +1 -1
- package/dist/line-chart/index.js +3 -16
- package/dist/line-chart/index.js.map +1 -1
- package/dist/line-chart/line-chart.d.ts +272 -119
- package/dist/line-chart/line-chart.d.ts.map +1 -1
- package/dist/line-chart/line-chart.js +309 -257
- package/dist/line-chart/line-chart.js.map +1 -1
- package/dist/line-chart/shared.d.ts +73 -130
- package/dist/line-chart/shared.d.ts.map +1 -1
- package/dist/line-chart/shared.js +195 -156
- package/dist/line-chart/shared.js.map +1 -1
- package/dist/line-chart/x-scale.d.ts +16 -55
- package/dist/line-chart/x-scale.d.ts.map +1 -1
- package/dist/line-chart/x-scale.js +44 -111
- package/dist/line-chart/x-scale.js.map +1 -1
- package/dist/nodes.d.ts +3 -13
- package/dist/nodes.d.ts.map +1 -1
- package/dist/nodes.js +9 -13
- package/dist/nodes.js.map +1 -1
- package/dist/pie-chart/index.d.ts +3 -15
- package/dist/pie-chart/index.d.ts.map +1 -1
- package/dist/pie-chart/index.js +1 -11
- package/dist/pie-chart/index.js.map +1 -1
- package/dist/pie-chart/pie-chart.d.ts +147 -85
- package/dist/pie-chart/pie-chart.d.ts.map +1 -1
- package/dist/pie-chart/pie-chart.js +65 -86
- package/dist/pie-chart/pie-chart.js.map +1 -1
- package/dist/pie-chart/pie-ring.d.ts +65 -196
- package/dist/pie-chart/pie-ring.d.ts.map +1 -1
- package/dist/pie-chart/pie-ring.js +154 -292
- package/dist/pie-chart/pie-ring.js.map +1 -1
- package/dist/pie-chart/shared.d.ts +78 -172
- package/dist/pie-chart/shared.d.ts.map +1 -1
- package/dist/pie-chart/shared.js +54 -177
- package/dist/pie-chart/shared.js.map +1 -1
- package/dist/radar-chart/index.d.ts +5 -0
- package/dist/radar-chart/index.d.ts.map +1 -0
- package/dist/radar-chart/index.js +5 -0
- package/dist/radar-chart/index.js.map +1 -0
- package/dist/radar-chart/radar-chart.d.ts +218 -0
- package/dist/radar-chart/radar-chart.d.ts.map +1 -0
- package/dist/radar-chart/radar-chart.js +224 -0
- package/dist/radar-chart/radar-chart.js.map +1 -0
- package/dist/radar-chart/radar-grid.d.ts +43 -0
- package/dist/radar-chart/radar-grid.d.ts.map +1 -0
- package/dist/radar-chart/radar-grid.js +61 -0
- package/dist/radar-chart/radar-grid.js.map +1 -0
- package/dist/radar-chart/radar-series.d.ts +59 -0
- package/dist/radar-chart/radar-series.d.ts.map +1 -0
- package/dist/radar-chart/radar-series.js +71 -0
- package/dist/radar-chart/radar-series.js.map +1 -0
- package/dist/radar-chart/shared.d.ts +73 -0
- package/dist/radar-chart/shared.d.ts.map +1 -0
- package/dist/radar-chart/shared.js +123 -0
- package/dist/radar-chart/shared.js.map +1 -0
- package/dist/radial-chart/index.d.ts +6 -0
- package/dist/radial-chart/index.d.ts.map +1 -0
- package/dist/radial-chart/index.js +4 -0
- package/dist/radial-chart/index.js.map +1 -0
- package/dist/radial-chart/radial-chart.d.ts +216 -0
- package/dist/radial-chart/radial-chart.d.ts.map +1 -0
- package/dist/radial-chart/radial-chart.js +174 -0
- package/dist/radial-chart/radial-chart.js.map +1 -0
- package/dist/radial-chart/radial-rings.d.ts +121 -0
- package/dist/radial-chart/radial-rings.d.ts.map +1 -0
- package/dist/radial-chart/radial-rings.js +273 -0
- package/dist/radial-chart/radial-rings.js.map +1 -0
- package/dist/radial-chart/shared.d.ts +73 -0
- package/dist/radial-chart/shared.d.ts.map +1 -0
- package/dist/radial-chart/shared.js +79 -0
- package/dist/radial-chart/shared.js.map +1 -0
- package/package.json +4 -4
- package/registry.json +21 -6
- package/src/bar-chart/bar-chart.ts +274 -315
- package/src/bar-chart/category-axis.ts +53 -88
- package/src/bar-chart/chart-bars.ts +108 -205
- package/src/bar-chart/index.ts +8 -19
- package/src/bar-chart/shared.ts +81 -309
- package/src/engine.ts +8 -17
- package/src/index.ts +2 -0
- package/src/kit/cartesian.ts +118 -55
- package/src/kit/chart-canvas.ts +50 -80
- package/src/kit/chart-legend.ts +36 -97
- package/src/kit/entries.ts +4 -1
- package/src/kit/horizontal-axis.ts +114 -0
- package/src/kit/motion.ts +18 -2
- package/src/{pie-chart → kit}/pie-legend.ts +25 -65
- package/src/kit/polar.ts +54 -0
- package/src/kit/shared.ts +104 -487
- package/src/kit/style.ts +257 -0
- package/src/kit/vertical-axis.ts +81 -115
- package/src/line-chart/chart-line.ts +185 -91
- package/src/line-chart/chart-region.ts +14 -31
- package/src/line-chart/index.ts +14 -22
- package/src/line-chart/line-chart.ts +377 -315
- package/src/line-chart/shared.ts +229 -304
- package/src/line-chart/x-scale.ts +44 -121
- package/src/nodes.ts +9 -13
- package/src/pie-chart/index.ts +3 -35
- package/src/pie-chart/pie-chart.ts +83 -134
- package/src/pie-chart/pie-ring.ts +162 -368
- package/src/pie-chart/shared.ts +92 -379
- package/src/radar-chart/index.ts +4 -0
- package/src/radar-chart/radar-chart.ts +287 -0
- package/src/radar-chart/radar-grid.ts +78 -0
- package/src/radar-chart/radar-series.ts +88 -0
- package/src/radar-chart/shared.ts +146 -0
- package/src/radial-chart/index.ts +5 -0
- package/src/radial-chart/radial-chart.ts +210 -0
- package/src/radial-chart/radial-rings.ts +328 -0
- package/src/radial-chart/shared.ts +114 -0
- package/dist/browser/chunks/chunk-TJNHLQGV.js +0 -2
- package/dist/browser/chunks/chunk-TJNHLQGV.js.map +0 -7
- package/dist/kit/chart-border.d.ts +0 -27
- package/dist/kit/chart-border.d.ts.map +0 -1
- package/dist/kit/chart-border.js +0 -28
- package/dist/kit/chart-border.js.map +0 -1
- package/dist/line-chart/horizontal-axis.d.ts +0 -79
- package/dist/line-chart/horizontal-axis.d.ts.map +0 -1
- package/dist/line-chart/horizontal-axis.js +0 -129
- package/dist/line-chart/horizontal-axis.js.map +0 -1
- package/dist/pie-chart/pie-legend.d.ts.map +0 -1
- package/dist/pie-chart/pie-legend.js +0 -117
- package/dist/pie-chart/pie-legend.js.map +0 -1
- package/src/kit/chart-border.ts +0 -50
- package/src/line-chart/horizontal-axis.ts +0 -151
package/src/pie-chart/shared.ts
CHANGED
|
@@ -1,166 +1,73 @@
|
|
|
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
|
-
import {
|
|
18
|
-
lerpNumber,
|
|
19
|
-
shadowOps,
|
|
20
|
-
strokeOps,
|
|
21
|
-
textOps,
|
|
22
|
-
type DataRecord,
|
|
23
|
-
type Fill,
|
|
24
|
-
type Shadow,
|
|
25
|
-
type ShadowResolved,
|
|
26
|
-
type Stroke,
|
|
27
|
-
type StrokeResolved,
|
|
28
|
-
type TextStyle,
|
|
29
|
-
type Vector2,
|
|
30
|
-
} from "@motionscript/sdk"
|
|
1
|
+
import { Types, type Fill, type TextStyle, type Stroke, type FillResolved as _FillResolved } from "@motionscript/sdk"
|
|
31
2
|
|
|
32
3
|
import {
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
text,
|
|
42
|
-
type LegendConfig,
|
|
43
|
-
type LegendTheme,
|
|
44
|
-
} from "../kit/shared"
|
|
4
|
+
CHART_COLORS,
|
|
5
|
+
CHART_LEGEND,
|
|
6
|
+
CHART_TEXT,
|
|
7
|
+
legendConfigOf,
|
|
8
|
+
type ChartStyleBase,
|
|
9
|
+
} from "../kit/style"
|
|
10
|
+
import { num, text, type LegendConfig } from "../kit/shared"
|
|
11
|
+
import { DEG, PIE_SPOTLIGHT_DIM, ringPoint, selectionFade, type CenterConfig, type PieSlice } from "../kit/polar"
|
|
45
12
|
|
|
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
|
-
*/
|
|
55
|
-
export interface PieSlice {
|
|
56
|
-
/** Legend caption and label text for this slice. */
|
|
57
|
-
category: string
|
|
58
|
-
/** This slice's magnitude. Its wedge covers `value / (sum of all values)`. */
|
|
59
|
-
value: number
|
|
60
|
-
/** Wedge and swatch paint. */
|
|
61
|
-
fill: Fill
|
|
62
|
-
}
|
|
13
|
+
export { DEG, PIE_SPOTLIGHT_DIM, ringPoint, selectionFade, type CenterConfig, type PieSlice }
|
|
63
14
|
|
|
64
15
|
/**
|
|
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.
|
|
16
|
+
* One slice per row, painted from `palette` by row order (cycling). Paint comes
|
|
17
|
+
* from the palette, not the rows, because a pie's mark is a row where a line's
|
|
18
|
+
* colour belongs to a column; that lets a pie be fed a CSV of plain numbers.
|
|
73
19
|
*/
|
|
74
|
-
export function resolvePieSlices(
|
|
75
|
-
data: DataRecord[],
|
|
76
|
-
category: string,
|
|
77
|
-
value: string,
|
|
78
|
-
palette: Fill[]
|
|
79
|
-
): PieSlice[] {
|
|
20
|
+
export function resolvePieSlices(data: readonly Record<string, unknown>[], category: string, value: string, palette: readonly Fill[]): PieSlice[] {
|
|
80
21
|
return data.map((row, i) => ({
|
|
81
|
-
category: text(row, category),
|
|
82
|
-
value: num(row, value),
|
|
83
|
-
fill:
|
|
84
|
-
palette.length > 0 ? palette[i % palette.length] : ("white" as Fill),
|
|
22
|
+
category: text(row as never, category),
|
|
23
|
+
value: num(row as never, value),
|
|
24
|
+
fill: palette.length > 0 ? palette[i % palette.length] : ("white" as Fill),
|
|
85
25
|
}))
|
|
86
26
|
}
|
|
87
27
|
|
|
88
28
|
/** What a {@link PieChart}'s labels say. */
|
|
89
29
|
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
|
-
*/
|
|
30
|
+
/** Two lines, whose order flips with placement: an outside label reads category over percentage, an inside label reads the percentage big over a small category. */
|
|
97
31
|
| "both"
|
|
98
|
-
/** The category only. */
|
|
99
32
|
| "category"
|
|
100
|
-
/** The percentage only — the pick when a legend already names the categories. */
|
|
101
33
|
| "percent"
|
|
102
|
-
/** No labels and no leader lines
|
|
34
|
+
/** No labels and no leader lines. */
|
|
103
35
|
| "none"
|
|
104
36
|
|
|
105
|
-
/**
|
|
106
|
-
|
|
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
|
-
*/
|
|
113
|
-
export type PieLabelPlacement = "inside" | "leader" | "none"
|
|
37
|
+
/** `"inside"` centres the label in the wedge's band with no leader or gutter, so the ring draws bigger; `"outside"` sets it on a leader, which a sliver too narrow for text needs. */
|
|
38
|
+
export type PieLabelPlacement = "inside" | "outside" | "none"
|
|
114
39
|
|
|
115
|
-
/** Degrees → radians. */
|
|
116
|
-
export const DEG = Math.PI / 180
|
|
117
40
|
|
|
118
41
|
/** A slice's plotted value: negatives and non-finite values count as 0. */
|
|
119
42
|
export function sliceValue(slice: PieSlice): number {
|
|
120
43
|
return slice.value > 0 && Number.isFinite(slice.value) ? slice.value : 0
|
|
121
44
|
}
|
|
122
45
|
|
|
123
|
-
/** Sum of every slice's plotted value
|
|
46
|
+
/** Sum of every slice's plotted value, the denominator of every share. */
|
|
124
47
|
export function sliceTotal(slices: PieSlice[]): number {
|
|
125
48
|
let sum = 0
|
|
126
49
|
for (const slice of slices) sum += sliceValue(slice)
|
|
127
50
|
return sum
|
|
128
51
|
}
|
|
129
52
|
|
|
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
|
-
*/
|
|
135
|
-
export function ringPoint(r: number, a: number): Vector2 {
|
|
136
|
-
return { x: r * Math.sin(a), y: r * Math.cos(a) }
|
|
137
|
-
}
|
|
138
|
-
|
|
139
53
|
/** One slice's resolved angular span on the ring. */
|
|
140
54
|
export interface PieArc {
|
|
141
55
|
/** Index of the slice in the source list. */
|
|
142
56
|
index: number
|
|
143
|
-
/**
|
|
57
|
+
/** Radians clockwise from 12 o'clock. */
|
|
144
58
|
a0: number
|
|
145
|
-
/** End angle, radians clockwise from 12 o'clock. */
|
|
146
59
|
a1: number
|
|
147
|
-
/**
|
|
60
|
+
/** Where the slice's label and pop-out point. */
|
|
148
61
|
mid: number
|
|
149
|
-
/**
|
|
62
|
+
/** Share of the total, `[0, 1]`. */
|
|
150
63
|
fraction: number
|
|
151
64
|
}
|
|
152
65
|
|
|
153
66
|
/**
|
|
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.
|
|
67
|
+
* One arc per positive slice, walking clockwise from `startDegrees` and abutting.
|
|
68
|
+
* Arcs are the undivided shares: a fixed angular gap would pinch shut at the
|
|
69
|
+
* hole, so the gap is applied when the wedge is drawn, as a constant-width inset
|
|
70
|
+
* off both ends.
|
|
164
71
|
*/
|
|
165
72
|
export function pieArcs(slices: PieSlice[], startDegrees: number): PieArc[] {
|
|
166
73
|
const total = sliceTotal(slices)
|
|
@@ -180,32 +87,21 @@ export function pieArcs(slices: PieSlice[], startDegrees: number): PieArc[] {
|
|
|
180
87
|
return arcs
|
|
181
88
|
}
|
|
182
89
|
|
|
183
|
-
|
|
184
|
-
export interface LabelSlot {
|
|
90
|
+
interface LabelSlot {
|
|
185
91
|
/** Where the label would sit if nothing else were in the way. */
|
|
186
92
|
ideal: number
|
|
187
|
-
/**
|
|
93
|
+
/** Written by {@link relaxLabels}. */
|
|
188
94
|
y: number
|
|
189
95
|
}
|
|
190
96
|
|
|
191
97
|
/**
|
|
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.
|
|
98
|
+
* Spread one column of labels so none overlap, each near its `ideal` y and all
|
|
99
|
+
* inside `[bottom, top]`. A top-down pass clears each label from the one above
|
|
100
|
+
* by `minGap`; a reverse pass lifts a tail pushed past `bottom`. When the column
|
|
101
|
+
* cannot hold them all, the reverse pass wins and the crowding is shared evenly.
|
|
102
|
+
* Mutates each `y` and returns `slots` ordered top to bottom.
|
|
202
103
|
*/
|
|
203
|
-
export function relaxLabels<T extends LabelSlot>(
|
|
204
|
-
slots: T[],
|
|
205
|
-
minGap: number,
|
|
206
|
-
top: number,
|
|
207
|
-
bottom: number
|
|
208
|
-
): T[] {
|
|
104
|
+
export function relaxLabels<T extends LabelSlot>(slots: T[], minGap: number, top: number, bottom: number): T[] {
|
|
209
105
|
slots.sort((a, b) => b.ideal - a.ideal)
|
|
210
106
|
|
|
211
107
|
let ceiling = top
|
|
@@ -223,274 +119,91 @@ export function relaxLabels<T extends LabelSlot>(
|
|
|
223
119
|
return slots
|
|
224
120
|
}
|
|
225
121
|
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
/** The ring's geometry and paint — everything about the wedges but their colour. */
|
|
122
|
+
/** The ring's geometry: everything about the wedges but their colour. */
|
|
229
123
|
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
|
-
*/
|
|
124
|
+
/** Channel between adjacent slices in px, constant across the band (an angular gap would splay open toward the rim). */
|
|
235
125
|
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
|
-
*/
|
|
126
|
+
/** 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
127
|
holeRadius: number
|
|
243
|
-
/**
|
|
128
|
+
/** Degrees clockwise from 12 o'clock where the first slice begins. */
|
|
244
129
|
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
|
-
*/
|
|
130
|
+
/** 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
131
|
selectedGrow: number
|
|
251
|
-
/**
|
|
252
|
-
|
|
253
|
-
/** Optional drop shadow on every wedge. */
|
|
254
|
-
shadow: Shadow
|
|
132
|
+
/** Rounding of each wedge corner, px. */
|
|
133
|
+
cornerRadius: number
|
|
255
134
|
}
|
|
256
135
|
|
|
257
136
|
/** How the ring's labels are set, and the leader lines that point at them. */
|
|
258
137
|
export interface LabelConfig {
|
|
259
|
-
/** The category caption
|
|
138
|
+
/** The category caption outside the ring. */
|
|
260
139
|
textStyle: TextStyle
|
|
261
|
-
/** The percentage
|
|
140
|
+
/** The percentage outside the ring. */
|
|
262
141
|
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
|
-
*/
|
|
142
|
+
/** Both lines of a label inside its wedge; its fill is set per wedge, for contrast against the paint. */
|
|
268
143
|
insideStyle: TextStyle
|
|
269
|
-
/**
|
|
144
|
+
/** Runs from a wedge out to its label. */
|
|
270
145
|
leaderStroke: Stroke
|
|
271
146
|
}
|
|
272
147
|
|
|
273
|
-
/**
|
|
274
|
-
export interface
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
}
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
export type PieChartTheme = {
|
|
293
|
-
sliceFills?: Fill[]
|
|
294
|
-
ring?: Partial<RingConfig>
|
|
295
|
-
label?: Partial<LabelConfig>
|
|
296
|
-
center?: Partial<CenterConfig>
|
|
297
|
-
legend?: LegendTheme
|
|
298
|
-
spotlight?: Partial<PieChartConfig["spotlight"]>
|
|
299
|
-
}
|
|
300
|
-
|
|
301
|
-
const LABEL_TEXT_STYLE: TextStyle = {
|
|
302
|
-
fontSize: 26,
|
|
303
|
-
fontWeight: 600,
|
|
304
|
-
textAlign: "center",
|
|
305
|
-
fill: CHART_PALETTE.markerColor,
|
|
306
|
-
}
|
|
307
|
-
|
|
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
|
-
*/
|
|
313
|
-
export const PIE_SPOTLIGHT_DIM = 0.1
|
|
314
|
-
|
|
315
|
-
/** The pie chart's out-of-the-box look. */
|
|
316
|
-
export const DEFAULT_PIE_CHART_THEME: PieChartConfig = {
|
|
317
|
-
sliceFills: DEFAULT_SERIES_FILLS,
|
|
318
|
-
ring: {
|
|
319
|
-
gap: 6,
|
|
320
|
-
// A donut rather than a solid pie by default: the hole is the largest
|
|
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.
|
|
323
|
-
holeRadius: 0.6,
|
|
324
|
-
startAngle: 0,
|
|
325
|
-
selectedGrow: 1.08,
|
|
326
|
-
stroke: [],
|
|
327
|
-
shadow: [],
|
|
328
|
-
},
|
|
329
|
-
label: {
|
|
330
|
-
textStyle: LABEL_TEXT_STYLE,
|
|
331
|
-
percentStyle: { ...LABEL_TEXT_STYLE, fontWeight: 700 },
|
|
332
|
-
// Inside labels sit on the wedge's paint, which is a mid-tone from the
|
|
333
|
-
// palette — so they lead with the panel colour rather than the axis ink.
|
|
334
|
-
insideStyle: { ...LABEL_TEXT_STYLE, fontWeight: 800, fill: "#15130e" },
|
|
335
|
-
leaderStroke: { weight: 2, fill: CHART_PALETTE.markerColor },
|
|
148
|
+
/** What a pie's `style` resolves to. */
|
|
149
|
+
export interface PieStyle extends ChartStyleBase {
|
|
150
|
+
readonly innerRadius: number
|
|
151
|
+
readonly sliceGap: number
|
|
152
|
+
readonly sliceRadius: number
|
|
153
|
+
readonly labelPlacement: PieLabelPlacement
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
/** The groups of a pie's `style`: no grid, as nothing is ruled. */
|
|
157
|
+
export const PIE_STYLE_GROUPS = {
|
|
158
|
+
colors: CHART_COLORS,
|
|
159
|
+
text: CHART_TEXT,
|
|
160
|
+
marks: {
|
|
161
|
+
/** Hole radius as a fraction of the outer radius: 0 is a pie, about 0.6 a donut. */
|
|
162
|
+
innerRadius: Types.number(0, { min: 0, max: 0.9, step: 0.05 }),
|
|
163
|
+
/** Channel between neighbouring wedges, px. */
|
|
164
|
+
sliceGap: Types.number(3, { min: 0, max: 40, step: 1, unit: "px" }),
|
|
165
|
+
/** Rounding of each wedge corner, px. */
|
|
166
|
+
sliceRadius: Types.number(4, { min: 0, max: 60, step: 1, unit: "px" }),
|
|
336
167
|
},
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
fontWeight: 800,
|
|
341
|
-
textAlign: "center",
|
|
342
|
-
fill: CHART_PALETTE.markerColor,
|
|
343
|
-
},
|
|
344
|
-
captionStyle: {
|
|
345
|
-
fontSize: 22,
|
|
346
|
-
fontWeight: 500,
|
|
347
|
-
textAlign: "center",
|
|
348
|
-
fill: CHART_PALETTE.markerColor,
|
|
349
|
-
},
|
|
168
|
+
labels: {
|
|
169
|
+
/** Read as the chart is built: inside the wedge, outside on a leader, or none. */
|
|
170
|
+
labelPlacement: Types.oneOf(["inside", "outside", "none"] as const, { animatable: false }),
|
|
350
171
|
},
|
|
351
|
-
legend:
|
|
352
|
-
spotlight: { dimOpacity: PIE_SPOTLIGHT_DIM },
|
|
353
|
-
}
|
|
354
|
-
|
|
355
|
-
function resolveRingConfig(
|
|
356
|
-
ring: Partial<RingConfig> | undefined,
|
|
357
|
-
previous: RingConfig
|
|
358
|
-
): RingConfig {
|
|
359
|
-
return {
|
|
360
|
-
gap: ring?.gap ?? previous.gap,
|
|
361
|
-
holeRadius: ring?.holeRadius ?? previous.holeRadius,
|
|
362
|
-
startAngle: ring?.startAngle ?? previous.startAngle,
|
|
363
|
-
selectedGrow: ring?.selectedGrow ?? previous.selectedGrow,
|
|
364
|
-
stroke: strokeOps.resolve(ring?.stroke ?? previous.stroke),
|
|
365
|
-
shadow: shadowOps.resolve(ring?.shadow ?? previous.shadow),
|
|
366
|
-
}
|
|
172
|
+
legend: CHART_LEGEND,
|
|
367
173
|
}
|
|
368
174
|
|
|
369
|
-
function
|
|
370
|
-
|
|
371
|
-
to: RingConfig,
|
|
372
|
-
t: number
|
|
373
|
-
): RingConfig {
|
|
374
|
-
return {
|
|
375
|
-
gap: lerpNumber(from.gap, to.gap, t),
|
|
376
|
-
holeRadius: lerpNumber(from.holeRadius, to.holeRadius, t),
|
|
377
|
-
startAngle: lerpNumber(from.startAngle, to.startAngle, t),
|
|
378
|
-
selectedGrow: lerpNumber(from.selectedGrow, to.selectedGrow, t),
|
|
379
|
-
stroke: strokeOps.lerp(
|
|
380
|
-
from.stroke as StrokeResolved[],
|
|
381
|
-
to.stroke as StrokeResolved[],
|
|
382
|
-
t
|
|
383
|
-
),
|
|
384
|
-
shadow: shadowOps.lerp(
|
|
385
|
-
from.shadow as ShadowResolved[],
|
|
386
|
-
to.shadow as ShadowResolved[],
|
|
387
|
-
t
|
|
388
|
-
),
|
|
389
|
-
}
|
|
175
|
+
export function legendOf(style: PieStyle): LegendConfig {
|
|
176
|
+
return legendConfigOf(style)
|
|
390
177
|
}
|
|
391
178
|
|
|
392
|
-
function
|
|
393
|
-
|
|
394
|
-
previous: LabelConfig
|
|
395
|
-
): LabelConfig {
|
|
396
|
-
return {
|
|
397
|
-
textStyle: textOps.resolveStyle(label?.textStyle, previous.textStyle),
|
|
398
|
-
percentStyle: textOps.resolveStyle(
|
|
399
|
-
label?.percentStyle,
|
|
400
|
-
previous.percentStyle
|
|
401
|
-
),
|
|
402
|
-
insideStyle: textOps.resolveStyle(label?.insideStyle, previous.insideStyle),
|
|
403
|
-
leaderStroke: strokeOps.resolve(
|
|
404
|
-
label?.leaderStroke ?? previous.leaderStroke
|
|
405
|
-
),
|
|
406
|
-
}
|
|
179
|
+
function face(style: PieStyle): Pick<TextStyle, "fontFamily"> {
|
|
180
|
+
return style.fontFamily ? { fontFamily: style.fontFamily } : {}
|
|
407
181
|
}
|
|
408
182
|
|
|
409
|
-
function
|
|
410
|
-
|
|
411
|
-
to: LabelConfig,
|
|
412
|
-
t: number
|
|
413
|
-
): LabelConfig {
|
|
414
|
-
return {
|
|
415
|
-
textStyle: textOps.lerpStyle(from.textStyle, to.textStyle, t),
|
|
416
|
-
percentStyle: textOps.lerpStyle(from.percentStyle, to.percentStyle, t),
|
|
417
|
-
insideStyle: textOps.lerpStyle(from.insideStyle, to.insideStyle, t),
|
|
418
|
-
leaderStroke: strokeOps.lerp(
|
|
419
|
-
from.leaderStroke as StrokeResolved[],
|
|
420
|
-
to.leaderStroke as StrokeResolved[],
|
|
421
|
-
t
|
|
422
|
-
),
|
|
423
|
-
}
|
|
183
|
+
export function ringConfigOf(style: PieStyle): RingConfig {
|
|
184
|
+
return { gap: style.sliceGap, holeRadius: style.innerRadius, startAngle: 0, selectedGrow: 1.08, cornerRadius: style.sliceRadius }
|
|
424
185
|
}
|
|
425
186
|
|
|
426
|
-
function
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
): CenterConfig {
|
|
187
|
+
export function labelConfigOf(style: PieStyle): LabelConfig {
|
|
188
|
+
const base: TextStyle = { ...face(style), fontSize: style.fontSize, fontWeight: style.fontWeight, textAlign: "center", fill: style.muted }
|
|
189
|
+
const bold = Math.min(900, style.fontWeight + 200)
|
|
430
190
|
return {
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
),
|
|
191
|
+
textStyle: base,
|
|
192
|
+
percentStyle: { ...base, fontWeight: bold, fill: style.foreground },
|
|
193
|
+
insideStyle: { ...base, fontSize: Math.round(style.fontSize * 1.9), fontWeight: bold, fill: "#111113" },
|
|
194
|
+
leaderStroke: { weight: 2, fill: style.muted },
|
|
436
195
|
}
|
|
437
196
|
}
|
|
438
197
|
|
|
439
|
-
function
|
|
440
|
-
from: CenterConfig,
|
|
441
|
-
to: CenterConfig,
|
|
442
|
-
t: number
|
|
443
|
-
): CenterConfig {
|
|
198
|
+
export function centerConfigOf(style: PieStyle): CenterConfig {
|
|
444
199
|
return {
|
|
445
|
-
labelStyle:
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
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
|
-
*/
|
|
456
|
-
export function resolvePieChartTheme(
|
|
457
|
-
theme: PieChartTheme | undefined,
|
|
458
|
-
previous?: PieChartConfig
|
|
459
|
-
): PieChartConfig {
|
|
460
|
-
const base = previous ?? DEFAULT_PIE_CHART_THEME
|
|
461
|
-
return {
|
|
462
|
-
sliceFills: resolveSeriesFills(theme?.sliceFills, base.sliceFills),
|
|
463
|
-
ring: resolveRingConfig(theme?.ring, base.ring),
|
|
464
|
-
label: resolveLabelConfig(theme?.label, base.label),
|
|
465
|
-
center: resolveCenterConfig(theme?.center, base.center),
|
|
466
|
-
legend: resolveLegendConfig(theme?.legend, base.legend),
|
|
467
|
-
spotlight: {
|
|
468
|
-
dimOpacity: theme?.spotlight?.dimOpacity ?? base.spotlight.dimOpacity,
|
|
469
|
-
},
|
|
470
|
-
}
|
|
471
|
-
}
|
|
472
|
-
|
|
473
|
-
/** {@link PieChart}'s `theme` property tween — leaf by leaf. */
|
|
474
|
-
export function lerpPieChartTheme(
|
|
475
|
-
from: PieChartConfig,
|
|
476
|
-
to: PieChartConfig,
|
|
477
|
-
t: number
|
|
478
|
-
): PieChartConfig {
|
|
479
|
-
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
|
-
sliceFills: lerpSeriesFills(from.sliceFills, to.sliceFills, t),
|
|
484
|
-
ring: lerpRingConfig(from.ring, to.ring, t),
|
|
485
|
-
label: lerpLabelConfig(from.label, to.label, t),
|
|
486
|
-
center: lerpCenterConfig(from.center, to.center, t),
|
|
487
|
-
legend: lerpLegendConfig(from.legend, to.legend, t),
|
|
488
|
-
spotlight: {
|
|
489
|
-
dimOpacity: lerpNumber(
|
|
490
|
-
from.spotlight.dimOpacity,
|
|
491
|
-
to.spotlight.dimOpacity,
|
|
492
|
-
t
|
|
493
|
-
),
|
|
200
|
+
labelStyle: {
|
|
201
|
+
...face(style),
|
|
202
|
+
fontSize: Math.round(style.fontSize * 1.8),
|
|
203
|
+
fontWeight: Math.min(900, style.fontWeight + 300),
|
|
204
|
+
textAlign: "center",
|
|
205
|
+
fill: style.foreground,
|
|
494
206
|
},
|
|
207
|
+
captionStyle: { ...face(style), fontSize: Math.round(style.fontSize * 0.8), fontWeight: style.fontWeight, textAlign: "center", fill: style.muted },
|
|
495
208
|
}
|
|
496
209
|
}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export { RadarChart, radarFractions, radarScale, type RadarValues } from "./radar-chart"
|
|
2
|
+
export { RadarGrid } from "./radar-grid"
|
|
3
|
+
export { RadarSeries, seriesVertices } from "./radar-series"
|
|
4
|
+
export { RADAR_GRID, RADAR_MARKS, fitRadius, niceCeil, spokeDirection } from "./shared"
|