@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,117 +0,0 @@
1
- import { Flow, Paint, Rect, Shapes, Text, Types, component2d, textOps, } from "@motionscript/sdk";
2
- import { fresh } from "../kit/entries.js";
3
- import { armed, arrival, arrivalOf, arrivalsEntry, beats, frameEase } from "../kit/motion.js";
4
- import { DEFAULT_LEGEND, clampUnit, } from "../kit/shared.js";
5
- /** How far an entry starts to the right of its slot when it enters, in px. */
6
- const SLIDE = 16;
7
- /**
8
- * The legend of a {@link PieChart}: a **vertical-flow column** of entries, one per
9
- * slice, each pairing the slice's caption with its colour swatch.
10
- *
11
- * A column rather than the cartesian charts' row, because it sits *beside* the
12
- * ring rather than beneath the plot — a pie is as tall as it is wide, so the
13
- * space it leaves over is a vertical strip. It is otherwise the same entry: the
14
- * swatch carries the colour, the caption reads in the legend's neutral ink, and
15
- * both take the shared {@link LegendConfig}.
16
- *
17
- * There is no show/hide here. A pie's marks are its *rows*, and a row that
18
- * shouldn't be in the chart shouldn't be in the table — removing one would
19
- * silently re-cut every other wedge's share, which is a different chart rather
20
- * than the same chart with something hidden. Selection is what a pie has
21
- * instead, and it arrives through {@link PieLegendProps.dims}.
22
- */
23
- export const PieLegend = component2d({
24
- key: "pieLegend",
25
- state: {
26
- ...Paint.state,
27
- ...Flow.state,
28
- ...Shapes.corners,
29
- slices: fresh(() => []),
30
- legend: Types.custom(DEFAULT_LEGEND),
31
- dims: fresh(() => []),
32
- arrivals: arrivalsEntry(),
33
- },
34
- // A column, for the reason `ChartLegend` gives for its row: a `Rect` given no
35
- // flow lays its children out `freeform`, every caption on top of the last.
36
- defaults: {
37
- flow: "vertical",
38
- align: "centerLeft",
39
- width: "hug",
40
- height: "hug",
41
- gap: (state) => state.legend.itemGap / 3,
42
- padding: (state) => state.legend.padding,
43
- },
44
- // One entry per slice, ordered top → bottom. The selection dim rides a wrapper
45
- // rather than the entry itself, so the entrance can animate the entry's own
46
- // opacity without the two fighting over one value — opacity multiplies down
47
- // the tree, which is exactly the composition wanted here.
48
- children: (state) => {
49
- const marker = state.legend.markerStyle;
50
- return state.slices.map((_, index) => Rect({
51
- width: "hug",
52
- height: "hug",
53
- flow: "freeform",
54
- opacity: () => clampUnit(state.dims[index] ?? 1),
55
- children: [
56
- Rect({
57
- flow: "horizontal",
58
- gap: 12,
59
- align: "center",
60
- width: "hug",
61
- height: "hug",
62
- opacity: () => arrivalOf(state.arrivals, index).opacity,
63
- x: () => arrivalOf(state.arrivals, index).shift,
64
- children: [
65
- Rect({
66
- width: marker.height,
67
- height: marker.height,
68
- cornerRadius: marker.borderRadius,
69
- fill: () => state.slices[index]?.fill ?? "white",
70
- stroke: marker.stroke,
71
- shadow: marker.shadow,
72
- }),
73
- Text({
74
- ...textOps.styleProps(state.legend.textStyle),
75
- text: () => state.slices[index]?.category ?? "",
76
- }),
77
- ],
78
- }),
79
- ],
80
- }));
81
- },
82
- commands: {
83
- ...Paint.commands,
84
- /**
85
- * Every entry at its pre-entrance state, shifted right and transparent: what
86
- * a chart shows ahead of this legend's own beat in its entrance, since
87
- * `enter` arms the entries only on its first frame.
88
- */
89
- arm: (timeline, state) => timeline.animate(() => ({ arrivals: armed([...state.slices.keys()], { opacity: 0, shift: SLIDE }) })),
90
- /**
91
- * Slide + fade every entry in from the right, staggered top-to-bottom. Its
92
- * first frame arms them all: shifted right and transparent.
93
- *
94
- * The timeline's duration is the **whole** entrance: the stagger is carved
95
- * out of it rather than added on top, so the command takes exactly the time
96
- * the timeline laid out for it.
97
- */
98
- enter: (timeline, state) => {
99
- const count = state.slices.length;
100
- if (count === 0)
101
- return timeline.wait(timeline.duration);
102
- const from = { opacity: 0, shift: SLIDE };
103
- const arm = timeline.animate(() => ({ arrivals: armed([...state.slices.keys()], from) }));
104
- // Each gap is a fifth of an entry's own move, the rhythm the axes use;
105
- // solved for from the budget rather than added to it.
106
- const per = timeline.duration / (1 + Math.max(0, count - 1) / 5);
107
- const step = per / 5;
108
- const ease = frameEase(timeline);
109
- const entries = state.slices.map((_, i) => ({ at: i * step, duration: per, piece: arrival(timeline, i, from, ease) }));
110
- return timeline.together([arm, beats(timeline, entries)]);
111
- },
112
- },
113
- measure: (measurer, state, children) => Flow.measure(measurer, state, children, Paint.inset(state)),
114
- layout: (box, state, children) => Flow.layout(box, state, children, Paint.inset(state)),
115
- render: (ctx, state) => Paint.draw(ctx, state, Shapes.rect(ctx.size, state)),
116
- });
117
- //# sourceMappingURL=pie-legend.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"pie-legend.js","sourceRoot":"","sources":["../../src/pie-chart/pie-legend.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n Flow,\n Paint,\n Rect,\n Shapes,\n Text,\n Types,\n component2d,\n textOps,\n type NodeOf,\n type RectProps,\n // Named so the column's emitted type reaches the Rect surface it spreads through the sdk, not core.\n type Alignment as _Alignment,\n type CornerRadiusResolved as _CornerRadiusResolved,\n type CornerStyleResolved as _CornerStyleResolved,\n type Fill as _Fill,\n type FillResolved as _FillResolved,\n type FlowMode as _FlowMode,\n type FlowResolved as _FlowResolved,\n type GapSize as _GapSize,\n type Node2D as _Node2D,\n type Node2DProps as _Node2DProps,\n type RectCornerRadius as _RectCornerRadius,\n type RectCornerStyle as _RectCornerStyle,\n type Shadow as _Shadow,\n type ShadowResolved as _ShadowResolved,\n type Stroke as _Stroke,\n type StrokeResolved as _StrokeResolved,\n type Vector2 as _Vector2,\n} from \"@motionscript/sdk\"\n\nimport { fresh } from \"../kit/entries\"\nimport { armed, arrival, arrivalOf, arrivalsEntry, beats, frameEase, type Arrival } from \"../kit/motion\"\nimport {\n DEFAULT_LEGEND,\n clampUnit,\n type LegendConfig,\n} from \"../kit/shared\"\nimport type { PieSlice } from \"./shared\"\n\nexport interface PieLegendProps extends RectProps {\n /** One entry per slice, in ring order. */\n slices: PieSlice[]\n /** Padding, caption style, gaps, and swatch geometry — see {@link LegendConfig}. */\n legend: LegendConfig\n /**\n * Per-slice opacity multiplier, by index — the same signal that dims the ring,\n * so an entry fades in step with the wedge it names. Missing entries read as 1.\n */\n dims: number[]\n}\n\n/** How far an entry starts to the right of its slot when it enters, in px. */\nconst SLIDE = 16\n\n/**\n * The legend of a {@link PieChart}: a **vertical-flow column** of entries, one per\n * slice, each pairing the slice's caption with its colour swatch.\n *\n * A column rather than the cartesian charts' row, because it sits *beside* the\n * ring rather than beneath the plot — a pie is as tall as it is wide, so the\n * space it leaves over is a vertical strip. It is otherwise the same entry: the\n * swatch carries the colour, the caption reads in the legend's neutral ink, and\n * both take the shared {@link LegendConfig}.\n *\n * There is no show/hide here. A pie's marks are its *rows*, and a row that\n * shouldn't be in the chart shouldn't be in the table — removing one would\n * silently re-cut every other wedge's share, which is a different chart rather\n * than the same chart with something hidden. Selection is what a pie has\n * instead, and it arrives through {@link PieLegendProps.dims}.\n */\nexport const PieLegend = component2d({\n key: \"pieLegend\",\n state: {\n ...Paint.state,\n ...Flow.state,\n ...Shapes.corners,\n slices: fresh<PieSlice[]>(() => []),\n legend: Types.custom<LegendConfig>(DEFAULT_LEGEND),\n dims: fresh<number[]>(() => []),\n arrivals: arrivalsEntry(),\n },\n // A column, for the reason `ChartLegend` gives for its row: a `Rect` given no\n // flow lays its children out `freeform`, every caption on top of the last.\n defaults: {\n flow: \"vertical\",\n align: \"centerLeft\",\n width: \"hug\",\n height: \"hug\",\n gap: (state) => state.legend.itemGap / 3,\n padding: (state) => state.legend.padding,\n },\n // One entry per slice, ordered top → bottom. The selection dim rides a wrapper\n // rather than the entry itself, so the entrance can animate the entry's own\n // opacity without the two fighting over one value — opacity multiplies down\n // the tree, which is exactly the composition wanted here.\n children: (state) => {\n const marker = state.legend.markerStyle\n return state.slices.map((_, index) =>\n Rect({\n width: \"hug\",\n height: \"hug\",\n flow: \"freeform\",\n opacity: () => clampUnit(state.dims[index] ?? 1),\n children: [\n Rect({\n flow: \"horizontal\",\n gap: 12,\n align: \"center\",\n width: \"hug\",\n height: \"hug\",\n opacity: () => arrivalOf(state.arrivals, index).opacity,\n x: () => arrivalOf(state.arrivals, index).shift,\n children: [\n Rect({\n width: marker.height,\n height: marker.height,\n cornerRadius: marker.borderRadius,\n fill: () => state.slices[index]?.fill ?? \"white\",\n stroke: marker.stroke,\n shadow: marker.shadow,\n }),\n Text({\n ...textOps.styleProps(state.legend.textStyle),\n text: () => state.slices[index]?.category ?? \"\",\n }),\n ],\n }),\n ],\n })\n )\n },\n commands: {\n ...Paint.commands,\n /**\n * Every entry at its pre-entrance state, shifted right and transparent: what\n * a chart shows ahead of this legend's own beat in its entrance, since\n * `enter` arms the entries only on its first frame.\n */\n arm: (timeline, state) =>\n timeline.animate(() => ({ arrivals: armed([...state.slices.keys()], { opacity: 0, shift: SLIDE }) })),\n /**\n * Slide + fade every entry in from the right, staggered top-to-bottom. Its\n * first frame arms them all: shifted right and transparent.\n *\n * The timeline's duration is the **whole** entrance: the stagger is carved\n * out of it rather than added on top, so the command takes exactly the time\n * the timeline laid out for it.\n */\n enter: (timeline, state) => {\n const count = state.slices.length\n if (count === 0) return timeline.wait(timeline.duration)\n const from: Arrival = { opacity: 0, shift: SLIDE }\n const arm = timeline.animate(() => ({ arrivals: armed([...state.slices.keys()], from) }))\n // Each gap is a fifth of an entry's own move, the rhythm the axes use;\n // solved for from the budget rather than added to it.\n const per = timeline.duration / (1 + Math.max(0, count - 1) / 5)\n const step = per / 5\n const ease = frameEase(timeline)\n const entries = state.slices.map((_, i) => ({ at: i * step, duration: per, piece: arrival(timeline, i, from, ease) }))\n return timeline.together([arm, beats(timeline, entries)])\n },\n },\n measure: (measurer, state, children) => Flow.measure(measurer, state, children, Paint.inset(state)),\n layout: (box, state, children) => Flow.layout(box, state, children, Paint.inset(state)),\n render: (ctx, state) => Paint.draw(ctx, state, Shapes.rect(ctx.size, state)),\n})\n\nexport type PieLegend = NodeOf<typeof PieLegend>\n"]}
@@ -1,50 +0,0 @@
1
- import {
2
- Graphics2D,
3
- Types,
4
- component2d,
5
- strokeOps,
6
- type NodeOf,
7
- // Named so this component's emitted type reaches `Node2D` through the sdk, not core.
8
- type Node2D as _Node2D,
9
- type Node2DProps,
10
- type Stroke,
11
- type StrokeResolved,
12
- } from "@motionscript/sdk"
13
-
14
- import { loadPaints } from "./paints"
15
- import { DEFAULT_PLOT_AREA } from "./shared"
16
-
17
- export interface ChartBorderProps extends Node2DProps {
18
- /**
19
- * The plot's thick frame stroke. The stroke resolver's default `align` is
20
- * `'inside'`, so the whole weight sits within the layout box unless the theme
21
- * overrides it.
22
- */
23
- stroke: Stroke
24
- }
25
-
26
- /**
27
- * The thick **frame** around a cartesian chart's plot: an empty rect wearing an
28
- * inside stroke, sized from its box.
29
- *
30
- * Split out of {@link ChartCanvas} (the filled + gridded backdrop) so it can be
31
- * placed *after* the marks in the tree — the box then frames the plot **over**
32
- * them, so a line or a column running to the edge tucks cleanly under the border
33
- * rather than sitting on top of it.
34
- */
35
- export const ChartBorder = component2d({
36
- key: "chartBorder",
37
- state: {
38
- stroke: Types.custom<Stroke, StrokeResolved[]>(DEFAULT_PLOT_AREA.stroke, { resolve: strokeOps.resolve, lerp: strokeOps.lerp }),
39
- },
40
- load: (loader, state) => loadPaints(loader, { strokes: [state.stroke] }),
41
- render: (ctx, state) => {
42
- const { width, height } = ctx.size
43
- if (width <= 0 || height <= 0) return
44
- // Empty rect (no fill) with the themed stroke, so the frame sits over the
45
- // marks but the box interior stays clear.
46
- ctx.draw(new Graphics2D().rect({ x: 0, y: 0, width, height }).stroke(state.stroke))
47
- },
48
- })
49
-
50
- export type ChartBorder = NodeOf<typeof ChartBorder>
@@ -1,151 +0,0 @@
1
- import {
2
- Rect,
3
- Text,
4
- Types,
5
- component2d,
6
- insetsOps,
7
- textOps,
8
- type NodeOf,
9
- type Node2D,
10
- } from "@motionscript/sdk";
11
-
12
- import { asParts, fresh } from "../kit/entries";
13
- import { armed, arrival, arrivalOf, arrivalsEntry, beats, frameEase, type Arrival } from "../kit/motion";
14
- import {
15
- DEFAULT_X_AXIS,
16
- enterBudget,
17
- type AxisConfig,
18
- type Range,
19
- } from "../kit/shared";
20
- import { markerCaption } from "../kit/vertical-axis";
21
-
22
- /** How far a marker starts above its slot when it enters, in px. */
23
- const SLIDE = 24;
24
-
25
- /**
26
- * The horizontal axis of a {@link LineChart}: a **row of {@link Text} nodes**,
27
- * one per marker value, running left-to-right. Pure layout — the mirror of the
28
- * kit's `VerticalAxis`, including its bound (not snapshotted) marker text and
29
- * positions, and its landed resting state.
30
- *
31
- * Markers are always placed **by value** across the range, whatever they say:
32
- * `2026-03` sits three months along, not three slots along. That is what keeps
33
- * it distinct from a bar chart's bottom axis, which centres a caption on each
34
- * slot and has no scale at all — the two only look alike. What the values are
35
- * captioned *as* is the chart's to decide, in `captions`.
36
- *
37
- * `enter` staggers the markers in **left-to-right**: each starts lifted a
38
- * little above its slot and transparent, then fades and slides down into place.
39
- */
40
- export const HorizontalAxis = component2d({
41
- key: "horizontalAxis",
42
- state: {
43
- /** `[min, max]` of the horizontal scale the markers are positioned within. */
44
- range: fresh<Range>(() => [0, 1]),
45
- /** Tick values (in data units) to draw along the axis. */
46
- markers: fresh<number[]>(() => []),
47
- /**
48
- * How each marker is captioned, one per entry of `markers` and in its order.
49
- * Empty (the default), or short, captions a marker with its bare numeric
50
- * reading; a chart whose x column names months or categories writes those.
51
- */
52
- captions: fresh<string[]>(() => []),
53
- /** Marker/caption text style, grid stroke, and gutters — see {@link AxisConfig}. */
54
- axis: Types.custom<AxisConfig>(DEFAULT_X_AXIS),
55
- /** Caption drawn centred in the gutter beneath the marker row; empty (the default) hides it. */
56
- label: "",
57
- arrivals: arrivalsEntry(),
58
- },
59
- // A plain node filled its parent; one made of its markers would hug them.
60
- defaults: { width: "fill", height: "fill" },
61
- children: (state) => {
62
- const tickPad = insetsOps.resolve(state.axis.tickPadding).top;
63
- const titlePad = insetsOps.resolve(state.axis.titlePadding).top;
64
-
65
- const column: Node2D[] = [];
66
- // The column's parent is the axis: its box is what the markers are placed in.
67
- const box = () => (column[0]?.parent as Node2D | null | undefined)?.layoutBounds ?? { width: 0, height: 0 };
68
- /** The live tick values, left → right — re-read every frame, not snapshotted. */
69
- const sorted = () => [...state.markers].sort((a, b) => a - b);
70
- /** Tick `i`'s fraction across the live `range`, 0 at the left edge. */
71
- const fracAt = (value: number | undefined) => {
72
- if (value === undefined) return 0;
73
- const [min, max] = state.range;
74
- return max <= min ? 0 : (value - min) / (max - min);
75
- };
76
-
77
- const markers = state.markers.map((_, i) => {
78
- const marker: Node2D = new Text({
79
- ...textOps.styleProps(state.axis.labelStyle),
80
- text: () => {
81
- const value = sorted()[i];
82
- return value === undefined ? "" : markerCaption(state, value);
83
- },
84
- x: () => {
85
- const width = box().width;
86
- const centred = (fracAt(sorted()[i]) - 0.5) * width;
87
- // Held inside the axis's right edge, which is also the *chart's*: the
88
- // last marker is centred on the last grid line, so half of `2026-06`
89
- // — or of `3300` — would be drawn outside the node's box and outside
90
- // the rectangle the editor draws around it. Only the right edge is
91
- // held, because the left one has the vertical axis's whole gutter
92
- // beyond it, and a marker overhanging into that is inside the chart.
93
- const limit = width / 2 - marker.layoutBounds.width / 2;
94
- return Math.min(centred, limit);
95
- },
96
- opacity: () => arrivalOf(state.arrivals, i).opacity,
97
- y: () => arrivalOf(state.arrivals, i).shift,
98
- });
99
- return marker;
100
- });
101
-
102
- const children: Node2D[] = [
103
- new Rect({ width: "fill", height: "hug", padding: { top: tickPad }, children: markers }),
104
- ];
105
- if (state.label !== "") {
106
- children.push(
107
- new Text({
108
- ...textOps.styleProps(state.axis.titleStyle),
109
- text: state.label,
110
- opacity: () => arrivalOf(state.arrivals, "label").opacity,
111
- y: () => arrivalOf(state.arrivals, "label").shift,
112
- }),
113
- );
114
- }
115
-
116
- column.push(new Rect({ flow: "vertical", gap: titlePad, height: "hug", width: "fill", children }));
117
- return asParts(column);
118
- },
119
- commands: {
120
- /**
121
- * Stagger the markers in left to right, then slide the caption in after
122
- * them. Its first frame arms them all: lifted by `slide` and transparent.
123
- *
124
- * The timeline's duration is the **whole** entrance — see
125
- * `VerticalAxis.enter` and {@link enterBudget}.
126
- */
127
- enter: {
128
- args: {
129
- stagger: Types.custom<number | undefined>(undefined),
130
- slide: Types.number(SLIDE),
131
- },
132
- run: (timeline, state) => {
133
- const { stagger, slide } = timeline.args;
134
- const count = state.markers.length;
135
- const captioned = state.label !== "";
136
- const from: Arrival = { opacity: 0, shift: slide };
137
- const keys = [...state.markers.keys(), ...(captioned ? ["label"] : [])];
138
- const arm = timeline.animate(() => ({ arrivals: armed(keys, from) }));
139
- if (count === 0) return arm;
140
- const { step, per, caption } = enterBudget(timeline.duration, count, captioned, stagger);
141
- const ease = frameEase(timeline);
142
- // Left marker leads: the row is already left → right.
143
- const markers = state.markers.map((_, i) => ({ at: i * step, duration: per, piece: arrival(timeline, i, from, ease) }));
144
- const label = captioned ? [{ at: (count - 1) * step + per, duration: caption, piece: arrival(timeline, "label", from, ease) }] : [];
145
- return timeline.together([arm, beats(timeline, [...markers, ...label])]);
146
- },
147
- },
148
- },
149
- });
150
-
151
- export type HorizontalAxis = NodeOf<typeof HorizontalAxis>;