@motionscript/charts 0.1.1-alpha.1 → 0.1.1-alpha.3
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 +23 -0
- package/README.md +21 -12
- package/dist/bar-chart/bar-chart.d.ts +113 -81
- 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 +10 -4
- package/dist/bar-chart/shared.d.ts.map +1 -1
- package/dist/bar-chart/shared.js +12 -5
- package/dist/bar-chart/shared.js.map +1 -1
- package/dist/browser/chunks/chunk-FJHNBNPQ.js +2 -0
- package/dist/browser/chunks/chunk-FJHNBNPQ.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 +344 -76
- package/dist/kit/style.d.ts.map +1 -1
- package/dist/kit/style.js +198 -101
- package/dist/kit/style.js.map +1 -1
- package/dist/line-chart/line-chart.d.ts +97 -73
- 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/line-chart/shared.d.ts +2 -1
- package/dist/line-chart/shared.d.ts.map +1 -1
- package/dist/line-chart/shared.js +2 -0
- package/dist/line-chart/shared.js.map +1 -1
- package/dist/pie-chart/pie-chart.d.ts +43 -49
- 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 -10
- package/dist/pie-chart/shared.d.ts.map +1 -1
- package/dist/pie-chart/shared.js +18 -20
- package/dist/pie-chart/shared.js.map +1 -1
- package/dist/radar-chart/radar-chart.d.ts +37 -49
- package/dist/radar-chart/radar-chart.d.ts.map +1 -1
- package/dist/radar-chart/radar-chart.js +10 -11
- 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 +4 -2
- package/dist/radar-chart/shared.d.ts.map +1 -1
- package/dist/radar-chart/shared.js +3 -0
- package/dist/radar-chart/shared.js.map +1 -1
- package/dist/radial-chart/radial-chart.d.ts +37 -49
- 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 -11
- package/dist/radial-chart/shared.d.ts.map +1 -1
- package/dist/radial-chart/shared.js +12 -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 +23 -5
- package/src/kit/cartesian.ts +10 -7
- package/src/kit/style.ts +216 -118
- package/src/line-chart/line-chart.ts +14 -8
- package/src/line-chart/shared.ts +3 -1
- package/src/pie-chart/pie-chart.ts +3 -1
- package/src/pie-chart/shared.ts +26 -22
- package/src/radar-chart/radar-chart.ts +14 -13
- package/src/radar-chart/radar-grid.ts +18 -13
- package/src/radar-chart/shared.ts +5 -2
- package/src/radial-chart/radial-chart.ts +3 -1
- package/src/radial-chart/shared.ts +26 -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,15 @@
|
|
|
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, set the way a text node is, in the group of the
|
|
7
|
+
* part it labels: axis text with its axis, values with the labels, the
|
|
8
|
+
* legend's captions with the legend.
|
|
6
9
|
*/
|
|
7
10
|
|
|
8
11
|
import {
|
|
9
12
|
Types,
|
|
13
|
+
textOps,
|
|
10
14
|
type Fill,
|
|
11
15
|
type StrokeResolved,
|
|
12
16
|
type Stroke as _Stroke,
|
|
@@ -19,29 +23,36 @@ import {
|
|
|
19
23
|
|
|
20
24
|
import type { AxisConfig, ChartSeries, LegendConfig } from "./shared"
|
|
21
25
|
|
|
22
|
-
/** The style family every chart shares;
|
|
26
|
+
/** The style family every chart shares; the built-in presets are written for it. */
|
|
23
27
|
export const CHART_STYLE_FAMILY = "chart"
|
|
24
28
|
|
|
25
|
-
/** The out-of-the-box series
|
|
29
|
+
/** The out-of-the-box series fills, cycled by series (or row) index. */
|
|
26
30
|
export const CHART_PALETTE_DEFAULT = ["#4f8cff", "#2fbf8f", "#f5a524", "#e5547a", "#9b6cf0"] as const
|
|
27
31
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
32
|
+
/** The default ink of values, legend captions and centre text, on the dark stage. */
|
|
33
|
+
export const CHART_FOREGROUND = "#f4f4f5"
|
|
34
|
+
/** The default ink of axis markers, axis captions and spoke labels. */
|
|
35
|
+
export const CHART_MUTED = "#a1a1aa"
|
|
36
|
+
|
|
37
|
+
export const CHART_SERIES = {
|
|
38
|
+
/** A fill per series (a pie's or radial's per row), cycled; each may stack several paints. */
|
|
39
|
+
seriesFills: Types.palette(CHART_PALETTE_DEFAULT),
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export const CHART_BACKGROUND = {
|
|
43
|
+
/** Behind the whole chart, legend and axes included; none by default, so the chart reads against the stage. */
|
|
36
44
|
background: Types.fill([]),
|
|
37
45
|
}
|
|
38
46
|
|
|
39
|
-
export const
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
47
|
+
export const GRID_BACKGROUND = {
|
|
48
|
+
...CHART_BACKGROUND,
|
|
49
|
+
/** Behind the plot only, under the grid lines; none by default. */
|
|
50
|
+
gridBackground: Types.fill([]),
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/** The figures printed by the marks, for a chart's `labels` group. */
|
|
54
|
+
export const VALUE_TEXT = {
|
|
55
|
+
valueText: Types.typography({ fontSize: 24, fontWeight: 600, fill: CHART_FOREGROUND }),
|
|
45
56
|
}
|
|
46
57
|
|
|
47
58
|
export const CARTESIAN_GRID = {
|
|
@@ -49,12 +60,23 @@ export const CARTESIAN_GRID = {
|
|
|
49
60
|
horizontalGrid: Types.stroke({ weight: 2, fill: "white/15" }),
|
|
50
61
|
/** The lines under the category markers; empty draws none. */
|
|
51
62
|
verticalGrid: Types.stroke([]),
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export const CARTESIAN_AXES = {
|
|
52
66
|
/** The baseline and the value-axis rule; empty draws none. */
|
|
53
67
|
axisStroke: Types.stroke([]),
|
|
54
68
|
/** Read as the chart is built: whether the category (x) axis row is drawn. */
|
|
55
69
|
showXAxis: Types.boolean(true, { animatable: false }),
|
|
70
|
+
/** The markers under the plot: categories on a column chart, values on a horizontal bar chart. */
|
|
71
|
+
xLabelText: Types.typography({ fontSize: 28, fontWeight: 500, fill: CHART_MUTED }),
|
|
72
|
+
/** The caption under the plot. */
|
|
73
|
+
xTitleText: Types.typography({ fontSize: 31, fontWeight: 600, fill: CHART_MUTED }),
|
|
56
74
|
/** Read as the chart is built: whether the value (y) axis column is drawn. */
|
|
57
75
|
showYAxis: Types.boolean(true, { animatable: false }),
|
|
76
|
+
/** The markers beside the plot. */
|
|
77
|
+
yLabelText: Types.typography({ fontSize: 28, fontWeight: 500, fill: CHART_MUTED }),
|
|
78
|
+
/** The caption beside the plot, turned on its side. */
|
|
79
|
+
yTitleText: Types.typography({ fontSize: 31, fontWeight: 600, fill: CHART_MUTED }),
|
|
58
80
|
}
|
|
59
81
|
|
|
60
82
|
export type LegendPosition = "bottom" | "top" | "right" | "none"
|
|
@@ -64,27 +86,27 @@ export const CHART_LEGEND = {
|
|
|
64
86
|
/** Read as the chart is built: where the legend sits, or `none`. */
|
|
65
87
|
legend: Types.oneOf(["bottom", "top", "right", "none"] as const, { animatable: false }),
|
|
66
88
|
swatch: Types.oneOf(["square", "circle", "line"] as const),
|
|
67
|
-
|
|
68
|
-
|
|
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
|
|
89
|
+
/** The legend's captions. */
|
|
90
|
+
legendText: Types.typography({ fontSize: 28, fontWeight: 500, fill: CHART_FOREGROUND }),
|
|
76
91
|
}
|
|
77
92
|
|
|
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,16 @@ 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_AXES,
|
|
122
|
+
...VALUE_TEXT,
|
|
123
|
+
legendText: CHART_LEGEND.legendText,
|
|
124
|
+
labelText: Types.typography(),
|
|
125
|
+
centerText: Types.typography(),
|
|
126
|
+
spokeText: Types.typography(),
|
|
97
127
|
horizontalGrid: CARTESIAN_GRID.horizontalGrid,
|
|
98
128
|
gridStroke: Types.stroke([]),
|
|
99
129
|
spokeStroke: Types.stroke([]),
|
|
@@ -101,147 +131,154 @@ const _PRESET_RULES = {
|
|
|
101
131
|
}
|
|
102
132
|
|
|
103
133
|
/** A preset's fields: any chart's, so one preset dresses every chart. */
|
|
104
|
-
export type ChartStylePreset = StyleInput<typeof
|
|
134
|
+
export type ChartStylePreset = StyleInput<typeof _PRESET_FIELDS>
|
|
135
|
+
|
|
136
|
+
/** Every text field's ink: `foreground` for values, the legend and centre text, `muted` for markers and captions. */
|
|
137
|
+
function inks(foreground: string, muted: string) {
|
|
138
|
+
const on = (fill: string) => ({ fill })
|
|
139
|
+
return {
|
|
140
|
+
xLabelText: on(muted),
|
|
141
|
+
xTitleText: on(muted),
|
|
142
|
+
yLabelText: on(muted),
|
|
143
|
+
yTitleText: on(muted),
|
|
144
|
+
valueText: on(foreground),
|
|
145
|
+
legendText: on(foreground),
|
|
146
|
+
labelText: on(muted),
|
|
147
|
+
centerText: on(foreground),
|
|
148
|
+
spokeText: on(muted),
|
|
149
|
+
} satisfies ChartStylePreset
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/** Every rule a chart draws, and a radial's track, in one paint. */
|
|
153
|
+
function rules(fill: string) {
|
|
154
|
+
return {
|
|
155
|
+
horizontalGrid: { weight: 2, fill },
|
|
156
|
+
gridStroke: { weight: 2, fill },
|
|
157
|
+
spokeStroke: { weight: 2, fill },
|
|
158
|
+
track: fill,
|
|
159
|
+
} satisfies ChartStylePreset
|
|
160
|
+
}
|
|
105
161
|
|
|
106
162
|
/** Ready-made looks, each stating every colour so applying one replaces the last. */
|
|
107
163
|
export const CHART_STYLE_PRESETS = {
|
|
108
164
|
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" },
|
|
165
|
+
seriesFills: [...CHART_PALETTE_DEFAULT],
|
|
166
|
+
...inks(CHART_FOREGROUND, CHART_MUTED),
|
|
167
|
+
...rules("white/15"),
|
|
115
168
|
track: "white/10",
|
|
116
169
|
background: [],
|
|
170
|
+
gridBackground: [],
|
|
117
171
|
},
|
|
118
172
|
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",
|
|
173
|
+
seriesFills: ["#2563eb", "#059669", "#d97706", "#db2777", "#7c3aed"],
|
|
174
|
+
...inks("#1c1917", "#57534e"),
|
|
175
|
+
...rules("#1c1917/12"),
|
|
126
176
|
background: "#fafaf9",
|
|
177
|
+
gridBackground: [],
|
|
127
178
|
},
|
|
128
179
|
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",
|
|
180
|
+
seriesFills: ["#d9603b", "#8194ad", "#e0a93b", "#5e9d6b", "#9ec85a"],
|
|
181
|
+
...inks("#e9e1d4", "#8a7f70"),
|
|
182
|
+
...rules("#2c2820"),
|
|
136
183
|
background: "#15130e",
|
|
184
|
+
gridBackground: [],
|
|
137
185
|
},
|
|
138
186
|
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",
|
|
187
|
+
seriesFills: ["#22d3ee", "#a3e635", "#f472b6", "#facc15", "#818cf8"],
|
|
188
|
+
...inks("#ffffff", "#94a3b8"),
|
|
189
|
+
...rules("white/10"),
|
|
146
190
|
background: [],
|
|
191
|
+
gridBackground: [],
|
|
147
192
|
},
|
|
148
193
|
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",
|
|
194
|
+
seriesFills: ["#0ea5e9", "#14b8a6", "#6366f1", "#38bdf8", "#2dd4bf"],
|
|
195
|
+
...inks("#e0f2fe", "#7dd3fc"),
|
|
196
|
+
...rules("#7dd3fc/20"),
|
|
156
197
|
background: "#082f49",
|
|
198
|
+
gridBackground: [],
|
|
157
199
|
},
|
|
158
200
|
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",
|
|
201
|
+
seriesFills: ["#f97316", "#ef4444", "#ec4899", "#f59e0b", "#a855f7"],
|
|
202
|
+
...inks("#fff7ed", "#fdba74"),
|
|
203
|
+
...rules("#fdba74/20"),
|
|
166
204
|
background: [],
|
|
205
|
+
gridBackground: [],
|
|
167
206
|
},
|
|
168
207
|
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",
|
|
208
|
+
seriesFills: ["#a5b4fc", "#f9a8d4", "#86efac", "#fde68a", "#7dd3fc"],
|
|
209
|
+
...inks("#f8fafc", "#cbd5e1"),
|
|
210
|
+
...rules("white/12"),
|
|
176
211
|
background: [],
|
|
212
|
+
gridBackground: [],
|
|
177
213
|
},
|
|
178
214
|
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",
|
|
215
|
+
seriesFills: ["#fafafa", "#a3a3a3", "#737373", "#d4d4d4", "#525252"],
|
|
216
|
+
...inks("#fafafa", "#a3a3a3"),
|
|
217
|
+
...rules("white/12"),
|
|
186
218
|
background: [],
|
|
219
|
+
gridBackground: [],
|
|
187
220
|
},
|
|
188
221
|
} satisfies Record<string, ChartStylePreset>
|
|
189
222
|
|
|
190
|
-
/** Series `index`'s
|
|
191
|
-
export function paletteFill(style: Pick<ChartStyleBase, "
|
|
223
|
+
/** Series `index`'s fill: its own `fill`, else the style's series fills, cycled. */
|
|
224
|
+
export function paletteFill(style: Pick<ChartStyleBase, "seriesFills">, index: number, own?: Fill): Fill {
|
|
192
225
|
if (own != null) return own
|
|
193
|
-
const
|
|
194
|
-
return
|
|
226
|
+
const fills = style.seriesFills
|
|
227
|
+
return fills.length > 0 ? fills[index % fills.length] : "white"
|
|
195
228
|
}
|
|
196
229
|
|
|
197
230
|
/** {@link paletteFill} for a series. */
|
|
198
|
-
export function seriesFill(style: Pick<ChartStyleBase, "
|
|
231
|
+
export function seriesFill(style: Pick<ChartStyleBase, "seriesFills">, series: ChartSeries, index: number): Fill {
|
|
199
232
|
return paletteFill(style, index, series.fill)
|
|
200
233
|
}
|
|
201
234
|
|
|
202
|
-
|
|
203
|
-
|
|
235
|
+
/** A text style's size in px; an autofit or unset size reads as `fallback`. */
|
|
236
|
+
export function sizeOf(text: TextStyle | undefined, fallback = 28): number {
|
|
237
|
+
const size = text?.fontSize
|
|
238
|
+
return typeof size === "number" && Number.isFinite(size) ? size : fallback
|
|
204
239
|
}
|
|
205
240
|
|
|
206
|
-
/**
|
|
207
|
-
|
|
208
|
-
|
|
241
|
+
/**
|
|
242
|
+
* `text` as a text node takes it, aligned where the chart puts it, with `over`
|
|
243
|
+
* laid on top. Unset keys are dropped: an `undefined` spread onto a node is a
|
|
244
|
+
* value, and an unset face must stay unset so the host's default applies.
|
|
245
|
+
*/
|
|
246
|
+
export function placedText(text: TextStyle, textAlign: TextStyle["textAlign"] = "center", over?: TextStyle): TextStyle {
|
|
247
|
+
return textOps.styleProps({ ...textOps.mergeStyle(text, over), textAlign }) as TextStyle
|
|
209
248
|
}
|
|
210
249
|
|
|
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
|
-
}
|
|
250
|
+
/** Axis markers on `axis`, aligned as that axis sets them. */
|
|
251
|
+
export function markerText(style: Pick<CartesianStyle, "xLabelText" | "yLabelText">, axis: "x" | "y"): TextStyle {
|
|
252
|
+
return placedText(axis === "x" ? style.xLabelText : style.yLabelText, axis === "x" ? "center" : "right")
|
|
220
253
|
}
|
|
221
254
|
|
|
222
|
-
/**
|
|
223
|
-
export function valueText(style:
|
|
224
|
-
return
|
|
255
|
+
/** The figures printed by the marks. */
|
|
256
|
+
export function valueText(style: Pick<CartesianStyle, "valueText">, textAlign: TextStyle["textAlign"] = "center"): TextStyle {
|
|
257
|
+
return placedText(style.valueText, textAlign)
|
|
225
258
|
}
|
|
226
259
|
|
|
227
260
|
/**
|
|
228
261
|
* The kit axis parts' config from a style. `x` is the axis under the plot;
|
|
229
262
|
* its grid lines are vertical (one per x marker), the `y` axis's horizontal.
|
|
230
263
|
*/
|
|
231
|
-
export function axisConfigOf(
|
|
232
|
-
|
|
264
|
+
export function axisConfigOf(
|
|
265
|
+
style: Pick<CartesianStyle, "verticalGrid" | "horizontalGrid" | "xLabelText" | "xTitleText" | "yLabelText" | "yTitleText">,
|
|
266
|
+
axis: "x" | "y"
|
|
267
|
+
): AxisConfig {
|
|
268
|
+
const gap = Math.round(sizeOf(axis === "x" ? style.xLabelText : style.yLabelText) * 0.6)
|
|
233
269
|
return {
|
|
234
|
-
labelStyle: markerText(style, axis
|
|
235
|
-
titleStyle:
|
|
270
|
+
labelStyle: markerText(style, axis),
|
|
271
|
+
titleStyle: placedText(axis === "x" ? style.xTitleText : style.yTitleText),
|
|
236
272
|
lineStyle: axis === "x" ? style.verticalGrid : style.horizontalGrid,
|
|
237
273
|
tickPadding: axis === "x" ? { top: gap } : { right: gap },
|
|
238
274
|
titlePadding: axis === "x" ? { top: Math.round(gap / 2) } : { right: gap },
|
|
239
275
|
}
|
|
240
276
|
}
|
|
241
277
|
|
|
242
|
-
/** The kit legend's config: captions in
|
|
243
|
-
export function legendConfigOf(style:
|
|
244
|
-
const
|
|
278
|
+
/** The kit legend's config: captions in `legendText`, the swatch shaped by `swatch` and sized off the captions. */
|
|
279
|
+
export function legendConfigOf(style: Pick<ChartStyleBase, "swatch" | "legendText">): LegendConfig {
|
|
280
|
+
const type = sizeOf(style.legendText)
|
|
281
|
+
const size = Math.max(8, Math.round(type * 0.6))
|
|
245
282
|
const marker =
|
|
246
283
|
style.swatch === "line"
|
|
247
284
|
? { width: Math.round(size * 2), height: Math.max(3, Math.round(size / 4)), borderRadius: size }
|
|
@@ -250,8 +287,69 @@ export function legendConfigOf(style: ChartTextStyle & Pick<ChartStyleBase, "swa
|
|
|
250
287
|
: { width: size, height: size, borderRadius: Math.round(size / 4) }
|
|
251
288
|
return {
|
|
252
289
|
padding: 0,
|
|
253
|
-
textStyle:
|
|
254
|
-
itemGap: Math.round(
|
|
290
|
+
textStyle: placedText(style.legendText, "left"),
|
|
291
|
+
itemGap: Math.round(type * 1.4),
|
|
255
292
|
markerStyle: { ...marker, stroke: [], shadow: [] },
|
|
256
293
|
}
|
|
257
294
|
}
|
|
295
|
+
|
|
296
|
+
/** Which text a chart drew from the retired shared text fields. */
|
|
297
|
+
export type ChartTextLayout = "cartesian" | "pie" | "radar" | "radial"
|
|
298
|
+
|
|
299
|
+
/**
|
|
300
|
+
* The `style` entry's hook for documents written before every run of text got
|
|
301
|
+
* its own field (0.1.1-alpha.1): `barRadius` became `barCornerRadius`, `palette` became `seriesFills`; `foreground`,
|
|
302
|
+
* `muted` and the one `fontFamily`/`fontSize`/`fontWeight` became the text
|
|
303
|
+
* fields as each chart derived them; a cartesian or radar `background`, which
|
|
304
|
+
* painted the plot, became `gridBackground`. A current style comes back as is.
|
|
305
|
+
*/
|
|
306
|
+
export function migrateChartStyle(layout: ChartTextLayout): (value: unknown) => unknown {
|
|
307
|
+
return (value) => {
|
|
308
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) return value
|
|
309
|
+
const old = value as Record<string, unknown>
|
|
310
|
+
const retired = ["palette", "foreground", "muted", "fontFamily", "fontSize", "fontWeight", "barRadius"]
|
|
311
|
+
const plotBackground = layout === "cartesian" || layout === "radar"
|
|
312
|
+
if (!retired.some((key) => key in old) && !(plotBackground && "background" in old && !("gridBackground" in old))) return value
|
|
313
|
+
|
|
314
|
+
const { palette, foreground, muted, fontFamily, fontSize, fontWeight, barRadius, ...rest } = old
|
|
315
|
+
const out: Record<string, unknown> = { ...rest }
|
|
316
|
+
// A bar radius rounded only the far end; its replacement states every corner.
|
|
317
|
+
if (typeof barRadius === "number" && out.barCornerRadius === undefined) {
|
|
318
|
+
out.barCornerRadius = { topLeft: barRadius, topRight: barRadius, bottomRight: 0, bottomLeft: 0 }
|
|
319
|
+
}
|
|
320
|
+
if (palette !== undefined && out.seriesFills === undefined) out.seriesFills = palette
|
|
321
|
+
if (plotBackground && "background" in old && !("gridBackground" in old)) {
|
|
322
|
+
out.gridBackground = old.background
|
|
323
|
+
delete out.background
|
|
324
|
+
}
|
|
325
|
+
const size = typeof fontSize === "number" ? fontSize : undefined
|
|
326
|
+
const weight = typeof fontWeight === "number" ? fontWeight : undefined
|
|
327
|
+
const text = (ink: unknown, scale = 1, heavier = 0): Record<string, unknown> | undefined => {
|
|
328
|
+
const t: Record<string, unknown> = {}
|
|
329
|
+
if (typeof fontFamily === "string" && fontFamily !== "") t.fontFamily = fontFamily
|
|
330
|
+
if (size !== undefined) t.fontSize = Math.round(size * scale)
|
|
331
|
+
if (weight !== undefined) t.fontWeight = Math.min(900, weight + heavier)
|
|
332
|
+
if (ink !== undefined) t.fill = ink
|
|
333
|
+
return Object.keys(t).length > 0 ? t : undefined
|
|
334
|
+
}
|
|
335
|
+
const fields: Record<string, Record<string, unknown> | undefined> = { legendText: text(foreground) }
|
|
336
|
+
if (layout === "cartesian") {
|
|
337
|
+
fields.xLabelText = text(muted)
|
|
338
|
+
fields.yLabelText = text(muted)
|
|
339
|
+
fields.xTitleText = text(muted, 1.1, 100)
|
|
340
|
+
fields.yTitleText = text(muted, 1.1, 100)
|
|
341
|
+
fields.valueText = text(foreground, 0.85, 100)
|
|
342
|
+
} else if (layout === "pie") {
|
|
343
|
+
fields.labelText = text(muted)
|
|
344
|
+
fields.valueText = text(foreground, 1, 200)
|
|
345
|
+
fields.centerText = text(foreground, 1.8, 300)
|
|
346
|
+
} else if (layout === "radar") {
|
|
347
|
+
fields.spokeText = text(muted)
|
|
348
|
+
} else {
|
|
349
|
+
fields.labelText = text(muted)
|
|
350
|
+
fields.centerText = text(foreground, 1.8, 300)
|
|
351
|
+
}
|
|
352
|
+
for (const [key, field] of Object.entries(fields)) if (field && out[key] === undefined) out[key] = field
|
|
353
|
+
return out
|
|
354
|
+
}
|
|
355
|
+
}
|
|
@@ -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_AXES,
|
|
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, grid: CARTESIAN_GRID, axes: CARTESIAN_AXES, 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,
|
package/src/line-chart/shared.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import { Types, type DataRecord, type Vector2 } from "@motionscript/sdk"
|
|
1
|
+
import { Types, type DataRecord, type Vector2, type TextStyle as _TextStyle } from "@motionscript/sdk"
|
|
2
2
|
|
|
3
3
|
import { niceRange, num, type ChartSeries, type Range } from "../kit/shared"
|
|
4
|
+
import { VALUE_TEXT } from "../kit/style"
|
|
4
5
|
import type { XScale } from "./x-scale"
|
|
5
6
|
|
|
6
7
|
/** One polyline of a {@link LineChart}: {@link ChartSeries} under the name for what it draws here. */
|
|
@@ -395,4 +396,5 @@ export const LINE_MARKS = {
|
|
|
395
396
|
export const LINE_LABELS = {
|
|
396
397
|
/** Read as the chart is built: each point's value printed above or below it, or none. */
|
|
397
398
|
values: Types.oneOf(["none", "above", "below"] as const, { animatable: false }),
|
|
399
|
+
...VALUE_TEXT,
|
|
398
400
|
}
|
|
@@ -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) => {
|