@motionscript/charts 0.1.1-alpha.0 → 0.1.1-alpha.2

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 (207) hide show
  1. package/CHANGELOG.md +34 -0
  2. package/README.md +109 -28
  3. package/dist/bar-chart/bar-chart.d.ts +265 -39
  4. package/dist/bar-chart/bar-chart.d.ts.map +1 -1
  5. package/dist/bar-chart/bar-chart.js +219 -171
  6. package/dist/bar-chart/bar-chart.js.map +1 -1
  7. package/dist/bar-chart/category-axis.d.ts +12 -7
  8. package/dist/bar-chart/category-axis.d.ts.map +1 -1
  9. package/dist/bar-chart/category-axis.js +56 -42
  10. package/dist/bar-chart/category-axis.js.map +1 -1
  11. package/dist/bar-chart/chart-bars.d.ts +76 -52
  12. package/dist/bar-chart/chart-bars.d.ts.map +1 -1
  13. package/dist/bar-chart/chart-bars.js +105 -89
  14. package/dist/bar-chart/chart-bars.js.map +1 -1
  15. package/dist/bar-chart/index.d.ts +4 -4
  16. package/dist/bar-chart/index.d.ts.map +1 -1
  17. package/dist/bar-chart/index.js +2 -2
  18. package/dist/bar-chart/index.js.map +1 -1
  19. package/dist/bar-chart/shared.d.ts +41 -65
  20. package/dist/bar-chart/shared.d.ts.map +1 -1
  21. package/dist/bar-chart/shared.js +73 -117
  22. package/dist/bar-chart/shared.js.map +1 -1
  23. package/dist/browser/chunks/chunk-7XCRX6GE.js +2 -0
  24. package/dist/browser/chunks/chunk-7XCRX6GE.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 +5 -2
  30. package/dist/engine.d.ts.map +1 -1
  31. package/dist/engine.js +5 -2
  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 +51 -15
  38. package/dist/kit/cartesian.d.ts.map +1 -1
  39. package/dist/kit/cartesian.js +81 -29
  40. package/dist/kit/cartesian.js.map +1 -1
  41. package/dist/kit/chart-canvas.d.ts +32 -24
  42. package/dist/kit/chart-canvas.d.ts.map +1 -1
  43. package/dist/kit/chart-canvas.js +52 -46
  44. package/dist/kit/chart-canvas.js.map +1 -1
  45. package/dist/kit/chart-legend.d.ts +8 -4
  46. package/dist/kit/chart-legend.d.ts.map +1 -1
  47. package/dist/kit/chart-legend.js +30 -20
  48. package/dist/kit/chart-legend.js.map +1 -1
  49. package/dist/{line-chart → kit}/horizontal-axis.d.ts +31 -6
  50. package/dist/kit/horizontal-axis.d.ts.map +1 -0
  51. package/dist/{line-chart → kit}/horizontal-axis.js +23 -24
  52. package/dist/kit/horizontal-axis.js.map +1 -0
  53. package/dist/kit/motion.d.ts +5 -0
  54. package/dist/kit/motion.d.ts.map +1 -1
  55. package/dist/kit/motion.js +8 -0
  56. package/dist/kit/motion.js.map +1 -1
  57. package/dist/{pie-chart → kit}/pie-legend.d.ts +3 -3
  58. package/dist/kit/pie-legend.d.ts.map +1 -0
  59. package/dist/{pie-chart → kit}/pie-legend.js +11 -10
  60. package/dist/kit/pie-legend.js.map +1 -0
  61. package/dist/kit/polar.d.ts +31 -0
  62. package/dist/kit/polar.d.ts.map +1 -0
  63. package/dist/kit/polar.js +33 -0
  64. package/dist/kit/polar.js.map +1 -0
  65. package/dist/kit/shared.d.ts +11 -50
  66. package/dist/kit/shared.d.ts.map +1 -1
  67. package/dist/kit/shared.js +58 -141
  68. package/dist/kit/shared.js.map +1 -1
  69. package/dist/kit/style.d.ts +517 -0
  70. package/dist/kit/style.d.ts.map +1 -0
  71. package/dist/kit/style.js +292 -0
  72. package/dist/kit/style.js.map +1 -0
  73. package/dist/kit/vertical-axis.d.ts +22 -2
  74. package/dist/kit/vertical-axis.d.ts.map +1 -1
  75. package/dist/kit/vertical-axis.js +56 -43
  76. package/dist/kit/vertical-axis.js.map +1 -1
  77. package/dist/line-chart/chart-line.d.ts +95 -3
  78. package/dist/line-chart/chart-line.d.ts.map +1 -1
  79. package/dist/line-chart/chart-line.js +165 -53
  80. package/dist/line-chart/chart-line.js.map +1 -1
  81. package/dist/line-chart/chart-region.d.ts +1 -1
  82. package/dist/line-chart/chart-region.d.ts.map +1 -1
  83. package/dist/line-chart/chart-region.js +7 -8
  84. package/dist/line-chart/chart-region.js.map +1 -1
  85. package/dist/line-chart/index.d.ts +5 -6
  86. package/dist/line-chart/index.d.ts.map +1 -1
  87. package/dist/line-chart/index.js +3 -4
  88. package/dist/line-chart/index.js.map +1 -1
  89. package/dist/line-chart/line-chart.d.ts +281 -18
  90. package/dist/line-chart/line-chart.d.ts.map +1 -1
  91. package/dist/line-chart/line-chart.js +284 -134
  92. package/dist/line-chart/line-chart.js.map +1 -1
  93. package/dist/line-chart/shared.d.ts +51 -34
  94. package/dist/line-chart/shared.d.ts.map +1 -1
  95. package/dist/line-chart/shared.js +175 -83
  96. package/dist/line-chart/shared.js.map +1 -1
  97. package/dist/line-chart/x-scale.d.ts +2 -0
  98. package/dist/line-chart/x-scale.d.ts.map +1 -1
  99. package/dist/line-chart/x-scale.js +15 -0
  100. package/dist/line-chart/x-scale.js.map +1 -1
  101. package/dist/nodes.d.ts +3 -1
  102. package/dist/nodes.d.ts.map +1 -1
  103. package/dist/nodes.js +9 -1
  104. package/dist/nodes.js.map +1 -1
  105. package/dist/pie-chart/index.d.ts +2 -3
  106. package/dist/pie-chart/index.d.ts.map +1 -1
  107. package/dist/pie-chart/index.js +1 -2
  108. package/dist/pie-chart/index.js.map +1 -1
  109. package/dist/pie-chart/pie-chart.d.ts +114 -18
  110. package/dist/pie-chart/pie-chart.d.ts.map +1 -1
  111. package/dist/pie-chart/pie-chart.js +38 -24
  112. package/dist/pie-chart/pie-chart.js.map +1 -1
  113. package/dist/pie-chart/pie-ring.d.ts +16 -5
  114. package/dist/pie-chart/pie-ring.d.ts.map +1 -1
  115. package/dist/pie-chart/pie-ring.js +89 -55
  116. package/dist/pie-chart/pie-ring.js.map +1 -1
  117. package/dist/pie-chart/shared.d.ts +61 -62
  118. package/dist/pie-chart/shared.d.ts.map +1 -1
  119. package/dist/pie-chart/shared.js +44 -123
  120. package/dist/pie-chart/shared.js.map +1 -1
  121. package/dist/radar-chart/index.d.ts +5 -0
  122. package/dist/radar-chart/index.d.ts.map +1 -0
  123. package/dist/radar-chart/index.js +5 -0
  124. package/dist/radar-chart/index.js.map +1 -0
  125. package/dist/radar-chart/radar-chart.d.ts +212 -0
  126. package/dist/radar-chart/radar-chart.d.ts.map +1 -0
  127. package/dist/radar-chart/radar-chart.js +224 -0
  128. package/dist/radar-chart/radar-chart.js.map +1 -0
  129. package/dist/radar-chart/radar-grid.d.ts +49 -0
  130. package/dist/radar-chart/radar-grid.d.ts.map +1 -0
  131. package/dist/radar-chart/radar-grid.js +63 -0
  132. package/dist/radar-chart/radar-grid.js.map +1 -0
  133. package/dist/radar-chart/radar-series.d.ts +59 -0
  134. package/dist/radar-chart/radar-series.d.ts.map +1 -0
  135. package/dist/radar-chart/radar-series.js +71 -0
  136. package/dist/radar-chart/radar-series.js.map +1 -0
  137. package/dist/radar-chart/shared.d.ts +78 -0
  138. package/dist/radar-chart/shared.d.ts.map +1 -0
  139. package/dist/radar-chart/shared.js +129 -0
  140. package/dist/radar-chart/shared.js.map +1 -0
  141. package/dist/radial-chart/index.d.ts +6 -0
  142. package/dist/radial-chart/index.d.ts.map +1 -0
  143. package/dist/radial-chart/index.js +4 -0
  144. package/dist/radial-chart/index.js.map +1 -0
  145. package/dist/radial-chart/radial-chart.d.ts +210 -0
  146. package/dist/radial-chart/radial-chart.d.ts.map +1 -0
  147. package/dist/radial-chart/radial-chart.js +175 -0
  148. package/dist/radial-chart/radial-chart.js.map +1 -0
  149. package/dist/radial-chart/radial-rings.d.ts +121 -0
  150. package/dist/radial-chart/radial-rings.d.ts.map +1 -0
  151. package/dist/radial-chart/radial-rings.js +273 -0
  152. package/dist/radial-chart/radial-rings.js.map +1 -0
  153. package/dist/radial-chart/shared.d.ts +77 -0
  154. package/dist/radial-chart/shared.d.ts.map +1 -0
  155. package/dist/radial-chart/shared.js +78 -0
  156. package/dist/radial-chart/shared.js.map +1 -0
  157. package/package.json +4 -4
  158. package/registry.json +21 -6
  159. package/src/bar-chart/bar-chart.ts +263 -179
  160. package/src/bar-chart/category-axis.ts +46 -40
  161. package/src/bar-chart/chart-bars.ts +136 -117
  162. package/src/bar-chart/index.ts +7 -10
  163. package/src/bar-chart/shared.ts +88 -233
  164. package/src/engine.ts +5 -2
  165. package/src/index.ts +2 -0
  166. package/src/kit/cartesian.ts +121 -40
  167. package/src/kit/chart-canvas.ts +49 -54
  168. package/src/kit/chart-legend.ts +28 -20
  169. package/src/kit/horizontal-axis.ts +114 -0
  170. package/src/kit/motion.ts +7 -0
  171. package/src/{pie-chart → kit}/pie-legend.ts +12 -11
  172. package/src/kit/polar.ts +54 -0
  173. package/src/kit/shared.ts +55 -277
  174. package/src/kit/style.ts +353 -0
  175. package/src/kit/vertical-axis.ts +67 -48
  176. package/src/line-chart/chart-line.ts +176 -54
  177. package/src/line-chart/chart-region.ts +6 -10
  178. package/src/line-chart/index.ts +14 -10
  179. package/src/line-chart/line-chart.ts +353 -151
  180. package/src/line-chart/shared.ts +193 -174
  181. package/src/line-chart/x-scale.ts +13 -0
  182. package/src/nodes.ts +9 -1
  183. package/src/pie-chart/index.ts +2 -23
  184. package/src/pie-chart/pie-chart.ts +54 -35
  185. package/src/pie-chart/pie-ring.ts +92 -71
  186. package/src/pie-chart/shared.ts +80 -270
  187. package/src/radar-chart/index.ts +4 -0
  188. package/src/radar-chart/radar-chart.ts +290 -0
  189. package/src/radar-chart/radar-grid.ts +83 -0
  190. package/src/radar-chart/radar-series.ts +88 -0
  191. package/src/radar-chart/shared.ts +153 -0
  192. package/src/radial-chart/index.ts +5 -0
  193. package/src/radial-chart/radial-chart.ts +212 -0
  194. package/src/radial-chart/radial-rings.ts +328 -0
  195. package/src/radial-chart/shared.ts +126 -0
  196. package/dist/browser/chunks/chunk-4SMRCU24.js +0 -2
  197. package/dist/browser/chunks/chunk-4SMRCU24.js.map +0 -7
  198. package/dist/kit/chart-border.d.ts +0 -18
  199. package/dist/kit/chart-border.d.ts.map +0 -1
  200. package/dist/kit/chart-border.js +0 -23
  201. package/dist/kit/chart-border.js.map +0 -1
  202. package/dist/line-chart/horizontal-axis.d.ts.map +0 -1
  203. package/dist/line-chart/horizontal-axis.js.map +0 -1
  204. package/dist/pie-chart/pie-legend.d.ts.map +0 -1
  205. package/dist/pie-chart/pie-legend.js.map +0 -1
  206. package/src/kit/chart-border.ts +0 -36
  207. package/src/line-chart/horizontal-axis.ts +0 -122
@@ -1 +1 @@
1
- {"version":3,"file":"chart-canvas.js","sourceRoot":"","sources":["../../src/kit/chart-canvas.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n Graphics2D,\n Types,\n component2d,\n fillOps,\n shadowOps,\n strokeOps,\n type Fill,\n type FillResolved,\n type NodeOf,\n // Named so this component's emitted type reaches `Node2D` and its props through the sdk, not core.\n type Node2D as _Node2D,\n type Node2DProps as _Node2DProps,\n type Shadow,\n type ShadowResolved,\n type Stroke,\n type StrokeResolved,\n} from \"@motionscript/sdk\"\n\nimport { fresh } from \"./entries\"\nimport { loadPaints } from \"./paints\"\nimport {\n DEFAULT_PLOT_AREA,\n DEFAULT_X_AXIS,\n DEFAULT_Y_AXIS,\n type Range,\n} from \"./shared\"\n\nconst strokeEntry = (initial: Stroke) =>\n Types.custom<Stroke, StrokeResolved[]>(initial, { resolve: strokeOps.resolve, lerp: strokeOps.lerp })\n\n/**\n * The plot backdrop: a filled panel and a grid, sized from its box. The frame is\n * a separate {@link ChartBorder} drawn over the marks. Grid lines are placed from\n * the axis markers' data-unit positions mapped through the extents, so each lands\n * under its marker however the markers are spaced.\n */\nexport const ChartCanvas = component2d({\n key: \"chartCanvas\",\n state: {\n fill: Types.custom<Fill, FillResolved[]>(DEFAULT_PLOT_AREA.fill, { resolve: fillOps.resolve, lerp: fillOps.lerp }),\n shadow: Types.custom<Shadow, ShadowResolved[]>(DEFAULT_PLOT_AREA.shadow, { resolve: shadowOps.resolve, lerp: shadowOps.lerp }),\n xGridStroke: strokeEntry(DEFAULT_X_AXIS.lineStyle),\n yGridStroke: strokeEntry(DEFAULT_Y_AXIS.lineStyle),\n /**\n * Data-unit positions of the vertical grid lines, one per horizontal marker.\n * Positions at or past an extent edge coincide with the border and are\n * skipped. Empty for a categorical axis, which has no numeric position to rule at.\n */\n xMarkers: fresh<number[]>(() => []),\n yMarkers: fresh<number[]>(() => []),\n /** `[min, max]` of each scale, mapping the markers into the panel. */\n xExtent: fresh<Range>(() => [0, 1]),\n yExtent: fresh<Range>(() => [0, 1]),\n },\n // The grid strokes sit past the conventional slots, so they are named here.\n load: (loader, state) =>\n loadPaints(loader, { fills: [state.fill], shadows: [state.shadow], strokes: [state.xGridStroke, state.yGridStroke] }),\n render: (ctx, state) => {\n const { width: W, height: H } = ctx.size\n if (W <= 0 || H <= 0) return\n\n ctx.draw(new Graphics2D().rect({ x: 0, y: 0, width: W, height: H }).shadow(state.shadow).fill(state.fill))\n\n const [xMin, xMax] = state.xExtent\n const [yMin, yMax] = state.yExtent\n // A zero-width extent maps to 0.\n const xFrac = (v: number) => (xMax <= xMin ? 0 : (v - xMin) / (xMax - xMin))\n const yFrac = (v: number) => (yMax <= yMin ? 0 : (v - yMin) / (yMax - yMin))\n // Markers on or past the edges would sit under the border.\n const interior = (f: number) => f > 1e-6 && f < 1 - 1e-6\n\n const xStroke = state.xGridStroke[0]\n if (xStroke) {\n const xGrid = new Graphics2D()\n for (const v of state.xMarkers) {\n const f = xFrac(v)\n if (!interior(f)) continue\n xGrid.rect({ x: -W / 2 + f * W, y: 0, width: xStroke.weight, height: H })\n }\n ctx.draw(xGrid.fill(xStroke.fill))\n }\n\n const yStroke = state.yGridStroke[0]\n if (yStroke) {\n const yGrid = new Graphics2D()\n for (const v of state.yMarkers) {\n const f = yFrac(v)\n if (!interior(f)) continue\n yGrid.rect({ x: 0, y: -H / 2 + f * H, width: W, height: yStroke.weight })\n }\n ctx.draw(yGrid.fill(yStroke.fill))\n }\n },\n})\n\nexport type ChartCanvas = NodeOf<typeof ChartCanvas>\n"]}
1
+ {"version":3,"file":"chart-canvas.js","sourceRoot":"","sources":["../../src/kit/chart-canvas.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n Graphics2D,\n Types,\n component2d,\n fillOps,\n strokeOps,\n type Fill,\n type FillResolved,\n type NodeOf,\n // Named so this component's emitted type reaches `Node2D` and its props through the sdk, not core.\n type Node2D as _Node2D,\n type Node2DProps as _Node2DProps,\n type RenderContext,\n type Stroke,\n type StrokeResolved,\n type Vector2,\n} from \"@motionscript/sdk\"\n\nimport { fresh } from \"./entries\"\nimport { loadPaints } from \"./paints\"\nimport type { Range } from \"./shared\"\n\nconst strokeEntry = () => Types.custom<Stroke, StrokeResolved[]>([], { resolve: strokeOps.resolve, lerp: strokeOps.lerp })\n\n/** Where along a scale a fraction lands in a run of `length` px centred on 0. */\nfunction along(value: number, [min, max]: Range, length: number): number {\n return (max <= min ? 0 : (value - min) / (max - min) - 0.5) * length\n}\n\n/** One rule per draw: stroking a union of open paths paints nothing, and a dashed rule keeps its own phase. */\nexport function rule(ctx: RenderContext, from: Vector2, to: Vector2, stroke: StrokeResolved[], opacity = 1): void {\n ctx.draw(new Graphics2D().line({ points: [from, to] }).stroke(stroke).opacity(opacity))\n}\n\n/**\n * The plot backdrop: the panel fill, the grid and the axis rules, sized from its\n * box. Grid lines are placed from data-unit marker positions mapped through the\n * extents, so each lands under its marker however the markers are spaced; a line\n * on an edge is drawn just inside it.\n */\nexport const ChartCanvas = component2d({\n key: \"chartCanvas\",\n state: {\n fill: Types.custom<Fill, FillResolved[]>([], { resolve: fillOps.resolve, lerp: fillOps.lerp }),\n /** The vertical grid lines, one per `xMarkers` entry. */\n xGridStroke: strokeEntry(),\n /** The horizontal grid lines, one per `yMarkers` entry. */\n yGridStroke: strokeEntry(),\n xMarkers: fresh<number[]>(() => []),\n yMarkers: fresh<number[]>(() => []),\n /** `[min, max]` of each scale, mapping the markers into the panel. */\n xExtent: fresh<Range>(() => [0, 1]),\n yExtent: fresh<Range>(() => [0, 1]),\n /** The baseline and the value-axis rule. */\n axisStroke: strokeEntry(),\n /** Where the horizontal axis rule sits on the y scale, or `null` for none. */\n xAxisAt: Types.custom<number | null>(null),\n /** Where the vertical axis rule sits on the x scale, or `null` for none. */\n yAxisAt: Types.custom<number | null>(null),\n },\n load: (loader, state) => loadPaints(loader, { fills: [state.fill], strokes: [state.xGridStroke, state.yGridStroke, state.axisStroke] }),\n render: (ctx, state) => {\n const { width: W, height: H } = ctx.size\n if (W <= 0 || H <= 0) return\n if (state.fill.length > 0) ctx.draw(new Graphics2D().rect({ x: 0, y: 0, width: W, height: H }).fill(state.fill))\n\n const inside = (at: number, length: number, weight: number) => Math.max(-length / 2 + weight / 2, Math.min(length / 2 - weight / 2, at))\n const verticals = (values: readonly number[], stroke: StrokeResolved[]) => {\n const weight = stroke[0]?.weight ?? 0\n if (weight <= 0) return\n for (const v of values) {\n const x = inside(along(v, state.xExtent, W), W, weight)\n rule(ctx, { x, y: -H / 2 }, { x, y: H / 2 }, stroke)\n }\n }\n const horizontals = (values: readonly number[], stroke: StrokeResolved[]) => {\n const weight = stroke[0]?.weight ?? 0\n if (weight <= 0) return\n for (const v of values) {\n const y = inside(along(v, state.yExtent, H), H, weight)\n rule(ctx, { x: -W / 2, y }, { x: W / 2, y }, stroke)\n }\n }\n const within = ([min, max]: Range) => (v: number) => v >= Math.min(min, max) - 1e-9 && v <= Math.max(min, max) + 1e-9\n verticals(state.xMarkers.filter(within(state.xExtent)), state.xGridStroke)\n horizontals(state.yMarkers.filter(within(state.yExtent)), state.yGridStroke)\n if (state.xAxisAt !== null) horizontals([state.xAxisAt], state.axisStroke)\n if (state.yAxisAt !== null) verticals([state.yAxisAt], state.axisStroke)\n },\n})\n\nexport type ChartCanvas = NodeOf<typeof ChartCanvas>\n"]}
@@ -1,13 +1,17 @@
1
1
  import { type Fill, type NodeOf, type Alignment as _Alignment, type CornerRadiusResolved as _CornerRadiusResolved, type CornerStyleResolved as _CornerStyleResolved, type FillResolved as _FillResolved, type FlowMode as _FlowMode, type FlowResolved as _FlowResolved, type GapSize as _GapSize, type Node2D as _Node2D, type Node2DProps as _Node2DProps, type RectCornerRadius as _RectCornerRadius, type RectCornerStyle as _RectCornerStyle, type Shadow as _Shadow, type ShadowResolved as _ShadowResolved, type Stroke as _Stroke, type StrokeResolved as _StrokeResolved, type Vector2 as _Vector2 } from "@motionscript/sdk";
2
2
  import { type ChartSeries, type LegendConfig } from "./shared.js";
3
- /** A chart's legend: a horizontal row with one entry per series, each a caption and a colour swatch. This node is the row, so the parent places it. */
3
+ /**
4
+ * A chart's legend: one entry per series, a colour swatch then its caption, in
5
+ * a row (`flow: "horizontal"`) or a column (`"vertical"`, for a legend beside
6
+ * the plot). This node is the row, so the parent places it.
7
+ */
4
8
  export declare const ChartLegend: import("@motionscript/sdk").Component<"chartLegend", import("@motionscript/sdk").Merge<import("@motionscript/sdk").Box2DState, {
5
9
  readonly series: import("@motionscript/sdk").Type<ChartSeries[], ChartSeries[], false>;
6
10
  /** Swatch colour by series index, resolved by the chart; a missing entry is white. */
7
11
  readonly swatches: import("@motionscript/sdk").Type<Fill[], Fill[], false>;
8
12
  readonly legend: import("@motionscript/sdk").Type<LegendConfig, LegendConfig, false>;
9
13
  /**
10
- * How far each entry is shown, `0`-`1`, driving its width, opacity and gap
14
+ * How far each entry is shown, `0`-`1`, driving its length, opacity and gap
11
15
  * together. Entries are never added or removed: "is this node a child" is not
12
16
  * a function of time, while collapsed-but-present is.
13
17
  */
@@ -50,7 +54,7 @@ export declare const ChartLegend: import("@motionscript/sdk").Component<"chartLe
50
54
  readonly swatches: import("@motionscript/sdk").Type<Fill[], Fill[], false>;
51
55
  readonly legend: import("@motionscript/sdk").Type<LegendConfig, LegendConfig, false>;
52
56
  /**
53
- * How far each entry is shown, `0`-`1`, driving its width, opacity and gap
57
+ * How far each entry is shown, `0`-`1`, driving its length, opacity and gap
54
58
  * together. Entries are never added or removed: "is this node a child" is not
55
59
  * a function of time, while collapsed-but-present is.
56
60
  */
@@ -75,7 +79,7 @@ export declare const ChartLegend: import("@motionscript/sdk").Component<"chartLe
75
79
  readonly swatches: import("@motionscript/sdk").Type<Fill[], Fill[], false>;
76
80
  readonly legend: import("@motionscript/sdk").Type<LegendConfig, LegendConfig, false>;
77
81
  /**
78
- * How far each entry is shown, `0`-`1`, driving its width, opacity and gap
82
+ * How far each entry is shown, `0`-`1`, driving its length, opacity and gap
79
83
  * together. Entries are never added or removed: "is this node a child" is not
80
84
  * a function of time, while collapsed-but-present is.
81
85
  */
@@ -1 +1 @@
1
- {"version":3,"file":"chart-legend.d.ts","sourceRoot":"","sources":["../../src/kit/chart-legend.ts"],"names":[],"mappings":"AAAA,OAAO,EASL,KAAK,IAAI,EACT,KAAK,MAAM,EAEX,KAAK,SAAS,IAAI,UAAU,EAC5B,KAAK,oBAAoB,IAAI,qBAAqB,EAClD,KAAK,mBAAmB,IAAI,oBAAoB,EAChD,KAAK,YAAY,IAAI,aAAa,EAClC,KAAK,QAAQ,IAAI,SAAS,EAC1B,KAAK,YAAY,IAAI,aAAa,EAClC,KAAK,OAAO,IAAI,QAAQ,EACxB,KAAK,MAAM,IAAI,OAAO,EACtB,KAAK,WAAW,IAAI,YAAY,EAChC,KAAK,gBAAgB,IAAI,iBAAiB,EAC1C,KAAK,eAAe,IAAI,gBAAgB,EACxC,KAAK,MAAM,IAAI,OAAO,EACtB,KAAK,cAAc,IAAI,eAAe,EACtC,KAAK,MAAM,IAAI,OAAO,EACtB,KAAK,cAAc,IAAI,eAAe,EACtC,KAAK,OAAO,IAAI,QAAQ,EACzB,MAAM,mBAAmB,CAAA;AAI1B,OAAO,EAGL,KAAK,WAAW,EAChB,KAAK,YAAY,EAClB,MAAM,UAAU,CAAA;AA8BjB,uJAAuJ;AACvJ,eAAO,MAAM,WAAW;;IAKpB,sFAAsF;;;IAGtF;;;;OAIG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;IAPH,sFAAsF;;;IAGtF;;;;OAIG;;;;;;;;;;;;;;;;;;IAPH,sFAAsF;;;IAGtF;;;;OAIG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;2BAuEL,CAAA;AAQF,MAAM,MAAM,WAAW,GAAG,MAAM,CAAC,OAAO,WAAW,CAAC,CAAA"}
1
+ {"version":3,"file":"chart-legend.d.ts","sourceRoot":"","sources":["../../src/kit/chart-legend.ts"],"names":[],"mappings":"AAAA,OAAO,EAQL,KAAK,IAAI,EACT,KAAK,MAAM,EAEX,KAAK,SAAS,IAAI,UAAU,EAC5B,KAAK,oBAAoB,IAAI,qBAAqB,EAClD,KAAK,mBAAmB,IAAI,oBAAoB,EAChD,KAAK,YAAY,IAAI,aAAa,EAClC,KAAK,QAAQ,IAAI,SAAS,EAC1B,KAAK,YAAY,IAAI,aAAa,EAClC,KAAK,OAAO,IAAI,QAAQ,EACxB,KAAK,MAAM,IAAI,OAAO,EACtB,KAAK,WAAW,IAAI,YAAY,EAChC,KAAK,gBAAgB,IAAI,iBAAiB,EAC1C,KAAK,eAAe,IAAI,gBAAgB,EACxC,KAAK,MAAM,IAAI,OAAO,EACtB,KAAK,cAAc,IAAI,eAAe,EACtC,KAAK,MAAM,IAAI,OAAO,EACtB,KAAK,cAAc,IAAI,eAAe,EACtC,KAAK,OAAO,IAAI,QAAQ,EACzB,MAAM,mBAAmB,CAAA;AAK1B,OAAO,EAGL,KAAK,WAAW,EAChB,KAAK,YAAY,EAClB,MAAM,UAAU,CAAA;AA+BjB;;;;GAIG;AACH,eAAO,MAAM,WAAW;;IAKpB,sFAAsF;;;IAGtF;;;;OAIG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;IAPH,sFAAsF;;;IAGtF;;;;OAIG;;;;;;;;;;;;;;;;;;IAPH,sFAAsF;;;IAGtF;;;;OAIG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;2BA0EL,CAAA;AAQF,MAAM,MAAM,WAAW,GAAG,MAAM,CAAC,OAAO,WAAW,CAAC,CAAA"}
@@ -1,21 +1,24 @@
1
- import { Flow, Paint, Rect, Shapes, Text, Types, component2d, textOps, } from "@motionscript/sdk";
1
+ import { Flow, Paint, Rect, Shapes, Text, Types, component2d, } from "@motionscript/sdk";
2
+ import { boundText } from "./cartesian.js";
2
3
  import { fresh } from "./entries.js";
3
4
  import { frameEase } from "./motion.js";
4
5
  import { DEFAULT_LEGEND, SPOTLIGHT_DIM, } from "./shared.js";
5
6
  const row = { ...Paint.state, ...Flow.state, ...Shapes.corners };
6
7
  /**
7
- * One legend entry, hugging its caption and swatch; `shown` scales that width
8
- * from nothing. The width is measured from the content at any reveal, so a frame
9
- * never depends on the entry once having been fully shown (`"hug"` is not a
10
- * number to scale until something measures it).
8
+ * One legend entry, hugging its swatch and caption; `shown` scales its length
9
+ * along the legend (`along`) from nothing. The length is measured from the
10
+ * content at any reveal, so a frame never depends on the entry once having been
11
+ * fully shown.
11
12
  */
12
13
  const LegendEntry = component2d({
13
14
  key: "legendEntry",
14
- state: { ...row, shown: Types.number(1) },
15
+ state: { ...row, shown: Types.number(1), along: Types.oneOf(["horizontal", "vertical"]) },
15
16
  commands: { ...Paint.commands },
16
17
  measure: (measurer, state, children) => {
17
18
  const size = Flow.measure(measurer, state, children, Paint.inset(state));
18
- return state.shown >= 1 ? size : { ...size, width: state.shown * size.width };
19
+ if (state.shown >= 1)
20
+ return size;
21
+ return state.along === "vertical" ? { ...size, height: state.shown * size.height } : { ...size, width: state.shown * size.width };
19
22
  },
20
23
  layout: (box, state, children) => Flow.layout(box, state, children, Paint.inset(state)),
21
24
  render: (ctx, state) => Paint.draw(ctx, state, Shapes.rect(ctx.size, state)),
@@ -29,7 +32,11 @@ function revealOf(value, previous) {
29
32
  next[Number(index)] = reveal;
30
33
  return next;
31
34
  }
32
- /** A chart's legend: a horizontal row with one entry per series, each a caption and a colour swatch. This node is the row, so the parent places it. */
35
+ /**
36
+ * A chart's legend: one entry per series, a colour swatch then its caption, in
37
+ * a row (`flow: "horizontal"`) or a column (`"vertical"`, for a legend beside
38
+ * the plot). This node is the row, so the parent places it.
39
+ */
33
40
  export const ChartLegend = component2d({
34
41
  key: "chartLegend",
35
42
  state: {
@@ -39,7 +46,7 @@ export const ChartLegend = component2d({
39
46
  swatches: fresh(() => []),
40
47
  legend: Types.custom(DEFAULT_LEGEND),
41
48
  /**
42
- * How far each entry is shown, `0`-`1`, driving its width, opacity and gap
49
+ * How far each entry is shown, `0`-`1`, driving its length, opacity and gap
43
50
  * together. Entries are never added or removed: "is this node a child" is not
44
51
  * a function of time, while collapsed-but-present is.
45
52
  */
@@ -50,33 +57,36 @@ export const ChartLegend = component2d({
50
57
  flow: "horizontal",
51
58
  width: "hug",
52
59
  height: "hug",
53
- gap: (state) => state.legend.itemGap,
60
+ gap: (state) => (state.flow === "vertical" ? Math.round(state.legend.itemGap * 0.4) : state.legend.itemGap),
54
61
  padding: (state) => state.legend.padding,
62
+ align: (state) => (state.flow === "vertical" ? "centerLeft" : "center"),
55
63
  reveal: (state) => state.series.map((s) => ((s.enabled ?? true) ? 1 : 0)),
56
64
  },
57
65
  // Every entry stays a child, so one always comes back where it belongs.
58
66
  children: (state) => {
59
- const marker = state.legend.markerStyle;
67
+ const marker = () => state.legend.markerStyle;
68
+ const along = state.flow === "vertical" ? "vertical" : "horizontal";
60
69
  return state.series.map((series, index) => LegendEntry({
61
70
  flow: "horizontal",
62
- gap: 12,
71
+ gap: () => Math.round((state.legend.textStyle.fontSize ?? 24) * 0.45),
63
72
  align: "center",
64
73
  width: "hug",
65
74
  height: "hug",
66
- // The width follows the reveal in `measure`, and the flanking gap closes with the entry.
75
+ along,
76
+ // The length follows the reveal in `measure`, and the flanking gap closes with the entry.
67
77
  opacity: () => state.reveal[index] ?? 0,
68
78
  gapScale: () => state.reveal[index] ?? 0,
69
79
  shown: () => state.reveal[index] ?? 0,
70
80
  children: [
71
- Text({ ...textOps.styleProps(state.legend.textStyle), text: series.label }),
72
81
  Rect({
73
- width: marker.width,
74
- height: marker.height,
75
- cornerRadius: marker.borderRadius,
76
- fill: state.swatches[index] ?? "white",
77
- stroke: marker.stroke,
78
- shadow: marker.shadow,
82
+ width: () => marker().width,
83
+ height: () => marker().height,
84
+ cornerRadius: () => marker().borderRadius,
85
+ fill: () => state.swatches[index] ?? "white",
86
+ stroke: () => marker().stroke,
87
+ shadow: () => marker().shadow,
79
88
  }),
89
+ Text({ ...boundText(() => state.legend.textStyle), text: series.label || series.field }),
80
90
  ],
81
91
  }));
82
92
  },
@@ -1 +1 @@
1
- {"version":3,"file":"chart-legend.js","sourceRoot":"","sources":["../../src/kit/chart-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 Fill,\n type NodeOf,\n // Named so the row'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 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 \"./entries\"\nimport { frameEase } from \"./motion\"\nimport {\n DEFAULT_LEGEND,\n SPOTLIGHT_DIM,\n type ChartSeries,\n type LegendConfig,\n} from \"./shared\"\n\nconst row = { ...Paint.state, ...Flow.state, ...Shapes.corners }\n\n/**\n * One legend entry, hugging its caption and swatch; `shown` scales that width\n * from nothing. The width is measured from the content at any reveal, so a frame\n * never depends on the entry once having been fully shown (`\"hug\"` is not a\n * number to scale until something measures it).\n */\nconst LegendEntry = component2d({\n key: \"legendEntry\",\n state: { ...row, shown: Types.number(1) },\n commands: { ...Paint.commands },\n measure: (measurer, state, children) => {\n const size = Flow.measure(measurer, state, children, Paint.inset(state))\n return state.shown >= 1 ? size : { ...size, width: state.shown * size.width }\n },\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\n/** Written whole, or one entry at a time as `{ [index]: reveal }`, which leaves the others as the frame left them so two series can move at once. */\nfunction revealOf(value: readonly number[] | Readonly<Record<number, number>>, previous?: number[]): number[] {\n if (Array.isArray(value)) return value as number[]\n const next = [...(previous ?? [])]\n for (const [index, reveal] of Object.entries(value)) next[Number(index)] = reveal\n return next\n}\n\n/** A chart's legend: a horizontal row with one entry per series, each a caption and a colour swatch. This node is the row, so the parent places it. */\nexport const ChartLegend = component2d({\n key: \"chartLegend\",\n state: {\n ...row,\n series: fresh<ChartSeries[]>(() => []),\n /** Swatch colour by series index, resolved by the chart; a missing entry is white. */\n swatches: fresh<Fill[]>(() => []),\n legend: Types.custom<LegendConfig>(DEFAULT_LEGEND),\n /**\n * How far each entry is shown, `0`-`1`, driving its width, opacity and gap\n * together. Entries are never added or removed: \"is this node a child\" is not\n * a function of time, while collapsed-but-present is.\n */\n reveal: fresh<number[], number[] | Readonly<Record<number, number>>>(() => [], { resolve: revealOf }),\n },\n // The flow must be stated: a `Rect` with none lays children out `freeform`, stacking every entry on one spot.\n defaults: {\n flow: \"horizontal\",\n width: \"hug\",\n height: \"hug\",\n gap: (state) => state.legend.itemGap,\n padding: (state) => state.legend.padding,\n reveal: (state) => state.series.map((s) => ((s.enabled ?? true) ? 1 : 0)),\n },\n // Every entry stays a child, so one always comes back where it belongs.\n children: (state) => {\n const marker = state.legend.markerStyle\n return state.series.map((series, index) =>\n LegendEntry({\n flow: \"horizontal\",\n gap: 12,\n align: \"center\",\n width: \"hug\",\n height: \"hug\",\n // The width follows the reveal in `measure`, and the flanking gap closes with the entry.\n opacity: () => state.reveal[index] ?? 0,\n gapScale: () => state.reveal[index] ?? 0,\n shown: () => state.reveal[index] ?? 0,\n children: [\n Text({ ...textOps.styleProps(state.legend.textStyle), text: series.label }),\n Rect({\n width: marker.width,\n height: marker.height,\n cornerRadius: marker.borderRadius,\n fill: state.swatches[index] ?? \"white\",\n stroke: marker.stroke,\n shadow: marker.shadow,\n }),\n ],\n })\n )\n },\n commands: {\n ...Paint.commands,\n /** Grow series `index`'s entry back in at its position, the siblings sliding over. */\n showSeries: {\n args: { index: Types.number(0, { min: 0, step: 1 }) },\n // An index that is not a series takes its slot and moves nothing.\n run: (timeline, state) => (timeline.args.index in state.series\n ? timeline.animate(revealFrame(state, timeline.args.index, 1, frameEase(timeline)))\n : timeline.wait(timeline.duration)),\n },\n /** The mirror of `showSeries`. */\n hideSeries: {\n args: { index: Types.number(0, { min: 0, step: 1 }) },\n run: (timeline, state) => (timeline.args.index in state.series\n ? timeline.animate(revealFrame(state, timeline.args.index, 0, frameEase(timeline)))\n : timeline.wait(timeline.duration)),\n },\n /** Dim every entry but `index` to {@link SPOTLIGHT_DIM}, in place. A hidden series' entry is left as `hideSeries` left it. */\n spotlightSeries: {\n args: { index: Types.number(0, { min: 0, step: 1 }) },\n run: (timeline, state, children) =>\n timeline.together(\n children.flatMap((entry, i) =>\n (state.reveal[i] ?? 0) > 0 ? [entry.to({ opacity: i === timeline.args.index ? 1 : SPOTLIGHT_DIM })] : []\n )\n ),\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\n/** One entry's reveal ramping to `target` from where the command finds it, so a hide after a show starts where the show left it. */\nfunction revealFrame(state: { readonly reveal: readonly number[] }, index: number, target: number, ease: (t: number) => number) {\n const from = state.reveal[index] ?? 0\n return (t: number) => ({ reveal: { [index]: from + (target - from) * ease(t) } })\n}\n\nexport type ChartLegend = NodeOf<typeof ChartLegend>\n"]}
1
+ {"version":3,"file":"chart-legend.js","sourceRoot":"","sources":["../../src/kit/chart-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 type Fill,\n type NodeOf,\n // Named so the row'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 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 { boundText } from \"./cartesian\"\nimport { fresh } from \"./entries\"\nimport { frameEase } from \"./motion\"\nimport {\n DEFAULT_LEGEND,\n SPOTLIGHT_DIM,\n type ChartSeries,\n type LegendConfig,\n} from \"./shared\"\n\nconst row = { ...Paint.state, ...Flow.state, ...Shapes.corners }\n\n/**\n * One legend entry, hugging its swatch and caption; `shown` scales its length\n * along the legend (`along`) from nothing. The length is measured from the\n * content at any reveal, so a frame never depends on the entry once having been\n * fully shown.\n */\nconst LegendEntry = component2d({\n key: \"legendEntry\",\n state: { ...row, shown: Types.number(1), along: Types.oneOf([\"horizontal\", \"vertical\"] as const) },\n commands: { ...Paint.commands },\n measure: (measurer, state, children) => {\n const size = Flow.measure(measurer, state, children, Paint.inset(state))\n if (state.shown >= 1) return size\n return state.along === \"vertical\" ? { ...size, height: state.shown * size.height } : { ...size, width: state.shown * size.width }\n },\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\n/** Written whole, or one entry at a time as `{ [index]: reveal }`, which leaves the others as the frame left them so two series can move at once. */\nfunction revealOf(value: readonly number[] | Readonly<Record<number, number>>, previous?: number[]): number[] {\n if (Array.isArray(value)) return value as number[]\n const next = [...(previous ?? [])]\n for (const [index, reveal] of Object.entries(value)) next[Number(index)] = reveal\n return next\n}\n\n/**\n * A chart's legend: one entry per series, a colour swatch then its caption, in\n * a row (`flow: \"horizontal\"`) or a column (`\"vertical\"`, for a legend beside\n * the plot). This node is the row, so the parent places it.\n */\nexport const ChartLegend = component2d({\n key: \"chartLegend\",\n state: {\n ...row,\n series: fresh<ChartSeries[]>(() => []),\n /** Swatch colour by series index, resolved by the chart; a missing entry is white. */\n swatches: fresh<Fill[]>(() => []),\n legend: Types.custom<LegendConfig>(DEFAULT_LEGEND),\n /**\n * How far each entry is shown, `0`-`1`, driving its length, opacity and gap\n * together. Entries are never added or removed: \"is this node a child\" is not\n * a function of time, while collapsed-but-present is.\n */\n reveal: fresh<number[], number[] | Readonly<Record<number, number>>>(() => [], { resolve: revealOf }),\n },\n // The flow must be stated: a `Rect` with none lays children out `freeform`, stacking every entry on one spot.\n defaults: {\n flow: \"horizontal\",\n width: \"hug\",\n height: \"hug\",\n gap: (state) => (state.flow === \"vertical\" ? Math.round(state.legend.itemGap * 0.4) : state.legend.itemGap),\n padding: (state) => state.legend.padding,\n align: (state) => (state.flow === \"vertical\" ? \"centerLeft\" : \"center\"),\n reveal: (state) => state.series.map((s) => ((s.enabled ?? true) ? 1 : 0)),\n },\n // Every entry stays a child, so one always comes back where it belongs.\n children: (state) => {\n const marker = () => state.legend.markerStyle\n const along = state.flow === \"vertical\" ? \"vertical\" : \"horizontal\"\n return state.series.map((series, index) =>\n LegendEntry({\n flow: \"horizontal\",\n gap: () => Math.round(((state.legend.textStyle.fontSize as number | undefined) ?? 24) * 0.45),\n align: \"center\",\n width: \"hug\",\n height: \"hug\",\n along,\n // The length follows the reveal in `measure`, and the flanking gap closes with the entry.\n opacity: () => state.reveal[index] ?? 0,\n gapScale: () => state.reveal[index] ?? 0,\n shown: () => state.reveal[index] ?? 0,\n children: [\n Rect({\n width: () => marker().width,\n height: () => marker().height,\n cornerRadius: () => marker().borderRadius,\n fill: () => state.swatches[index] ?? \"white\",\n stroke: () => marker().stroke,\n shadow: () => marker().shadow,\n }),\n Text({ ...boundText(() => state.legend.textStyle), text: series.label || series.field }),\n ],\n })\n )\n },\n commands: {\n ...Paint.commands,\n /** Grow series `index`'s entry back in at its position, the siblings sliding over. */\n showSeries: {\n args: { index: Types.number(0, { min: 0, step: 1 }) },\n // An index that is not a series takes its slot and moves nothing.\n run: (timeline, state) => (timeline.args.index in state.series\n ? timeline.animate(revealFrame(state, timeline.args.index, 1, frameEase(timeline)))\n : timeline.wait(timeline.duration)),\n },\n /** The mirror of `showSeries`. */\n hideSeries: {\n args: { index: Types.number(0, { min: 0, step: 1 }) },\n run: (timeline, state) => (timeline.args.index in state.series\n ? timeline.animate(revealFrame(state, timeline.args.index, 0, frameEase(timeline)))\n : timeline.wait(timeline.duration)),\n },\n /** Dim every entry but `index` to {@link SPOTLIGHT_DIM}, in place. A hidden series' entry is left as `hideSeries` left it. */\n spotlightSeries: {\n args: { index: Types.number(0, { min: 0, step: 1 }) },\n run: (timeline, state, children) =>\n timeline.together(\n children.flatMap((entry, i) =>\n (state.reveal[i] ?? 0) > 0 ? [entry.to({ opacity: i === timeline.args.index ? 1 : SPOTLIGHT_DIM })] : []\n )\n ),\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\n/** One entry's reveal ramping to `target` from where the command finds it, so a hide after a show starts where the show left it. */\nfunction revealFrame(state: { readonly reveal: readonly number[] }, index: number, target: number, ease: (t: number) => number) {\n const from = state.reveal[index] ?? 0\n return (t: number) => ({ reveal: { [index]: from + (target - from) * ease(t) } })\n}\n\nexport type ChartLegend = NodeOf<typeof ChartLegend>\n"]}
@@ -1,11 +1,12 @@
1
1
  import { type NodeOf, type Node2D, type Node2DProps as _Node2DProps } from "@motionscript/sdk";
2
- import { type Arrival } from "../kit/motion.js";
3
- import { type AxisConfig, type Range } from "../kit/shared.js";
2
+ import { type Arrival } from "./motion.js";
3
+ import { type AxisConfig, type Range } from "./shared.js";
4
4
  /**
5
- * The horizontal axis of a {@link LineChart}: a row of {@link Text} markers, the
6
- * mirror of `VerticalAxis` (bound text and positions, landed resting state).
7
- * Markers are placed by value across the range, whatever they say: `2026-03`
8
- * sits three months along, not three slots along, unlike a bar chart's axis.
5
+ * A row of {@link Text} markers under the plot, the mirror of `VerticalAxis`
6
+ * (bound text and positions, landed resting state). Markers are placed by value
7
+ * across the range, whatever they say: `2026-03` sits three months along, not
8
+ * three slots along, unlike a bar chart's category axis. An end marker may hang
9
+ * past the row by `overhang` px on either side, then is held inside it.
9
10
  */
10
11
  export declare const HorizontalAxis: import("@motionscript/sdk").Component<"horizontalAxis", import("@motionscript/sdk").Merge<import("@motionscript/sdk").Box2DState, {
11
12
  readonly range: import("@motionscript/sdk").Type<Range, Range, false>;
@@ -16,6 +17,14 @@ export declare const HorizontalAxis: import("@motionscript/sdk").Component<"hori
16
17
  readonly axis: import("@motionscript/sdk").Type<AxisConfig, AxisConfig, false>;
17
18
  /** Caption centred beneath the markers; empty hides it. */
18
19
  readonly label: "";
20
+ /** How far past the row's left and right ends a marker may reach, in px. */
21
+ readonly overhang: import("@motionscript/sdk").Type<{
22
+ left: number;
23
+ right: number;
24
+ }, {
25
+ left: number;
26
+ right: number;
27
+ }, false>;
19
28
  readonly arrivals: import("@motionscript/sdk").Type<Readonly<Record<string, Arrival>>, Readonly<Record<string, Arrival>>, false>;
20
29
  }>, {
21
30
  readonly enter: {
@@ -31,6 +40,14 @@ export declare const HorizontalAxis: import("@motionscript/sdk").Component<"hori
31
40
  readonly axis: import("@motionscript/sdk").Type<AxisConfig, AxisConfig, false>;
32
41
  /** Caption centred beneath the markers; empty hides it. */
33
42
  readonly label: "";
43
+ /** How far past the row's left and right ends a marker may reach, in px. */
44
+ readonly overhang: import("@motionscript/sdk").Type<{
45
+ left: number;
46
+ right: number;
47
+ }, {
48
+ left: number;
49
+ right: number;
50
+ }, false>;
34
51
  readonly arrivals: import("@motionscript/sdk").Type<Readonly<Record<string, Arrival>>, Readonly<Record<string, Arrival>>, false>;
35
52
  }>, null, import("@motionscript/sdk").BuiltNode<import("@motionscript/sdk").Merge<import("@motionscript/sdk").Box2DState, {
36
53
  readonly range: import("@motionscript/sdk").Type<Range, Range, false>;
@@ -41,6 +58,14 @@ export declare const HorizontalAxis: import("@motionscript/sdk").Component<"hori
41
58
  readonly axis: import("@motionscript/sdk").Type<AxisConfig, AxisConfig, false>;
42
59
  /** Caption centred beneath the markers; empty hides it. */
43
60
  readonly label: "";
61
+ /** How far past the row's left and right ends a marker may reach, in px. */
62
+ readonly overhang: import("@motionscript/sdk").Type<{
63
+ left: number;
64
+ right: number;
65
+ }, {
66
+ left: number;
67
+ right: number;
68
+ }, false>;
44
69
  readonly arrivals: import("@motionscript/sdk").Type<Readonly<Record<string, Arrival>>, Readonly<Record<string, Arrival>>, false>;
45
70
  }>, {
46
71
  readonly enter: {
@@ -0,0 +1 @@
1
+ {"version":3,"file":"horizontal-axis.d.ts","sourceRoot":"","sources":["../../src/kit/horizontal-axis.ts"],"names":[],"mappings":"AAAA,OAAO,EAKL,KAAK,MAAM,EAEX,KAAK,MAAM,EACX,KAAK,WAAW,IAAI,YAAY,EACjC,MAAM,mBAAmB,CAAA;AAK1B,OAAO,EAA8D,KAAK,OAAO,EAAE,MAAM,UAAU,CAAA;AACnG,OAAO,EAAkB,KAAK,UAAU,EAAE,KAAK,KAAK,EAAE,MAAM,UAAU,CAAA;AAKtE;;;;;;GAMG;AACH,eAAO,MAAM,cAAc;;IAIvB,iCAAiC;;IAEjC,0FAA0F;;;IAG1F,2DAA2D;;IAE3D,4EAA4E;;cAC7C,MAAM;eAAS,MAAM;;cAArB,MAAM;eAAS,MAAM;;;;;;;;;;IARpD,iCAAiC;;IAEjC,0FAA0F;;;IAG1F,2DAA2D;;IAE3D,4EAA4E;;cAC7C,MAAM;eAAS,MAAM;;cAArB,MAAM;eAAS,MAAM;;;;;IARpD,iCAAiC;;IAEjC,0FAA0F;;;IAG1F,2DAA2D;;IAE3D,4EAA4E;;cAC7C,MAAM;eAAS,MAAM;;cAArB,MAAM;eAAS,MAAM;;;;;;;;0BAwEtD,CAAA;AAEF,MAAM,MAAM,cAAc,GAAG,MAAM,CAAC,OAAO,cAAc,CAAC,CAAA"}
@@ -1,15 +1,17 @@
1
- import { Rect, Text, Types, component2d, insetsOps, textOps, } from "@motionscript/sdk";
1
+ import { Rect, Text, Types, component2d, } from "@motionscript/sdk";
2
2
  import { enterBudget } from "@motionscript/sdk/component";
3
- import { asParts, fresh } from "../kit/entries.js";
4
- import { armed, arrival, arrivalOf, arrivalsEntry, beats, frameEase } from "../kit/motion.js";
5
- import { DEFAULT_X_AXIS, } from "../kit/shared.js";
6
- import { markerCaption } from "../kit/vertical-axis.js";
3
+ import { boundText } from "./cartesian.js";
4
+ import { asParts, fresh } from "./entries.js";
5
+ import { armed, arrival, arrivalOf, arrivalsEntry, beats, frameEase } from "./motion.js";
6
+ import { DEFAULT_X_AXIS } from "./shared.js";
7
+ import { axisPads, markerCaption } from "./vertical-axis.js";
7
8
  const SLIDE = 24;
8
9
  /**
9
- * The horizontal axis of a {@link LineChart}: a row of {@link Text} markers, the
10
- * mirror of `VerticalAxis` (bound text and positions, landed resting state).
11
- * Markers are placed by value across the range, whatever they say: `2026-03`
12
- * sits three months along, not three slots along, unlike a bar chart's axis.
10
+ * A row of {@link Text} markers under the plot, the mirror of `VerticalAxis`
11
+ * (bound text and positions, landed resting state). Markers are placed by value
12
+ * across the range, whatever they say: `2026-03` sits three months along, not
13
+ * three slots along, unlike a bar chart's category axis. An end marker may hang
14
+ * past the row by `overhang` px on either side, then is held inside it.
13
15
  */
14
16
  export const HorizontalAxis = component2d({
15
17
  key: "horizontalAxis",
@@ -22,16 +24,14 @@ export const HorizontalAxis = component2d({
22
24
  axis: Types.custom(DEFAULT_X_AXIS),
23
25
  /** Caption centred beneath the markers; empty hides it. */
24
26
  label: "",
27
+ /** How far past the row's left and right ends a marker may reach, in px. */
28
+ overhang: Types.custom({ left: 0, right: 0 }),
25
29
  arrivals: arrivalsEntry(),
26
30
  },
27
31
  // A node made of markers would otherwise hug them.
28
32
  defaults: { width: "fill", height: "fill" },
29
33
  children: (state) => {
30
- const tickPad = insetsOps.resolve(state.axis.tickPadding).top;
31
- const titlePad = insetsOps.resolve(state.axis.titlePadding).top;
32
- const column = [];
33
- // The column's parent is the axis, whose box the markers are placed in.
34
- const box = () => column[0]?.parent?.layoutBounds ?? { width: 0, height: 0 };
34
+ const pads = () => axisPads(state.axis, "top");
35
35
  const sorted = () => [...state.markers].sort((a, b) => a - b);
36
36
  const fracAt = (value) => {
37
37
  if (value === undefined)
@@ -40,18 +40,18 @@ export const HorizontalAxis = component2d({
40
40
  return max <= min ? 0 : (value - min) / (max - min);
41
41
  };
42
42
  const markers = state.markers.map((_, i) => {
43
+ const width = () => marker.parent?.layoutBounds.width ?? 0;
43
44
  const marker = new Text({
44
- ...textOps.styleProps(state.axis.labelStyle),
45
+ ...boundText(() => state.axis.labelStyle),
45
46
  text: () => {
46
47
  const value = sorted()[i];
47
48
  return value === undefined ? "" : markerCaption(state, value);
48
49
  },
49
50
  x: () => {
50
- const width = box().width;
51
- const centred = (fracAt(sorted()[i]) - 0.5) * width;
52
- // Held inside the right edge, the chart's own: the last marker is centred on the last grid line, so half of it would fall outside the box. The left edge has the vertical axis's gutter beyond it.
53
- const limit = width / 2 - marker.layoutBounds.width / 2;
54
- return Math.min(centred, limit);
51
+ const w = width();
52
+ const half = marker.layoutBounds.width / 2;
53
+ const centred = (fracAt(sorted()[i]) - 0.5) * w;
54
+ return Math.max(-w / 2 - state.overhang.left + half, Math.min(w / 2 + state.overhang.right - half, centred));
55
55
  },
56
56
  opacity: () => arrivalOf(state.arrivals, i).opacity,
57
57
  y: () => arrivalOf(state.arrivals, i).shift,
@@ -59,18 +59,17 @@ export const HorizontalAxis = component2d({
59
59
  return marker;
60
60
  });
61
61
  const children = [
62
- new Rect({ width: "fill", height: "hug", padding: { top: tickPad }, children: markers }),
62
+ new Rect({ width: "fill", height: "hug", padding: () => ({ top: pads().tick }), children: markers }),
63
63
  ];
64
64
  if (state.label !== "") {
65
65
  children.push(new Text({
66
- ...textOps.styleProps(state.axis.titleStyle),
66
+ ...boundText(() => state.axis.titleStyle),
67
67
  text: state.label,
68
68
  opacity: () => arrivalOf(state.arrivals, "label").opacity,
69
69
  y: () => arrivalOf(state.arrivals, "label").shift,
70
70
  }));
71
71
  }
72
- column.push(new Rect({ flow: "vertical", gap: titlePad, height: "hug", width: "fill", children }));
73
- return asParts(column);
72
+ return asParts([new Rect({ flow: "vertical", gap: () => pads().title, height: "hug", width: "fill", children })]);
74
73
  },
75
74
  commands: {
76
75
  /** Stagger markers in left to right, then the caption. The duration is the whole entrance ({@link enterBudget}). */
@@ -0,0 +1 @@
1
+ {"version":3,"file":"horizontal-axis.js","sourceRoot":"","sources":["../../src/kit/horizontal-axis.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n Rect,\n Text,\n Types,\n component2d,\n type NodeOf,\n // Named so this component's emitted type reaches `Node2D` and its props through the sdk, not core.\n type Node2D,\n type Node2DProps as _Node2DProps,\n} from \"@motionscript/sdk\"\nimport { enterBudget } from \"@motionscript/sdk/component\"\n\nimport { boundText } from \"./cartesian\"\nimport { asParts, fresh } from \"./entries\"\nimport { armed, arrival, arrivalOf, arrivalsEntry, beats, frameEase, type Arrival } from \"./motion\"\nimport { DEFAULT_X_AXIS, type AxisConfig, type Range } from \"./shared\"\nimport { axisPads, markerCaption } from \"./vertical-axis\"\n\nconst SLIDE = 24\n\n/**\n * A row of {@link Text} markers under the plot, the mirror of `VerticalAxis`\n * (bound text and positions, landed resting state). Markers are placed by value\n * across the range, whatever they say: `2026-03` sits three months along, not\n * three slots along, unlike a bar chart's category axis. An end marker may hang\n * past the row by `overhang` px on either side, then is held inside it.\n */\nexport const HorizontalAxis = component2d({\n key: \"horizontalAxis\",\n state: {\n range: fresh<Range>(() => [0, 1]),\n /** Tick values in data units. */\n markers: fresh<number[]>(() => []),\n /** One caption per marker in its order; a missing one reads as the bare numeric value. */\n captions: fresh<string[]>(() => []),\n axis: Types.custom<AxisConfig>(DEFAULT_X_AXIS),\n /** Caption centred beneath the markers; empty hides it. */\n label: \"\",\n /** How far past the row's left and right ends a marker may reach, in px. */\n overhang: Types.custom<{ left: number; right: number }>({ left: 0, right: 0 }),\n arrivals: arrivalsEntry(),\n },\n // A node made of markers would otherwise hug them.\n defaults: { width: \"fill\", height: \"fill\" },\n children: (state) => {\n const pads = () => axisPads(state.axis, \"top\")\n const sorted = () => [...state.markers].sort((a, b) => a - b)\n const fracAt = (value: number | undefined) => {\n if (value === undefined) return 0\n const [min, max] = state.range\n return max <= min ? 0 : (value - min) / (max - min)\n }\n\n const markers = state.markers.map((_, i) => {\n const width = () => (marker.parent as Node2D | null)?.layoutBounds.width ?? 0\n const marker: Node2D = new Text({\n ...boundText(() => state.axis.labelStyle),\n text: () => {\n const value = sorted()[i]\n return value === undefined ? \"\" : markerCaption(state, value)\n },\n x: () => {\n const w = width()\n const half = marker.layoutBounds.width / 2\n const centred = (fracAt(sorted()[i]) - 0.5) * w\n return Math.max(-w / 2 - state.overhang.left + half, Math.min(w / 2 + state.overhang.right - half, centred))\n },\n opacity: () => arrivalOf(state.arrivals, i).opacity,\n y: () => arrivalOf(state.arrivals, i).shift,\n })\n return marker\n })\n\n const children: Node2D[] = [\n new Rect({ width: \"fill\", height: \"hug\", padding: () => ({ top: pads().tick }), children: markers }) as unknown as Node2D,\n ]\n if (state.label !== \"\") {\n children.push(\n new Text({\n ...boundText(() => state.axis.titleStyle),\n text: state.label,\n opacity: () => arrivalOf(state.arrivals, \"label\").opacity,\n y: () => arrivalOf(state.arrivals, \"label\").shift,\n }) as unknown as Node2D\n )\n }\n return asParts([new Rect({ flow: \"vertical\", gap: () => pads().title, height: \"hug\", width: \"fill\", children }) as unknown as Node2D])\n },\n commands: {\n /** Stagger markers in left to right, then the caption. The duration is the whole entrance ({@link enterBudget}). */\n enter: {\n args: {\n stagger: Types.custom<number | undefined>(undefined),\n slide: Types.number(SLIDE),\n },\n run: (timeline, state) => {\n const { stagger, slide } = timeline.args\n const count = state.markers.length\n const captioned = state.label !== \"\"\n const from: Arrival = { opacity: 0, shift: slide }\n const keys = [...state.markers.keys(), ...(captioned ? [\"label\"] : [])]\n const arm = timeline.animate(() => ({ arrivals: armed(keys, from) }))\n if (count === 0) return arm\n const { step, per, caption } = enterBudget(timeline.duration, count, captioned, stagger)\n const ease = frameEase(timeline)\n const markers = state.markers.map((_, i) => ({ at: i * step, duration: per, piece: arrival(timeline, i, from, ease) }))\n const label = captioned ? [{ at: (count - 1) * step + per, duration: caption, piece: arrival(timeline, \"label\", from, ease) }] : []\n return timeline.together([arm, beats(timeline, [...markers, ...label])])\n },\n },\n },\n})\n\nexport type HorizontalAxis = NodeOf<typeof HorizontalAxis>\n"]}
@@ -3,6 +3,11 @@ import { type EasingFunction, type Piece, type Timeline } from "@motionscript/sd
3
3
  export type AnyTimeline = Timeline<any, any>;
4
4
  /** How far a legend starts below its slot when `enter` brings it in, in px. */
5
5
  export declare const LEGEND_SLIDE = 20;
6
+ /** Where a legend waits before it comes up: below its place, above it, or to its right. */
7
+ export declare function legendAway(at: string): {
8
+ x?: number;
9
+ y?: number;
10
+ };
6
11
  /** Marker count when an axis states no ticks. */
7
12
  export declare const DEFAULT_MARKERS = 5;
8
13
  /** How a cartesian `enter` splits its duration: axes arriving, marks drawing, legend coming up. Sums to 1. */
@@ -1 +1 @@
1
- {"version":3,"file":"motion.d.ts","sourceRoot":"","sources":["../../src/kit/motion.ts"],"names":[],"mappings":"AAAA,OAAO,EAAkB,KAAK,cAAc,EAAE,KAAK,KAAK,EAAE,KAAK,QAAQ,EAAE,MAAM,mBAAmB,CAAA;AAIlG,oGAAoG;AACpG,MAAM,MAAM,WAAW,GAAG,QAAQ,CAAC,GAAG,EAAE,GAAG,CAAC,CAAA;AAE5C,+EAA+E;AAC/E,eAAO,MAAM,YAAY,KAAK,CAAA;AAE9B,iDAAiD;AACjD,eAAO,MAAM,eAAe,IAAI,CAAA;AAEhC,8GAA8G;AAC9G,eAAO,MAAM,WAAW;;;;CAAoD,CAAA;AAE5E;;;;GAIG;AACH,wBAAgB,SAAS,CAAC,QAAQ,EAAE;IAAE,QAAQ,CAAC,MAAM,EAAE,cAAc,GAAG,SAAS,CAAA;CAAE,GAAG,cAAc,CAEnG;AAED,8FAA8F;AAC9F,MAAM,WAAW,IAAI;IACnB,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAA;IACnB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAA;IACzB,QAAQ,CAAC,KAAK,EAAE,KAAK,CAAA;CACtB;AAED;;;;;GAKG;AACH,wBAAgB,KAAK,CAAC,QAAQ,EAAE,WAAW,EAAE,KAAK,EAAE,SAAS,IAAI,EAAE,GAAG,KAAK,CAW1E;AAED,gHAAgH;AAChH,MAAM,WAAW,OAAO;IACtB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;IACxB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;CACvB;AAED,kEAAkE;AAClE,MAAM,MAAM,QAAQ,GAAG,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAA;AAIxD;;;;GAIG;AACH,wBAAgB,aAAa,kHAE5B;AAED,wBAAgB,SAAS,CAAC,QAAQ,EAAE,QAAQ,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,CAE3E;AAED,gFAAgF;AAChF,wBAAgB,KAAK,CAAC,IAAI,EAAE,SAAS,CAAC,MAAM,GAAG,MAAM,CAAC,EAAE,EAAE,IAAI,EAAE,OAAO,GAAG,QAAQ,CAIjF;AAED;;;GAGG;AACH,wBAAgB,OAAO,CAAC,QAAQ,EAAE,WAAW,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,cAAc,GAAG,KAAK,CAY/G"}
1
+ {"version":3,"file":"motion.d.ts","sourceRoot":"","sources":["../../src/kit/motion.ts"],"names":[],"mappings":"AAAA,OAAO,EAAkB,KAAK,cAAc,EAAE,KAAK,KAAK,EAAE,KAAK,QAAQ,EAAE,MAAM,mBAAmB,CAAA;AAIlG,oGAAoG;AACpG,MAAM,MAAM,WAAW,GAAG,QAAQ,CAAC,GAAG,EAAE,GAAG,CAAC,CAAA;AAE5C,+EAA+E;AAC/E,eAAO,MAAM,YAAY,KAAK,CAAA;AAE9B,2FAA2F;AAC3F,wBAAgB,UAAU,CAAC,EAAE,EAAE,MAAM,GAAG;IAAE,CAAC,CAAC,EAAE,MAAM,CAAC;IAAC,CAAC,CAAC,EAAE,MAAM,CAAA;CAAE,CAIjE;AAED,iDAAiD;AACjD,eAAO,MAAM,eAAe,IAAI,CAAA;AAEhC,8GAA8G;AAC9G,eAAO,MAAM,WAAW;;;;CAAoD,CAAA;AAE5E;;;;GAIG;AACH,wBAAgB,SAAS,CAAC,QAAQ,EAAE;IAAE,QAAQ,CAAC,MAAM,EAAE,cAAc,GAAG,SAAS,CAAA;CAAE,GAAG,cAAc,CAEnG;AAED,8FAA8F;AAC9F,MAAM,WAAW,IAAI;IACnB,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAA;IACnB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAA;IACzB,QAAQ,CAAC,KAAK,EAAE,KAAK,CAAA;CACtB;AAED;;;;;GAKG;AACH,wBAAgB,KAAK,CAAC,QAAQ,EAAE,WAAW,EAAE,KAAK,EAAE,SAAS,IAAI,EAAE,GAAG,KAAK,CAW1E;AAED,gHAAgH;AAChH,MAAM,WAAW,OAAO;IACtB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;IACxB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;CACvB;AAED,kEAAkE;AAClE,MAAM,MAAM,QAAQ,GAAG,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAA;AAIxD;;;;GAIG;AACH,wBAAgB,aAAa,kHAE5B;AAED,wBAAgB,SAAS,CAAC,QAAQ,EAAE,QAAQ,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,CAE3E;AAED,gFAAgF;AAChF,wBAAgB,KAAK,CAAC,IAAI,EAAE,SAAS,CAAC,MAAM,GAAG,MAAM,CAAC,EAAE,EAAE,IAAI,EAAE,OAAO,GAAG,QAAQ,CAIjF;AAED;;;GAGG;AACH,wBAAgB,OAAO,CAAC,QAAQ,EAAE,WAAW,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,cAAc,GAAG,KAAK,CAY/G"}
@@ -2,6 +2,14 @@ import { Types, easeOut } from "@motionscript/sdk";
2
2
  const identity = (t) => t;
3
3
  /** How far a legend starts below its slot when `enter` brings it in, in px. */
4
4
  export const LEGEND_SLIDE = 20;
5
+ /** Where a legend waits before it comes up: below its place, above it, or to its right. */
6
+ export function legendAway(at) {
7
+ if (at === "top")
8
+ return { y: LEGEND_SLIDE };
9
+ if (at === "right")
10
+ return { x: LEGEND_SLIDE };
11
+ return { y: -LEGEND_SLIDE };
12
+ }
5
13
  /** Marker count when an axis states no ticks. */
6
14
  export const DEFAULT_MARKERS = 5;
7
15
  /** How a cartesian `enter` splits its duration: axes arriving, marks drawing, legend coming up. Sums to 1. */
@@ -1 +1 @@
1
- {"version":3,"file":"motion.js","sourceRoot":"","sources":["../../src/kit/motion.ts"],"names":[],"mappings":"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","sourcesContent":["import { Types, easeOut, type EasingFunction, type Piece, type Timeline } from \"@motionscript/sdk\"\n\nconst identity: EasingFunction = (t) => t\n\n/** Any component's timeline: these helpers only build pieces, which every timeline builds alike. */\nexport type AnyTimeline = Timeline<any, any>\n\n/** How far a legend starts below its slot when `enter` brings it in, in px. */\nexport const LEGEND_SLIDE = 20\n\n/** Marker count when an axis states no ticks. */\nexport const DEFAULT_MARKERS = 5\n\n/** How a cartesian `enter` splits its duration: axes arriving, marks drawing, legend coming up. Sums to 1. */\nexport const ENTER_SHARE = { axes: 0.35, marks: 0.5, legend: 0.15 } as const\n\n/**\n * The ease a part's own frames apply to `t`: none when its command was given\n * one, which already eases every frame the command builds, and otherwise the\n * ease-out every chart command defaults to.\n */\nexport function frameEase(timeline: { readonly easing: EasingFunction | undefined }): EasingFunction {\n return timeline.easing ? identity : easeOut()\n}\n\n/** A piece of a command placed at its own time: from `at` seconds, for `duration` seconds. */\nexport interface Beat {\n readonly at: number\n readonly duration: number\n readonly piece: Piece\n}\n\n/**\n * `beats` at their own offsets over the command, each eased on its own and held\n * at its last frame once it is done. A beat ahead of the playhead writes nothing,\n * so whatever stands before it is written by a piece ahead of these. Beats that\n * run past the command are played faster to end with it.\n */\nexport function beats(timeline: AnyTimeline, parts: readonly Beat[]): Piece {\n const total = timeline.duration\n const natural = parts.reduce((end, part) => Math.max(end, part.at + part.duration), 0)\n const k = natural > total && natural > 0 ? total / natural : 1\n return timeline.together(\n parts.map(({ at, duration, piece }) => {\n const start = at * k\n const length = duration * k\n return timeline.sequence([timeline.wait(start), piece, timeline.wait(Math.max(0, total - start - length))])\n })\n )\n}\n\n/** How far one piece of a staggered entrance has come: its opacity, and how far it still sits off its place. */\nexport interface Arrival {\n readonly opacity: number\n readonly shift: number\n}\n\n/** Each piece's arrival, by key. An unlisted piece has landed. */\nexport type Arrivals = Readonly<Record<string, Arrival>>\n\nconst LANDED: Arrival = { opacity: 1, shift: 0 }\n\n/**\n * A part's private record of its pieces' arrivals. A write merges into it, so\n * beats placed apart each write their own piece and leave the others as the\n * frame left them.\n */\nexport function arrivalsEntry() {\n return Types.custom<Arrivals>({}, { private: true, resolve: (next: Arrivals, previous?: Arrivals) => ({ ...previous, ...next }) })\n}\n\nexport function arrivalOf(arrivals: Arrivals, key: string | number): Arrival {\n return arrivals[key] ?? LANDED\n}\n\n/** Every piece in `keys` armed at `from`: the first frame of their entrance. */\nexport function armed(keys: readonly (string | number)[], from: Arrival): Arrivals {\n const record: Record<string, Arrival> = {}\n for (const key of keys) record[key] = from\n return record\n}\n\n/**\n * A piece arriving from `from` over its beat: a part armed at `from` tweened to\n * `{ opacity: 1, [axis]: 0 }`, eased by `ease`.\n */\nexport function arrival(timeline: AnyTimeline, key: string | number, from: Arrival, ease: EasingFunction): Piece {\n return timeline.animate((t) => {\n const e = ease(t)\n return {\n arrivals: {\n [key]: {\n opacity: from.opacity + (LANDED.opacity - from.opacity) * e,\n shift: from.shift + (LANDED.shift - from.shift) * e,\n },\n },\n }\n })\n}\n"]}
1
+ {"version":3,"file":"motion.js","sourceRoot":"","sources":["../../src/kit/motion.ts"],"names":[],"mappings":"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","sourcesContent":["import { Types, easeOut, type EasingFunction, type Piece, type Timeline } from \"@motionscript/sdk\"\n\nconst identity: EasingFunction = (t) => t\n\n/** Any component's timeline: these helpers only build pieces, which every timeline builds alike. */\nexport type AnyTimeline = Timeline<any, any>\n\n/** How far a legend starts below its slot when `enter` brings it in, in px. */\nexport const LEGEND_SLIDE = 20\n\n/** Where a legend waits before it comes up: below its place, above it, or to its right. */\nexport function legendAway(at: string): { x?: number; y?: number } {\n if (at === \"top\") return { y: LEGEND_SLIDE }\n if (at === \"right\") return { x: LEGEND_SLIDE }\n return { y: -LEGEND_SLIDE }\n}\n\n/** Marker count when an axis states no ticks. */\nexport const DEFAULT_MARKERS = 5\n\n/** How a cartesian `enter` splits its duration: axes arriving, marks drawing, legend coming up. Sums to 1. */\nexport const ENTER_SHARE = { axes: 0.35, marks: 0.5, legend: 0.15 } as const\n\n/**\n * The ease a part's own frames apply to `t`: none when its command was given\n * one, which already eases every frame the command builds, and otherwise the\n * ease-out every chart command defaults to.\n */\nexport function frameEase(timeline: { readonly easing: EasingFunction | undefined }): EasingFunction {\n return timeline.easing ? identity : easeOut()\n}\n\n/** A piece of a command placed at its own time: from `at` seconds, for `duration` seconds. */\nexport interface Beat {\n readonly at: number\n readonly duration: number\n readonly piece: Piece\n}\n\n/**\n * `beats` at their own offsets over the command, each eased on its own and held\n * at its last frame once it is done. A beat ahead of the playhead writes nothing,\n * so whatever stands before it is written by a piece ahead of these. Beats that\n * run past the command are played faster to end with it.\n */\nexport function beats(timeline: AnyTimeline, parts: readonly Beat[]): Piece {\n const total = timeline.duration\n const natural = parts.reduce((end, part) => Math.max(end, part.at + part.duration), 0)\n const k = natural > total && natural > 0 ? total / natural : 1\n return timeline.together(\n parts.map(({ at, duration, piece }) => {\n const start = at * k\n const length = duration * k\n return timeline.sequence([timeline.wait(start), piece, timeline.wait(Math.max(0, total - start - length))])\n })\n )\n}\n\n/** How far one piece of a staggered entrance has come: its opacity, and how far it still sits off its place. */\nexport interface Arrival {\n readonly opacity: number\n readonly shift: number\n}\n\n/** Each piece's arrival, by key. An unlisted piece has landed. */\nexport type Arrivals = Readonly<Record<string, Arrival>>\n\nconst LANDED: Arrival = { opacity: 1, shift: 0 }\n\n/**\n * A part's private record of its pieces' arrivals. A write merges into it, so\n * beats placed apart each write their own piece and leave the others as the\n * frame left them.\n */\nexport function arrivalsEntry() {\n return Types.custom<Arrivals>({}, { private: true, resolve: (next: Arrivals, previous?: Arrivals) => ({ ...previous, ...next }) })\n}\n\nexport function arrivalOf(arrivals: Arrivals, key: string | number): Arrival {\n return arrivals[key] ?? LANDED\n}\n\n/** Every piece in `keys` armed at `from`: the first frame of their entrance. */\nexport function armed(keys: readonly (string | number)[], from: Arrival): Arrivals {\n const record: Record<string, Arrival> = {}\n for (const key of keys) record[key] = from\n return record\n}\n\n/**\n * A piece arriving from `from` over its beat: a part armed at `from` tweened to\n * `{ opacity: 1, [axis]: 0 }`, eased by `ease`.\n */\nexport function arrival(timeline: AnyTimeline, key: string | number, from: Arrival, ease: EasingFunction): Piece {\n return timeline.animate((t) => {\n const e = ease(t)\n return {\n arrivals: {\n [key]: {\n opacity: from.opacity + (LANDED.opacity - from.opacity) * e,\n shift: from.shift + (LANDED.shift - from.shift) * e,\n },\n },\n }\n })\n}\n"]}
@@ -1,7 +1,7 @@
1
1
  import { type NodeOf, type Alignment as _Alignment, type CornerRadiusResolved as _CornerRadiusResolved, type CornerStyleResolved as _CornerStyleResolved, type Fill as _Fill, type FillResolved as _FillResolved, type FlowMode as _FlowMode, type FlowResolved as _FlowResolved, type GapSize as _GapSize, type Node2D as _Node2D, type Node2DProps as _Node2DProps, type RectCornerRadius as _RectCornerRadius, type RectCornerStyle as _RectCornerStyle, type Shadow as _Shadow, type ShadowResolved as _ShadowResolved, type Stroke as _Stroke, type StrokeResolved as _StrokeResolved, type Vector2 as _Vector2 } from "@motionscript/sdk";
2
- import { type Arrival } from "../kit/motion.js";
3
- import { type LegendConfig } from "../kit/shared.js";
4
- import type { PieSlice } from "./shared.js";
2
+ import { type Arrival } from "./motion.js";
3
+ import { type LegendConfig } from "./shared.js";
4
+ import type { PieSlice } from "./polar.js";
5
5
  /**
6
6
  * The legend of a {@link PieChart}: a column (it sits beside the ring, in the
7
7
  * vertical strip it leaves) of caption-and-swatch entries, one per slice. No
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pie-legend.d.ts","sourceRoot":"","sources":["../../src/kit/pie-legend.ts"],"names":[],"mappings":"AAAA,OAAO,EAUL,KAAK,MAAM,EAEX,KAAK,SAAS,IAAI,UAAU,EAC5B,KAAK,oBAAoB,IAAI,qBAAqB,EAClD,KAAK,mBAAmB,IAAI,oBAAoB,EAChD,KAAK,IAAI,IAAI,KAAK,EAClB,KAAK,YAAY,IAAI,aAAa,EAClC,KAAK,QAAQ,IAAI,SAAS,EAC1B,KAAK,YAAY,IAAI,aAAa,EAClC,KAAK,OAAO,IAAI,QAAQ,EACxB,KAAK,MAAM,IAAI,OAAO,EACtB,KAAK,WAAW,IAAI,YAAY,EAChC,KAAK,gBAAgB,IAAI,iBAAiB,EAC1C,KAAK,eAAe,IAAI,gBAAgB,EACxC,KAAK,MAAM,IAAI,OAAO,EACtB,KAAK,cAAc,IAAI,eAAe,EACtC,KAAK,MAAM,IAAI,OAAO,EACtB,KAAK,cAAc,IAAI,eAAe,EACtC,KAAK,OAAO,IAAI,QAAQ,EACzB,MAAM,mBAAmB,CAAA;AAG1B,OAAO,EAA8D,KAAK,OAAO,EAAE,MAAM,UAAU,CAAA;AACnG,OAAO,EAAkB,KAAK,YAAY,EAAE,MAAM,UAAU,CAAA;AAC5D,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAA;AAIvC;;;;;GAKG;AACH,eAAO,MAAM,SAAS;IAMlB,0CAA0C;;;IAG1C,iIAAiI;;;;;;;;;;;;;;;;;;;;;;;;;;;;;IAHjI,0CAA0C;;;IAG1C,iIAAiI;;;;;;;;;;;;;;;;;;IAHjI,0CAA0C;;;IAG1C,iIAAiI;;;;;;;;;;;;;;;;;;;;;;;;;;;;2BAyEnI,CAAA;AAEF,MAAM,MAAM,SAAS,GAAG,MAAM,CAAC,OAAO,SAAS,CAAC,CAAA"}
@@ -1,7 +1,7 @@
1
1
  import { Flow, Paint, Rect, Shapes, Text, Types, clamp, 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 } from "../kit/shared.js";
2
+ import { fresh } from "./entries.js";
3
+ import { armed, arrival, arrivalOf, arrivalsEntry, beats, frameEase } from "./motion.js";
4
+ import { DEFAULT_LEGEND } from "./shared.js";
5
5
  const SLIDE = 16;
6
6
  /**
7
7
  * The legend of a {@link PieChart}: a column (it sits beside the ring, in the
@@ -33,7 +33,7 @@ export const PieLegend = component2d({
33
33
  },
34
34
  // The selection dim rides a wrapper, so the entrance animates the entry's own opacity without the two fighting over one value.
35
35
  children: (state) => {
36
- const marker = state.legend.markerStyle;
36
+ const marker = () => state.legend.markerStyle;
37
37
  return state.slices.map((_, index) => Rect({
38
38
  width: "hug",
39
39
  height: "hug",
@@ -42,7 +42,7 @@ export const PieLegend = component2d({
42
42
  children: [
43
43
  Rect({
44
44
  flow: "horizontal",
45
- gap: 12,
45
+ gap: () => Math.round(state.legend.textStyle.fontSize === undefined ? 12 : Number(state.legend.textStyle.fontSize) * 0.4),
46
46
  align: "center",
47
47
  width: "hug",
48
48
  height: "hug",
@@ -50,15 +50,16 @@ export const PieLegend = component2d({
50
50
  x: () => arrivalOf(state.arrivals, index).shift,
51
51
  children: [
52
52
  Rect({
53
- width: marker.height,
54
- height: marker.height,
55
- cornerRadius: marker.borderRadius,
53
+ width: () => marker().width,
54
+ height: () => marker().height,
55
+ cornerRadius: () => marker().borderRadius,
56
56
  fill: () => state.slices[index]?.fill ?? "white",
57
- stroke: marker.stroke,
58
- shadow: marker.shadow,
59
57
  }),
60
58
  Text({
61
59
  ...textOps.styleProps(state.legend.textStyle),
60
+ fill: () => state.legend.textStyle.fill ?? "white",
61
+ fontSize: () => state.legend.textStyle.fontSize ?? 28,
62
+ fontWeight: () => state.legend.textStyle.fontWeight ?? 500,
62
63
  text: () => state.slices[index]?.category ?? "",
63
64
  }),
64
65
  ],