@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/bar-chart/shared.ts
CHANGED
|
@@ -1,149 +1,82 @@
|
|
|
1
|
-
|
|
2
|
-
* Types and pure helpers specific to the {@link BarChart} family: how a row's
|
|
3
|
-
* values become the spans its columns cover, how the value axis is scaled around
|
|
4
|
-
* them, and the theme the bars add to the shared chart vocabulary.
|
|
5
|
-
*
|
|
6
|
-
* Everything a line or a pie chart would want too — what a series is, the axis
|
|
7
|
-
* domain model, the theme leaf resolvers, the legend and plot-panel branches —
|
|
8
|
-
* lives in `../kit/shared.ts` and is imported here.
|
|
9
|
-
*
|
|
10
|
-
* Kept renderer-agnostic (no `Node` imports) so a host's inspector mapping
|
|
11
|
-
* can build a partial {@link BarChartTheme} without
|
|
12
|
-
* pulling the components in.
|
|
13
|
-
*/
|
|
14
|
-
|
|
15
|
-
import {
|
|
16
|
-
fillOps,
|
|
17
|
-
lerpNumber,
|
|
18
|
-
shadowOps,
|
|
19
|
-
strokeOps,
|
|
20
|
-
textOps,
|
|
21
|
-
type DataRecord,
|
|
22
|
-
type Fill,
|
|
23
|
-
type Shadow,
|
|
24
|
-
type ShadowResolved,
|
|
25
|
-
type Stroke,
|
|
26
|
-
type StrokeResolved,
|
|
27
|
-
type TextStyle,
|
|
28
|
-
} from "@motionscript/sdk"
|
|
1
|
+
import { Types, fillOps, type DataRecord, type Fill } from "@motionscript/sdk"
|
|
29
2
|
|
|
30
|
-
import {
|
|
31
|
-
CHART_PALETTE,
|
|
32
|
-
DEFAULT_LEGEND,
|
|
33
|
-
DEFAULT_PLOT_AREA,
|
|
34
|
-
DEFAULT_SERIES_FILLS,
|
|
35
|
-
DEFAULT_X_AXIS,
|
|
36
|
-
DEFAULT_Y_AXIS,
|
|
37
|
-
SPOTLIGHT_DIM,
|
|
38
|
-
lerpAxisConfig,
|
|
39
|
-
lerpLegendConfig,
|
|
40
|
-
lerpPlotArea,
|
|
41
|
-
lerpSeriesFills,
|
|
42
|
-
niceStep,
|
|
43
|
-
num,
|
|
44
|
-
resolveAxisConfig,
|
|
45
|
-
resolveLegendConfig,
|
|
46
|
-
resolvePlotArea,
|
|
47
|
-
resolveSeriesFills,
|
|
48
|
-
text,
|
|
49
|
-
type AxisConfig,
|
|
50
|
-
type ChartSeries,
|
|
51
|
-
type LegendConfig,
|
|
52
|
-
type LegendTheme,
|
|
53
|
-
type PlotAreaConfig,
|
|
54
|
-
type Range,
|
|
55
|
-
} from "../kit/shared"
|
|
3
|
+
import { niceRange, num, text, type ChartSeries, type Range } from "../kit/shared"
|
|
56
4
|
|
|
57
|
-
/**
|
|
58
|
-
* One series in a {@link BarChart}: a column of the table drawn as one bar per
|
|
59
|
-
* row — side by side with its siblings within each category's slot, or stacked
|
|
60
|
-
* on top of them.
|
|
61
|
-
*
|
|
62
|
-
* The family-wide {@link ChartSeries} under a name that says what it draws here.
|
|
63
|
-
*/
|
|
5
|
+
/** One bar per row, beside its siblings in the category slot or stacked on them: {@link ChartSeries} under the name for what it draws here. */
|
|
64
6
|
export type BarSeries = ChartSeries
|
|
65
7
|
|
|
8
|
+
/** How the series share a category: side by side, stacked, or stacked as shares of 100. */
|
|
9
|
+
export type BarStack = "none" | "stacked" | "percent"
|
|
10
|
+
|
|
11
|
+
/** Which way the bars grow: up from a category row, or right from a category column. */
|
|
12
|
+
export type BarOrientation = "vertical" | "horizontal"
|
|
13
|
+
|
|
66
14
|
/**
|
|
67
|
-
* One
|
|
68
|
-
* value
|
|
69
|
-
*
|
|
70
|
-
* A span rather than a height because that is the one shape both modes fit. A
|
|
71
|
-
* grouped bar runs from zero to its value; a stacked segment runs from the
|
|
72
|
-
* running total below it to that total plus its own value. Negative values fall
|
|
73
|
-
* out of the same arithmetic — the span simply runs the other way — so a chart
|
|
74
|
-
* with a loss in it needs no special case, only a baseline that isn't the floor.
|
|
15
|
+
* One bar's span in data units. A span rather than a height because every mode
|
|
16
|
+
* fits it, and a negative value just runs the other way with no special case.
|
|
75
17
|
*/
|
|
76
18
|
export interface BarSpan {
|
|
77
|
-
/**
|
|
19
|
+
/** Where the base sits: 0 apart, the running total below (or above, for a negative) when stacked. */
|
|
78
20
|
from: number
|
|
79
|
-
/** Value its far end reaches. */
|
|
80
21
|
to: number
|
|
81
|
-
/**
|
|
22
|
+
/** `to - from`. */
|
|
82
23
|
value: number
|
|
24
|
+
/** Whether it ends its stack on its side of zero, so its far end is rounded. */
|
|
25
|
+
outer: boolean
|
|
83
26
|
}
|
|
84
27
|
|
|
85
28
|
/**
|
|
86
|
-
*
|
|
87
|
-
*
|
|
88
|
-
*
|
|
89
|
-
*
|
|
90
|
-
*
|
|
91
|
-
* span so the returned list stays index-aligned with the series list.
|
|
29
|
+
* One row's spans, in series order. Stacking keeps a running sum on each side
|
|
30
|
+
* of zero, so a negative value stacks downward from the baseline rather than
|
|
31
|
+
* eating into the positives. In `percent` each value is its share of the row's
|
|
32
|
+
* total magnitude. A zero value still yields a degenerate span, keeping the list
|
|
33
|
+
* index-aligned with the series. `weights` scale each series' contribution.
|
|
92
34
|
*/
|
|
93
|
-
export function barSpans(
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
const value = num(row, s.field)
|
|
101
|
-
if (!stacked) return { from: 0, to: value, value }
|
|
102
|
-
const from = running
|
|
103
|
-
running += value
|
|
104
|
-
return { from, to: running, value }
|
|
105
|
-
})
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
/**
|
|
109
|
-
* The index of the series whose segment ends a row's stack — the one that gets
|
|
110
|
-
* the rounded cap, so a stack reads as one capped column rather than a pile of
|
|
111
|
-
* separate boxes. `-1` when every value in the row is zero.
|
|
112
|
-
*
|
|
113
|
-
* Read off the *last non-zero* series rather than the last one listed, so a
|
|
114
|
-
* series that happens to be 0 in this row doesn't take the cap and leave the
|
|
115
|
-
* segment below it square.
|
|
116
|
-
*/
|
|
117
|
-
export function stackCapIndex(row: DataRecord, series: BarSeries[]): number {
|
|
118
|
-
let last = -1
|
|
119
|
-
for (let i = 0; i < series.length; i++) {
|
|
120
|
-
if (num(row, series[i].field) !== 0) last = i
|
|
35
|
+
export function barSpans(row: DataRecord, series: readonly BarSeries[], stack: BarStack, weights: readonly number[] = []): BarSpan[] {
|
|
36
|
+
const weight = (i: number) => Math.max(0, weights[i] ?? 1)
|
|
37
|
+
if (stack !== "stacked" && stack !== "percent") {
|
|
38
|
+
return series.map((s) => {
|
|
39
|
+
const value = num(row, s.field)
|
|
40
|
+
return { from: 0, to: value, value, outer: true }
|
|
41
|
+
})
|
|
121
42
|
}
|
|
122
|
-
|
|
43
|
+
const values = series.map((s, i) => num(row, s.field) * weight(i))
|
|
44
|
+
const total = values.reduce((sum, v) => sum + Math.abs(v), 0)
|
|
45
|
+
const scaled = stack === "percent" ? values.map((v) => (total > 0 ? (v / total) * 100 : 0)) : values
|
|
46
|
+
let lastUp = -1
|
|
47
|
+
let lastDown = -1
|
|
48
|
+
scaled.forEach((v, i) => {
|
|
49
|
+
if (v > 0) lastUp = i
|
|
50
|
+
else if (v < 0) lastDown = i
|
|
51
|
+
})
|
|
52
|
+
let up = 0
|
|
53
|
+
let down = 0
|
|
54
|
+
return scaled.map((value, i) => {
|
|
55
|
+
if (value < 0) {
|
|
56
|
+
const from = down
|
|
57
|
+
down += value
|
|
58
|
+
return { from, to: down, value, outer: i === lastDown }
|
|
59
|
+
}
|
|
60
|
+
const from = up
|
|
61
|
+
up += value
|
|
62
|
+
return { from, to: up, value, outer: i === lastUp }
|
|
63
|
+
})
|
|
123
64
|
}
|
|
124
65
|
|
|
125
|
-
/** The captions beneath the baseline: one per row, read off the category column. */
|
|
126
66
|
export function barCategories(data: DataRecord[], field: string): string[] {
|
|
127
67
|
return data.map((row) => text(row, field))
|
|
128
68
|
}
|
|
129
69
|
|
|
130
70
|
/**
|
|
131
|
-
* The `[min, max]` of everything
|
|
132
|
-
*
|
|
133
|
-
*
|
|
134
|
-
* zero: a scale starting at 40 draws a column three times its neighbour for a
|
|
135
|
-
* 10% difference, which is the classic way to lie with one. A chart that really
|
|
136
|
-
* does want a cropped axis says so with an explicit range.
|
|
71
|
+
* The `[min, max]` of everything drawn, zero always included: a scale starting
|
|
72
|
+
* at 40 draws a bar three times its neighbour for a 10% difference. A cropped
|
|
73
|
+
* axis takes an explicit range.
|
|
137
74
|
*/
|
|
138
|
-
export function barValueExtent(
|
|
139
|
-
data: DataRecord[],
|
|
140
|
-
series: BarSeries[],
|
|
141
|
-
stacked: boolean
|
|
142
|
-
): Range {
|
|
75
|
+
export function barValueExtent(data: DataRecord[], series: readonly BarSeries[], stack: BarStack, weights?: readonly number[]): Range {
|
|
143
76
|
let lo = 0
|
|
144
77
|
let hi = 0
|
|
145
78
|
for (const row of data) {
|
|
146
|
-
for (const span of barSpans(row, series,
|
|
79
|
+
for (const span of barSpans(row, series, stack, weights)) {
|
|
147
80
|
lo = Math.min(lo, span.from, span.to)
|
|
148
81
|
hi = Math.max(hi, span.from, span.to)
|
|
149
82
|
}
|
|
@@ -151,204 +84,43 @@ export function barValueExtent(
|
|
|
151
84
|
return lo < hi ? [lo, hi] : [0, 1]
|
|
152
85
|
}
|
|
153
86
|
|
|
154
|
-
/**
|
|
155
|
-
* Effective value scale: the explicit `range`, else the data's extent nudged out
|
|
156
|
-
* to tidy step bounds so the markers read as round numbers.
|
|
157
|
-
*
|
|
158
|
-
* `divisions` is how many segments the auto-range is stepped into (one fewer
|
|
159
|
-
* than the marker count), so the ticks the axis draws land on the bounds this
|
|
160
|
-
* picks.
|
|
161
|
-
*/
|
|
87
|
+
/** The explicit `range`, else the data's extent nudged to tidy bounds (a percent stack reads to 100); `divisions` (one fewer than the marker count) makes the drawn ticks land on them. */
|
|
162
88
|
export function resolveBarScale(
|
|
163
89
|
data: DataRecord[],
|
|
164
|
-
series: BarSeries[],
|
|
165
|
-
|
|
90
|
+
series: readonly BarSeries[],
|
|
91
|
+
stack: BarStack,
|
|
166
92
|
range: Range | undefined,
|
|
167
|
-
divisions: number
|
|
93
|
+
divisions: number,
|
|
94
|
+
weights?: readonly number[]
|
|
168
95
|
): Range {
|
|
169
96
|
if (range) return range
|
|
170
|
-
const [lo, hi] = barValueExtent(data, series,
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
return [Math.floor(lo / step) * step, Math.ceil(hi / step) * step]
|
|
97
|
+
const [lo, hi] = barValueExtent(data, series, stack, weights)
|
|
98
|
+
if (stack === "percent") return [lo < 0 ? -100 : 0, hi > 0 ? 100 : 0]
|
|
99
|
+
return niceRange([lo, hi], divisions)
|
|
174
100
|
}
|
|
175
101
|
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
/**
|
|
186
|
-
* Fraction of its category's slot the bars fill, `[0, 1]`. The remainder is
|
|
187
|
-
* the channel between one category and the next, which is what makes a bar
|
|
188
|
-
* chart read as groups rather than as a solid block.
|
|
189
|
-
*/
|
|
190
|
-
slotFill: number
|
|
191
|
-
/** Gap between the grouped bars *within* one slot, in px. Ignored when stacked. */
|
|
192
|
-
groupGap: number
|
|
193
|
-
/** Optional outline on every column. */
|
|
194
|
-
stroke: Stroke
|
|
195
|
-
/** Optional drop shadow on every column. */
|
|
196
|
-
shadow: Shadow
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
/** A {@link BarChart}'s full, resolved visual theme. */
|
|
200
|
-
export interface BarChartConfig {
|
|
201
|
-
/**
|
|
202
|
-
* Default per-series bar paint, cycled by series index (`i % length`). A
|
|
203
|
-
* series' own `fill`, when set, replaces its entry outright.
|
|
204
|
-
*/
|
|
205
|
-
seriesFills: Fill[]
|
|
206
|
-
bar: BarStyleConfig
|
|
207
|
-
/** The category axis beneath the baseline. */
|
|
208
|
-
xAxis: AxisConfig
|
|
209
|
-
/** The numeric value axis up the left. */
|
|
210
|
-
yAxis: AxisConfig
|
|
211
|
-
plotArea: PlotAreaConfig
|
|
212
|
-
legend: LegendConfig
|
|
213
|
-
/** The figure printed on each column, when the chart prints them. */
|
|
214
|
-
valueStyle: TextStyle
|
|
215
|
-
/** How far everything *outside* a spotlit series or category fades. */
|
|
216
|
-
spotlight: { dimOpacity: number }
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
/** Author-facing partial theme — every branch optional and deep-merged. */
|
|
220
|
-
export type BarChartTheme = {
|
|
221
|
-
seriesFills?: Fill[]
|
|
222
|
-
bar?: Partial<BarStyleConfig>
|
|
223
|
-
xAxis?: Partial<AxisConfig>
|
|
224
|
-
yAxis?: Partial<AxisConfig>
|
|
225
|
-
plotArea?: Partial<PlotAreaConfig>
|
|
226
|
-
legend?: LegendTheme
|
|
227
|
-
valueStyle?: TextStyle
|
|
228
|
-
spotlight?: Partial<BarChartConfig["spotlight"]>
|
|
229
|
-
}
|
|
230
|
-
|
|
231
|
-
/** The bar chart's out-of-the-box look. */
|
|
232
|
-
export const DEFAULT_BAR_CHART_THEME: BarChartConfig = {
|
|
233
|
-
seriesFills: DEFAULT_SERIES_FILLS,
|
|
234
|
-
bar: {
|
|
235
|
-
cornerRadius: 6,
|
|
236
|
-
slotFill: 0.72,
|
|
237
|
-
groupGap: 6,
|
|
238
|
-
stroke: [],
|
|
239
|
-
shadow: [],
|
|
240
|
-
},
|
|
241
|
-
// A category axis names its slots, so its captions are set like a caption
|
|
242
|
-
// rather than like a numeric marker: same size, less weight than the line
|
|
243
|
-
// chart's ticks, and centred on the slot.
|
|
244
|
-
xAxis: {
|
|
245
|
-
...DEFAULT_X_AXIS,
|
|
246
|
-
labelStyle: { ...DEFAULT_X_AXIS.labelStyle, fontWeight: 600 },
|
|
247
|
-
tickPadding: 16,
|
|
248
|
-
},
|
|
249
|
-
yAxis: DEFAULT_Y_AXIS,
|
|
250
|
-
plotArea: DEFAULT_PLOT_AREA,
|
|
251
|
-
legend: DEFAULT_LEGEND,
|
|
252
|
-
valueStyle: {
|
|
253
|
-
fontSize: 24,
|
|
254
|
-
fontWeight: 700,
|
|
255
|
-
textAlign: "center",
|
|
256
|
-
fill: CHART_PALETTE.markerColor,
|
|
257
|
-
},
|
|
258
|
-
spotlight: { dimOpacity: SPOTLIGHT_DIM },
|
|
259
|
-
}
|
|
260
|
-
|
|
261
|
-
function resolveBarStyle(
|
|
262
|
-
bar: Partial<BarStyleConfig> | undefined,
|
|
263
|
-
previous: BarStyleConfig
|
|
264
|
-
): BarStyleConfig {
|
|
265
|
-
return {
|
|
266
|
-
cornerRadius: bar?.cornerRadius ?? previous.cornerRadius,
|
|
267
|
-
slotFill: bar?.slotFill ?? previous.slotFill,
|
|
268
|
-
groupGap: bar?.groupGap ?? previous.groupGap,
|
|
269
|
-
stroke: strokeOps.resolve(bar?.stroke ?? previous.stroke),
|
|
270
|
-
shadow: shadowOps.resolve(bar?.shadow ?? previous.shadow),
|
|
271
|
-
}
|
|
102
|
+
/** Black or white, whichever reads on `fill`: a figure printed inside a bar. */
|
|
103
|
+
export function inkOn(fill: Fill): string {
|
|
104
|
+
const layers = fillOps.resolve(fill)
|
|
105
|
+
const top = layers[layers.length - 1]
|
|
106
|
+
if (top?.type !== "solid") return "white"
|
|
107
|
+
const [r, g, b] = top.color
|
|
108
|
+
const lift = (c: number) => (c <= 0.03928 ? c / 12.92 : ((c + 0.055) / 1.055) ** 2.4)
|
|
109
|
+
const luminance = 0.2126 * lift(r) + 0.7152 * lift(g) + 0.0722 * lift(b)
|
|
110
|
+
return luminance > 0.4 ? "#0a0a0a" : "white"
|
|
272
111
|
}
|
|
273
112
|
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
groupGap: lerpNumber(from.groupGap, to.groupGap, t),
|
|
283
|
-
stroke: strokeOps.lerp(
|
|
284
|
-
from.stroke as StrokeResolved[],
|
|
285
|
-
to.stroke as StrokeResolved[],
|
|
286
|
-
t
|
|
287
|
-
),
|
|
288
|
-
shadow: shadowOps.lerp(
|
|
289
|
-
from.shadow as ShadowResolved[],
|
|
290
|
-
to.shadow as ShadowResolved[],
|
|
291
|
-
t
|
|
292
|
-
),
|
|
293
|
-
}
|
|
294
|
-
}
|
|
295
|
-
|
|
296
|
-
/**
|
|
297
|
-
* {@link BarChart}'s `theme` entry's resolve: merges the author's partial
|
|
298
|
-
* {@link BarChartTheme} onto `previous` (falling back to
|
|
299
|
-
* {@link DEFAULT_BAR_CHART_THEME} on first resolution), fully resolving every
|
|
300
|
-
* leaf so the tween below always has resolved data to interpolate.
|
|
301
|
-
*/
|
|
302
|
-
export function resolveBarChartTheme(
|
|
303
|
-
theme: BarChartTheme | undefined,
|
|
304
|
-
previous?: BarChartConfig
|
|
305
|
-
): BarChartConfig {
|
|
306
|
-
const base = previous ?? DEFAULT_BAR_CHART_THEME
|
|
307
|
-
return {
|
|
308
|
-
seriesFills: resolveSeriesFills(theme?.seriesFills, base.seriesFills),
|
|
309
|
-
bar: resolveBarStyle(theme?.bar, base.bar),
|
|
310
|
-
xAxis: resolveAxisConfig(theme?.xAxis, base.xAxis),
|
|
311
|
-
yAxis: resolveAxisConfig(theme?.yAxis, base.yAxis),
|
|
312
|
-
plotArea: resolvePlotArea(theme?.plotArea, base.plotArea),
|
|
313
|
-
legend: resolveLegendConfig(theme?.legend, base.legend),
|
|
314
|
-
valueStyle: textOps.resolveStyle(theme?.valueStyle, base.valueStyle),
|
|
315
|
-
spotlight: {
|
|
316
|
-
dimOpacity: theme?.spotlight?.dimOpacity ?? base.spotlight.dimOpacity,
|
|
317
|
-
},
|
|
318
|
-
}
|
|
319
|
-
}
|
|
320
|
-
|
|
321
|
-
/** {@link BarChart}'s `theme` entry's lerp — leaf by leaf. */
|
|
322
|
-
export function lerpBarChartTheme(
|
|
323
|
-
from: BarChartConfig,
|
|
324
|
-
to: BarChartConfig,
|
|
325
|
-
t: number
|
|
326
|
-
): BarChartConfig {
|
|
327
|
-
return {
|
|
328
|
-
seriesFills: lerpSeriesFills(from.seriesFills, to.seriesFills, t),
|
|
329
|
-
bar: lerpBarStyle(from.bar, to.bar, t),
|
|
330
|
-
xAxis: lerpAxisConfig(from.xAxis, to.xAxis, t),
|
|
331
|
-
yAxis: lerpAxisConfig(from.yAxis, to.yAxis, t),
|
|
332
|
-
plotArea: lerpPlotArea(from.plotArea, to.plotArea, t),
|
|
333
|
-
legend: lerpLegendConfig(from.legend, to.legend, t),
|
|
334
|
-
valueStyle: textOps.lerpStyle(from.valueStyle, to.valueStyle, t),
|
|
335
|
-
spotlight: {
|
|
336
|
-
dimOpacity: lerpNumber(
|
|
337
|
-
from.spotlight.dimOpacity,
|
|
338
|
-
to.spotlight.dimOpacity,
|
|
339
|
-
t
|
|
340
|
-
),
|
|
341
|
-
},
|
|
342
|
-
}
|
|
113
|
+
/** A bar chart's own look: how round and how wide the bars are. */
|
|
114
|
+
export const BAR_MARKS = {
|
|
115
|
+
/** Radius of each bar's far corners; a stacked segment inside the stack stays square. */
|
|
116
|
+
barRadius: Types.number(10, { min: 0, max: 64, step: 1, unit: "px" }),
|
|
117
|
+
/** How much of its category slot a bar group fills, the rest spacing the groups. */
|
|
118
|
+
barWidth: Types.number(0.7, { min: 0.05, max: 1, step: 0.05 }),
|
|
119
|
+
/** Space between the bars of one group, side by side. */
|
|
120
|
+
barGap: Types.number(6, { min: 0, max: 40, step: 1, unit: "px" }),
|
|
343
121
|
}
|
|
344
122
|
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
series: BarSeries,
|
|
349
|
-
index: number
|
|
350
|
-
): Fill {
|
|
351
|
-
if (series.fill != null) return fillOps.resolve(series.fill)
|
|
352
|
-
if (palette.length === 0) return "white"
|
|
353
|
-
return palette[index % palette.length]
|
|
123
|
+
export const BAR_LABELS = {
|
|
124
|
+
/** Read as the chart is built: each bar's value past its end, inside it, or none. */
|
|
125
|
+
values: Types.oneOf(["none", "end", "inside"] as const, { animatable: false }),
|
|
354
126
|
}
|
package/src/engine.ts
CHANGED
|
@@ -1,28 +1,19 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* The chart
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
* reach for these to `@motionscript/charts`, so the plot panel, the value
|
|
7
|
-
* axis, the legend and the number formatting stay a real dependency. That is
|
|
8
|
-
* not a size decision — it is the only arrangement that works.
|
|
9
|
-
*
|
|
10
|
-
* A chart identifies its own parts by component identity (`VerticalAxis.is`).
|
|
11
|
-
* Copy `VerticalAxis` alongside your fork and there are now two of it — two
|
|
12
|
-
* components answering one name, and whichever one your chart did not get is
|
|
13
|
-
* a part it never finds, with no error. Keeping the kit external makes that
|
|
14
|
-
* impossible rather than unlikely.
|
|
15
|
-
*
|
|
16
|
-
* It also means a fix to the axis reaches a forked chart without a re-add,
|
|
17
|
-
* which is the half of the bargain you get back for it.
|
|
2
|
+
* The chart kit: what the five charts share, and what a copied chart does not
|
|
3
|
+
* copy. A chart finds its parts by component identity (`VerticalAxis.is`), so a
|
|
4
|
+
* second copy of `VerticalAxis` would be a second component that the chart never
|
|
5
|
+
* finds, with no error. Copies import the kit from `@motionscript/charts`.
|
|
18
6
|
*/
|
|
19
7
|
export * from "./kit/shared"
|
|
8
|
+
export * from "./kit/style"
|
|
20
9
|
export * from "./kit/cartesian"
|
|
21
|
-
export * from "./kit/chart-border"
|
|
22
10
|
export * from "./kit/chart-canvas"
|
|
11
|
+
export * from "./kit/horizontal-axis"
|
|
23
12
|
export * from "./kit/chart-legend"
|
|
24
13
|
export * from "./kit/entries"
|
|
25
14
|
export * from "./kit/motion"
|
|
26
15
|
export * from "./kit/paints"
|
|
16
|
+
export * from "./kit/pie-legend"
|
|
17
|
+
export * from "./kit/polar"
|
|
27
18
|
export * from "./kit/vertical-axis"
|
|
28
19
|
export * from "@motionscript/sdk/component"
|