@motionscript/charts 0.1.1-alpha.1 → 0.1.1-alpha.2
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 +15 -0
- package/README.md +19 -8
- package/dist/bar-chart/bar-chart.d.ts +97 -65
- package/dist/bar-chart/bar-chart.d.ts.map +1 -1
- package/dist/bar-chart/bar-chart.js +9 -5
- package/dist/bar-chart/bar-chart.js.map +1 -1
- package/dist/bar-chart/chart-bars.d.ts +29 -5
- package/dist/bar-chart/chart-bars.d.ts.map +1 -1
- package/dist/bar-chart/chart-bars.js +33 -8
- package/dist/bar-chart/chart-bars.js.map +1 -1
- package/dist/bar-chart/shared.d.ts +9 -4
- package/dist/bar-chart/shared.d.ts.map +1 -1
- package/dist/bar-chart/shared.js +10 -5
- package/dist/bar-chart/shared.js.map +1 -1
- package/dist/browser/chunks/chunk-7XCRX6GE.js +2 -0
- package/dist/browser/chunks/chunk-7XCRX6GE.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/kit/cartesian.d.ts +6 -4
- package/dist/kit/cartesian.d.ts.map +1 -1
- package/dist/kit/cartesian.js +5 -3
- package/dist/kit/cartesian.js.map +1 -1
- package/dist/kit/style.d.ts +339 -76
- package/dist/kit/style.d.ts.map +1 -1
- package/dist/kit/style.js +196 -101
- package/dist/kit/style.js.map +1 -1
- package/dist/line-chart/line-chart.d.ts +81 -57
- package/dist/line-chart/line-chart.d.ts.map +1 -1
- package/dist/line-chart/line-chart.js +9 -7
- package/dist/line-chart/line-chart.js.map +1 -1
- package/dist/pie-chart/pie-chart.d.ts +43 -43
- package/dist/pie-chart/pie-chart.d.ts.map +1 -1
- package/dist/pie-chart/pie-chart.js +2 -1
- package/dist/pie-chart/pie-chart.js.map +1 -1
- package/dist/pie-chart/shared.d.ts +15 -8
- package/dist/pie-chart/shared.d.ts.map +1 -1
- package/dist/pie-chart/shared.js +21 -20
- package/dist/pie-chart/shared.js.map +1 -1
- package/dist/radar-chart/index.d.ts +1 -1
- package/dist/radar-chart/index.d.ts.map +1 -1
- package/dist/radar-chart/index.js +1 -1
- package/dist/radar-chart/index.js.map +1 -1
- package/dist/radar-chart/radar-chart.d.ts +37 -43
- package/dist/radar-chart/radar-chart.d.ts.map +1 -1
- package/dist/radar-chart/radar-chart.js +12 -12
- package/dist/radar-chart/radar-chart.js.map +1 -1
- package/dist/radar-chart/radar-grid.d.ts +7 -1
- package/dist/radar-chart/radar-grid.d.ts.map +1 -1
- package/dist/radar-chart/radar-grid.js +17 -15
- package/dist/radar-chart/radar-grid.js.map +1 -1
- package/dist/radar-chart/shared.d.ts +7 -2
- package/dist/radar-chart/shared.d.ts.map +1 -1
- package/dist/radar-chart/shared.js +6 -0
- package/dist/radar-chart/shared.js.map +1 -1
- package/dist/radial-chart/radial-chart.d.ts +37 -43
- package/dist/radial-chart/radial-chart.d.ts.map +1 -1
- package/dist/radial-chart/radial-chart.js +2 -1
- package/dist/radial-chart/radial-chart.js.map +1 -1
- package/dist/radial-chart/shared.d.ts +13 -9
- package/dist/radial-chart/shared.d.ts.map +1 -1
- package/dist/radial-chart/shared.js +15 -16
- package/dist/radial-chart/shared.js.map +1 -1
- package/package.json +4 -4
- package/src/bar-chart/bar-chart.ts +17 -6
- package/src/bar-chart/chart-bars.ts +48 -9
- package/src/bar-chart/shared.ts +20 -5
- package/src/kit/cartesian.ts +10 -7
- package/src/kit/style.ts +214 -118
- package/src/line-chart/line-chart.ts +14 -8
- package/src/pie-chart/pie-chart.ts +3 -1
- package/src/pie-chart/shared.ts +30 -22
- package/src/radar-chart/index.ts +1 -1
- package/src/radar-chart/radar-chart.ts +16 -13
- package/src/radar-chart/radar-grid.ts +18 -13
- package/src/radar-chart/shared.ts +9 -2
- package/src/radial-chart/radial-chart.ts +3 -1
- package/src/radial-chart/shared.ts +30 -18
- package/dist/browser/chunks/chunk-R3Q5EE6E.js +0 -2
- package/dist/browser/chunks/chunk-R3Q5EE6E.js.map +0 -7
package/src/kit/style.ts
CHANGED
|
@@ -2,11 +2,14 @@
|
|
|
2
2
|
* The chart family's style: one `style` entry per chart, built from the groups
|
|
3
3
|
* below, so a look saved off one chart lays onto any other (a field the other
|
|
4
4
|
* chart does not draw is dropped). A chart spreads the shared groups and adds a
|
|
5
|
-
* `marks` group of its own.
|
|
5
|
+
* `marks` group of its own. Every run of text a chart draws is its own
|
|
6
|
+
* `Types.typography` field, so axis labels, titles, values and the legend are
|
|
7
|
+
* each set the way a text node is.
|
|
6
8
|
*/
|
|
7
9
|
|
|
8
10
|
import {
|
|
9
11
|
Types,
|
|
12
|
+
textOps,
|
|
10
13
|
type Fill,
|
|
11
14
|
type StrokeResolved,
|
|
12
15
|
type Stroke as _Stroke,
|
|
@@ -19,29 +22,50 @@ import {
|
|
|
19
22
|
|
|
20
23
|
import type { AxisConfig, ChartSeries, LegendConfig } from "./shared"
|
|
21
24
|
|
|
22
|
-
/** The style family every chart shares;
|
|
25
|
+
/** The style family every chart shares; the built-in presets are written for it. */
|
|
23
26
|
export const CHART_STYLE_FAMILY = "chart"
|
|
24
27
|
|
|
25
|
-
/** The out-of-the-box series
|
|
28
|
+
/** The out-of-the-box series fills, cycled by series (or row) index. */
|
|
26
29
|
export const CHART_PALETTE_DEFAULT = ["#4f8cff", "#2fbf8f", "#f5a524", "#e5547a", "#9b6cf0"] as const
|
|
27
30
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
31
|
+
/** The default ink of values, legend captions and centre text, on the dark stage. */
|
|
32
|
+
export const CHART_FOREGROUND = "#f4f4f5"
|
|
33
|
+
/** The default ink of axis markers, axis captions and spoke labels. */
|
|
34
|
+
export const CHART_MUTED = "#a1a1aa"
|
|
35
|
+
|
|
36
|
+
export const CHART_SERIES = {
|
|
37
|
+
/** A fill per series (a pie's or radial's per row), cycled; each may stack several paints. */
|
|
38
|
+
seriesFills: Types.palette(CHART_PALETTE_DEFAULT),
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export const CHART_BACKGROUND = {
|
|
42
|
+
/** Behind the whole chart, legend and axes included; none by default, so the chart reads against the stage. */
|
|
36
43
|
background: Types.fill([]),
|
|
37
44
|
}
|
|
38
45
|
|
|
39
|
-
export const
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
46
|
+
export const GRID_BACKGROUND = {
|
|
47
|
+
...CHART_BACKGROUND,
|
|
48
|
+
/** Behind the plot only, under the grid lines; none by default. */
|
|
49
|
+
gridBackground: Types.fill([]),
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/** The legend's captions, which every chart with a legend draws. */
|
|
53
|
+
export const LEGEND_TEXT = {
|
|
54
|
+
legendText: Types.typography({ fontSize: 28, fontWeight: 500, fill: CHART_FOREGROUND }),
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export const CARTESIAN_TEXT = {
|
|
58
|
+
/** The markers under the plot: categories on a column chart, values on a horizontal bar chart. */
|
|
59
|
+
xLabelText: Types.typography({ fontSize: 28, fontWeight: 500, fill: CHART_MUTED }),
|
|
60
|
+
/** The caption under the plot. */
|
|
61
|
+
xTitleText: Types.typography({ fontSize: 31, fontWeight: 600, fill: CHART_MUTED }),
|
|
62
|
+
/** The markers beside the plot. */
|
|
63
|
+
yLabelText: Types.typography({ fontSize: 28, fontWeight: 500, fill: CHART_MUTED }),
|
|
64
|
+
/** The caption beside the plot, turned on its side. */
|
|
65
|
+
yTitleText: Types.typography({ fontSize: 31, fontWeight: 600, fill: CHART_MUTED }),
|
|
66
|
+
/** The figures printed by the marks. */
|
|
67
|
+
valueText: Types.typography({ fontSize: 24, fontWeight: 600, fill: CHART_FOREGROUND }),
|
|
68
|
+
...LEGEND_TEXT,
|
|
45
69
|
}
|
|
46
70
|
|
|
47
71
|
export const CARTESIAN_GRID = {
|
|
@@ -66,25 +90,23 @@ export const CHART_LEGEND = {
|
|
|
66
90
|
swatch: Types.oneOf(["square", "circle", "line"] as const),
|
|
67
91
|
}
|
|
68
92
|
|
|
69
|
-
/** The type fields, and the two text colours: what every text helper reads. */
|
|
70
|
-
export interface ChartTextStyle {
|
|
71
|
-
readonly foreground: Fill
|
|
72
|
-
readonly muted: Fill
|
|
73
|
-
readonly fontFamily: string
|
|
74
|
-
readonly fontSize: number
|
|
75
|
-
readonly fontWeight: number
|
|
76
|
-
}
|
|
77
|
-
|
|
78
93
|
/** The fields every chart's style resolves, whichever groups it spreads. */
|
|
79
|
-
export interface ChartStyleBase
|
|
80
|
-
readonly
|
|
94
|
+
export interface ChartStyleBase {
|
|
95
|
+
readonly seriesFills: readonly Fill[]
|
|
81
96
|
readonly background: Fill
|
|
97
|
+
readonly legendText: TextStyle
|
|
82
98
|
readonly legend: LegendPosition
|
|
83
99
|
readonly swatch: LegendSwatch
|
|
84
100
|
}
|
|
85
101
|
|
|
86
|
-
/** The cartesian
|
|
102
|
+
/** The cartesian fields, for the line and bar charts. */
|
|
87
103
|
export interface CartesianStyle extends ChartStyleBase {
|
|
104
|
+
readonly gridBackground: Fill
|
|
105
|
+
readonly xLabelText: TextStyle
|
|
106
|
+
readonly xTitleText: TextStyle
|
|
107
|
+
readonly yLabelText: TextStyle
|
|
108
|
+
readonly yTitleText: TextStyle
|
|
109
|
+
readonly valueText: TextStyle
|
|
88
110
|
readonly horizontalGrid: StrokeResolved[]
|
|
89
111
|
readonly verticalGrid: StrokeResolved[]
|
|
90
112
|
readonly axisStroke: StrokeResolved[]
|
|
@@ -92,8 +114,14 @@ export interface CartesianStyle extends ChartStyleBase {
|
|
|
92
114
|
readonly showYAxis: boolean
|
|
93
115
|
}
|
|
94
116
|
|
|
95
|
-
/** The
|
|
96
|
-
const
|
|
117
|
+
/** The fields a preset restates so its rules and text read on its background (a chart keeps those it draws). */
|
|
118
|
+
const _PRESET_FIELDS = {
|
|
119
|
+
...CHART_SERIES,
|
|
120
|
+
...GRID_BACKGROUND,
|
|
121
|
+
...CARTESIAN_TEXT,
|
|
122
|
+
labelText: Types.typography(),
|
|
123
|
+
centerText: Types.typography(),
|
|
124
|
+
spokeText: Types.typography(),
|
|
97
125
|
horizontalGrid: CARTESIAN_GRID.horizontalGrid,
|
|
98
126
|
gridStroke: Types.stroke([]),
|
|
99
127
|
spokeStroke: Types.stroke([]),
|
|
@@ -101,147 +129,154 @@ const _PRESET_RULES = {
|
|
|
101
129
|
}
|
|
102
130
|
|
|
103
131
|
/** A preset's fields: any chart's, so one preset dresses every chart. */
|
|
104
|
-
export type ChartStylePreset = StyleInput<typeof
|
|
132
|
+
export type ChartStylePreset = StyleInput<typeof _PRESET_FIELDS>
|
|
133
|
+
|
|
134
|
+
/** Every text field's ink: `foreground` for values, the legend and centre text, `muted` for markers and captions. */
|
|
135
|
+
function inks(foreground: string, muted: string) {
|
|
136
|
+
const on = (fill: string) => ({ fill })
|
|
137
|
+
return {
|
|
138
|
+
xLabelText: on(muted),
|
|
139
|
+
xTitleText: on(muted),
|
|
140
|
+
yLabelText: on(muted),
|
|
141
|
+
yTitleText: on(muted),
|
|
142
|
+
valueText: on(foreground),
|
|
143
|
+
legendText: on(foreground),
|
|
144
|
+
labelText: on(muted),
|
|
145
|
+
centerText: on(foreground),
|
|
146
|
+
spokeText: on(muted),
|
|
147
|
+
} satisfies ChartStylePreset
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/** Every rule a chart draws, and a radial's track, in one paint. */
|
|
151
|
+
function rules(fill: string) {
|
|
152
|
+
return {
|
|
153
|
+
horizontalGrid: { weight: 2, fill },
|
|
154
|
+
gridStroke: { weight: 2, fill },
|
|
155
|
+
spokeStroke: { weight: 2, fill },
|
|
156
|
+
track: fill,
|
|
157
|
+
} satisfies ChartStylePreset
|
|
158
|
+
}
|
|
105
159
|
|
|
106
160
|
/** Ready-made looks, each stating every colour so applying one replaces the last. */
|
|
107
161
|
export const CHART_STYLE_PRESETS = {
|
|
108
162
|
midnight: {
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
horizontalGrid: { weight: 2, fill: "white/15" },
|
|
113
|
-
gridStroke: { weight: 2, fill: "white/15" },
|
|
114
|
-
spokeStroke: { weight: 2, fill: "white/15" },
|
|
163
|
+
seriesFills: [...CHART_PALETTE_DEFAULT],
|
|
164
|
+
...inks(CHART_FOREGROUND, CHART_MUTED),
|
|
165
|
+
...rules("white/15"),
|
|
115
166
|
track: "white/10",
|
|
116
167
|
background: [],
|
|
168
|
+
gridBackground: [],
|
|
117
169
|
},
|
|
118
170
|
paper: {
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
horizontalGrid: { weight: 2, fill: "#1c1917/12" },
|
|
123
|
-
gridStroke: { weight: 2, fill: "#1c1917/12" },
|
|
124
|
-
spokeStroke: { weight: 2, fill: "#1c1917/12" },
|
|
125
|
-
track: "#1c1917/12",
|
|
171
|
+
seriesFills: ["#2563eb", "#059669", "#d97706", "#db2777", "#7c3aed"],
|
|
172
|
+
...inks("#1c1917", "#57534e"),
|
|
173
|
+
...rules("#1c1917/12"),
|
|
126
174
|
background: "#fafaf9",
|
|
175
|
+
gridBackground: [],
|
|
127
176
|
},
|
|
128
177
|
ember: {
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
horizontalGrid: { weight: 2, fill: "#2c2820" },
|
|
133
|
-
gridStroke: { weight: 2, fill: "#2c2820" },
|
|
134
|
-
spokeStroke: { weight: 2, fill: "#2c2820" },
|
|
135
|
-
track: "#2c2820",
|
|
178
|
+
seriesFills: ["#d9603b", "#8194ad", "#e0a93b", "#5e9d6b", "#9ec85a"],
|
|
179
|
+
...inks("#e9e1d4", "#8a7f70"),
|
|
180
|
+
...rules("#2c2820"),
|
|
136
181
|
background: "#15130e",
|
|
182
|
+
gridBackground: [],
|
|
137
183
|
},
|
|
138
184
|
neon: {
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
horizontalGrid: { weight: 2, fill: "white/10" },
|
|
143
|
-
gridStroke: { weight: 2, fill: "white/10" },
|
|
144
|
-
spokeStroke: { weight: 2, fill: "white/10" },
|
|
145
|
-
track: "white/10",
|
|
185
|
+
seriesFills: ["#22d3ee", "#a3e635", "#f472b6", "#facc15", "#818cf8"],
|
|
186
|
+
...inks("#ffffff", "#94a3b8"),
|
|
187
|
+
...rules("white/10"),
|
|
146
188
|
background: [],
|
|
189
|
+
gridBackground: [],
|
|
147
190
|
},
|
|
148
191
|
ocean: {
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
horizontalGrid: { weight: 2, fill: "#7dd3fc/20" },
|
|
153
|
-
gridStroke: { weight: 2, fill: "#7dd3fc/20" },
|
|
154
|
-
spokeStroke: { weight: 2, fill: "#7dd3fc/20" },
|
|
155
|
-
track: "#7dd3fc/20",
|
|
192
|
+
seriesFills: ["#0ea5e9", "#14b8a6", "#6366f1", "#38bdf8", "#2dd4bf"],
|
|
193
|
+
...inks("#e0f2fe", "#7dd3fc"),
|
|
194
|
+
...rules("#7dd3fc/20"),
|
|
156
195
|
background: "#082f49",
|
|
196
|
+
gridBackground: [],
|
|
157
197
|
},
|
|
158
198
|
sunset: {
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
horizontalGrid: { weight: 2, fill: "#fdba74/20" },
|
|
163
|
-
gridStroke: { weight: 2, fill: "#fdba74/20" },
|
|
164
|
-
spokeStroke: { weight: 2, fill: "#fdba74/20" },
|
|
165
|
-
track: "#fdba74/20",
|
|
199
|
+
seriesFills: ["#f97316", "#ef4444", "#ec4899", "#f59e0b", "#a855f7"],
|
|
200
|
+
...inks("#fff7ed", "#fdba74"),
|
|
201
|
+
...rules("#fdba74/20"),
|
|
166
202
|
background: [],
|
|
203
|
+
gridBackground: [],
|
|
167
204
|
},
|
|
168
205
|
pastel: {
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
horizontalGrid: { weight: 2, fill: "white/12" },
|
|
173
|
-
gridStroke: { weight: 2, fill: "white/12" },
|
|
174
|
-
spokeStroke: { weight: 2, fill: "white/12" },
|
|
175
|
-
track: "white/12",
|
|
206
|
+
seriesFills: ["#a5b4fc", "#f9a8d4", "#86efac", "#fde68a", "#7dd3fc"],
|
|
207
|
+
...inks("#f8fafc", "#cbd5e1"),
|
|
208
|
+
...rules("white/12"),
|
|
176
209
|
background: [],
|
|
210
|
+
gridBackground: [],
|
|
177
211
|
},
|
|
178
212
|
mono: {
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
horizontalGrid: { weight: 2, fill: "white/12" },
|
|
183
|
-
gridStroke: { weight: 2, fill: "white/12" },
|
|
184
|
-
spokeStroke: { weight: 2, fill: "white/12" },
|
|
185
|
-
track: "white/12",
|
|
213
|
+
seriesFills: ["#fafafa", "#a3a3a3", "#737373", "#d4d4d4", "#525252"],
|
|
214
|
+
...inks("#fafafa", "#a3a3a3"),
|
|
215
|
+
...rules("white/12"),
|
|
186
216
|
background: [],
|
|
217
|
+
gridBackground: [],
|
|
187
218
|
},
|
|
188
219
|
} satisfies Record<string, ChartStylePreset>
|
|
189
220
|
|
|
190
|
-
/** Series `index`'s
|
|
191
|
-
export function paletteFill(style: Pick<ChartStyleBase, "
|
|
221
|
+
/** Series `index`'s fill: its own `fill`, else the style's series fills, cycled. */
|
|
222
|
+
export function paletteFill(style: Pick<ChartStyleBase, "seriesFills">, index: number, own?: Fill): Fill {
|
|
192
223
|
if (own != null) return own
|
|
193
|
-
const
|
|
194
|
-
return
|
|
224
|
+
const fills = style.seriesFills
|
|
225
|
+
return fills.length > 0 ? fills[index % fills.length] : "white"
|
|
195
226
|
}
|
|
196
227
|
|
|
197
228
|
/** {@link paletteFill} for a series. */
|
|
198
|
-
export function seriesFill(style: Pick<ChartStyleBase, "
|
|
229
|
+
export function seriesFill(style: Pick<ChartStyleBase, "seriesFills">, series: ChartSeries, index: number): Fill {
|
|
199
230
|
return paletteFill(style, index, series.fill)
|
|
200
231
|
}
|
|
201
232
|
|
|
202
|
-
|
|
203
|
-
|
|
233
|
+
/** A text style's size in px; an autofit or unset size reads as `fallback`. */
|
|
234
|
+
export function sizeOf(text: TextStyle | undefined, fallback = 28): number {
|
|
235
|
+
const size = text?.fontSize
|
|
236
|
+
return typeof size === "number" && Number.isFinite(size) ? size : fallback
|
|
204
237
|
}
|
|
205
238
|
|
|
206
|
-
/**
|
|
207
|
-
|
|
208
|
-
|
|
239
|
+
/**
|
|
240
|
+
* `text` as a text node takes it, aligned where the chart puts it, with `over`
|
|
241
|
+
* laid on top. Unset keys are dropped: an `undefined` spread onto a node is a
|
|
242
|
+
* value, and an unset face must stay unset so the host's default applies.
|
|
243
|
+
*/
|
|
244
|
+
export function placedText(text: TextStyle, textAlign: TextStyle["textAlign"] = "center", over?: TextStyle): TextStyle {
|
|
245
|
+
return textOps.styleProps({ ...textOps.mergeStyle(text, over), textAlign }) as TextStyle
|
|
209
246
|
}
|
|
210
247
|
|
|
211
|
-
/** Axis
|
|
212
|
-
export function
|
|
213
|
-
return
|
|
214
|
-
...face(style),
|
|
215
|
-
fontSize: Math.round(style.fontSize * 1.1),
|
|
216
|
-
fontWeight: Math.min(900, style.fontWeight + 100),
|
|
217
|
-
textAlign: "center",
|
|
218
|
-
fill: style.muted,
|
|
219
|
-
}
|
|
248
|
+
/** Axis markers on `axis`, aligned as that axis sets them. */
|
|
249
|
+
export function markerText(style: Pick<CartesianStyle, "xLabelText" | "yLabelText">, axis: "x" | "y"): TextStyle {
|
|
250
|
+
return placedText(axis === "x" ? style.xLabelText : style.yLabelText, axis === "x" ? "center" : "right")
|
|
220
251
|
}
|
|
221
252
|
|
|
222
|
-
/**
|
|
223
|
-
export function valueText(style:
|
|
224
|
-
return
|
|
253
|
+
/** The figures printed by the marks. */
|
|
254
|
+
export function valueText(style: Pick<CartesianStyle, "valueText">, textAlign: TextStyle["textAlign"] = "center"): TextStyle {
|
|
255
|
+
return placedText(style.valueText, textAlign)
|
|
225
256
|
}
|
|
226
257
|
|
|
227
258
|
/**
|
|
228
259
|
* The kit axis parts' config from a style. `x` is the axis under the plot;
|
|
229
260
|
* its grid lines are vertical (one per x marker), the `y` axis's horizontal.
|
|
230
261
|
*/
|
|
231
|
-
export function axisConfigOf(
|
|
232
|
-
|
|
262
|
+
export function axisConfigOf(
|
|
263
|
+
style: Pick<CartesianStyle, "verticalGrid" | "horizontalGrid" | "xLabelText" | "xTitleText" | "yLabelText" | "yTitleText">,
|
|
264
|
+
axis: "x" | "y"
|
|
265
|
+
): AxisConfig {
|
|
266
|
+
const gap = Math.round(sizeOf(axis === "x" ? style.xLabelText : style.yLabelText) * 0.6)
|
|
233
267
|
return {
|
|
234
|
-
labelStyle: markerText(style, axis
|
|
235
|
-
titleStyle:
|
|
268
|
+
labelStyle: markerText(style, axis),
|
|
269
|
+
titleStyle: placedText(axis === "x" ? style.xTitleText : style.yTitleText),
|
|
236
270
|
lineStyle: axis === "x" ? style.verticalGrid : style.horizontalGrid,
|
|
237
271
|
tickPadding: axis === "x" ? { top: gap } : { right: gap },
|
|
238
272
|
titlePadding: axis === "x" ? { top: Math.round(gap / 2) } : { right: gap },
|
|
239
273
|
}
|
|
240
274
|
}
|
|
241
275
|
|
|
242
|
-
/** The kit legend's config: captions in
|
|
243
|
-
export function legendConfigOf(style:
|
|
244
|
-
const
|
|
276
|
+
/** The kit legend's config: captions in `legendText`, the swatch shaped by `swatch` and sized off the captions. */
|
|
277
|
+
export function legendConfigOf(style: Pick<ChartStyleBase, "swatch" | "legendText">): LegendConfig {
|
|
278
|
+
const type = sizeOf(style.legendText)
|
|
279
|
+
const size = Math.max(8, Math.round(type * 0.6))
|
|
245
280
|
const marker =
|
|
246
281
|
style.swatch === "line"
|
|
247
282
|
? { width: Math.round(size * 2), height: Math.max(3, Math.round(size / 4)), borderRadius: size }
|
|
@@ -250,8 +285,69 @@ export function legendConfigOf(style: ChartTextStyle & Pick<ChartStyleBase, "swa
|
|
|
250
285
|
: { width: size, height: size, borderRadius: Math.round(size / 4) }
|
|
251
286
|
return {
|
|
252
287
|
padding: 0,
|
|
253
|
-
textStyle:
|
|
254
|
-
itemGap: Math.round(
|
|
288
|
+
textStyle: placedText(style.legendText, "left"),
|
|
289
|
+
itemGap: Math.round(type * 1.4),
|
|
255
290
|
markerStyle: { ...marker, stroke: [], shadow: [] },
|
|
256
291
|
}
|
|
257
292
|
}
|
|
293
|
+
|
|
294
|
+
/** Which text a chart drew from the retired shared text fields. */
|
|
295
|
+
export type ChartTextLayout = "cartesian" | "pie" | "radar" | "radial"
|
|
296
|
+
|
|
297
|
+
/**
|
|
298
|
+
* The `style` entry's hook for documents written before every run of text got
|
|
299
|
+
* its own field (0.1.1-alpha.1): `barRadius` became `barCornerRadius`, `palette` became `seriesFills`; `foreground`,
|
|
300
|
+
* `muted` and the one `fontFamily`/`fontSize`/`fontWeight` became the text
|
|
301
|
+
* fields as each chart derived them; a cartesian or radar `background`, which
|
|
302
|
+
* painted the plot, became `gridBackground`. A current style comes back as is.
|
|
303
|
+
*/
|
|
304
|
+
export function migrateChartStyle(layout: ChartTextLayout): (value: unknown) => unknown {
|
|
305
|
+
return (value) => {
|
|
306
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) return value
|
|
307
|
+
const old = value as Record<string, unknown>
|
|
308
|
+
const retired = ["palette", "foreground", "muted", "fontFamily", "fontSize", "fontWeight", "barRadius"]
|
|
309
|
+
const plotBackground = layout === "cartesian" || layout === "radar"
|
|
310
|
+
if (!retired.some((key) => key in old) && !(plotBackground && "background" in old && !("gridBackground" in old))) return value
|
|
311
|
+
|
|
312
|
+
const { palette, foreground, muted, fontFamily, fontSize, fontWeight, barRadius, ...rest } = old
|
|
313
|
+
const out: Record<string, unknown> = { ...rest }
|
|
314
|
+
// A bar radius rounded only the far end; its replacement states every corner.
|
|
315
|
+
if (typeof barRadius === "number" && out.barCornerRadius === undefined) {
|
|
316
|
+
out.barCornerRadius = { topLeft: barRadius, topRight: barRadius, bottomRight: 0, bottomLeft: 0 }
|
|
317
|
+
}
|
|
318
|
+
if (palette !== undefined && out.seriesFills === undefined) out.seriesFills = palette
|
|
319
|
+
if (plotBackground && "background" in old && !("gridBackground" in old)) {
|
|
320
|
+
out.gridBackground = old.background
|
|
321
|
+
delete out.background
|
|
322
|
+
}
|
|
323
|
+
const size = typeof fontSize === "number" ? fontSize : undefined
|
|
324
|
+
const weight = typeof fontWeight === "number" ? fontWeight : undefined
|
|
325
|
+
const text = (ink: unknown, scale = 1, heavier = 0): Record<string, unknown> | undefined => {
|
|
326
|
+
const t: Record<string, unknown> = {}
|
|
327
|
+
if (typeof fontFamily === "string" && fontFamily !== "") t.fontFamily = fontFamily
|
|
328
|
+
if (size !== undefined) t.fontSize = Math.round(size * scale)
|
|
329
|
+
if (weight !== undefined) t.fontWeight = Math.min(900, weight + heavier)
|
|
330
|
+
if (ink !== undefined) t.fill = ink
|
|
331
|
+
return Object.keys(t).length > 0 ? t : undefined
|
|
332
|
+
}
|
|
333
|
+
const fields: Record<string, Record<string, unknown> | undefined> = { legendText: text(foreground) }
|
|
334
|
+
if (layout === "cartesian") {
|
|
335
|
+
fields.xLabelText = text(muted)
|
|
336
|
+
fields.yLabelText = text(muted)
|
|
337
|
+
fields.xTitleText = text(muted, 1.1, 100)
|
|
338
|
+
fields.yTitleText = text(muted, 1.1, 100)
|
|
339
|
+
fields.valueText = text(foreground, 0.85, 100)
|
|
340
|
+
} else if (layout === "pie") {
|
|
341
|
+
fields.labelText = text(muted)
|
|
342
|
+
fields.valueText = text(foreground, 1, 200)
|
|
343
|
+
fields.centerText = text(foreground, 1.8, 300)
|
|
344
|
+
} else if (layout === "radar") {
|
|
345
|
+
fields.spokeText = text(muted)
|
|
346
|
+
} else {
|
|
347
|
+
fields.labelText = text(muted)
|
|
348
|
+
fields.centerText = text(foreground, 1.8, 300)
|
|
349
|
+
}
|
|
350
|
+
for (const [key, field] of Object.entries(fields)) if (field && out[key] === undefined) out[key] = field
|
|
351
|
+
return out
|
|
352
|
+
}
|
|
353
|
+
}
|
|
@@ -12,6 +12,7 @@ import {
|
|
|
12
12
|
// Named so this component's emitted type reaches `Node2D` and its props through the sdk, not core.
|
|
13
13
|
type Node2D,
|
|
14
14
|
type Node2DProps as _Node2DProps,
|
|
15
|
+
type TextStyle as _TextStyle,
|
|
15
16
|
type Fill as _Fill,
|
|
16
17
|
type FillResolved as _FillResolved,
|
|
17
18
|
type Stroke as _Stroke,
|
|
@@ -46,14 +47,17 @@ import {
|
|
|
46
47
|
} from "../kit/shared"
|
|
47
48
|
import {
|
|
48
49
|
CARTESIAN_GRID,
|
|
49
|
-
|
|
50
|
+
CARTESIAN_TEXT,
|
|
50
51
|
CHART_LEGEND,
|
|
52
|
+
CHART_SERIES,
|
|
51
53
|
CHART_STYLE_FAMILY,
|
|
52
54
|
CHART_STYLE_PRESETS,
|
|
53
|
-
|
|
55
|
+
GRID_BACKGROUND,
|
|
54
56
|
axisConfigOf,
|
|
55
57
|
legendConfigOf,
|
|
58
|
+
migrateChartStyle,
|
|
56
59
|
seriesFill,
|
|
60
|
+
sizeOf,
|
|
57
61
|
valueText,
|
|
58
62
|
} from "../kit/style"
|
|
59
63
|
import { VerticalAxis, axisPads } from "../kit/vertical-axis"
|
|
@@ -75,7 +79,7 @@ import { resolveXScale, type XScale } from "./x-scale"
|
|
|
75
79
|
|
|
76
80
|
const LINE_STYLE = Types.style(
|
|
77
81
|
CHART_STYLE_FAMILY,
|
|
78
|
-
{
|
|
82
|
+
{ series: CHART_SERIES, background: GRID_BACKGROUND, text: CARTESIAN_TEXT, grid: CARTESIAN_GRID, marks: LINE_MARKS, labels: LINE_LABELS, legend: CHART_LEGEND },
|
|
79
83
|
{ presets: CHART_STYLE_PRESETS }
|
|
80
84
|
)
|
|
81
85
|
|
|
@@ -352,6 +356,7 @@ export const LineChart = component2d({
|
|
|
352
356
|
height: "fill",
|
|
353
357
|
weights: (state) => state.series.map((s) => ((s.enabled ?? true) ? 1 : 0)),
|
|
354
358
|
},
|
|
359
|
+
migrate: { style: migrateChartStyle("cartesian") },
|
|
355
360
|
allowedChildren: [],
|
|
356
361
|
children: (state) => {
|
|
357
362
|
const style = () => state.style
|
|
@@ -450,7 +455,7 @@ export const LineChart = component2d({
|
|
|
450
455
|
state.data.forEach((_row, k) => {
|
|
451
456
|
const line = lines[i]
|
|
452
457
|
const label: Node2D = new Text({
|
|
453
|
-
...boundText(() =>
|
|
458
|
+
...boundText(() => valueText(style())),
|
|
454
459
|
text: () => {
|
|
455
460
|
const value = pixels(i).values[k]
|
|
456
461
|
return value === undefined ? "" : yLabel(state, value, domains().y.ticks)
|
|
@@ -462,7 +467,7 @@ export const LineChart = component2d({
|
|
|
462
467
|
},
|
|
463
468
|
y: () => {
|
|
464
469
|
const point = pixels(i).points[k]
|
|
465
|
-
const clear = label.layoutBounds.height / 2 + (style().dots ? style().dotSize / 2 : style().strokeWidth / 2) + Math.round(style().
|
|
470
|
+
const clear = label.layoutBounds.height / 2 + (style().dots ? style().dotSize / 2 : style().strokeWidth / 2) + Math.round(sizeOf(style().valueText) * 0.25)
|
|
466
471
|
return (point?.y ?? 0) + (below ? -clear : clear)
|
|
467
472
|
},
|
|
468
473
|
// In once the trace has passed its point.
|
|
@@ -492,7 +497,7 @@ export const LineChart = component2d({
|
|
|
492
497
|
clip: () => state.clipped || overflows(state, domains()),
|
|
493
498
|
children: [
|
|
494
499
|
new ChartCanvas({
|
|
495
|
-
fill: () => style().
|
|
500
|
+
fill: () => style().gridBackground,
|
|
496
501
|
xGridStroke: () => style().verticalGrid,
|
|
497
502
|
yGridStroke: () => style().horizontalGrid,
|
|
498
503
|
xMarkers: () => domains().x.ticks,
|
|
@@ -506,8 +511,8 @@ export const LineChart = component2d({
|
|
|
506
511
|
// Under the marks, so the highlight does not tint the stretch it picks out.
|
|
507
512
|
new ChartRegion({
|
|
508
513
|
box: spotlight,
|
|
509
|
-
stroke: () => ({ weight: 2, fill: fade(style().
|
|
510
|
-
fill: () => fade(style().
|
|
514
|
+
stroke: () => ({ weight: 2, fill: fade(style().valueText.fill ?? "white", 0.7), dash: [14, 12], cap: "butt", align: "center" }),
|
|
515
|
+
fill: () => fade(style().valueText.fill ?? "white", 0.06),
|
|
511
516
|
reveal: () => state.regionReveal,
|
|
512
517
|
}),
|
|
513
518
|
...areas,
|
|
@@ -518,6 +523,7 @@ export const LineChart = component2d({
|
|
|
518
523
|
|
|
519
524
|
return asParts([
|
|
520
525
|
cartesianFrame({
|
|
526
|
+
background: () => style().background,
|
|
521
527
|
left: vAxis,
|
|
522
528
|
ghost,
|
|
523
529
|
plot,
|
|
@@ -10,6 +10,7 @@ import {
|
|
|
10
10
|
// Named so this component's emitted type reaches `Node2D` and its props through the sdk, not core.
|
|
11
11
|
type Node2D,
|
|
12
12
|
type Node2DProps as _Node2DProps,
|
|
13
|
+
type TextStyle as _TextStyle,
|
|
13
14
|
type Fill as _Fill,
|
|
14
15
|
type FillResolved as _FillResolved,
|
|
15
16
|
} from "@motionscript/sdk"
|
|
@@ -18,7 +19,7 @@ import { asParts, fresh } from "../kit/entries"
|
|
|
18
19
|
import { beats, type AnyTimeline, type Beat } from "../kit/motion"
|
|
19
20
|
import { PieLegend } from "../kit/pie-legend"
|
|
20
21
|
import { PieRing } from "./pie-ring"
|
|
21
|
-
import { CHART_STYLE_FAMILY, CHART_STYLE_PRESETS, paletteFill } from "../kit/style"
|
|
22
|
+
import { CHART_STYLE_FAMILY, CHART_STYLE_PRESETS, migrateChartStyle, paletteFill } from "../kit/style"
|
|
22
23
|
import {
|
|
23
24
|
PIE_SPOTLIGHT_DIM,
|
|
24
25
|
PIE_STYLE_GROUPS,
|
|
@@ -127,6 +128,7 @@ export const PieChart = component2d({
|
|
|
127
128
|
height: "fill",
|
|
128
129
|
pops: (state) => slicesOf(state).map(() => 0),
|
|
129
130
|
},
|
|
131
|
+
migrate: { style: migrateChartStyle("pie") },
|
|
130
132
|
allowedChildren: [],
|
|
131
133
|
// The structure (a label and legend row per row) is read once; slices, paint and selection are bound, so a theme tween re-paints and a `set` of `data` re-cuts.
|
|
132
134
|
children: (state) => {
|
package/src/pie-chart/shared.ts
CHANGED
|
@@ -1,10 +1,15 @@
|
|
|
1
1
|
import { Types, type Fill, type TextStyle, type Stroke, type FillResolved as _FillResolved } from "@motionscript/sdk"
|
|
2
2
|
|
|
3
3
|
import {
|
|
4
|
-
|
|
4
|
+
CHART_BACKGROUND,
|
|
5
|
+
CHART_FOREGROUND,
|
|
5
6
|
CHART_LEGEND,
|
|
6
|
-
|
|
7
|
+
CHART_MUTED,
|
|
8
|
+
CHART_SERIES,
|
|
9
|
+
LEGEND_TEXT,
|
|
7
10
|
legendConfigOf,
|
|
11
|
+
placedText,
|
|
12
|
+
sizeOf,
|
|
8
13
|
type ChartStyleBase,
|
|
9
14
|
} from "../kit/style"
|
|
10
15
|
import { num, text, type LegendConfig } from "../kit/shared"
|
|
@@ -13,7 +18,7 @@ import { DEG, PIE_SPOTLIGHT_DIM, ringPoint, selectionFade, type CenterConfig, ty
|
|
|
13
18
|
export { DEG, PIE_SPOTLIGHT_DIM, ringPoint, selectionFade, type CenterConfig, type PieSlice }
|
|
14
19
|
|
|
15
20
|
/**
|
|
16
|
-
* One slice per row, painted from `
|
|
21
|
+
* One slice per row, painted from `seriesFills` by row order (cycling). Paint comes
|
|
17
22
|
* from the palette, not the rows, because a pie's mark is a row where a line's
|
|
18
23
|
* colour belongs to a column; that lets a pie be fed a CSV of plain numbers.
|
|
19
24
|
*/
|
|
@@ -147,6 +152,9 @@ export interface LabelConfig {
|
|
|
147
152
|
|
|
148
153
|
/** What a pie's `style` resolves to. */
|
|
149
154
|
export interface PieStyle extends ChartStyleBase {
|
|
155
|
+
readonly labelText: TextStyle
|
|
156
|
+
readonly valueText: TextStyle
|
|
157
|
+
readonly centerText: TextStyle
|
|
150
158
|
readonly innerRadius: number
|
|
151
159
|
readonly sliceGap: number
|
|
152
160
|
readonly sliceRadius: number
|
|
@@ -155,8 +163,17 @@ export interface PieStyle extends ChartStyleBase {
|
|
|
155
163
|
|
|
156
164
|
/** The groups of a pie's `style`: no grid, as nothing is ruled. */
|
|
157
165
|
export const PIE_STYLE_GROUPS = {
|
|
158
|
-
|
|
159
|
-
|
|
166
|
+
series: CHART_SERIES,
|
|
167
|
+
background: CHART_BACKGROUND,
|
|
168
|
+
text: {
|
|
169
|
+
/** Each slice's name. */
|
|
170
|
+
labelText: Types.typography({ fontSize: 28, fontWeight: 500, fill: CHART_MUTED }),
|
|
171
|
+
/** Each slice's share or value, beside its name; inside a wedge both are set larger, inked for contrast. */
|
|
172
|
+
valueText: Types.typography({ fontSize: 28, fontWeight: 700, fill: CHART_FOREGROUND }),
|
|
173
|
+
/** The summary in the hole; its caption is set smaller, in the slice names' ink. */
|
|
174
|
+
centerText: Types.typography({ fontSize: 50, fontWeight: 800, fill: CHART_FOREGROUND }),
|
|
175
|
+
...LEGEND_TEXT,
|
|
176
|
+
},
|
|
160
177
|
marks: {
|
|
161
178
|
/** Hole radius as a fraction of the outer radius: 0 is a pie, about 0.6 a donut. */
|
|
162
179
|
innerRadius: Types.number(0, { min: 0, max: 0.9, step: 0.05 }),
|
|
@@ -176,34 +193,25 @@ export function legendOf(style: PieStyle): LegendConfig {
|
|
|
176
193
|
return legendConfigOf(style)
|
|
177
194
|
}
|
|
178
195
|
|
|
179
|
-
function face(style: PieStyle): Pick<TextStyle, "fontFamily"> {
|
|
180
|
-
return style.fontFamily ? { fontFamily: style.fontFamily } : {}
|
|
181
|
-
}
|
|
182
|
-
|
|
183
196
|
export function ringConfigOf(style: PieStyle): RingConfig {
|
|
184
197
|
return { gap: style.sliceGap, holeRadius: style.innerRadius, startAngle: 0, selectedGrow: 1.08, cornerRadius: style.sliceRadius }
|
|
185
198
|
}
|
|
186
199
|
|
|
187
200
|
export function labelConfigOf(style: PieStyle): LabelConfig {
|
|
188
|
-
const base
|
|
189
|
-
const
|
|
201
|
+
const base = placedText(style.labelText)
|
|
202
|
+
const value = placedText(style.valueText)
|
|
190
203
|
return {
|
|
191
204
|
textStyle: base,
|
|
192
|
-
percentStyle:
|
|
193
|
-
insideStyle: { ...
|
|
194
|
-
leaderStroke: { weight: 2, fill: style.
|
|
205
|
+
percentStyle: value,
|
|
206
|
+
insideStyle: { ...value, fontSize: Math.round(sizeOf(style.valueText) * 1.9), fill: "#111113" },
|
|
207
|
+
leaderStroke: { weight: 2, fill: style.labelText.fill ?? "white" },
|
|
195
208
|
}
|
|
196
209
|
}
|
|
197
210
|
|
|
198
211
|
export function centerConfigOf(style: PieStyle): CenterConfig {
|
|
212
|
+
const size = sizeOf(style.centerText, 50)
|
|
199
213
|
return {
|
|
200
|
-
labelStyle:
|
|
201
|
-
|
|
202
|
-
fontSize: Math.round(style.fontSize * 1.8),
|
|
203
|
-
fontWeight: Math.min(900, style.fontWeight + 300),
|
|
204
|
-
textAlign: "center",
|
|
205
|
-
fill: style.foreground,
|
|
206
|
-
},
|
|
207
|
-
captionStyle: { ...face(style), fontSize: Math.round(style.fontSize * 0.8), fontWeight: style.fontWeight, textAlign: "center", fill: style.muted },
|
|
214
|
+
labelStyle: placedText(style.centerText),
|
|
215
|
+
captionStyle: placedText(style.labelText, "center", { fontSize: Math.round(size * 0.45) }),
|
|
208
216
|
}
|
|
209
217
|
}
|