@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.
Files changed (215) hide show
  1. package/CHANGELOG.md +38 -0
  2. package/README.md +98 -28
  3. package/dist/bar-chart/bar-chart.d.ts +242 -119
  4. package/dist/bar-chart/bar-chart.d.ts.map +1 -1
  5. package/dist/bar-chart/bar-chart.js +231 -260
  6. package/dist/bar-chart/bar-chart.js.map +1 -1
  7. package/dist/bar-chart/category-axis.d.ts +16 -38
  8. package/dist/bar-chart/category-axis.d.ts.map +1 -1
  9. package/dist/bar-chart/category-axis.js +59 -68
  10. package/dist/bar-chart/category-axis.js.map +1 -1
  11. package/dist/bar-chart/chart-bars.d.ts +60 -91
  12. package/dist/bar-chart/chart-bars.d.ts.map +1 -1
  13. package/dist/bar-chart/chart-bars.js +88 -125
  14. package/dist/bar-chart/chart-bars.js.map +1 -1
  15. package/dist/bar-chart/index.d.ts +5 -13
  16. package/dist/bar-chart/index.d.ts.map +1 -1
  17. package/dist/bar-chart/index.js +2 -9
  18. package/dist/bar-chart/index.js.map +1 -1
  19. package/dist/bar-chart/shared.d.ts +40 -132
  20. package/dist/bar-chart/shared.d.ts.map +1 -1
  21. package/dist/bar-chart/shared.js +70 -158
  22. package/dist/bar-chart/shared.js.map +1 -1
  23. package/dist/browser/chunks/chunk-R3Q5EE6E.js +2 -0
  24. package/dist/browser/chunks/chunk-R3Q5EE6E.js.map +7 -0
  25. package/dist/browser/index.js +1 -1
  26. package/dist/browser/index.js.map +4 -4
  27. package/dist/browser/kit.js +1 -1
  28. package/dist/browser/manifest.json +1 -1
  29. package/dist/engine.d.ts +8 -17
  30. package/dist/engine.d.ts.map +1 -1
  31. package/dist/engine.js +8 -17
  32. package/dist/engine.js.map +1 -1
  33. package/dist/index.d.ts +2 -0
  34. package/dist/index.d.ts.map +1 -1
  35. package/dist/index.js +2 -0
  36. package/dist/index.js.map +1 -1
  37. package/dist/kit/cartesian.d.ts +49 -35
  38. package/dist/kit/cartesian.d.ts.map +1 -1
  39. package/dist/kit/cartesian.js +80 -42
  40. package/dist/kit/cartesian.js.map +1 -1
  41. package/dist/kit/chart-canvas.d.ts +37 -40
  42. package/dist/kit/chart-canvas.d.ts.map +1 -1
  43. package/dist/kit/chart-canvas.js +53 -47
  44. package/dist/kit/chart-canvas.js.map +1 -1
  45. package/dist/kit/chart-legend.d.ts +16 -59
  46. package/dist/kit/chart-legend.d.ts.map +1 -1
  47. package/dist/kit/chart-legend.js +38 -76
  48. package/dist/kit/chart-legend.js.map +1 -1
  49. package/dist/kit/entries.d.ts +2 -0
  50. package/dist/kit/entries.d.ts.map +1 -1
  51. package/dist/kit/entries.js +2 -0
  52. package/dist/kit/entries.js.map +1 -1
  53. package/dist/kit/horizontal-axis.d.ts +77 -0
  54. package/dist/kit/horizontal-axis.d.ts.map +1 -0
  55. package/dist/kit/horizontal-axis.js +99 -0
  56. package/dist/kit/horizontal-axis.js.map +1 -0
  57. package/dist/kit/motion.d.ts +16 -3
  58. package/dist/kit/motion.d.ts.map +1 -1
  59. package/dist/kit/motion.js +15 -1
  60. package/dist/kit/motion.js.map +1 -1
  61. package/dist/{pie-chart → kit}/pie-legend.d.ts +14 -29
  62. package/dist/kit/pie-legend.d.ts.map +1 -0
  63. package/dist/kit/pie-legend.js +93 -0
  64. package/dist/kit/pie-legend.js.map +1 -0
  65. package/dist/kit/polar.d.ts +31 -0
  66. package/dist/kit/polar.d.ts.map +1 -0
  67. package/dist/kit/polar.js +33 -0
  68. package/dist/kit/polar.js.map +1 -0
  69. package/dist/kit/shared.d.ts +60 -214
  70. package/dist/kit/shared.d.ts.map +1 -1
  71. package/dist/kit/shared.js +91 -291
  72. package/dist/kit/shared.js.map +1 -1
  73. package/dist/kit/style.d.ts +254 -0
  74. package/dist/kit/style.d.ts.map +1 -0
  75. package/dist/kit/style.js +197 -0
  76. package/dist/kit/style.js.map +1 -0
  77. package/dist/kit/vertical-axis.d.ts +36 -45
  78. package/dist/kit/vertical-axis.d.ts.map +1 -1
  79. package/dist/kit/vertical-axis.js +69 -81
  80. package/dist/kit/vertical-axis.js.map +1 -1
  81. package/dist/line-chart/chart-line.d.ts +107 -40
  82. package/dist/line-chart/chart-line.d.ts.map +1 -1
  83. package/dist/line-chart/chart-line.js +172 -89
  84. package/dist/line-chart/chart-line.js.map +1 -1
  85. package/dist/line-chart/chart-region.d.ts +14 -27
  86. package/dist/line-chart/chart-region.d.ts.map +1 -1
  87. package/dist/line-chart/chart-region.js +13 -28
  88. package/dist/line-chart/chart-region.js.map +1 -1
  89. package/dist/line-chart/index.d.ts +5 -18
  90. package/dist/line-chart/index.d.ts.map +1 -1
  91. package/dist/line-chart/index.js +3 -16
  92. package/dist/line-chart/index.js.map +1 -1
  93. package/dist/line-chart/line-chart.d.ts +272 -119
  94. package/dist/line-chart/line-chart.d.ts.map +1 -1
  95. package/dist/line-chart/line-chart.js +309 -257
  96. package/dist/line-chart/line-chart.js.map +1 -1
  97. package/dist/line-chart/shared.d.ts +73 -130
  98. package/dist/line-chart/shared.d.ts.map +1 -1
  99. package/dist/line-chart/shared.js +195 -156
  100. package/dist/line-chart/shared.js.map +1 -1
  101. package/dist/line-chart/x-scale.d.ts +16 -55
  102. package/dist/line-chart/x-scale.d.ts.map +1 -1
  103. package/dist/line-chart/x-scale.js +44 -111
  104. package/dist/line-chart/x-scale.js.map +1 -1
  105. package/dist/nodes.d.ts +3 -13
  106. package/dist/nodes.d.ts.map +1 -1
  107. package/dist/nodes.js +9 -13
  108. package/dist/nodes.js.map +1 -1
  109. package/dist/pie-chart/index.d.ts +3 -15
  110. package/dist/pie-chart/index.d.ts.map +1 -1
  111. package/dist/pie-chart/index.js +1 -11
  112. package/dist/pie-chart/index.js.map +1 -1
  113. package/dist/pie-chart/pie-chart.d.ts +147 -85
  114. package/dist/pie-chart/pie-chart.d.ts.map +1 -1
  115. package/dist/pie-chart/pie-chart.js +65 -86
  116. package/dist/pie-chart/pie-chart.js.map +1 -1
  117. package/dist/pie-chart/pie-ring.d.ts +65 -196
  118. package/dist/pie-chart/pie-ring.d.ts.map +1 -1
  119. package/dist/pie-chart/pie-ring.js +154 -292
  120. package/dist/pie-chart/pie-ring.js.map +1 -1
  121. package/dist/pie-chart/shared.d.ts +78 -172
  122. package/dist/pie-chart/shared.d.ts.map +1 -1
  123. package/dist/pie-chart/shared.js +54 -177
  124. package/dist/pie-chart/shared.js.map +1 -1
  125. package/dist/radar-chart/index.d.ts +5 -0
  126. package/dist/radar-chart/index.d.ts.map +1 -0
  127. package/dist/radar-chart/index.js +5 -0
  128. package/dist/radar-chart/index.js.map +1 -0
  129. package/dist/radar-chart/radar-chart.d.ts +218 -0
  130. package/dist/radar-chart/radar-chart.d.ts.map +1 -0
  131. package/dist/radar-chart/radar-chart.js +224 -0
  132. package/dist/radar-chart/radar-chart.js.map +1 -0
  133. package/dist/radar-chart/radar-grid.d.ts +43 -0
  134. package/dist/radar-chart/radar-grid.d.ts.map +1 -0
  135. package/dist/radar-chart/radar-grid.js +61 -0
  136. package/dist/radar-chart/radar-grid.js.map +1 -0
  137. package/dist/radar-chart/radar-series.d.ts +59 -0
  138. package/dist/radar-chart/radar-series.d.ts.map +1 -0
  139. package/dist/radar-chart/radar-series.js +71 -0
  140. package/dist/radar-chart/radar-series.js.map +1 -0
  141. package/dist/radar-chart/shared.d.ts +73 -0
  142. package/dist/radar-chart/shared.d.ts.map +1 -0
  143. package/dist/radar-chart/shared.js +123 -0
  144. package/dist/radar-chart/shared.js.map +1 -0
  145. package/dist/radial-chart/index.d.ts +6 -0
  146. package/dist/radial-chart/index.d.ts.map +1 -0
  147. package/dist/radial-chart/index.js +4 -0
  148. package/dist/radial-chart/index.js.map +1 -0
  149. package/dist/radial-chart/radial-chart.d.ts +216 -0
  150. package/dist/radial-chart/radial-chart.d.ts.map +1 -0
  151. package/dist/radial-chart/radial-chart.js +174 -0
  152. package/dist/radial-chart/radial-chart.js.map +1 -0
  153. package/dist/radial-chart/radial-rings.d.ts +121 -0
  154. package/dist/radial-chart/radial-rings.d.ts.map +1 -0
  155. package/dist/radial-chart/radial-rings.js +273 -0
  156. package/dist/radial-chart/radial-rings.js.map +1 -0
  157. package/dist/radial-chart/shared.d.ts +73 -0
  158. package/dist/radial-chart/shared.d.ts.map +1 -0
  159. package/dist/radial-chart/shared.js +79 -0
  160. package/dist/radial-chart/shared.js.map +1 -0
  161. package/package.json +4 -4
  162. package/registry.json +21 -6
  163. package/src/bar-chart/bar-chart.ts +274 -315
  164. package/src/bar-chart/category-axis.ts +53 -88
  165. package/src/bar-chart/chart-bars.ts +108 -205
  166. package/src/bar-chart/index.ts +8 -19
  167. package/src/bar-chart/shared.ts +81 -309
  168. package/src/engine.ts +8 -17
  169. package/src/index.ts +2 -0
  170. package/src/kit/cartesian.ts +118 -55
  171. package/src/kit/chart-canvas.ts +50 -80
  172. package/src/kit/chart-legend.ts +36 -97
  173. package/src/kit/entries.ts +4 -1
  174. package/src/kit/horizontal-axis.ts +114 -0
  175. package/src/kit/motion.ts +18 -2
  176. package/src/{pie-chart → kit}/pie-legend.ts +25 -65
  177. package/src/kit/polar.ts +54 -0
  178. package/src/kit/shared.ts +104 -487
  179. package/src/kit/style.ts +257 -0
  180. package/src/kit/vertical-axis.ts +81 -115
  181. package/src/line-chart/chart-line.ts +185 -91
  182. package/src/line-chart/chart-region.ts +14 -31
  183. package/src/line-chart/index.ts +14 -22
  184. package/src/line-chart/line-chart.ts +377 -315
  185. package/src/line-chart/shared.ts +229 -304
  186. package/src/line-chart/x-scale.ts +44 -121
  187. package/src/nodes.ts +9 -13
  188. package/src/pie-chart/index.ts +3 -35
  189. package/src/pie-chart/pie-chart.ts +83 -134
  190. package/src/pie-chart/pie-ring.ts +162 -368
  191. package/src/pie-chart/shared.ts +92 -379
  192. package/src/radar-chart/index.ts +4 -0
  193. package/src/radar-chart/radar-chart.ts +287 -0
  194. package/src/radar-chart/radar-grid.ts +78 -0
  195. package/src/radar-chart/radar-series.ts +88 -0
  196. package/src/radar-chart/shared.ts +146 -0
  197. package/src/radial-chart/index.ts +5 -0
  198. package/src/radial-chart/radial-chart.ts +210 -0
  199. package/src/radial-chart/radial-rings.ts +328 -0
  200. package/src/radial-chart/shared.ts +114 -0
  201. package/dist/browser/chunks/chunk-TJNHLQGV.js +0 -2
  202. package/dist/browser/chunks/chunk-TJNHLQGV.js.map +0 -7
  203. package/dist/kit/chart-border.d.ts +0 -27
  204. package/dist/kit/chart-border.d.ts.map +0 -1
  205. package/dist/kit/chart-border.js +0 -28
  206. package/dist/kit/chart-border.js.map +0 -1
  207. package/dist/line-chart/horizontal-axis.d.ts +0 -79
  208. package/dist/line-chart/horizontal-axis.d.ts.map +0 -1
  209. package/dist/line-chart/horizontal-axis.js +0 -129
  210. package/dist/line-chart/horizontal-axis.js.map +0 -1
  211. package/dist/pie-chart/pie-legend.d.ts.map +0 -1
  212. package/dist/pie-chart/pie-legend.js +0 -117
  213. package/dist/pie-chart/pie-legend.js.map +0 -1
  214. package/src/kit/chart-border.ts +0 -50
  215. package/src/line-chart/horizontal-axis.ts +0 -151
@@ -1,127 +1,86 @@
1
1
  import {
2
2
  Rect,
3
3
  Types,
4
+ clamp,
4
5
  component2d,
5
6
  easeOut,
6
7
  type ChildView,
7
8
  type DataRecord,
8
9
  type NodeOf,
9
- // Named so this component's emitted type reaches `Node2D` through the sdk, not core.
10
+ // Named so this component's emitted type reaches `Node2D` and its props through the sdk, not core.
10
11
  type Node2D,
11
- type Node2DProps,
12
- type Timeline,
12
+ type Node2DProps as _Node2DProps,
13
+ type Fill as _Fill,
14
+ type FillResolved as _FillResolved,
13
15
  } from "@motionscript/sdk"
14
16
 
15
17
  import { asParts, fresh } from "../kit/entries"
16
- import { beats, type Beat } from "../kit/motion"
17
- import { clampUnit } from "../kit/shared"
18
- import { PieLegend } from "./pie-legend"
18
+ import { beats, type AnyTimeline, type Beat } from "../kit/motion"
19
+ import { PieLegend } from "../kit/pie-legend"
19
20
  import { PieRing } from "./pie-ring"
21
+ import { CHART_STYLE_FAMILY, CHART_STYLE_PRESETS, paletteFill } from "../kit/style"
20
22
  import {
21
- DEFAULT_PIE_CHART_THEME,
22
- lerpPieChartTheme,
23
- resolvePieChartTheme,
23
+ PIE_SPOTLIGHT_DIM,
24
+ PIE_STYLE_GROUPS,
25
+ centerConfigOf,
26
+ labelConfigOf,
27
+ legendOf,
24
28
  resolvePieSlices,
25
- type PieChartConfig,
26
- type PieChartTheme,
29
+ ringConfigOf,
30
+ selectionFade,
27
31
  type PieLabelMode,
28
- type PieLabelPlacement,
29
32
  type PieSlice,
33
+ type PieStyle,
30
34
  } from "./shared"
31
35
 
32
- export interface PieChartProps extends Node2DProps {
33
- /** One row per slice — a plain record of field name to value. */
34
- data: DataRecord[]
35
- /** Key into each row for its slice caption, used on the labels and the legend. */
36
- categoryField: string
37
- /** Key into each row for its slice magnitude — the wedge's share of the ring. */
38
- valueField: string
39
- /**
40
- * The chart's visual theme — slice palette, ring geometry, label and centre
41
- * type, legend. Every branch is optional and deep-merged onto
42
- * {@link DEFAULT_PIE_CHART_THEME}.
43
- */
44
- theme?: PieChartTheme
45
- /** What the ring's labels say. */
46
- labelMode?: PieLabelMode
47
- /** Where they sit: inside their wedge, outside on a leader, or nowhere. */
48
- labelPlacement?: PieLabelPlacement
49
- /** Whether the legend column beside the ring is drawn at all. */
50
- showLegend?: boolean
51
- /** Gap between the ring and the legend, in px. */
52
- gap?: number
53
- /** Text set in the ring's hole — see `revealCenter`. */
54
- centerLabel?: string
55
- /** Smaller second line under {@link centerLabel}. */
56
- centerCaption?: string
57
- /**
58
- * Whether the hole's summary is drawn at rest. `true` (the default) is the
59
- * plain answer; a scene that wants the conclusion to land on a later beat sets
60
- * this `false` and runs `revealCenter` when it gets there.
61
- */
62
- showCenter?: boolean
63
- }
64
-
65
- /** What the selection reads: the rows, how they are cut, and the pops the commands move. */
66
36
  export interface PieChartValues {
67
37
  readonly data: DataRecord[]
68
38
  readonly categoryField: string
69
39
  readonly valueField: string
70
- readonly theme: PieChartConfig
40
+ readonly style: PieStyle
71
41
  readonly pops: number[]
72
42
  }
73
43
 
74
- /**
75
- * How `enter` divides its duration: the wedges sweeping in, then the labels and
76
- * the legend arriving together. Shares of the whole, so they sum to 1.
77
- */
44
+ /** How `enter` divides its duration: wedges sweeping in, then labels and legend together. Sums to 1. */
78
45
  const SWEEP_SHARE = 0.65
79
46
  const LABEL_SHARE = 0.35
80
47
 
81
48
  const SLICE_ARG = Types.number(0, { min: 0, step: 1 })
82
49
 
83
- /** The slices the ring and the legend draw, resolved from the rows and the palette. */
84
50
  function slicesOf(s: PieChartValues): PieSlice[] {
85
- return resolvePieSlices(s.data, s.categoryField, s.valueField, s.theme.sliceFills)
51
+ return resolvePieSlices(
52
+ s.data,
53
+ s.categoryField,
54
+ s.valueField,
55
+ s.data.map((_, i) => paletteFill(s.style, i))
56
+ )
86
57
  }
87
58
 
88
- /** How far slice `index` is pushed out, in `[0, 1]`. */
89
59
  function popAt(s: PieChartValues, index: number): number {
90
- return clampUnit(s.pops[index] ?? 0)
60
+ return clamp(s.pops[index] ?? 0, 0, 1)
91
61
  }
92
62
 
93
- /** Whether slice `index` is grown out past the ring's rim: past half way, so a slice mid-swell is not yet counted. */
63
+ /** Whether slice `index` is past half way out, so a slice mid-swell is not yet counted. */
94
64
  export function isSliceSelected(s: PieChartValues, index: number): boolean {
95
65
  return popAt(s, index) > 0.5
96
66
  }
97
67
 
98
- /**
99
- * How far each slice is dimmed by the selection, one entry per slice: 1 while
100
- * nothing is out, and for the selected slice itself; falling toward the theme's
101
- * `dimOpacity` for the rest in proportion to how far the selection has grown.
102
- */
68
+ /** Per-slice opacity: 1 while nothing is out and for the selected slice, falling toward `dimOpacity` for the rest as the selection grows. */
103
69
  function sliceDims(s: PieChartValues): number[] {
104
70
  let focus = 0
105
71
  for (let i = 0; i < s.data.length; i++) focus = Math.max(focus, popAt(s, i))
106
- const dim = s.theme.spotlight.dimOpacity
107
- if (focus <= 0) return s.data.map(() => 1)
108
- return s.data.map((_, i) => 1 - (1 - clampUnit(dim)) * focus * (1 - popAt(s, i)))
72
+ return s.data.map((_, i) => selectionFade(focus, PIE_SPOTLIGHT_DIM, popAt(s, i)))
109
73
  }
110
74
 
111
- type AnyTimeline = Timeline<any, any>
112
-
113
- /**
114
- * Every slice's pop eased from where it is to what `pop` asks for. A move that
115
- * changes nothing still takes its slot, writing nothing.
116
- */
75
+ /** Every slice's pop eased to what `pop` asks for. A move that changes nothing still takes its slot. */
117
76
  function popping(timeline: AnyTimeline, s: PieChartValues, pop: (index: number) => number) {
118
77
  const from = s.data.map((_, i) => popAt(s, i))
119
- const targets = s.data.map((_, i) => clampUnit(pop(i)))
78
+ const targets = s.data.map((_, i) => clamp(pop(i), 0, 1))
120
79
  if (from.every((value, i) => value === targets[i])) return timeline.wait(timeline.duration)
121
80
  return timeline.animate((t) => ({ pops: from.map((value, i) => value + (targets[i] - value) * t) }))
122
81
  }
123
82
 
124
- /** The ring and the legend, found through the chart's children: the last row of them is the one it built. */
83
+ /** The ring and legend among the children; the last row is the one the chart built. */
125
84
  function partsOf(children: readonly ChildView[]) {
126
85
  for (let i = children.length - 1; i >= 0; i--) {
127
86
  const row = children[i]
@@ -132,103 +91,97 @@ function partsOf(children: readonly ChildView[]) {
132
91
  }
133
92
 
134
93
  /**
135
- * A pie or donut chart, composed from two parts laid out by a horizontal-flow
136
- * {@link Rect} — the ring filling whatever the hugging legend leaves, both
137
- * centred:
94
+ * A pie or donut chart: a {@link PieRing} filling what the hugging {@link PieLegend}
95
+ * leaves. One mark per row, not per column, and nothing to hide, since dropping
96
+ * a slice would re-cut every other wedge; `selectSlice` and its siblings swell
97
+ * a slice and dim the rest instead.
138
98
  *
139
- * - {@link PieRing} — the wedges, each sized by its share of the total, with its
140
- * label inside its own band or pinned outside on a leader line, and the
141
- * summary in the hole.
142
- * - {@link PieLegend} — a column of swatch-and-caption rows naming each slice.
143
- *
144
- * **One mark per row, not per column**: a pie's palette is indexed by row and
145
- * there is nothing to hide, since dropping a slice would re-cut every other
146
- * wedge. `selectSlice` and its siblings are what a pie has instead — a slice
147
- * swells out of the ring and everything else dims behind it.
148
- *
149
- * **Resting state is the finished chart**; `enter` arms every part on its first
150
- * frame. The one exception is the hole's summary, which a scene can hold back
151
- * with `showCenter: false`, because it is usually the chart's *conclusion*.
99
+ * Resting state is the finished chart, except the hole's summary, which
100
+ * `showCenter: false` holds back for `revealCenter`.
152
101
  */
153
102
  export const PieChart = component2d({
154
103
  key: "pieChart",
155
104
  state: {
156
- data: fresh<DataRecord[]>(() => []),
105
+ data: fresh<DataRecord[]>(() => [], { kind: "dataset" }),
106
+ /** Slice caption, used on the labels and the legend. */
157
107
  categoryField: "",
108
+ /** Slice magnitude: the wedge's share of the ring. */
158
109
  valueField: "",
159
- theme: Types.custom<PieChartTheme, PieChartConfig>(DEFAULT_PIE_CHART_THEME, { resolve: resolvePieChartTheme, lerp: lerpPieChartTheme }),
110
+ style: Types.style(CHART_STYLE_FAMILY, PIE_STYLE_GROUPS, { presets: CHART_STYLE_PRESETS }),
111
+ /** What the ring's labels say. */
160
112
  labelMode: Types.custom<PieLabelMode>("percent"),
161
- labelPlacement: Types.custom<PieLabelPlacement>("leader"),
113
+ /** Gap between the ring and the legend, in px. */
162
114
  gap: 40,
115
+ /** Text set in the ring's hole. */
163
116
  centerLabel: "",
117
+ /** Smaller second line under `centerLabel`. */
164
118
  centerCaption: "",
165
- /**
166
- * How far each slice is pushed out of the ring, `0`–`1` per index. Held here
167
- * rather than on the ring because two parts read it: the wedges grow out by
168
- * it, and the legend entries dim by it. Seeded flush, one to a row.
169
- */
119
+ /** How far each slice is pushed out, `0`-`1` per index. Here, not on the ring, because the wedges and the legend both read it. */
170
120
  pops: fresh<number[]>(() => []),
171
- /** Read as the chart is built: the legend is a part the chart builds or doesn't. */
172
- showLegend: Types.boolean(true, { animatable: false }),
173
- /** Read as the chart is built: whether the hole's summary is drawn at rest. */
121
+ /** Read as the chart is built: whether the hole's summary is drawn at rest. A scene that wants it to land later sets `false` and runs `revealCenter`. */
174
122
  showCenter: Types.boolean(true, { animatable: false }),
175
123
  },
176
124
  defaults: {
177
- // Its parts fill it; a node made of them would hug them.
125
+ // A node made of parts that fill would otherwise hug them.
178
126
  width: "fill",
179
127
  height: "fill",
180
128
  pops: (state) => slicesOf(state).map(() => 0),
181
129
  },
182
130
  allowedChildren: [],
183
- // The slice *structure* (one label and one legend row per row) is read once;
184
- // the slices themselves, their paint and the selection are bound, so a theme
185
- // tween re-paints and a `set` of `data` re-cuts the wedges.
131
+ // 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.
186
132
  children: (state) => {
187
- const theme = () => state.theme
133
+ const style = () => state.style
188
134
  const slices = () => slicesOf(state)
189
135
  const parts: Node2D[] = [
190
136
  new PieRing({
191
137
  width: "fill",
192
138
  height: "fill",
193
139
  slices,
194
- ring: () => theme().ring,
195
- label: () => theme().label,
196
- centerStyle: () => theme().center,
140
+ ring: () => ringConfigOf(style()),
141
+ label: () => labelConfigOf(style()),
142
+ centerStyle: () => centerConfigOf(style()),
197
143
  labelMode: state.labelMode,
198
- labelPlacement: state.labelPlacement,
144
+ labelPlacement: state.style.labelPlacement,
199
145
  pops: () => state.pops,
200
- dimOpacity: () => theme().spotlight.dimOpacity,
146
+ dimOpacity: PIE_SPOTLIGHT_DIM,
201
147
  centerLabel: state.centerLabel,
202
148
  centerCaption: state.centerCaption,
203
149
  centerReveal: state.showCenter === false ? 0 : 1,
204
150
  }) as unknown as Node2D,
205
151
  ]
206
- if (state.showLegend !== false) {
207
- parts.push(
208
- new PieLegend({
209
- slices,
210
- legend: () => theme().legend,
211
- dims: () => sliceDims(state),
212
- // The column's spacing is the theme's as the chart was built, like the
213
- // entries' type and chips.
214
- gap: state.theme.legend.itemGap / 3,
215
- padding: state.theme.legend.padding,
216
- }) as unknown as Node2D
217
- )
152
+ const position = state.style.legend
153
+ if (position !== "none") {
154
+ const side = position === "right"
155
+ const legend = new PieLegend({
156
+ slices,
157
+ legend: () => legendOf(style()),
158
+ dims: () => sliceDims(state),
159
+ flow: side ? "vertical" : "horizontal",
160
+ align: "center",
161
+ // Spacing is fixed as built.
162
+ gap: side ? legendOf(state.style).itemGap / 3 : legendOf(state.style).itemGap,
163
+ padding: 0,
164
+ }) as unknown as Node2D
165
+ if (position === "top") parts.unshift(legend)
166
+ else parts.push(legend)
218
167
  }
219
- const row = new Rect({ flow: "horizontal", width: "fill", height: "fill", gap: state.gap, align: "center", children: parts })
168
+ const row = new Rect({
169
+ flow: position === "right" || position === "none" ? "horizontal" : "vertical",
170
+ width: "fill",
171
+ height: "fill",
172
+ gap: state.gap,
173
+ padding: 24,
174
+ align: "center",
175
+ fill: () => state.style.background,
176
+ children: parts,
177
+ })
220
178
  return asParts([row as unknown as Node2D])
221
179
  },
222
180
  commands: {
223
- /**
224
- * Animate the whole chart in: the wedges sweep in clockwise from the theme's
225
- * `startAngle`, then the labels fade up and the legend staggers in together.
226
- * Its first frame arms the wedges, the labels and the legend; the hole's
227
- * summary keeps its own signal. `duration` is the **whole** entrance.
228
- */
181
+ /** Wedges sweep in clockwise from `startAngle`, then labels fade up and the legend staggers in. `duration` is the whole entrance; the hole's summary keeps its own signal. */
229
182
  enter: {
230
183
  args: {},
231
- // Held from the chart's arrival, so it never shows finished ahead of its draw.
184
+ // Held from arrival, so it never shows finished ahead of its draw.
232
185
  entrance: true,
233
186
  easing: easeOut(),
234
187
  run: (timeline, _state, children) => {
@@ -247,11 +200,7 @@ export const PieChart = component2d({
247
200
  ])
248
201
  },
249
202
  },
250
- /**
251
- * Fade the hole's summary up. Held out of `enter` on purpose: what sits in a
252
- * donut's hole is usually the chart's conclusion, so the scene lands it on
253
- * its own beat. Pair it with `showCenter: false`.
254
- */
203
+ /** Fade the hole's summary up. Not part of `enter`: it is usually the conclusion, which a scene lands on its own beat. */
255
204
  revealCenter: {
256
205
  args: {},
257
206
  easing: easeOut(),