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

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 (124) hide show
  1. package/CHANGELOG.md +59 -0
  2. package/LICENSE +201 -201
  3. package/NOTICE +48 -0
  4. package/README.md +85 -0
  5. package/dist/bar-chart/bar-chart.d.ts +33 -19
  6. package/dist/bar-chart/bar-chart.d.ts.map +1 -1
  7. package/dist/bar-chart/bar-chart.js +68 -64
  8. package/dist/bar-chart/bar-chart.js.map +1 -1
  9. package/dist/bar-chart/category-axis.d.ts +2 -2
  10. package/dist/bar-chart/category-axis.d.ts.map +1 -1
  11. package/dist/bar-chart/category-axis.js +2 -2
  12. package/dist/bar-chart/category-axis.js.map +1 -1
  13. package/dist/bar-chart/chart-bars.d.ts +1 -1
  14. package/dist/bar-chart/chart-bars.d.ts.map +1 -1
  15. package/dist/bar-chart/chart-bars.js +2 -2
  16. package/dist/bar-chart/chart-bars.js.map +1 -1
  17. package/dist/bar-chart/index.js.map +1 -1
  18. package/dist/bar-chart/shared.d.ts +4 -4
  19. package/dist/bar-chart/shared.d.ts.map +1 -1
  20. package/dist/bar-chart/shared.js +4 -4
  21. package/dist/bar-chart/shared.js.map +1 -1
  22. package/dist/browser/chunks/chunk-5ZPBV6BU.js +2 -0
  23. package/dist/browser/chunks/chunk-5ZPBV6BU.js.map +7 -0
  24. package/dist/browser/index.js +1 -1
  25. package/dist/browser/index.js.map +3 -3
  26. package/dist/browser/kit.js +1 -1
  27. package/dist/browser/manifest.json +4 -3
  28. package/dist/engine.d.ts +1 -1
  29. package/dist/engine.d.ts.map +1 -1
  30. package/dist/engine.js +1 -1
  31. package/dist/engine.js.map +1 -1
  32. package/dist/index.js.map +1 -1
  33. package/dist/kit/chart-border.d.ts +1 -1
  34. package/dist/kit/chart-border.d.ts.map +1 -1
  35. package/dist/kit/chart-border.js +2 -2
  36. package/dist/kit/chart-border.js.map +1 -1
  37. package/dist/kit/chart-canvas.d.ts +1 -1
  38. package/dist/kit/chart-canvas.d.ts.map +1 -1
  39. package/dist/kit/chart-canvas.js +2 -2
  40. package/dist/kit/chart-canvas.js.map +1 -1
  41. package/dist/kit/chart-legend.d.ts +6 -27
  42. package/dist/kit/chart-legend.d.ts.map +1 -1
  43. package/dist/kit/chart-legend.js +33 -57
  44. package/dist/kit/chart-legend.js.map +1 -1
  45. package/dist/kit/shared.d.ts +31 -8
  46. package/dist/kit/shared.d.ts.map +1 -1
  47. package/dist/kit/shared.js +67 -11
  48. package/dist/kit/shared.js.map +1 -1
  49. package/dist/kit/vertical-axis.d.ts +2 -2
  50. package/dist/kit/vertical-axis.d.ts.map +1 -1
  51. package/dist/kit/vertical-axis.js +2 -2
  52. package/dist/kit/vertical-axis.js.map +1 -1
  53. package/dist/line-chart/chart-line.d.ts +3 -3
  54. package/dist/line-chart/chart-line.d.ts.map +1 -1
  55. package/dist/line-chart/chart-line.js +2 -2
  56. package/dist/line-chart/chart-line.js.map +1 -1
  57. package/dist/line-chart/chart-region.d.ts +1 -1
  58. package/dist/line-chart/chart-region.d.ts.map +1 -1
  59. package/dist/line-chart/chart-region.js +2 -2
  60. package/dist/line-chart/chart-region.js.map +1 -1
  61. package/dist/line-chart/horizontal-axis.d.ts +2 -2
  62. package/dist/line-chart/horizontal-axis.d.ts.map +1 -1
  63. package/dist/line-chart/horizontal-axis.js +2 -2
  64. package/dist/line-chart/horizontal-axis.js.map +1 -1
  65. package/dist/line-chart/index.js.map +1 -1
  66. package/dist/line-chart/line-chart.d.ts +25 -9
  67. package/dist/line-chart/line-chart.d.ts.map +1 -1
  68. package/dist/line-chart/line-chart.js +66 -43
  69. package/dist/line-chart/line-chart.js.map +1 -1
  70. package/dist/line-chart/shared.d.ts +4 -4
  71. package/dist/line-chart/shared.d.ts.map +1 -1
  72. package/dist/line-chart/shared.js +20 -5
  73. package/dist/line-chart/shared.js.map +1 -1
  74. package/dist/line-chart/x-scale.d.ts +2 -2
  75. package/dist/line-chart/x-scale.d.ts.map +1 -1
  76. package/dist/line-chart/x-scale.js +2 -2
  77. package/dist/line-chart/x-scale.js.map +1 -1
  78. package/dist/nodes.js.map +1 -1
  79. package/dist/pie-chart/index.js.map +1 -1
  80. package/dist/pie-chart/pie-chart.d.ts +16 -10
  81. package/dist/pie-chart/pie-chart.d.ts.map +1 -1
  82. package/dist/pie-chart/pie-chart.js +15 -16
  83. package/dist/pie-chart/pie-chart.js.map +1 -1
  84. package/dist/pie-chart/pie-legend.d.ts +2 -2
  85. package/dist/pie-chart/pie-legend.d.ts.map +1 -1
  86. package/dist/pie-chart/pie-legend.js +6 -6
  87. package/dist/pie-chart/pie-legend.js.map +1 -1
  88. package/dist/pie-chart/pie-ring.d.ts +14 -5
  89. package/dist/pie-chart/pie-ring.d.ts.map +1 -1
  90. package/dist/pie-chart/pie-ring.js +45 -15
  91. package/dist/pie-chart/pie-ring.js.map +1 -1
  92. package/dist/pie-chart/shared.d.ts +4 -4
  93. package/dist/pie-chart/shared.d.ts.map +1 -1
  94. package/dist/pie-chart/shared.js +4 -4
  95. package/dist/pie-chart/shared.js.map +1 -1
  96. package/package.json +6 -4
  97. package/registry.json +23 -23
  98. package/src/bar-chart/bar-chart.ts +820 -804
  99. package/src/bar-chart/category-axis.ts +177 -177
  100. package/src/bar-chart/chart-bars.ts +310 -310
  101. package/src/bar-chart/index.ts +31 -31
  102. package/src/bar-chart/shared.ts +354 -354
  103. package/src/engine.ts +26 -26
  104. package/src/index.ts +5 -5
  105. package/src/kit/chart-border.ts +61 -61
  106. package/src/kit/chart-canvas.ts +157 -157
  107. package/src/kit/chart-legend.ts +245 -250
  108. package/src/kit/shared.ts +733 -675
  109. package/src/kit/vertical-axis.ts +224 -224
  110. package/src/line-chart/chart-line.ts +208 -208
  111. package/src/line-chart/chart-region.ts +135 -135
  112. package/src/line-chart/horizontal-axis.ts +202 -202
  113. package/src/line-chart/index.ts +39 -39
  114. package/src/line-chart/line-chart.ts +961 -894
  115. package/src/line-chart/shared.ts +473 -459
  116. package/src/line-chart/x-scale.ts +367 -367
  117. package/src/nodes.ts +20 -20
  118. package/src/pie-chart/index.ts +37 -37
  119. package/src/pie-chart/pie-chart.ts +383 -374
  120. package/src/pie-chart/pie-legend.ts +159 -159
  121. package/src/pie-chart/pie-ring.ts +935 -903
  122. package/src/pie-chart/shared.ts +490 -490
  123. package/dist/browser/chunks/chunk-ZMBOBHYL.js +0 -2
  124. package/dist/browser/chunks/chunk-ZMBOBHYL.js.map +0 -7
@@ -1,177 +1,177 @@
1
- import {
2
- command,
3
- Node2D,
4
- Rect,
5
- Text,
6
- easeOut,
7
- property,
8
- insetsOps,
9
- textOps,
10
- type CommandArgs,
11
- type NodeConfig,
12
- type Node2DProps,
13
- } from "@motionscript/core"
14
-
15
- import {
16
- DEFAULT_X_AXIS,
17
- clampUnit,
18
- enterBudget,
19
- type AxisConfig,
20
- } from "../kit/shared"
21
- import { at, compose, hold, type Seekable } from "@motionscript/core/component"
22
-
23
- export interface CategoryAxisProps extends Node2DProps {
24
- /** One caption per category slot, left to right — the chart's rows, in order. */
25
- categories: string[]
26
- /** Caption/title text style and gutters — see {@link AxisConfig}. */
27
- axis: AxisConfig
28
- /**
29
- * Caption drawn centred in the gutter beneath the category row. Empty string
30
- * (the default) hides it.
31
- */
32
- label: string
33
- /**
34
- * Per-category opacity multiplier — how the chart's category spotlight reaches
35
- * the captions, so the name under a dimmed column dims with it. Missing
36
- * entries read as 1.
37
- */
38
- dims: number[]
39
- }
40
-
41
- /** How far a caption starts above its slot when it enters, in px. */
42
- const SLIDE = 24
43
-
44
- /**
45
- * The **categorical** horizontal axis of a {@link BarChart}: a row of
46
- * {@link Text} captions, one per row of the data, each centred on its slot
47
- * beneath the baseline.
48
- *
49
- * Not a variant of the line chart's `HorizontalAxis` and deliberately not
50
- * folded into it: that axis places markers by *value* across a numeric range,
51
- * this one divides its width into equal slots and centres a name in each. The
52
- * two look alike on screen and share nothing underneath — one reads a scale,
53
- * the other counts rows.
54
- *
55
- * The slot arithmetic is the same as the plot's, because this node is laid out
56
- * to exactly the plot's width (the chart reserves the axis gutter with an
57
- * invisible twin of the value axis, as the line chart does), so a caption lands
58
- * under the columns it names without either side being told about the other.
59
- *
60
- * {@link enter} staggers the captions in left-to-right, each lifted a little
61
- * above its slot and transparent; the resting state is the landed one, so a
62
- * chart that is never told to enter still reads as finished.
63
- */
64
- export class CategoryAxis extends Node2D<CategoryAxisProps> {
65
- @property({ default: () => [] as string[] }) declare categories: string[]
66
- @property({ default: DEFAULT_X_AXIS }) declare axis: AxisConfig
67
- @property({ default: "" }) declare label: string
68
- @property({ default: () => [] as number[] }) declare dims: number[]
69
-
70
- /** One caption per category, ordered left → right, for the staggered enter. */
71
- private captionNodes: Text[] = []
72
- /** The axis title, or null when `label` is empty. */
73
- private labelNode: Text | null = null
74
-
75
- /** Centre x of slot `i`, in the node's centred space. */
76
- private slotCenter(index: number): number {
77
- const n = this.categories.length
78
- if (n <= 0) return 0
79
- const width = this.layoutBounds.width
80
- return -width / 2 + (width / n) * (index + 0.5)
81
- }
82
-
83
- constructor(props?: NodeConfig<CategoryAxis, CategoryAxisProps>) {
84
- super(props)
85
-
86
- const tickPad = insetsOps.resolve(this.axis.tickPadding).top
87
- const titlePad = insetsOps.resolve(this.axis.titlePadding).top
88
-
89
- // Each caption sits in a hug box carrying the spotlight dim, so the
90
- // entrance can animate the text's own opacity without the two fighting over
91
- // one value — opacity multiplies down the tree, which is exactly the
92
- // composition wanted here.
93
- const slots = this.categories.map((caption, i) => {
94
- const text = new Text({
95
- ...textOps.styleProps(this.axis.labelStyle),
96
- text: caption,
97
- })
98
- this.captionNodes.push(text)
99
- return new Rect({
100
- width: "hug",
101
- height: "hug",
102
- flow: "freeform",
103
- opacity: () => clampUnit(this.dims[i] ?? 1),
104
- x: () => this.slotCenter(i),
105
- children: [text],
106
- })
107
- })
108
-
109
- const children: Node2D[] = [
110
- new Rect({
111
- width: "fill",
112
- height: "hug",
113
- padding: { top: tickPad },
114
- children: slots,
115
- }),
116
- ]
117
- if (this.label !== "") {
118
- this.labelNode = new Text({
119
- ...textOps.styleProps(this.axis.titleStyle),
120
- text: this.label,
121
- })
122
- children.push(this.labelNode)
123
- }
124
-
125
- this.add(new Rect({ flow: "vertical", gap: titlePad, height: "hug", width: "fill", children }))
126
- }
127
-
128
- /**
129
- * Primes every caption (and the title) at its pre-entrance state: lifted above
130
- * its slot and transparent. Idempotent, so the chart can arm its whole
131
- * composition before the first frame is drawn.
132
- */
133
- arm(slide = SLIDE): void {
134
- for (const caption of this.captionNodes) caption.set({ opacity: 0, y: slide })
135
- this.labelNode?.set({ opacity: 0, y: slide })
136
- }
137
-
138
- /**
139
- * Slide + fade every caption in from above, staggered left-to-right, then
140
- * bring the title down behind them.
141
- *
142
- * `args.duration` is the **whole** entrance — the stagger and the title are
143
- * carved out of it rather than added on top. See {@link enterBudget}.
144
- *
145
- * `args.data.stagger` is seconds between successive captions (derived when
146
- * omitted); `args.data.slide` is the starting upward offset in px (node space
147
- * is y-up, so a positive start slides *down* into place).
148
- */
149
- @command()
150
- enter(args: CommandArgs<{ stagger?: number; slide?: number }> & { duration: number }): Seekable {
151
- const { stagger, slide = SLIDE } = args.data ?? {}
152
- const easing = args.easing ?? easeOut()
153
- const nodes = this.captionNodes
154
- if (nodes.length === 0) return hold(args.duration)
155
- const { step, per, caption } = enterBudget(
156
- args.duration,
157
- nodes.length,
158
- this.labelNode !== null,
159
- stagger
160
- )
161
-
162
- this.arm(slide)
163
-
164
- // The stagger was `wait(i * step)` inside a generator, so its offsets only
165
- // existed while it ran. They were always `i * step`; placed, the same
166
- // entrance can be asked what it looks like partway through.
167
- const lastCaptionEnds = (nodes.length - 1) * step + per
168
- return compose([
169
- ...nodes.map((node, i) =>
170
- at(i * step, node.to({ data: { opacity: 1, y: 0 }, duration: per, easing }))
171
- ),
172
- ...(this.labelNode
173
- ? [at(lastCaptionEnds, this.labelNode.to({ data: { opacity: 1, y: 0 }, duration: caption, easing }))]
174
- : []),
175
- ])
176
- }
177
- }
1
+ import {
2
+ command,
3
+ Node2D,
4
+ Rect,
5
+ Text,
6
+ easeOut,
7
+ property,
8
+ insetsOps,
9
+ textOps,
10
+ type CommandArgs,
11
+ type NodeConfig,
12
+ type Node2DProps,
13
+ } from "@motionscript/sdk"
14
+
15
+ import {
16
+ DEFAULT_X_AXIS,
17
+ clampUnit,
18
+ enterBudget,
19
+ type AxisConfig,
20
+ } from "../kit/shared"
21
+ import { at, compose, hold, type Seekable } from "@motionscript/sdk/component"
22
+
23
+ export interface CategoryAxisProps extends Node2DProps {
24
+ /** One caption per category slot, left to right — the chart's rows, in order. */
25
+ categories: string[]
26
+ /** Caption/title text style and gutters — see {@link AxisConfig}. */
27
+ axis: AxisConfig
28
+ /**
29
+ * Caption drawn centred in the gutter beneath the category row. Empty string
30
+ * (the default) hides it.
31
+ */
32
+ label: string
33
+ /**
34
+ * Per-category opacity multiplier — how the chart's category spotlight reaches
35
+ * the captions, so the name under a dimmed column dims with it. Missing
36
+ * entries read as 1.
37
+ */
38
+ dims: number[]
39
+ }
40
+
41
+ /** How far a caption starts above its slot when it enters, in px. */
42
+ const SLIDE = 24
43
+
44
+ /**
45
+ * The **categorical** horizontal axis of a {@link BarChart}: a row of
46
+ * {@link Text} captions, one per row of the data, each centred on its slot
47
+ * beneath the baseline.
48
+ *
49
+ * Not a variant of the line chart's `HorizontalAxis` and deliberately not
50
+ * folded into it: that axis places markers by *value* across a numeric range,
51
+ * this one divides its width into equal slots and centres a name in each. The
52
+ * two look alike on screen and share nothing underneath — one reads a scale,
53
+ * the other counts rows.
54
+ *
55
+ * The slot arithmetic is the same as the plot's, because this node is laid out
56
+ * to exactly the plot's width (the chart reserves the axis gutter with an
57
+ * invisible twin of the value axis, as the line chart does), so a caption lands
58
+ * under the columns it names without either side being told about the other.
59
+ *
60
+ * {@link enter} staggers the captions in left-to-right, each lifted a little
61
+ * above its slot and transparent; the resting state is the landed one, so a
62
+ * chart that is never told to enter still reads as finished.
63
+ */
64
+ export class CategoryAxis extends Node2D<CategoryAxisProps> {
65
+ @property({ default: () => [] as string[] }) declare categories: string[]
66
+ @property({ default: DEFAULT_X_AXIS }) declare axis: AxisConfig
67
+ @property({ default: "" }) declare label: string
68
+ @property({ default: () => [] as number[] }) declare dims: number[]
69
+
70
+ /** One caption per category, ordered left → right, for the staggered enter. */
71
+ private captionNodes: Text[] = []
72
+ /** The axis title, or null when `label` is empty. */
73
+ private labelNode: Text | null = null
74
+
75
+ /** Centre x of slot `i`, in the node's centred space. */
76
+ private slotCenter(index: number): number {
77
+ const n = this.categories.length
78
+ if (n <= 0) return 0
79
+ const width = this.layoutBounds.width
80
+ return -width / 2 + (width / n) * (index + 0.5)
81
+ }
82
+
83
+ constructor(props?: NodeConfig<CategoryAxis, CategoryAxisProps>) {
84
+ super(props)
85
+
86
+ const tickPad = insetsOps.resolve(this.axis.tickPadding).top
87
+ const titlePad = insetsOps.resolve(this.axis.titlePadding).top
88
+
89
+ // Each caption sits in a hug box carrying the spotlight dim, so the
90
+ // entrance can animate the text's own opacity without the two fighting over
91
+ // one value — opacity multiplies down the tree, which is exactly the
92
+ // composition wanted here.
93
+ const slots = this.categories.map((caption, i) => {
94
+ const text = new Text({
95
+ ...textOps.styleProps(this.axis.labelStyle),
96
+ text: caption,
97
+ })
98
+ this.captionNodes.push(text)
99
+ return new Rect({
100
+ width: "hug",
101
+ height: "hug",
102
+ flow: "freeform",
103
+ opacity: () => clampUnit(this.dims[i] ?? 1),
104
+ x: () => this.slotCenter(i),
105
+ children: [text],
106
+ })
107
+ })
108
+
109
+ const children: Node2D[] = [
110
+ new Rect({
111
+ width: "fill",
112
+ height: "hug",
113
+ padding: { top: tickPad },
114
+ children: slots,
115
+ }),
116
+ ]
117
+ if (this.label !== "") {
118
+ this.labelNode = new Text({
119
+ ...textOps.styleProps(this.axis.titleStyle),
120
+ text: this.label,
121
+ })
122
+ children.push(this.labelNode)
123
+ }
124
+
125
+ this.add(new Rect({ flow: "vertical", gap: titlePad, height: "hug", width: "fill", children }))
126
+ }
127
+
128
+ /**
129
+ * Primes every caption (and the title) at its pre-entrance state: lifted above
130
+ * its slot and transparent. Idempotent, so the chart can arm its whole
131
+ * composition before the first frame is drawn.
132
+ */
133
+ arm(slide = SLIDE): void {
134
+ for (const caption of this.captionNodes) caption.set({ opacity: 0, y: slide })
135
+ this.labelNode?.set({ opacity: 0, y: slide })
136
+ }
137
+
138
+ /**
139
+ * Slide + fade every caption in from above, staggered left-to-right, then
140
+ * bring the title down behind them.
141
+ *
142
+ * `args.duration` is the **whole** entrance — the stagger and the title are
143
+ * carved out of it rather than added on top. See {@link enterBudget}.
144
+ *
145
+ * `args.data.stagger` is seconds between successive captions (derived when
146
+ * omitted); `args.data.slide` is the starting upward offset in px (node space
147
+ * is y-up, so a positive start slides *down* into place).
148
+ */
149
+ @command()
150
+ enter(args: CommandArgs<{ stagger?: number; slide?: number }> & { duration: number }): Seekable {
151
+ const { stagger, slide = SLIDE } = args.data ?? {}
152
+ const easing = args.easing ?? easeOut()
153
+ const nodes = this.captionNodes
154
+ if (nodes.length === 0) return hold(args.duration)
155
+ const { step, per, caption } = enterBudget(
156
+ args.duration,
157
+ nodes.length,
158
+ this.labelNode !== null,
159
+ stagger
160
+ )
161
+
162
+ this.arm(slide)
163
+
164
+ // The stagger was `wait(i * step)` inside a generator, so its offsets only
165
+ // existed while it ran. They were always `i * step`; placed, the same
166
+ // entrance can be asked what it looks like partway through.
167
+ const lastCaptionEnds = (nodes.length - 1) * step + per
168
+ return compose([
169
+ ...nodes.map((node, i) =>
170
+ at(i * step, node.to({ data: { opacity: 1, y: 0 }, duration: per, easing }))
171
+ ),
172
+ ...(this.labelNode
173
+ ? [at(lastCaptionEnds, this.labelNode.to({ data: { opacity: 1, y: 0 }, duration: caption, easing }))]
174
+ : []),
175
+ ])
176
+ }
177
+ }