@motionscript/charts 0.1.1-alpha.1 → 0.1.1-alpha.3

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 (81) hide show
  1. package/CHANGELOG.md +23 -0
  2. package/README.md +21 -12
  3. package/dist/bar-chart/bar-chart.d.ts +113 -81
  4. package/dist/bar-chart/bar-chart.d.ts.map +1 -1
  5. package/dist/bar-chart/bar-chart.js +9 -5
  6. package/dist/bar-chart/bar-chart.js.map +1 -1
  7. package/dist/bar-chart/chart-bars.d.ts +29 -5
  8. package/dist/bar-chart/chart-bars.d.ts.map +1 -1
  9. package/dist/bar-chart/chart-bars.js +33 -8
  10. package/dist/bar-chart/chart-bars.js.map +1 -1
  11. package/dist/bar-chart/shared.d.ts +10 -4
  12. package/dist/bar-chart/shared.d.ts.map +1 -1
  13. package/dist/bar-chart/shared.js +12 -5
  14. package/dist/bar-chart/shared.js.map +1 -1
  15. package/dist/browser/chunks/chunk-FJHNBNPQ.js +2 -0
  16. package/dist/browser/chunks/chunk-FJHNBNPQ.js.map +7 -0
  17. package/dist/browser/index.js +1 -1
  18. package/dist/browser/index.js.map +3 -3
  19. package/dist/browser/kit.js +1 -1
  20. package/dist/browser/manifest.json +1 -1
  21. package/dist/kit/cartesian.d.ts +6 -4
  22. package/dist/kit/cartesian.d.ts.map +1 -1
  23. package/dist/kit/cartesian.js +5 -3
  24. package/dist/kit/cartesian.js.map +1 -1
  25. package/dist/kit/style.d.ts +344 -76
  26. package/dist/kit/style.d.ts.map +1 -1
  27. package/dist/kit/style.js +198 -101
  28. package/dist/kit/style.js.map +1 -1
  29. package/dist/line-chart/line-chart.d.ts +97 -73
  30. package/dist/line-chart/line-chart.d.ts.map +1 -1
  31. package/dist/line-chart/line-chart.js +9 -7
  32. package/dist/line-chart/line-chart.js.map +1 -1
  33. package/dist/line-chart/shared.d.ts +2 -1
  34. package/dist/line-chart/shared.d.ts.map +1 -1
  35. package/dist/line-chart/shared.js +2 -0
  36. package/dist/line-chart/shared.js.map +1 -1
  37. package/dist/pie-chart/pie-chart.d.ts +43 -49
  38. package/dist/pie-chart/pie-chart.d.ts.map +1 -1
  39. package/dist/pie-chart/pie-chart.js +2 -1
  40. package/dist/pie-chart/pie-chart.js.map +1 -1
  41. package/dist/pie-chart/shared.d.ts +15 -10
  42. package/dist/pie-chart/shared.d.ts.map +1 -1
  43. package/dist/pie-chart/shared.js +18 -20
  44. package/dist/pie-chart/shared.js.map +1 -1
  45. package/dist/radar-chart/radar-chart.d.ts +37 -49
  46. package/dist/radar-chart/radar-chart.d.ts.map +1 -1
  47. package/dist/radar-chart/radar-chart.js +10 -11
  48. package/dist/radar-chart/radar-chart.js.map +1 -1
  49. package/dist/radar-chart/radar-grid.d.ts +7 -1
  50. package/dist/radar-chart/radar-grid.d.ts.map +1 -1
  51. package/dist/radar-chart/radar-grid.js +17 -15
  52. package/dist/radar-chart/radar-grid.js.map +1 -1
  53. package/dist/radar-chart/shared.d.ts +4 -2
  54. package/dist/radar-chart/shared.d.ts.map +1 -1
  55. package/dist/radar-chart/shared.js +3 -0
  56. package/dist/radar-chart/shared.js.map +1 -1
  57. package/dist/radial-chart/radial-chart.d.ts +37 -49
  58. package/dist/radial-chart/radial-chart.d.ts.map +1 -1
  59. package/dist/radial-chart/radial-chart.js +2 -1
  60. package/dist/radial-chart/radial-chart.js.map +1 -1
  61. package/dist/radial-chart/shared.d.ts +13 -11
  62. package/dist/radial-chart/shared.d.ts.map +1 -1
  63. package/dist/radial-chart/shared.js +12 -16
  64. package/dist/radial-chart/shared.js.map +1 -1
  65. package/package.json +4 -4
  66. package/src/bar-chart/bar-chart.ts +17 -6
  67. package/src/bar-chart/chart-bars.ts +48 -9
  68. package/src/bar-chart/shared.ts +23 -5
  69. package/src/kit/cartesian.ts +10 -7
  70. package/src/kit/style.ts +216 -118
  71. package/src/line-chart/line-chart.ts +14 -8
  72. package/src/line-chart/shared.ts +3 -1
  73. package/src/pie-chart/pie-chart.ts +3 -1
  74. package/src/pie-chart/shared.ts +26 -22
  75. package/src/radar-chart/radar-chart.ts +14 -13
  76. package/src/radar-chart/radar-grid.ts +18 -13
  77. package/src/radar-chart/shared.ts +5 -2
  78. package/src/radial-chart/radial-chart.ts +3 -1
  79. package/src/radial-chart/shared.ts +26 -18
  80. package/dist/browser/chunks/chunk-R3Q5EE6E.js +0 -2
  81. package/dist/browser/chunks/chunk-R3Q5EE6E.js.map +0 -7
@@ -11,6 +11,11 @@ import {
11
11
  // Named so this component's emitted type reaches `Node2D` and its props through the sdk, not core.
12
12
  type Node2D,
13
13
  type Node2DProps as _Node2DProps,
14
+ type TextStyle as _TextStyle,
15
+ type CornerRadiusResolved as _CornerRadiusResolved,
16
+ type CornerStyleResolved as _CornerStyleResolved,
17
+ type RectCornerRadius as _RectCornerRadius,
18
+ type RectCornerStyle as _RectCornerStyle,
14
19
  type Fill as _Fill,
15
20
  type FillResolved as _FillResolved,
16
21
  type Stroke as _Stroke,
@@ -44,12 +49,14 @@ import {
44
49
  } from "../kit/shared"
45
50
  import {
46
51
  CARTESIAN_GRID,
47
- CHART_COLORS,
52
+ CARTESIAN_AXES,
48
53
  CHART_LEGEND,
54
+ CHART_SERIES,
49
55
  CHART_STYLE_FAMILY,
50
56
  CHART_STYLE_PRESETS,
51
- CHART_TEXT,
57
+ GRID_BACKGROUND,
52
58
  axisConfigOf,
59
+ migrateChartStyle,
53
60
  legendConfigOf,
54
61
  seriesFill,
55
62
  valueText,
@@ -70,7 +77,7 @@ import {
70
77
 
71
78
  const BAR_STYLE = Types.style(
72
79
  CHART_STYLE_FAMILY,
73
- { colors: CHART_COLORS, text: CHART_TEXT, grid: CARTESIAN_GRID, marks: BAR_MARKS, labels: BAR_LABELS, legend: CHART_LEGEND },
80
+ { series: CHART_SERIES, background: GRID_BACKGROUND, grid: CARTESIAN_GRID, axes: CARTESIAN_AXES, marks: BAR_MARKS, labels: BAR_LABELS, legend: CHART_LEGEND },
74
81
  { presets: CHART_STYLE_PRESETS }
75
82
  )
76
83
 
@@ -195,6 +202,7 @@ function seriesBars(s: BarState, index: number, plot: Size2D): BarGeometry[] {
195
202
  from: valueToPx(domain, along, span.from),
196
203
  to: valueToPx(domain, along, span.to),
197
204
  round: span.outer,
205
+ grounded: !stacked || span.from === 0,
198
206
  caption,
199
207
  }
200
208
  })
@@ -283,6 +291,7 @@ export const BarChart = component2d({
283
291
  weights: (state) => state.series.map((s) => ((s.enabled ?? true) ? 1 : 0)),
284
292
  showing: (state) => state.series.map((s) => s.enabled ?? true),
285
293
  },
294
+ migrate: { style: migrateChartStyle("cartesian") },
286
295
  allowedChildren: [],
287
296
  children: (state) => {
288
297
  const style = () => state.style
@@ -358,12 +367,13 @@ export const BarChart = component2d({
358
367
  baseline: () => valueToPx(domain(), runs(state, plotSize()).along, baselineValue(domain())),
359
368
  horizontal,
360
369
  fill: () => seriesFill(style(), s, i),
361
- cornerRadius: () => style().barRadius,
370
+ cornerRadius: () => style().barCornerRadius,
371
+ cornerStyle: () => style().barCornerStyle,
362
372
  growth: () => state.growth,
363
373
  dims: () => categoryDims(state),
364
374
  // A figure past a stack's end prints the stack's total; a percent stack has none worth printing, so prints its shares inside.
365
375
  values: () => (built.values === "end" && state.stack === "percent" ? "inside" : built.values),
366
- valueStyle: () => ({ ...valueText(style()), fontSize: Math.round(style().fontSize * 0.85) }),
376
+ valueStyle: () => valueText(style()),
367
377
  enabled: () => state.showing[i] ?? (s.enabled ?? true),
368
378
  }) as unknown as Node2D
369
379
  )
@@ -385,7 +395,7 @@ export const BarChart = component2d({
385
395
  plot = new Rect({
386
396
  children: [
387
397
  new ChartCanvas({
388
- fill: () => style().background,
398
+ fill: () => style().gridBackground,
389
399
  xGridStroke: () => style().verticalGrid,
390
400
  yGridStroke: () => style().horizontalGrid,
391
401
  xMarkers: horizontal ? () => domain().ticks : boundaries,
@@ -402,6 +412,7 @@ export const BarChart = component2d({
402
412
 
403
413
  return asParts([
404
414
  cartesianFrame({
415
+ background: () => style().background,
405
416
  left,
406
417
  ghost,
407
418
  plot,
@@ -11,7 +11,11 @@ import {
11
11
  // Named so this component's emitted type reaches `Node2D` and its props through the sdk, not core.
12
12
  type Node2D,
13
13
  type Node2DProps as _Node2DProps,
14
+ type RectCornerRadius as _RectCornerRadius,
15
+ type RectCornerStyle as _RectCornerStyle,
14
16
  type TextStyle,
17
+ type CornerRadiusResolved,
18
+ type CornerStyleResolved,
15
19
  } from "@motionscript/sdk"
16
20
 
17
21
  import { boundText } from "../kit/cartesian"
@@ -35,8 +39,10 @@ export interface BarGeometry {
35
39
  from: number
36
40
  /** The far end at full growth. */
37
41
  to: number
38
- /** Whether the far end takes the corner radius (the stack's last segment on its side). */
42
+ /** Whether the far end takes the far corners (the stack's last segment on its side). */
39
43
  round: boolean
44
+ /** Whether the base takes the base corners (it stands on the baseline: every grouped bar, a stack's first segment). */
45
+ grounded?: boolean
40
46
  /** The figure printed by the bar; empty for none. */
41
47
  caption: string
42
48
  }
@@ -106,7 +112,9 @@ export const ChartBars = component2d({
106
112
  /** Bars grow along x from a category column rather than along y from a row. */
107
113
  horizontal: false,
108
114
  fill: Types.custom<Fill, FillResolved[]>([], { resolve: fillOps.resolve, lerp: fillOps.lerp }),
109
- cornerRadius: 8,
115
+ /** As for a bar standing up from the baseline: `topLeft`/`topRight` round its far end, the bottom pair its base. */
116
+ cornerRadius: Types.cornerRadius(8),
117
+ cornerStyle: Types.cornerStyle("rounded"),
110
118
  /**
111
119
  * Rise of every bar in `[0, 1]` about the baseline, not each bar's own base:
112
120
  * every segment travels by the same factor, so a stack stays flush
@@ -163,17 +171,13 @@ export const ChartBars = component2d({
163
171
  const seam = bar.round ? 0 : 1
164
172
  const length = Math.abs(ends.end - ends.base) + seam
165
173
  if (length <= seam) continue
166
- const radius = bar.round ? Math.max(0, Math.min(state.cornerRadius, length, bar.thickness / 2)) : 0
167
174
  const forward = ends.end >= ends.base
168
175
  const mid = (ends.base + ends.end) / 2 + (forward ? seam : -seam) / 2
169
- // Corner keys are visual (y-up): only the far end rounds.
170
- const far = state.horizontal
171
- ? (forward ? { topRight: radius, bottomRight: radius, topLeft: 0, bottomLeft: 0 } : { topLeft: radius, bottomLeft: radius, topRight: 0, bottomRight: 0 })
172
- : (forward ? { topLeft: radius, topRight: radius, bottomLeft: 0, bottomRight: 0 } : { bottomLeft: radius, bottomRight: radius, topLeft: 0, topRight: 0 })
176
+ const corners = barCorners(state, bar, forward, length)
173
177
  // One `Graphics2D` per bar: opacity belongs to the command list, and the spotlight dims single bars.
174
178
  const g = new Graphics2D().rect(state.horizontal
175
- ? { x: mid, y: bar.center, width: length, height: bar.thickness, cornerRadius: radius > 0 ? far : 0 }
176
- : { x: bar.center, y: mid, width: bar.thickness, height: length, cornerRadius: radius > 0 ? far : 0 })
179
+ ? { x: mid, y: bar.center, width: length, height: bar.thickness, ...corners }
180
+ : { x: bar.center, y: mid, width: bar.thickness, height: length, ...corners })
177
181
  g.fill(state.fill)
178
182
  ctx.draw(g.opacity(dimAt(state, i)))
179
183
  }
@@ -184,6 +188,41 @@ export const ChartBars = component2d({
184
188
 
185
189
  export type ChartBars = NodeOf<typeof ChartBars>
186
190
 
191
+ type Corners<T> = { topLeft: T; topRight: T; bottomRight: T; bottomLeft: T }
192
+
193
+ /**
194
+ * Turns corners written for a bar standing up into the drawn bar's visual
195
+ * (y-up) corners: mirrored for a bar below zero, turned a quarter clockwise
196
+ * for one growing right, and both for one growing left.
197
+ */
198
+ function orient<T>(c: Corners<T>, horizontal: boolean, forward: boolean): Corners<T> {
199
+ if (!horizontal) return forward ? c : { topLeft: c.bottomLeft, topRight: c.bottomRight, bottomRight: c.topRight, bottomLeft: c.topLeft }
200
+ return forward
201
+ ? { topLeft: c.bottomLeft, topRight: c.topLeft, bottomRight: c.topRight, bottomLeft: c.bottomRight }
202
+ : { topLeft: c.topLeft, topRight: c.bottomLeft, bottomRight: c.bottomRight, bottomLeft: c.topRight }
203
+ }
204
+
205
+ /** Bar `bar`'s rect corners: the far pair only where its stack ends, the base pair only on the baseline, each held inside the bar. */
206
+ export function barCorners(
207
+ state: { readonly cornerRadius: CornerRadiusResolved; readonly cornerStyle: CornerStyleResolved; readonly horizontal: boolean },
208
+ bar: BarGeometry,
209
+ forward: boolean,
210
+ length: number
211
+ ): { cornerRadius: Corners<number>; cornerStyle: CornerStyleResolved } {
212
+ const grounded = bar.grounded ?? true
213
+ const r = state.cornerRadius
214
+ const ends = (bar.round ? 1 : 0) + (grounded ? 1 : 0)
215
+ const cap = Math.max(0, Math.min(bar.thickness / 2, ends > 1 ? length / 2 : length))
216
+ const fit = (v: number, on: boolean) => (on ? Math.max(0, Math.min(v, cap)) : 0)
217
+ const upright: Corners<number> = {
218
+ topLeft: fit(r.topLeft, bar.round),
219
+ topRight: fit(r.topRight, bar.round),
220
+ bottomRight: fit(r.bottomRight, grounded),
221
+ bottomLeft: fit(r.bottomLeft, grounded),
222
+ }
223
+ return { cornerRadius: orient(upright, state.horizontal, forward), cornerStyle: orient(state.cornerStyle, state.horizontal, forward) }
224
+ }
225
+
187
226
  /** A figure as the bar prints it when the chart gives no caption. */
188
227
  export function barCaption(value: number): string {
189
228
  return value === 0 ? "" : formatMarker(value)
@@ -1,6 +1,18 @@
1
- import { Types, fillOps, type DataRecord, type Fill } from "@motionscript/sdk"
1
+ import {
2
+ Types,
3
+ fillOps,
4
+ type DataRecord,
5
+ type Fill,
6
+ // Named so the emitted marks types reach these through the sdk, not core.
7
+ type CornerRadiusResolved as _CornerRadiusResolved,
8
+ type CornerStyleResolved as _CornerStyleResolved,
9
+ type RectCornerRadius as _RectCornerRadius,
10
+ type RectCornerStyle as _RectCornerStyle,
11
+ type TextStyle as _TextStyle,
12
+ } from "@motionscript/sdk"
2
13
 
3
14
  import { niceRange, num, text, type ChartSeries, type Range } from "../kit/shared"
15
+ import { VALUE_TEXT } from "../kit/style"
4
16
 
5
17
  /** One bar per row, beside its siblings in the category slot or stacked on them: {@link ChartSeries} under the name for what it draws here. */
6
18
  export type BarSeries = ChartSeries
@@ -110,12 +122,17 @@ export function inkOn(fill: Fill): string {
110
122
  return luminance > 0.4 ? "#0a0a0a" : "white"
111
123
  }
112
124
 
113
- /** A bar chart's own look: how round and how wide the bars are. */
125
+ /** A bar chart's own look: its corners, and how wide the bars are. */
114
126
  export const BAR_MARKS = {
115
- /** Radius of each bar's far corners; a stacked segment inside the stack stays square. */
116
- barRadius: Types.number(10, { min: 0, max: 64, step: 1, unit: "px" }),
127
+ /**
128
+ * Each corner as for a bar standing up from the baseline: the top pair round
129
+ * its far end, the bottom pair its base. Inside a stack, a segment's far end
130
+ * stays square unless it ends the stack, and its base unless it starts it.
131
+ */
132
+ barCornerRadius: Types.cornerRadius({ topLeft: 10, topRight: 10, bottomRight: 0, bottomLeft: 0 }),
133
+ barCornerStyle: Types.cornerStyle("rounded"),
117
134
  /** How much of its category slot a bar group fills, the rest spacing the groups. */
118
- barWidth: Types.number(0.7, { min: 0.05, max: 1, step: 0.05 }),
135
+ barWidth: Types.number(0.7, { min: 0.05, max: 1, step: 0.05, unit: "%", scale: 100 }),
119
136
  /** Space between the bars of one group, side by side. */
120
137
  barGap: Types.number(6, { min: 0, max: 40, step: 1, unit: "px" }),
121
138
  }
@@ -123,4 +140,5 @@ export const BAR_MARKS = {
123
140
  export const BAR_LABELS = {
124
141
  /** Read as the chart is built: each bar's value past its end, inside it, or none. */
125
142
  values: Types.oneOf(["none", "end", "inside"] as const, { animatable: false }),
143
+ ...VALUE_TEXT,
126
144
  }
@@ -12,24 +12,26 @@ import {
12
12
  type TextStyle,
13
13
  } from "@motionscript/sdk"
14
14
 
15
- import type { CartesianStyle, LegendPosition } from "./style"
15
+ import { sizeOf, type CartesianStyle, type LegendPosition } from "./style"
16
16
 
17
17
  /** Space between the frame's rows and columns: the plot and the legend, the axes and the legend. */
18
- export function frameGap(style: Pick<CartesianStyle, "fontSize">): number {
19
- return Math.round(style.fontSize * 0.9)
18
+ export function frameGap(style: Pick<CartesianStyle, "legendText">): number {
19
+ return Math.round(sizeOf(style.legendText) * 0.9)
20
20
  }
21
21
 
22
22
  /** Room above the plot for the top value marker, which is centred on the top grid line. */
23
- export function headroom(style: Pick<CartesianStyle, "fontSize">): number {
24
- return Math.ceil(style.fontSize * 0.7)
23
+ export function headroom(style: Pick<CartesianStyle, "yLabelText">): number {
24
+ return Math.ceil(sizeOf(style.yLabelText) * 0.7)
25
25
  }
26
26
 
27
27
  /** How far an end marker of a horizontal value axis may hang past the plot: half a short caption. */
28
- export function overhang(style: Pick<CartesianStyle, "fontSize">): number {
29
- return Math.ceil(style.fontSize * 1.1)
28
+ export function overhang(style: Pick<CartesianStyle, "xLabelText">): number {
29
+ return Math.ceil(sizeOf(style.xLabelText) * 1.1)
30
30
  }
31
31
 
32
32
  interface CartesianParts {
33
+ /** Behind the whole chart, axes and legend included. */
34
+ readonly background?: () => Fill
33
35
  /** The axis beside the plot, on its left. */
34
36
  readonly left?: Node2D
35
37
  /** An invisible twin of `left` under the plot, so the row there starts at the plot's left edge. */
@@ -72,6 +74,7 @@ export function cartesianFrame(parts: CartesianParts): Node2D {
72
74
  height: "fill",
73
75
  gap: parts.gap,
74
76
  padding: parts.padding,
77
+ ...(parts.background ? { fill: parts.background } : {}),
75
78
  children: [
76
79
  ...(legend && parts.legendAt === "top" ? [across(legend)] : []),
77
80
  main,