@motionscript/charts 0.1.0-alpha.5 → 0.1.0-alpha.7

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 (146) hide show
  1. package/CHANGELOG.md +40 -0
  2. package/README.md +30 -16
  3. package/dist/bar-chart/bar-chart.d.ts +133 -242
  4. package/dist/bar-chart/bar-chart.d.ts.map +1 -1
  5. package/dist/bar-chart/bar-chart.js +378 -569
  6. package/dist/bar-chart/bar-chart.js.map +1 -1
  7. package/dist/bar-chart/category-axis.d.ts +35 -41
  8. package/dist/bar-chart/category-axis.d.ts.map +1 -1
  9. package/dist/bar-chart/category-axis.js +93 -115
  10. package/dist/bar-chart/category-axis.js.map +1 -1
  11. package/dist/bar-chart/chart-bars.d.ts +50 -70
  12. package/dist/bar-chart/chart-bars.d.ts.map +1 -1
  13. package/dist/bar-chart/chart-bars.js +156 -210
  14. package/dist/bar-chart/chart-bars.js.map +1 -1
  15. package/dist/bar-chart/index.d.ts +2 -2
  16. package/dist/bar-chart/index.d.ts.map +1 -1
  17. package/dist/bar-chart/index.js +1 -1
  18. package/dist/bar-chart/index.js.map +1 -1
  19. package/dist/bar-chart/shared.d.ts +3 -3
  20. package/dist/bar-chart/shared.d.ts.map +1 -1
  21. package/dist/bar-chart/shared.js +3 -3
  22. package/dist/bar-chart/shared.js.map +1 -1
  23. package/dist/browser/chunks/chunk-XROBIRQK.js +2 -0
  24. package/dist/browser/chunks/chunk-XROBIRQK.js.map +7 -0
  25. package/dist/browser/index.js +1 -1
  26. package/dist/browser/index.js.map +3 -3
  27. package/dist/browser/kit.js +1 -1
  28. package/dist/browser/manifest.json +1 -1
  29. package/dist/engine.d.ts +8 -6
  30. package/dist/engine.d.ts.map +1 -1
  31. package/dist/engine.js +8 -6
  32. package/dist/engine.js.map +1 -1
  33. package/dist/kit/cartesian.d.ts +44 -0
  34. package/dist/kit/cartesian.d.ts.map +1 -0
  35. package/dist/kit/cartesian.js +59 -0
  36. package/dist/kit/cartesian.js.map +1 -0
  37. package/dist/kit/chart-border.d.ts +10 -11
  38. package/dist/kit/chart-border.d.ts.map +1 -1
  39. package/dist/kit/chart-border.js +15 -32
  40. package/dist/kit/chart-border.js.map +1 -1
  41. package/dist/kit/chart-canvas.d.ts +32 -21
  42. package/dist/kit/chart-canvas.d.ts.map +1 -1
  43. package/dist/kit/chart-canvas.js +33 -90
  44. package/dist/kit/chart-canvas.js.map +1 -1
  45. package/dist/kit/chart-legend.d.ts +123 -64
  46. package/dist/kit/chart-legend.d.ts.map +1 -1
  47. package/dist/kit/chart-legend.js +129 -154
  48. package/dist/kit/chart-legend.js.map +1 -1
  49. package/dist/kit/entries.d.ts +17 -0
  50. package/dist/kit/entries.d.ts.map +1 -0
  51. package/dist/kit/entries.js +19 -0
  52. package/dist/kit/entries.js.map +1 -0
  53. package/dist/kit/motion.d.ts +48 -0
  54. package/dist/kit/motion.d.ts.map +1 -0
  55. package/dist/kit/motion.js +63 -0
  56. package/dist/kit/motion.js.map +1 -0
  57. package/dist/kit/paints.d.ts +10 -0
  58. package/dist/kit/paints.d.ts.map +1 -0
  59. package/dist/kit/paints.js +12 -0
  60. package/dist/kit/paints.js.map +1 -0
  61. package/dist/kit/shared.d.ts +31 -3
  62. package/dist/kit/shared.d.ts.map +1 -1
  63. package/dist/kit/shared.js +42 -3
  64. package/dist/kit/shared.js.map +1 -1
  65. package/dist/kit/vertical-axis.d.ts +48 -61
  66. package/dist/kit/vertical-axis.d.ts.map +1 -1
  67. package/dist/kit/vertical-axis.js +97 -132
  68. package/dist/kit/vertical-axis.js.map +1 -1
  69. package/dist/line-chart/chart-line.d.ts +60 -77
  70. package/dist/line-chart/chart-line.d.ts.map +1 -1
  71. package/dist/line-chart/chart-line.js +100 -121
  72. package/dist/line-chart/chart-line.js.map +1 -1
  73. package/dist/line-chart/chart-region.d.ts +32 -33
  74. package/dist/line-chart/chart-region.d.ts.map +1 -1
  75. package/dist/line-chart/chart-region.js +31 -65
  76. package/dist/line-chart/chart-region.js.map +1 -1
  77. package/dist/line-chart/horizontal-axis.d.ts +58 -64
  78. package/dist/line-chart/horizontal-axis.d.ts.map +1 -1
  79. package/dist/line-chart/horizontal-axis.js +94 -123
  80. package/dist/line-chart/horizontal-axis.js.map +1 -1
  81. package/dist/line-chart/index.d.ts +2 -2
  82. package/dist/line-chart/index.d.ts.map +1 -1
  83. package/dist/line-chart/index.js +1 -1
  84. package/dist/line-chart/index.js.map +1 -1
  85. package/dist/line-chart/line-chart.d.ts +156 -319
  86. package/dist/line-chart/line-chart.d.ts.map +1 -1
  87. package/dist/line-chart/line-chart.js +403 -663
  88. package/dist/line-chart/line-chart.js.map +1 -1
  89. package/dist/line-chart/shared.d.ts +3 -3
  90. package/dist/line-chart/shared.d.ts.map +1 -1
  91. package/dist/line-chart/shared.js +3 -3
  92. package/dist/line-chart/shared.js.map +1 -1
  93. package/dist/nodes.d.ts +6 -9
  94. package/dist/nodes.d.ts.map +1 -1
  95. package/dist/nodes.js +7 -9
  96. package/dist/nodes.js.map +1 -1
  97. package/dist/pie-chart/index.d.ts +2 -2
  98. package/dist/pie-chart/index.d.ts.map +1 -1
  99. package/dist/pie-chart/index.js +1 -1
  100. package/dist/pie-chart/index.js.map +1 -1
  101. package/dist/pie-chart/pie-chart.d.ts +108 -132
  102. package/dist/pie-chart/pie-chart.d.ts.map +1 -1
  103. package/dist/pie-chart/pie-chart.js +200 -271
  104. package/dist/pie-chart/pie-chart.js.map +1 -1
  105. package/dist/pie-chart/pie-legend.d.ts +84 -31
  106. package/dist/pie-chart/pie-legend.d.ts.map +1 -1
  107. package/dist/pie-chart/pie-legend.js +93 -107
  108. package/dist/pie-chart/pie-legend.js.map +1 -1
  109. package/dist/pie-chart/pie-ring.d.ts +160 -131
  110. package/dist/pie-chart/pie-ring.d.ts.map +1 -1
  111. package/dist/pie-chart/pie-ring.js +333 -391
  112. package/dist/pie-chart/pie-ring.js.map +1 -1
  113. package/dist/pie-chart/shared.d.ts +2 -2
  114. package/dist/pie-chart/shared.d.ts.map +1 -1
  115. package/dist/pie-chart/shared.js +2 -2
  116. package/dist/pie-chart/shared.js.map +1 -1
  117. package/package.json +4 -4
  118. package/src/bar-chart/bar-chart.ts +415 -701
  119. package/src/bar-chart/category-axis.ts +95 -110
  120. package/src/bar-chart/chart-bars.ts +178 -200
  121. package/src/bar-chart/index.ts +2 -2
  122. package/src/bar-chart/shared.ts +3 -3
  123. package/src/engine.ts +8 -6
  124. package/src/kit/cartesian.ts +81 -0
  125. package/src/kit/chart-border.ts +22 -33
  126. package/src/kit/chart-canvas.ts +44 -79
  127. package/src/kit/chart-legend.ts +173 -189
  128. package/src/kit/entries.ts +20 -0
  129. package/src/kit/motion.ts +90 -0
  130. package/src/kit/paints.ts +15 -0
  131. package/src/kit/shared.ts +52 -3
  132. package/src/kit/vertical-axis.ts +110 -140
  133. package/src/line-chart/chart-line.ts +102 -154
  134. package/src/line-chart/chart-region.ts +35 -80
  135. package/src/line-chart/horizontal-axis.ts +101 -152
  136. package/src/line-chart/index.ts +2 -2
  137. package/src/line-chart/line-chart.ts +449 -825
  138. package/src/line-chart/shared.ts +3 -3
  139. package/src/nodes.ts +8 -10
  140. package/src/pie-chart/index.ts +2 -2
  141. package/src/pie-chart/pie-chart.ts +221 -311
  142. package/src/pie-chart/pie-legend.ts +124 -114
  143. package/src/pie-chart/pie-ring.ts +386 -473
  144. package/src/pie-chart/shared.ts +2 -2
  145. package/dist/browser/chunks/chunk-5ZPBV6BU.js +0 -2
  146. package/dist/browser/chunks/chunk-5ZPBV6BU.js.map +0 -7
@@ -1,21 +1,41 @@
1
1
  import {
2
- command,
2
+ Flow,
3
+ Paint,
3
4
  Rect,
5
+ Shapes,
4
6
  Text,
5
- easeOut,
6
- property,
7
+ Types,
8
+ component2d,
7
9
  textOps,
8
- type CommandArgs,
9
- type NodeConfig,
10
+ type NodeOf,
10
11
  type RectProps,
12
+ // Named so the column's emitted type reaches the Rect surface it spreads through the sdk, not core.
13
+ type Alignment as _Alignment,
14
+ type CornerRadiusResolved as _CornerRadiusResolved,
15
+ type CornerStyleResolved as _CornerStyleResolved,
16
+ type Fill as _Fill,
17
+ type FillResolved as _FillResolved,
18
+ type FlowMode as _FlowMode,
19
+ type FlowResolved as _FlowResolved,
20
+ type GapSize as _GapSize,
21
+ type Node2D as _Node2D,
22
+ type Node2DProps as _Node2DProps,
23
+ type RectCornerRadius as _RectCornerRadius,
24
+ type RectCornerStyle as _RectCornerStyle,
25
+ type Shadow as _Shadow,
26
+ type ShadowResolved as _ShadowResolved,
27
+ type Stroke as _Stroke,
28
+ type StrokeResolved as _StrokeResolved,
29
+ type Vector2 as _Vector2,
11
30
  } from "@motionscript/sdk"
12
31
 
32
+ import { fresh } from "../kit/entries"
33
+ import { armed, arrival, arrivalOf, arrivalsEntry, beats, frameEase, type Arrival } from "../kit/motion"
13
34
  import {
14
35
  DEFAULT_LEGEND,
15
36
  clampUnit,
16
37
  type LegendConfig,
17
38
  } from "../kit/shared"
18
- import { at, compose, hold, type Seekable } from "@motionscript/sdk/component"
19
39
  import type { PieSlice } from "./shared"
20
40
 
21
41
  export interface PieLegendProps extends RectProps {
@@ -47,113 +67,103 @@ const SLIDE = 16
47
67
  * shouldn't be in the chart shouldn't be in the table — removing one would
48
68
  * silently re-cut every other wedge's share, which is a different chart rather
49
69
  * than the same chart with something hidden. Selection is what a pie has
50
- * instead, and it arrives through {@link dims}.
70
+ * instead, and it arrives through {@link PieLegendProps.dims}.
51
71
  */
52
- export class PieLegend extends Rect {
53
- @property({ default: () => [] as PieSlice[] }) declare slices: PieSlice[]
54
- @property({ default: DEFAULT_LEGEND }) declare legend: LegendConfig
55
- @property({ default: () => [] as number[] }) declare dims: number[]
56
-
57
- /** One entry per slice, ordered top → bottom, for the staggered entrance. */
58
- private items: Rect[] = []
59
-
60
- constructor(props?: NodeConfig<PieLegend, PieLegendProps>) {
61
- // `flow` with the rest of the fixed geometry, and for the reason
62
- // {@link ChartLegend} spells out: a `Rect` given none lays its children out
63
- // `freeform`, so an unstated column is every caption drawn on top of the
64
- // last one.
65
- super({
66
- ...(props as RectProps),
67
- flow: "vertical",
68
- align: "centerLeft",
69
- width: "hug",
70
- height: "hug",
71
- })
72
-
73
- // Fixed column geometry last, so it wins over what super() applied.
74
- this.set({ gap: this.legend.itemGap / 3, padding: this.legend.padding })
75
-
76
- for (let i = 0; i < this.slices.length; i++) {
77
- const item = this.buildItem(i)
78
- this.items.push(item)
79
- // The selection dim rides a wrapper rather than the entry itself, so the
80
- // entrance can animate the entry's own opacity without the two fighting
81
- // over one value — opacity multiplies down the tree, which is exactly the
82
- // composition wanted here.
83
- this.add(
84
- new Rect({
85
- width: "hug",
86
- height: "hug",
87
- flow: "freeform",
88
- opacity: () => clampUnit(this.dims[i] ?? 1),
89
- children: [item],
90
- })
91
- )
92
- }
93
- }
94
-
95
- /** One entry: the slice's colour swatch beside its caption. */
96
- private buildItem(index: number): Rect {
97
- const marker = this.legend.markerStyle
98
- return new Rect({ flow: "horizontal",
99
- gap: 12,
100
- align: "center",
101
- width: "hug",
102
- height: "hug",
103
- children: [
104
- new Rect({
105
- width: marker.height,
106
- height: marker.height,
107
- cornerRadius: marker.borderRadius,
108
- fill: () => this.slices[index]?.fill ?? "white",
109
- stroke: marker.stroke,
110
- shadow: marker.shadow,
111
- }),
112
- new Text({
113
- ...textOps.styleProps(this.legend.textStyle),
114
- text: () => this.slices[index]?.category ?? "",
115
- }),
116
- ],
117
- })
118
- }
119
-
120
- /**
121
- * Prime every entry at its pre-entrance state: shifted right and transparent.
122
- *
123
- * Idempotent, and separate from {@link enter} so the chart can arm its whole
124
- * composition before the first frame is drawn — the resting state is the
125
- * finished legend, so there is nothing hidden to reveal unless a scene asks.
126
- */
127
- arm(slide = SLIDE): void {
128
- for (const item of this.items) item.set({ opacity: 0, x: slide })
129
- }
130
-
131
- /**
132
- * Slide + fade every entry in from the right, staggered top-to-bottom.
133
- *
134
- * `duration` is the **whole** entrance: the stagger is carved out of it rather
135
- * than added on top, so the command takes exactly the time the timeline laid
136
- * out for it.
137
- */
138
- @command()
139
- enter(
140
- args: CommandArgs<Record<string, never>> & { duration: number }
141
- ): Seekable {
142
- const duration = args.duration
143
- const easing = args.easing ?? easeOut()
144
- const items = this.items
145
- if (items.length === 0) return hold(duration)
146
- this.arm()
147
-
148
- // Each gap is a fifth of an entry's own move, the rhythm the axes use;
149
- // solved for from the budget rather than added to it. The offsets were
150
- // always `i * step` — written as `wait` inside a generator they merely only
151
- // existed while it ran.
152
- const per = duration / (1 + Math.max(0, items.length - 1) / 5)
153
- const step = per / 5
154
-
155
- return compose(
156
- items.map((item, i) => at(i * step, item.to({ data: { opacity: 1, x: 0 }, duration: per, easing })))
72
+ export const PieLegend = component2d({
73
+ key: "pieLegend",
74
+ state: {
75
+ ...Paint.state,
76
+ ...Flow.state,
77
+ ...Shapes.corners,
78
+ slices: fresh<PieSlice[]>(() => []),
79
+ legend: Types.custom<LegendConfig>(DEFAULT_LEGEND),
80
+ dims: fresh<number[]>(() => []),
81
+ arrivals: arrivalsEntry(),
82
+ },
83
+ // A column, for the reason `ChartLegend` gives for its row: a `Rect` given no
84
+ // flow lays its children out `freeform`, every caption on top of the last.
85
+ defaults: {
86
+ flow: "vertical",
87
+ align: "centerLeft",
88
+ width: "hug",
89
+ height: "hug",
90
+ gap: (state) => state.legend.itemGap / 3,
91
+ padding: (state) => state.legend.padding,
92
+ },
93
+ // One entry per slice, ordered top → bottom. The selection dim rides a wrapper
94
+ // rather than the entry itself, so the entrance can animate the entry's own
95
+ // opacity without the two fighting over one value — opacity multiplies down
96
+ // the tree, which is exactly the composition wanted here.
97
+ children: (state) => {
98
+ const marker = state.legend.markerStyle
99
+ return state.slices.map((_, index) =>
100
+ Rect({
101
+ width: "hug",
102
+ height: "hug",
103
+ flow: "freeform",
104
+ opacity: () => clampUnit(state.dims[index] ?? 1),
105
+ children: [
106
+ Rect({
107
+ flow: "horizontal",
108
+ gap: 12,
109
+ align: "center",
110
+ width: "hug",
111
+ height: "hug",
112
+ opacity: () => arrivalOf(state.arrivals, index).opacity,
113
+ x: () => arrivalOf(state.arrivals, index).shift,
114
+ children: [
115
+ Rect({
116
+ width: marker.height,
117
+ height: marker.height,
118
+ cornerRadius: marker.borderRadius,
119
+ fill: () => state.slices[index]?.fill ?? "white",
120
+ stroke: marker.stroke,
121
+ shadow: marker.shadow,
122
+ }),
123
+ Text({
124
+ ...textOps.styleProps(state.legend.textStyle),
125
+ text: () => state.slices[index]?.category ?? "",
126
+ }),
127
+ ],
128
+ }),
129
+ ],
130
+ })
157
131
  )
158
- }
159
- }
132
+ },
133
+ commands: {
134
+ ...Paint.commands,
135
+ /**
136
+ * Every entry at its pre-entrance state, shifted right and transparent: what
137
+ * a chart shows ahead of this legend's own beat in its entrance, since
138
+ * `enter` arms the entries only on its first frame.
139
+ */
140
+ arm: (timeline, state) =>
141
+ timeline.animate(() => ({ arrivals: armed([...state.slices.keys()], { opacity: 0, shift: SLIDE }) })),
142
+ /**
143
+ * Slide + fade every entry in from the right, staggered top-to-bottom. Its
144
+ * first frame arms them all: shifted right and transparent.
145
+ *
146
+ * The timeline's duration is the **whole** entrance: the stagger is carved
147
+ * out of it rather than added on top, so the command takes exactly the time
148
+ * the timeline laid out for it.
149
+ */
150
+ enter: (timeline, state) => {
151
+ const count = state.slices.length
152
+ if (count === 0) return timeline.wait(timeline.duration)
153
+ const from: Arrival = { opacity: 0, shift: SLIDE }
154
+ const arm = timeline.animate(() => ({ arrivals: armed([...state.slices.keys()], from) }))
155
+ // Each gap is a fifth of an entry's own move, the rhythm the axes use;
156
+ // solved for from the budget rather than added to it.
157
+ const per = timeline.duration / (1 + Math.max(0, count - 1) / 5)
158
+ const step = per / 5
159
+ const ease = frameEase(timeline)
160
+ const entries = state.slices.map((_, i) => ({ at: i * step, duration: per, piece: arrival(timeline, i, from, ease) }))
161
+ return timeline.together([arm, beats(timeline, entries)])
162
+ },
163
+ },
164
+ measure: (measurer, state, children) => Flow.measure(measurer, state, children, Paint.inset(state)),
165
+ layout: (box, state, children) => Flow.layout(box, state, children, Paint.inset(state)),
166
+ render: (ctx, state) => Paint.draw(ctx, state, Shapes.rect(ctx.size, state)),
167
+ })
168
+
169
+ export type PieLegend = NodeOf<typeof PieLegend>