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

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 (92) hide show
  1. package/CHANGELOG.md +4 -0
  2. package/LICENSE +201 -201
  3. package/NOTICE +49 -48
  4. package/README.md +99 -99
  5. package/dist/bar-chart/bar-chart.d.ts +2 -2
  6. package/dist/bar-chart/bar-chart.d.ts.map +1 -1
  7. package/dist/bar-chart/bar-chart.js +13 -5
  8. package/dist/bar-chart/bar-chart.js.map +1 -1
  9. package/dist/bar-chart/category-axis.js.map +1 -1
  10. package/dist/bar-chart/chart-bars.d.ts.map +1 -1
  11. package/dist/bar-chart/chart-bars.js +6 -7
  12. package/dist/bar-chart/chart-bars.js.map +1 -1
  13. package/dist/bar-chart/index.js.map +1 -1
  14. package/dist/bar-chart/shared.js.map +1 -1
  15. package/dist/browser/chunks/{chunk-XROBIRQK.js → chunk-TJNHLQGV.js} +1 -1
  16. package/dist/browser/chunks/chunk-TJNHLQGV.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/engine.d.ts +1 -1
  22. package/dist/engine.js +1 -1
  23. package/dist/engine.js.map +1 -1
  24. package/dist/index.js.map +1 -1
  25. package/dist/kit/cartesian.js.map +1 -1
  26. package/dist/kit/chart-border.js.map +1 -1
  27. package/dist/kit/chart-canvas.js.map +1 -1
  28. package/dist/kit/chart-legend.js.map +1 -1
  29. package/dist/kit/entries.js.map +1 -1
  30. package/dist/kit/motion.js.map +1 -1
  31. package/dist/kit/paints.js.map +1 -1
  32. package/dist/kit/shared.js.map +1 -1
  33. package/dist/kit/vertical-axis.js.map +1 -1
  34. package/dist/line-chart/chart-line.js.map +1 -1
  35. package/dist/line-chart/chart-region.js.map +1 -1
  36. package/dist/line-chart/horizontal-axis.js.map +1 -1
  37. package/dist/line-chart/index.d.ts +1 -1
  38. package/dist/line-chart/index.js +1 -1
  39. package/dist/line-chart/index.js.map +1 -1
  40. package/dist/line-chart/line-chart.d.ts +2 -2
  41. package/dist/line-chart/line-chart.d.ts.map +1 -1
  42. package/dist/line-chart/line-chart.js +16 -7
  43. package/dist/line-chart/line-chart.js.map +1 -1
  44. package/dist/line-chart/shared.js.map +1 -1
  45. package/dist/line-chart/x-scale.d.ts +3 -10
  46. package/dist/line-chart/x-scale.d.ts.map +1 -1
  47. package/dist/line-chart/x-scale.js +15 -4
  48. package/dist/line-chart/x-scale.js.map +1 -1
  49. package/dist/nodes.js.map +1 -1
  50. package/dist/pie-chart/index.js.map +1 -1
  51. package/dist/pie-chart/pie-chart.d.ts +2 -2
  52. package/dist/pie-chart/pie-chart.d.ts.map +1 -1
  53. package/dist/pie-chart/pie-chart.js +4 -1
  54. package/dist/pie-chart/pie-chart.js.map +1 -1
  55. package/dist/pie-chart/pie-legend.js.map +1 -1
  56. package/dist/pie-chart/pie-ring.js.map +1 -1
  57. package/dist/pie-chart/shared.d.ts +6 -0
  58. package/dist/pie-chart/shared.d.ts.map +1 -1
  59. package/dist/pie-chart/shared.js +8 -2
  60. package/dist/pie-chart/shared.js.map +1 -1
  61. package/package.json +5 -4
  62. package/registry.json +23 -23
  63. package/src/bar-chart/bar-chart.ts +542 -534
  64. package/src/bar-chart/category-axis.ts +162 -162
  65. package/src/bar-chart/chart-bars.ts +287 -288
  66. package/src/bar-chart/index.ts +31 -31
  67. package/src/bar-chart/shared.ts +354 -354
  68. package/src/engine.ts +28 -28
  69. package/src/index.ts +5 -5
  70. package/src/kit/cartesian.ts +81 -81
  71. package/src/kit/chart-border.ts +50 -50
  72. package/src/kit/chart-canvas.ts +122 -122
  73. package/src/kit/chart-legend.ts +229 -229
  74. package/src/kit/entries.ts +20 -20
  75. package/src/kit/motion.ts +90 -90
  76. package/src/kit/paints.ts +15 -15
  77. package/src/kit/shared.ts +782 -782
  78. package/src/kit/vertical-axis.ts +194 -194
  79. package/src/line-chart/chart-line.ts +156 -156
  80. package/src/line-chart/chart-region.ts +90 -90
  81. package/src/line-chart/horizontal-axis.ts +151 -151
  82. package/src/line-chart/index.ts +39 -39
  83. package/src/line-chart/line-chart.ts +598 -585
  84. package/src/line-chart/shared.ts +473 -473
  85. package/src/line-chart/x-scale.ts +382 -367
  86. package/src/nodes.ts +18 -18
  87. package/src/pie-chart/index.ts +37 -37
  88. package/src/pie-chart/pie-chart.ts +296 -293
  89. package/src/pie-chart/pie-legend.ts +169 -169
  90. package/src/pie-chart/pie-ring.ts +848 -848
  91. package/src/pie-chart/shared.ts +496 -490
  92. package/dist/browser/chunks/chunk-XROBIRQK.js.map +0 -7
@@ -1 +1 @@
1
- {"version":3,"file":"chart-bars.js","sourceRoot":"","sources":["../../src/bar-chart/chart-bars.ts"],"names":[],"mappings":"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","sourcesContent":["import {\r\n Graphics2D,\r\n Text,\r\n Types,\r\n component2d,\r\n fillOps,\r\n shadowOps,\r\n strokeOps,\r\n textOps,\r\n type Fill,\r\n type FillResolved,\r\n type NodeOf,\r\n // Named so this component's emitted type reaches `Node2D` through the sdk, not core.\r\n type Node2D as _Node2D,\r\n type Node2DProps,\r\n type Shadow,\r\n type ShadowResolved,\r\n type Stroke,\r\n type StrokeResolved,\r\n type TextStyle,\r\n} from \"@motionscript/sdk\"\r\n\r\nimport { fresh } from \"../kit/entries\"\r\nimport { frameEase } from \"../kit/motion\"\r\nimport { loadPaints } from \"../kit/paints\"\r\nimport { clampUnit, formatMarker } from \"../kit/shared\"\r\nimport { DEFAULT_BAR_CHART_THEME } from \"./shared\"\r\n\r\n/**\r\n * One drawn column, already mapped into the plot's **centred pixel space**\r\n * (origin at the plot centre, y-up) by the parent {@link BarChart}.\r\n *\r\n * `from`/`to` are the two ends of the column rather than a base and a height,\r\n * for the same reason {@link BarSpan} is a span: it is the one shape that fits a\r\n * grouped bar, a stacked segment and a negative value without a special case.\r\n */\r\nexport interface BarGeometry {\r\n /** Centre x of the column. */\r\n x: number\r\n /** Column width in px. */\r\n width: number\r\n /** Pixel y of the column's base — the baseline when grouped, the running stack top when stacked. */\r\n from: number\r\n /** Pixel y of its far end, at full {@link ChartBarsProps.growth}. */\r\n to: number\r\n /** Whether the far end takes the theme's corner radius (the stack's cap). */\r\n round: boolean\r\n /** The value this column plots, for the figure printed on it. */\r\n value: number\r\n /**\r\n * The figure printed on it, written as the chart's value axis writes its\r\n * ticks, so the two cannot disagree. Absent, the bare marker reading of\r\n * {@link value}.\r\n */\r\n caption?: string\r\n}\r\n\r\n/** Gap between a grouped column's end and the figure printed over it, in px. */\r\nconst VALUE_GAP = 10\r\n/** A segment shorter than this many multiples of the type size prints no figure. */\r\nconst VALUE_FIT = 1.6\r\n/** Fallback type size when the theme's value style leaves it unset or autofit. */\r\nconst VALUE_SIZE = 24\r\n\r\nexport interface ChartBarsProps extends Node2DProps {\r\n /** This series' columns, one per row of the chart's data. */\r\n bars: BarGeometry[]\r\n /** Pixel y of value 0 — where every column grows *from*. */\r\n baseline: number\r\n /** Column paint — this series' own, or the theme palette's entry for its index. */\r\n fill: Fill\r\n /** Optional outline on every column. */\r\n barStroke: Stroke\r\n /** Optional drop shadow on every column. */\r\n barShadow: Shadow\r\n /** Radius of the far-end corners, in px. */\r\n cornerRadius: number\r\n /**\r\n * Rise of every column in `[0, 1]`, measured **about the baseline**: 0\r\n * collapses them all onto it, 1 draws them at full length.\r\n *\r\n * Scaling about the baseline rather than about each column's own base is what\r\n * keeps a stack flush mid-animation — every segment's base and end travel by\r\n * the same factor, so the segments stay stacked instead of the upper ones\r\n * floating above the still-growing lower ones. It also means a negative\r\n * column grows *down* out of the same signal.\r\n */\r\n growth: number\r\n /**\r\n * Per-column opacity multiplier, by row index — how the chart's category\r\n * spotlight reaches a single column without touching the series it belongs\r\n * to. Missing entries read as 1.\r\n */\r\n dims: number[]\r\n /** Whether the figure each column plots is printed on it. */\r\n showValues: boolean\r\n /** Type style for those figures. */\r\n valueStyle: TextStyle\r\n /**\r\n * Whether this series draws at all — the wholesale switch behind\r\n * `BarChart.showSeries`/`hideSeries`, alongside the slot weight that animates\r\n * the reflow. Off skips the columns *and* their figures.\r\n */\r\n enabled: boolean\r\n}\r\n\r\n/** What the column geometry reads. */\r\ninterface Columns {\r\n readonly bars: readonly BarGeometry[]\r\n readonly baseline: number\r\n readonly growth: number\r\n readonly dims: readonly number[]\r\n readonly enabled: boolean\r\n readonly valueStyle: TextStyle\r\n}\r\n\r\n/**\r\n * How far column `i` is dimmed — the chart's category spotlight, and the series\r\n * switch on top of it.\r\n *\r\n * The captions read this rather than being hidden by `render`'s guard: they are\r\n * real child nodes, so a `return` in the draw would leave them on screen over a\r\n * series that isn't there.\r\n */\r\nfunction dimAt(s: Columns, index: number): number {\r\n if (!s.enabled) return 0\r\n return clampUnit(s.dims[index] ?? 1)\r\n}\r\n\r\n/** The two ends of column `i` at the current growth. */\r\nfunction endsAt(s: Columns, index: number): { base: number; end: number } | null {\r\n const bar = s.bars[index]\r\n if (!bar) return null\r\n const g = clampUnit(s.growth)\r\n return { base: s.baseline + (bar.from - s.baseline) * g, end: s.baseline + (bar.to - s.baseline) * g }\r\n}\r\n\r\n/** The type size the figures are set at, guarding the autofit/unset cases. */\r\nfunction valueSize(s: Columns): number {\r\n const size = s.valueStyle.fontSize\r\n return typeof size === \"number\" ? size : VALUE_SIZE\r\n}\r\n\r\n/** Whether column `i`'s figure sits inside the column (a stacked segment) or past its end. */\r\nfunction insideAt(s: Columns, index: number): boolean {\r\n const bar = s.bars[index]\r\n return bar !== undefined && Math.abs(bar.from - s.baseline) > 0.5\r\n}\r\n\r\n/**\r\n * The figure printed on column `i`, or `\"\"` where there is no room for it.\r\n *\r\n * Blanking the text rather than dropping the node keeps the caption list\r\n * index-aligned with the columns, so a chart that rescales into more room simply\r\n * starts printing again.\r\n */\r\nfunction captionAt(s: Columns, index: number): string {\r\n const bar = s.bars[index]\r\n if (!bar || bar.value === 0) return \"\"\r\n // An interior segment prints inside itself, so it has to be tall enough to\r\n // hold a line of type; a grouped column prints in the headroom above its end,\r\n // where the only constraint is the plot's own top.\r\n if (insideAt(s, index)) {\r\n const ends = endsAt(s, index)\r\n if (!ends) return \"\"\r\n if (Math.abs(ends.end - ends.base) < valueSize(s) * VALUE_FIT) return \"\"\r\n }\r\n return bar.caption ?? formatMarker(bar.value)\r\n}\r\n\r\n/** Where column `i`'s figure sits, in the node's centred y-up space. */\r\nfunction captionY(s: Columns, index: number): number {\r\n const ends = endsAt(s, index)\r\n if (!ends) return 0\r\n if (insideAt(s, index)) return (ends.base + ends.end) / 2\r\n // Text is centred on its `y`, so clear the column's end by the gap plus half a\r\n // line — on whichever side of the baseline the column actually reached.\r\n const direction = ends.end >= ends.base ? 1 : -1\r\n return ends.end + direction * (VALUE_GAP + valueSize(s) / 2)\r\n}\r\n\r\n/**\r\n * One **series** of a {@link BarChart}, drawn as one command list: its column in\r\n * every category, plus the figures printed on them.\r\n *\r\n * A node per series rather than a node per bar, deliberately — it is the same\r\n * unit `ChartLine` is, which is what lets `showSeries`/`hideSeries`/\r\n * `spotlightSeries` mean exactly the same thing on a bar chart as on a line\r\n * chart: enable the node, or tween its opacity. The columns' *positions* come\r\n * from the parent (it owns the slot arithmetic both modes share), so this part\r\n * only has to know how to draw a span and grow it.\r\n *\r\n * The figures are real {@link Text} children rather than glyphs in the command\r\n * list, so they take the theme's type style, are measured by the layout engine,\r\n * and ride the node's own opacity — which is why hiding the series takes its\r\n * figures with it for free.\r\n */\r\nexport const ChartBars = component2d({\r\n key: \"chartBars\",\r\n state: {\r\n bars: fresh<BarGeometry[]>(() => []),\r\n baseline: 0,\r\n fill: Types.custom<Fill, FillResolved[]>([], { resolve: fillOps.resolve, lerp: fillOps.lerp }),\r\n barStroke: Types.custom<Stroke, StrokeResolved[]>(DEFAULT_BAR_CHART_THEME.bar.stroke, { resolve: strokeOps.resolve, lerp: strokeOps.lerp }),\r\n barShadow: Types.custom<Shadow, ShadowResolved[]>(DEFAULT_BAR_CHART_THEME.bar.shadow, { resolve: shadowOps.resolve, lerp: shadowOps.lerp }),\r\n cornerRadius: DEFAULT_BAR_CHART_THEME.bar.cornerRadius,\r\n growth: 1,\r\n dims: fresh<number[]>(() => []),\r\n showValues: false,\r\n valueStyle: Types.custom<TextStyle>(DEFAULT_BAR_CHART_THEME.valueStyle),\r\n enabled: true,\r\n },\r\n // A plain node filled its parent; one made of its figures would hug them.\r\n defaults: { width: \"fill\", height: \"fill\" },\r\n // One caption per column, built once (the row count is fixed at construction,\r\n // like an axis's marker count) and bound to the live geometry so it travels\r\n // with its column as the chart grows, rescales or reflows.\r\n children: (state) => {\r\n if (!state.showValues) return []\r\n return state.bars.map((_, i) =>\r\n Text({\r\n ...textOps.styleProps(state.valueStyle),\r\n text: () => captionAt(state, i),\r\n x: () => state.bars[i]?.x ?? 0,\r\n y: () => captionY(state, i),\r\n opacity: () => dimAt(state, i),\r\n })\r\n )\r\n },\r\n // The column stroke and shadow are named past the conventional slots, so they are named here.\r\n load: (loader, state) =>\r\n loadPaints(loader, { fills: [state.fill], shadows: [state.barShadow], strokes: [state.barStroke] }),\r\n commands: {\r\n /**\r\n * Raise every column from the baseline by tweening `growth` `0 → 1`.\r\n *\r\n * The mirror of `ChartLine.enter`: same contract that the resting state is\r\n * the finished one, so the chart arms this back to 0 before animating rather\r\n * than the node hiding itself at construction. No priming to 0 first: the\r\n * value at `t` is a function of `t`, so a repeat call re-raises by definition.\r\n */\r\n enter: (timeline) => {\r\n const ease = frameEase(timeline)\r\n return timeline.animate((t) => ({ growth: ease(t) }))\r\n },\r\n },\r\n render: (ctx, state) => {\r\n if (state.enabled) {\r\n for (let i = 0; i < state.bars.length; i++) {\r\n const bar = state.bars[i]\r\n const ends = endsAt(state, i)\r\n if (!ends || bar.width <= 0) continue\r\n const height = Math.abs(ends.end - ends.base)\r\n if (height <= 0) continue\r\n\r\n const radius = bar.round ? Math.min(state.cornerRadius, height) : 0\r\n // A column per `Graphics2D` rather than one list for the series: the\r\n // category spotlight dims individual columns, and opacity is a property\r\n // of the command list rather than of a shape within it.\r\n const g = new Graphics2D()\r\n const rect = g.rect({\r\n x: bar.x,\r\n y: (ends.base + ends.end) / 2,\r\n width: bar.width,\r\n height,\r\n // This node's space is y-up, but the renderer's corner-radius keys are\r\n // labelled in canvas y-down space — so a column reaching *up* rounds\r\n // through the `bottom*` keys, and one reaching down through the `top*`\r\n // ones. Only the far end rounds, which is what makes a stack read as a\r\n // single capped column.\r\n cornerRadius:\r\n radius > 0\r\n ? ends.end >= ends.base\r\n ? { bottomLeft: radius, bottomRight: radius, topLeft: 0, topRight: 0 }\r\n : { topLeft: radius, topRight: radius, bottomLeft: 0, bottomRight: 0 }\r\n : 0,\r\n })\r\n rect.shadow(state.barShadow)\r\n rect.fill(state.fill)\r\n rect.stroke(state.barStroke)\r\n ctx.draw(g.opacity(dimAt(state, i)))\r\n }\r\n }\r\n ctx.children()\r\n },\r\n})\r\n\r\nexport type ChartBars = NodeOf<typeof ChartBars>\r\n"]}
1
+ {"version":3,"file":"chart-bars.js","sourceRoot":"","sources":["../../src/bar-chart/chart-bars.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n Graphics2D,\n Text,\n Types,\n component2d,\n fillOps,\n shadowOps,\n strokeOps,\n textOps,\n type Fill,\n type FillResolved,\n type NodeOf,\n // Named so this component's emitted type reaches `Node2D` through the sdk, not core.\n type Node2D as _Node2D,\n type Node2DProps,\n type Shadow,\n type ShadowResolved,\n type Stroke,\n type StrokeResolved,\n type TextStyle,\n} from \"@motionscript/sdk\"\n\nimport { fresh } from \"../kit/entries\"\nimport { frameEase } from \"../kit/motion\"\nimport { loadPaints } from \"../kit/paints\"\nimport { clampUnit, formatMarker } from \"../kit/shared\"\nimport { DEFAULT_BAR_CHART_THEME } from \"./shared\"\n\n/**\n * One drawn column, already mapped into the plot's **centred pixel space**\n * (origin at the plot centre, y-up) by the parent {@link BarChart}.\n *\n * `from`/`to` are the two ends of the column rather than a base and a height,\n * for the same reason {@link BarSpan} is a span: it is the one shape that fits a\n * grouped bar, a stacked segment and a negative value without a special case.\n */\nexport interface BarGeometry {\n /** Centre x of the column. */\n x: number\n /** Column width in px. */\n width: number\n /** Pixel y of the column's base — the baseline when grouped, the running stack top when stacked. */\n from: number\n /** Pixel y of its far end, at full {@link ChartBarsProps.growth}. */\n to: number\n /** Whether the far end takes the theme's corner radius (the stack's cap). */\n round: boolean\n /** The value this column plots, for the figure printed on it. */\n value: number\n /**\n * The figure printed on it, written as the chart's value axis writes its\n * ticks, so the two cannot disagree. Absent, the bare marker reading of\n * {@link value}.\n */\n caption?: string\n}\n\n/** Gap between a grouped column's end and the figure printed over it, in px. */\nconst VALUE_GAP = 10\n/** A segment shorter than this many multiples of the type size prints no figure. */\nconst VALUE_FIT = 1.6\n/** Fallback type size when the theme's value style leaves it unset or autofit. */\nconst VALUE_SIZE = 24\n\nexport interface ChartBarsProps extends Node2DProps {\n /** This series' columns, one per row of the chart's data. */\n bars: BarGeometry[]\n /** Pixel y of value 0 — where every column grows *from*. */\n baseline: number\n /** Column paint — this series' own, or the theme palette's entry for its index. */\n fill: Fill\n /** Optional outline on every column. */\n barStroke: Stroke\n /** Optional drop shadow on every column. */\n barShadow: Shadow\n /** Radius of the far-end corners, in px. */\n cornerRadius: number\n /**\n * Rise of every column in `[0, 1]`, measured **about the baseline**: 0\n * collapses them all onto it, 1 draws them at full length.\n *\n * Scaling about the baseline rather than about each column's own base is what\n * keeps a stack flush mid-animation — every segment's base and end travel by\n * the same factor, so the segments stay stacked instead of the upper ones\n * floating above the still-growing lower ones. It also means a negative\n * column grows *down* out of the same signal.\n */\n growth: number\n /**\n * Per-column opacity multiplier, by row index — how the chart's category\n * spotlight reaches a single column without touching the series it belongs\n * to. Missing entries read as 1.\n */\n dims: number[]\n /** Whether the figure each column plots is printed on it. */\n showValues: boolean\n /** Type style for those figures. */\n valueStyle: TextStyle\n /**\n * Whether this series draws at all — the wholesale switch behind\n * `BarChart.showSeries`/`hideSeries`, alongside the slot weight that animates\n * the reflow. Off skips the columns *and* their figures.\n */\n enabled: boolean\n}\n\n/** What the column geometry reads. */\ninterface Columns {\n readonly bars: readonly BarGeometry[]\n readonly baseline: number\n readonly growth: number\n readonly dims: readonly number[]\n readonly enabled: boolean\n readonly valueStyle: TextStyle\n}\n\n/**\n * How far column `i` is dimmed — the chart's category spotlight, and the series\n * switch on top of it.\n *\n * The captions read this rather than being hidden by `render`'s guard: they are\n * real child nodes, so a `return` in the draw would leave them on screen over a\n * series that isn't there.\n */\nfunction dimAt(s: Columns, index: number): number {\n if (!s.enabled) return 0\n return clampUnit(s.dims[index] ?? 1)\n}\n\n/** The two ends of column `i` at the current growth. */\nfunction endsAt(s: Columns, index: number): { base: number; end: number } | null {\n const bar = s.bars[index]\n if (!bar) return null\n const g = clampUnit(s.growth)\n return { base: s.baseline + (bar.from - s.baseline) * g, end: s.baseline + (bar.to - s.baseline) * g }\n}\n\n/** The type size the figures are set at, guarding the autofit/unset cases. */\nfunction valueSize(s: Columns): number {\n const size = s.valueStyle.fontSize\n return typeof size === \"number\" ? size : VALUE_SIZE\n}\n\n/** Whether column `i`'s figure sits inside the column (a stacked segment) or past its end. */\nfunction insideAt(s: Columns, index: number): boolean {\n const bar = s.bars[index]\n return bar !== undefined && Math.abs(bar.from - s.baseline) > 0.5\n}\n\n/**\n * The figure printed on column `i`, or `\"\"` where there is no room for it.\n *\n * Blanking the text rather than dropping the node keeps the caption list\n * index-aligned with the columns, so a chart that rescales into more room simply\n * starts printing again.\n */\nfunction captionAt(s: Columns, index: number): string {\n const bar = s.bars[index]\n if (!bar || bar.value === 0) return \"\"\n // An interior segment prints inside itself, so it has to be tall enough to\n // hold a line of type; a grouped column prints in the headroom above its end,\n // where the only constraint is the plot's own top.\n if (insideAt(s, index)) {\n const ends = endsAt(s, index)\n if (!ends) return \"\"\n if (Math.abs(ends.end - ends.base) < valueSize(s) * VALUE_FIT) return \"\"\n }\n return bar.caption ?? formatMarker(bar.value)\n}\n\n/** Where column `i`'s figure sits, in the node's centred y-up space. */\nfunction captionY(s: Columns, index: number): number {\n const ends = endsAt(s, index)\n if (!ends) return 0\n if (insideAt(s, index)) return (ends.base + ends.end) / 2\n // Text is centred on its `y`, so clear the column's end by the gap plus half a\n // line — on whichever side of the baseline the column actually reached.\n const direction = ends.end >= ends.base ? 1 : -1\n return ends.end + direction * (VALUE_GAP + valueSize(s) / 2)\n}\n\n/**\n * One **series** of a {@link BarChart}, drawn as one command list: its column in\n * every category, plus the figures printed on them.\n *\n * A node per series rather than a node per bar, deliberately — it is the same\n * unit `ChartLine` is, which is what lets `showSeries`/`hideSeries`/\n * `spotlightSeries` mean exactly the same thing on a bar chart as on a line\n * chart: enable the node, or tween its opacity. The columns' *positions* come\n * from the parent (it owns the slot arithmetic both modes share), so this part\n * only has to know how to draw a span and grow it.\n *\n * The figures are real {@link Text} children rather than glyphs in the command\n * list, so they take the theme's type style, are measured by the layout engine,\n * and ride the node's own opacity — which is why hiding the series takes its\n * figures with it for free.\n */\nexport const ChartBars = component2d({\n key: \"chartBars\",\n state: {\n bars: fresh<BarGeometry[]>(() => []),\n baseline: 0,\n fill: Types.custom<Fill, FillResolved[]>([], { resolve: fillOps.resolve, lerp: fillOps.lerp }),\n barStroke: Types.custom<Stroke, StrokeResolved[]>(DEFAULT_BAR_CHART_THEME.bar.stroke, { resolve: strokeOps.resolve, lerp: strokeOps.lerp }),\n barShadow: Types.custom<Shadow, ShadowResolved[]>(DEFAULT_BAR_CHART_THEME.bar.shadow, { resolve: shadowOps.resolve, lerp: shadowOps.lerp }),\n cornerRadius: DEFAULT_BAR_CHART_THEME.bar.cornerRadius,\n growth: 1,\n dims: fresh<number[]>(() => []),\n showValues: false,\n valueStyle: Types.custom<TextStyle>(DEFAULT_BAR_CHART_THEME.valueStyle),\n enabled: true,\n },\n // A plain node filled its parent; one made of its figures would hug them.\n defaults: { width: \"fill\", height: \"fill\" },\n // One caption per column, built once (the row count is fixed at construction,\n // like an axis's marker count) and bound to the live geometry so it travels\n // with its column as the chart grows, rescales or reflows.\n children: (state) => {\n if (!state.showValues) return []\n return state.bars.map((_, i) =>\n Text({\n ...textOps.styleProps(state.valueStyle),\n text: () => captionAt(state, i),\n x: () => state.bars[i]?.x ?? 0,\n y: () => captionY(state, i),\n opacity: () => dimAt(state, i),\n })\n )\n },\n // The column stroke and shadow are named past the conventional slots, so they are named here.\n load: (loader, state) =>\n loadPaints(loader, { fills: [state.fill], shadows: [state.barShadow], strokes: [state.barStroke] }),\n commands: {\n /**\n * Raise every column from the baseline by tweening `growth` `0 → 1`.\n *\n * The mirror of `ChartLine.enter`: same contract that the resting state is\n * the finished one, so the chart arms this back to 0 before animating rather\n * than the node hiding itself at construction. No priming to 0 first: the\n * value at `t` is a function of `t`, so a repeat call re-raises by definition.\n */\n enter: (timeline) => {\n const ease = frameEase(timeline)\n return timeline.animate((t) => ({ growth: ease(t) }))\n },\n },\n render: (ctx, state) => {\n if (state.enabled) {\n for (let i = 0; i < state.bars.length; i++) {\n const bar = state.bars[i]\n const ends = endsAt(state, i)\n if (!ends || bar.width <= 0) continue\n const height = Math.abs(ends.end - ends.base)\n if (height <= 0) continue\n\n const radius = bar.round ? Math.min(state.cornerRadius, height) : 0\n // A column per `Graphics2D` rather than one list for the series: the\n // category spotlight dims individual columns, and opacity is a property\n // of the command list rather than of a shape within it.\n const g = new Graphics2D()\n const rect = g.rect({\n x: bar.x,\n y: (ends.base + ends.end) / 2,\n width: bar.width,\n height,\n // Corner keys are visual (the renderer keeps them in y-up author space),\n // so a column reaching *up* rounds its `top*` corners and one reaching\n // down its `bottom*` ones. Only the far end rounds, which is what makes\n // a stack read as a single capped column.\n cornerRadius:\n radius > 0\n ? ends.end >= ends.base\n ? { topLeft: radius, topRight: radius, bottomLeft: 0, bottomRight: 0 }\n : { bottomLeft: radius, bottomRight: radius, topLeft: 0, topRight: 0 }\n : 0,\n })\n rect.shadow(state.barShadow)\n rect.fill(state.fill)\n rect.stroke(state.barStroke)\n ctx.draw(g.opacity(dimAt(state, i)))\n }\n }\n ctx.children()\n },\n})\n\nexport type ChartBars = NodeOf<typeof ChartBars>\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/bar-chart/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,OAAO,EAAE,QAAQ,EAAE,cAAc,EAAE,MAAM,aAAa,CAAA;AAEtD,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAA;AAExC,OAAO,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAA;AAE9C,OAAO,EACL,uBAAuB,EACvB,aAAa,EACb,QAAQ,EACR,cAAc,EACd,iBAAiB,EACjB,oBAAoB,EACpB,cAAc,EACd,eAAe,EACf,aAAa,GACd,MAAM,UAAU,CAAA","sourcesContent":["/**\r\n * The bar chart: grouped or stacked columns off a category axis.\r\n *\r\n * Shares the plot panel, the value axis and the legend with the line chart —\r\n * which is what lets a scene swap one for the other without re-theming — and\r\n * brings its own category axis, since a band scale is not a continuous one.\r\n */\r\nexport { BarChart, barChartDomain } from \"./bar-chart\"\r\nexport type { BarChartDomain, BarChartProps, BarChartValues } from \"./bar-chart\"\r\nexport { ChartBars } from \"./chart-bars\"\r\nexport type { BarGeometry, ChartBarsProps } from \"./chart-bars\"\r\nexport { CategoryAxis } from \"./category-axis\"\r\nexport type { CategoryAxisProps } from \"./category-axis\"\r\nexport {\r\n DEFAULT_BAR_CHART_THEME,\r\n barCategories,\r\n barSpans,\r\n barValueExtent,\r\n lerpBarChartTheme,\r\n resolveBarChartTheme,\r\n resolveBarFill,\r\n resolveBarScale,\r\n stackCapIndex,\r\n} from \"./shared\"\r\nexport type {\r\n BarChartConfig,\r\n BarChartTheme,\r\n BarSeries,\r\n BarSpan,\r\n BarStyleConfig,\r\n} from \"./shared\"\r\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/bar-chart/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,OAAO,EAAE,QAAQ,EAAE,cAAc,EAAE,MAAM,aAAa,CAAA;AAEtD,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAA;AAExC,OAAO,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAA;AAE9C,OAAO,EACL,uBAAuB,EACvB,aAAa,EACb,QAAQ,EACR,cAAc,EACd,iBAAiB,EACjB,oBAAoB,EACpB,cAAc,EACd,eAAe,EACf,aAAa,GACd,MAAM,UAAU,CAAA","sourcesContent":["/**\n * The bar chart: grouped or stacked columns off a category axis.\n *\n * Shares the plot panel, the value axis and the legend with the line chart —\n * which is what lets a scene swap one for the other without re-theming — and\n * brings its own category axis, since a band scale is not a continuous one.\n */\nexport { BarChart, barChartDomain } from \"./bar-chart\"\nexport type { BarChartDomain, BarChartProps, BarChartValues } from \"./bar-chart\"\nexport { ChartBars } from \"./chart-bars\"\nexport type { BarGeometry, ChartBarsProps } from \"./chart-bars\"\nexport { CategoryAxis } from \"./category-axis\"\nexport type { CategoryAxisProps } from \"./category-axis\"\nexport {\n DEFAULT_BAR_CHART_THEME,\n barCategories,\n barSpans,\n barValueExtent,\n lerpBarChartTheme,\n resolveBarChartTheme,\n resolveBarFill,\n resolveBarScale,\n stackCapIndex,\n} from \"./shared\"\nexport type {\n BarChartConfig,\n BarChartTheme,\n BarSeries,\n BarSpan,\n BarStyleConfig,\n} from \"./shared\"\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"shared.js","sourceRoot":"","sources":["../../src/bar-chart/shared.ts"],"names":[],"mappings":"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","sourcesContent":["/**\r\n * Types and pure helpers specific to the {@link BarChart} family: how a row's\r\n * values become the spans its columns cover, how the value axis is scaled around\r\n * them, and the theme the bars add to the shared chart vocabulary.\r\n *\r\n * Everything a line or a pie chart would want too — what a series is, the axis\r\n * domain model, the theme leaf resolvers, the legend and plot-panel branches —\r\n * lives in `../kit/shared.ts` and is imported here.\r\n *\r\n * Kept renderer-agnostic (no `Node` imports) so a host's inspector mapping\r\n * can build a partial {@link BarChartTheme} without\r\n * pulling the components in.\r\n */\r\n\r\nimport {\r\n fillOps,\r\n lerpNumber,\r\n shadowOps,\r\n strokeOps,\r\n textOps,\r\n type DataRecord,\r\n type Fill,\r\n type Shadow,\r\n type ShadowResolved,\r\n type Stroke,\r\n type StrokeResolved,\r\n type TextStyle,\r\n} from \"@motionscript/sdk\"\r\n\r\nimport {\r\n CHART_PALETTE,\r\n DEFAULT_LEGEND,\r\n DEFAULT_PLOT_AREA,\r\n DEFAULT_SERIES_FILLS,\r\n DEFAULT_X_AXIS,\r\n DEFAULT_Y_AXIS,\r\n SPOTLIGHT_DIM,\r\n lerpAxisConfig,\r\n lerpLegendConfig,\r\n lerpPlotArea,\r\n lerpSeriesFills,\r\n niceStep,\r\n num,\r\n resolveAxisConfig,\r\n resolveLegendConfig,\r\n resolvePlotArea,\r\n resolveSeriesFills,\r\n text,\r\n type AxisConfig,\r\n type ChartSeries,\r\n type LegendConfig,\r\n type LegendTheme,\r\n type PlotAreaConfig,\r\n type Range,\r\n} from \"../kit/shared\"\r\n\r\n/**\r\n * One series in a {@link BarChart}: a column of the table drawn as one bar per\r\n * row — side by side with its siblings within each category's slot, or stacked\r\n * on top of them.\r\n *\r\n * The family-wide {@link ChartSeries} under a name that says what it draws here.\r\n */\r\nexport type BarSeries = ChartSeries\r\n\r\n/**\r\n * One drawn bar's span in **data units**: the value its base sits at and the\r\n * value its far end reaches.\r\n *\r\n * A span rather than a height because that is the one shape both modes fit. A\r\n * grouped bar runs from zero to its value; a stacked segment runs from the\r\n * running total below it to that total plus its own value. Negative values fall\r\n * out of the same arithmetic — the span simply runs the other way — so a chart\r\n * with a loss in it needs no special case, only a baseline that isn't the floor.\r\n */\r\nexport interface BarSpan {\r\n /** Value the bar's base sits at — 0 when grouped, the running total when stacked. */\r\n from: number\r\n /** Value its far end reaches. */\r\n to: number\r\n /** The value this bar plots — `to - from`, kept for the figure printed on it. */\r\n value: number\r\n}\r\n\r\n/**\r\n * The spans one row's bars cover, one per series, in series order.\r\n *\r\n * Stacking is a running sum in the order the series are listed, which is also\r\n * the order they're drawn: series 0 sits on the baseline and each later one\r\n * rides on what's below it. A zero-valued series still yields a (degenerate)\r\n * span so the returned list stays index-aligned with the series list.\r\n */\r\nexport function barSpans(\r\n row: DataRecord,\r\n series: BarSeries[],\r\n stacked: boolean\r\n): BarSpan[] {\r\n let running = 0\r\n return series.map((s) => {\r\n const value = num(row, s.field)\r\n if (!stacked) return { from: 0, to: value, value }\r\n const from = running\r\n running += value\r\n return { from, to: running, value }\r\n })\r\n}\r\n\r\n/**\r\n * The index of the series whose segment ends a row's stack — the one that gets\r\n * the rounded cap, so a stack reads as one capped column rather than a pile of\r\n * separate boxes. `-1` when every value in the row is zero.\r\n *\r\n * Read off the *last non-zero* series rather than the last one listed, so a\r\n * series that happens to be 0 in this row doesn't take the cap and leave the\r\n * segment below it square.\r\n */\r\nexport function stackCapIndex(row: DataRecord, series: BarSeries[]): number {\r\n let last = -1\r\n for (let i = 0; i < series.length; i++) {\r\n if (num(row, series[i].field) !== 0) last = i\r\n }\r\n return last\r\n}\r\n\r\n/** The captions beneath the baseline: one per row, read off the category column. */\r\nexport function barCategories(data: DataRecord[], field: string): string[] {\r\n return data.map((row) => text(row, field))\r\n}\r\n\r\n/**\r\n * The `[min, max]` of everything the chart draws, **zero always included**.\r\n *\r\n * Zero is in the range whatever the data says, because a bar chart's baseline is\r\n * zero: a scale starting at 40 draws a column three times its neighbour for a\r\n * 10% difference, which is the classic way to lie with one. A chart that really\r\n * does want a cropped axis says so with an explicit range.\r\n */\r\nexport function barValueExtent(\r\n data: DataRecord[],\r\n series: BarSeries[],\r\n stacked: boolean\r\n): Range {\r\n let lo = 0\r\n let hi = 0\r\n for (const row of data) {\r\n for (const span of barSpans(row, series, stacked)) {\r\n lo = Math.min(lo, span.from, span.to)\r\n hi = Math.max(hi, span.from, span.to)\r\n }\r\n }\r\n return lo < hi ? [lo, hi] : [0, 1]\r\n}\r\n\r\n/**\r\n * Effective value scale: the explicit `range`, else the data's extent nudged out\r\n * to tidy step bounds so the markers read as round numbers.\r\n *\r\n * `divisions` is how many segments the auto-range is stepped into (one fewer\r\n * than the marker count), so the ticks the axis draws land on the bounds this\r\n * picks.\r\n */\r\nexport function resolveBarScale(\r\n data: DataRecord[],\r\n series: BarSeries[],\r\n stacked: boolean,\r\n range: Range | undefined,\r\n divisions: number\r\n): Range {\r\n if (range) return range\r\n const [lo, hi] = barValueExtent(data, series, stacked)\r\n const segments = Math.max(1, Math.round(divisions))\r\n const step = niceStep((hi - lo) / segments || 1)\r\n return [Math.floor(lo / step) * step, Math.ceil(hi / step) * step]\r\n}\r\n\r\n// ---- Theme ---------------------------------------------------------------\r\n\r\n/** How the columns themselves are drawn and how the slot is divided up. */\r\nexport interface BarStyleConfig {\r\n /**\r\n * Radius of a column's far-end corners, in px. Only the outer end rounds — an\r\n * interior stacked segment stays square, so the stack reads as one column.\r\n */\r\n cornerRadius: number\r\n /**\r\n * Fraction of its category's slot the bars fill, `[0, 1]`. The remainder is\r\n * the channel between one category and the next, which is what makes a bar\r\n * chart read as groups rather than as a solid block.\r\n */\r\n slotFill: number\r\n /** Gap between the grouped bars *within* one slot, in px. Ignored when stacked. */\r\n groupGap: number\r\n /** Optional outline on every column. */\r\n stroke: Stroke\r\n /** Optional drop shadow on every column. */\r\n shadow: Shadow\r\n}\r\n\r\n/** A {@link BarChart}'s full, resolved visual theme. */\r\nexport interface BarChartConfig {\r\n /**\r\n * Default per-series bar paint, cycled by series index (`i % length`). A\r\n * series' own `fill`, when set, replaces its entry outright.\r\n */\r\n seriesFills: Fill[]\r\n bar: BarStyleConfig\r\n /** The category axis beneath the baseline. */\r\n xAxis: AxisConfig\r\n /** The numeric value axis up the left. */\r\n yAxis: AxisConfig\r\n plotArea: PlotAreaConfig\r\n legend: LegendConfig\r\n /** The figure printed on each column, when the chart prints them. */\r\n valueStyle: TextStyle\r\n /** How far everything *outside* a spotlit series or category fades. */\r\n spotlight: { dimOpacity: number }\r\n}\r\n\r\n/** Author-facing partial theme — every branch optional and deep-merged. */\r\nexport type BarChartTheme = {\r\n seriesFills?: Fill[]\r\n bar?: Partial<BarStyleConfig>\r\n xAxis?: Partial<AxisConfig>\r\n yAxis?: Partial<AxisConfig>\r\n plotArea?: Partial<PlotAreaConfig>\r\n legend?: LegendTheme\r\n valueStyle?: TextStyle\r\n spotlight?: Partial<BarChartConfig[\"spotlight\"]>\r\n}\r\n\r\n/** The bar chart's out-of-the-box look. */\r\nexport const DEFAULT_BAR_CHART_THEME: BarChartConfig = {\r\n seriesFills: DEFAULT_SERIES_FILLS,\r\n bar: {\r\n cornerRadius: 6,\r\n slotFill: 0.72,\r\n groupGap: 6,\r\n stroke: [],\r\n shadow: [],\r\n },\r\n // A category axis names its slots, so its captions are set like a caption\r\n // rather than like a numeric marker: same size, less weight than the line\r\n // chart's ticks, and centred on the slot.\r\n xAxis: {\r\n ...DEFAULT_X_AXIS,\r\n labelStyle: { ...DEFAULT_X_AXIS.labelStyle, fontWeight: 600 },\r\n tickPadding: 16,\r\n },\r\n yAxis: DEFAULT_Y_AXIS,\r\n plotArea: DEFAULT_PLOT_AREA,\r\n legend: DEFAULT_LEGEND,\r\n valueStyle: {\r\n fontSize: 24,\r\n fontWeight: 700,\r\n textAlign: \"center\",\r\n fill: CHART_PALETTE.markerColor,\r\n },\r\n spotlight: { dimOpacity: SPOTLIGHT_DIM },\r\n}\r\n\r\nfunction resolveBarStyle(\r\n bar: Partial<BarStyleConfig> | undefined,\r\n previous: BarStyleConfig\r\n): BarStyleConfig {\r\n return {\r\n cornerRadius: bar?.cornerRadius ?? previous.cornerRadius,\r\n slotFill: bar?.slotFill ?? previous.slotFill,\r\n groupGap: bar?.groupGap ?? previous.groupGap,\r\n stroke: strokeOps.resolve(bar?.stroke ?? previous.stroke),\r\n shadow: shadowOps.resolve(bar?.shadow ?? previous.shadow),\r\n }\r\n}\r\n\r\nfunction lerpBarStyle(\r\n from: BarStyleConfig,\r\n to: BarStyleConfig,\r\n t: number\r\n): BarStyleConfig {\r\n return {\r\n cornerRadius: lerpNumber(from.cornerRadius, to.cornerRadius, t),\r\n slotFill: lerpNumber(from.slotFill, to.slotFill, t),\r\n groupGap: lerpNumber(from.groupGap, to.groupGap, t),\r\n stroke: strokeOps.lerp(\r\n from.stroke as StrokeResolved[],\r\n to.stroke as StrokeResolved[],\r\n t\r\n ),\r\n shadow: shadowOps.lerp(\r\n from.shadow as ShadowResolved[],\r\n to.shadow as ShadowResolved[],\r\n t\r\n ),\r\n }\r\n}\r\n\r\n/**\r\n * {@link BarChart}'s `theme` entry's resolve: merges the author's partial\r\n * {@link BarChartTheme} onto `previous` (falling back to\r\n * {@link DEFAULT_BAR_CHART_THEME} on first resolution), fully resolving every\r\n * leaf so the tween below always has resolved data to interpolate.\r\n */\r\nexport function resolveBarChartTheme(\r\n theme: BarChartTheme | undefined,\r\n previous?: BarChartConfig\r\n): BarChartConfig {\r\n const base = previous ?? DEFAULT_BAR_CHART_THEME\r\n return {\r\n seriesFills: resolveSeriesFills(theme?.seriesFills, base.seriesFills),\r\n bar: resolveBarStyle(theme?.bar, base.bar),\r\n xAxis: resolveAxisConfig(theme?.xAxis, base.xAxis),\r\n yAxis: resolveAxisConfig(theme?.yAxis, base.yAxis),\r\n plotArea: resolvePlotArea(theme?.plotArea, base.plotArea),\r\n legend: resolveLegendConfig(theme?.legend, base.legend),\r\n valueStyle: textOps.resolveStyle(theme?.valueStyle, base.valueStyle),\r\n spotlight: {\r\n dimOpacity: theme?.spotlight?.dimOpacity ?? base.spotlight.dimOpacity,\r\n },\r\n }\r\n}\r\n\r\n/** {@link BarChart}'s `theme` entry's lerp — leaf by leaf. */\r\nexport function lerpBarChartTheme(\r\n from: BarChartConfig,\r\n to: BarChartConfig,\r\n t: number\r\n): BarChartConfig {\r\n return {\r\n seriesFills: lerpSeriesFills(from.seriesFills, to.seriesFills, t),\r\n bar: lerpBarStyle(from.bar, to.bar, t),\r\n xAxis: lerpAxisConfig(from.xAxis, to.xAxis, t),\r\n yAxis: lerpAxisConfig(from.yAxis, to.yAxis, t),\r\n plotArea: lerpPlotArea(from.plotArea, to.plotArea, t),\r\n legend: lerpLegendConfig(from.legend, to.legend, t),\r\n valueStyle: textOps.lerpStyle(from.valueStyle, to.valueStyle, t),\r\n spotlight: {\r\n dimOpacity: lerpNumber(\r\n from.spotlight.dimOpacity,\r\n to.spotlight.dimOpacity,\r\n t\r\n ),\r\n },\r\n }\r\n}\r\n\r\n/** The paint series `index` is drawn in: its own override, else the palette's entry. */\r\nexport function resolveBarFill(\r\n palette: Fill[],\r\n series: BarSeries,\r\n index: number\r\n): Fill {\r\n if (series.fill != null) return fillOps.resolve(series.fill)\r\n if (palette.length === 0) return \"white\"\r\n return palette[index % palette.length]\r\n}\r\n"]}
1
+ {"version":3,"file":"shared.js","sourceRoot":"","sources":["../../src/bar-chart/shared.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Types and pure helpers specific to the {@link BarChart} family: how a row's\n * values become the spans its columns cover, how the value axis is scaled around\n * them, and the theme the bars add to the shared chart vocabulary.\n *\n * Everything a line or a pie chart would want too — what a series is, the axis\n * domain model, the theme leaf resolvers, the legend and plot-panel branches —\n * lives in `../kit/shared.ts` and is imported here.\n *\n * Kept renderer-agnostic (no `Node` imports) so a host's inspector mapping\n * can build a partial {@link BarChartTheme} without\n * pulling the components in.\n */\n\nimport {\n fillOps,\n lerpNumber,\n shadowOps,\n strokeOps,\n textOps,\n type DataRecord,\n type Fill,\n type Shadow,\n type ShadowResolved,\n type Stroke,\n type StrokeResolved,\n type TextStyle,\n} from \"@motionscript/sdk\"\n\nimport {\n CHART_PALETTE,\n DEFAULT_LEGEND,\n DEFAULT_PLOT_AREA,\n DEFAULT_SERIES_FILLS,\n DEFAULT_X_AXIS,\n DEFAULT_Y_AXIS,\n SPOTLIGHT_DIM,\n lerpAxisConfig,\n lerpLegendConfig,\n lerpPlotArea,\n lerpSeriesFills,\n niceStep,\n num,\n resolveAxisConfig,\n resolveLegendConfig,\n resolvePlotArea,\n resolveSeriesFills,\n text,\n type AxisConfig,\n type ChartSeries,\n type LegendConfig,\n type LegendTheme,\n type PlotAreaConfig,\n type Range,\n} from \"../kit/shared\"\n\n/**\n * One series in a {@link BarChart}: a column of the table drawn as one bar per\n * row — side by side with its siblings within each category's slot, or stacked\n * on top of them.\n *\n * The family-wide {@link ChartSeries} under a name that says what it draws here.\n */\nexport type BarSeries = ChartSeries\n\n/**\n * One drawn bar's span in **data units**: the value its base sits at and the\n * value its far end reaches.\n *\n * A span rather than a height because that is the one shape both modes fit. A\n * grouped bar runs from zero to its value; a stacked segment runs from the\n * running total below it to that total plus its own value. Negative values fall\n * out of the same arithmetic — the span simply runs the other way — so a chart\n * with a loss in it needs no special case, only a baseline that isn't the floor.\n */\nexport interface BarSpan {\n /** Value the bar's base sits at — 0 when grouped, the running total when stacked. */\n from: number\n /** Value its far end reaches. */\n to: number\n /** The value this bar plots — `to - from`, kept for the figure printed on it. */\n value: number\n}\n\n/**\n * The spans one row's bars cover, one per series, in series order.\n *\n * Stacking is a running sum in the order the series are listed, which is also\n * the order they're drawn: series 0 sits on the baseline and each later one\n * rides on what's below it. A zero-valued series still yields a (degenerate)\n * span so the returned list stays index-aligned with the series list.\n */\nexport function barSpans(\n row: DataRecord,\n series: BarSeries[],\n stacked: boolean\n): BarSpan[] {\n let running = 0\n return series.map((s) => {\n const value = num(row, s.field)\n if (!stacked) return { from: 0, to: value, value }\n const from = running\n running += value\n return { from, to: running, value }\n })\n}\n\n/**\n * The index of the series whose segment ends a row's stack — the one that gets\n * the rounded cap, so a stack reads as one capped column rather than a pile of\n * separate boxes. `-1` when every value in the row is zero.\n *\n * Read off the *last non-zero* series rather than the last one listed, so a\n * series that happens to be 0 in this row doesn't take the cap and leave the\n * segment below it square.\n */\nexport function stackCapIndex(row: DataRecord, series: BarSeries[]): number {\n let last = -1\n for (let i = 0; i < series.length; i++) {\n if (num(row, series[i].field) !== 0) last = i\n }\n return last\n}\n\n/** The captions beneath the baseline: one per row, read off the category column. */\nexport function barCategories(data: DataRecord[], field: string): string[] {\n return data.map((row) => text(row, field))\n}\n\n/**\n * The `[min, max]` of everything the chart draws, **zero always included**.\n *\n * Zero is in the range whatever the data says, because a bar chart's baseline is\n * zero: a scale starting at 40 draws a column three times its neighbour for a\n * 10% difference, which is the classic way to lie with one. A chart that really\n * does want a cropped axis says so with an explicit range.\n */\nexport function barValueExtent(\n data: DataRecord[],\n series: BarSeries[],\n stacked: boolean\n): Range {\n let lo = 0\n let hi = 0\n for (const row of data) {\n for (const span of barSpans(row, series, stacked)) {\n lo = Math.min(lo, span.from, span.to)\n hi = Math.max(hi, span.from, span.to)\n }\n }\n return lo < hi ? [lo, hi] : [0, 1]\n}\n\n/**\n * Effective value scale: the explicit `range`, else the data's extent nudged out\n * to tidy step bounds so the markers read as round numbers.\n *\n * `divisions` is how many segments the auto-range is stepped into (one fewer\n * than the marker count), so the ticks the axis draws land on the bounds this\n * picks.\n */\nexport function resolveBarScale(\n data: DataRecord[],\n series: BarSeries[],\n stacked: boolean,\n range: Range | undefined,\n divisions: number\n): Range {\n if (range) return range\n const [lo, hi] = barValueExtent(data, series, stacked)\n const segments = Math.max(1, Math.round(divisions))\n const step = niceStep((hi - lo) / segments || 1)\n return [Math.floor(lo / step) * step, Math.ceil(hi / step) * step]\n}\n\n// ---- Theme ---------------------------------------------------------------\n\n/** How the columns themselves are drawn and how the slot is divided up. */\nexport interface BarStyleConfig {\n /**\n * Radius of a column's far-end corners, in px. Only the outer end rounds — an\n * interior stacked segment stays square, so the stack reads as one column.\n */\n cornerRadius: number\n /**\n * Fraction of its category's slot the bars fill, `[0, 1]`. The remainder is\n * the channel between one category and the next, which is what makes a bar\n * chart read as groups rather than as a solid block.\n */\n slotFill: number\n /** Gap between the grouped bars *within* one slot, in px. Ignored when stacked. */\n groupGap: number\n /** Optional outline on every column. */\n stroke: Stroke\n /** Optional drop shadow on every column. */\n shadow: Shadow\n}\n\n/** A {@link BarChart}'s full, resolved visual theme. */\nexport interface BarChartConfig {\n /**\n * Default per-series bar paint, cycled by series index (`i % length`). A\n * series' own `fill`, when set, replaces its entry outright.\n */\n seriesFills: Fill[]\n bar: BarStyleConfig\n /** The category axis beneath the baseline. */\n xAxis: AxisConfig\n /** The numeric value axis up the left. */\n yAxis: AxisConfig\n plotArea: PlotAreaConfig\n legend: LegendConfig\n /** The figure printed on each column, when the chart prints them. */\n valueStyle: TextStyle\n /** How far everything *outside* a spotlit series or category fades. */\n spotlight: { dimOpacity: number }\n}\n\n/** Author-facing partial theme — every branch optional and deep-merged. */\nexport type BarChartTheme = {\n seriesFills?: Fill[]\n bar?: Partial<BarStyleConfig>\n xAxis?: Partial<AxisConfig>\n yAxis?: Partial<AxisConfig>\n plotArea?: Partial<PlotAreaConfig>\n legend?: LegendTheme\n valueStyle?: TextStyle\n spotlight?: Partial<BarChartConfig[\"spotlight\"]>\n}\n\n/** The bar chart's out-of-the-box look. */\nexport const DEFAULT_BAR_CHART_THEME: BarChartConfig = {\n seriesFills: DEFAULT_SERIES_FILLS,\n bar: {\n cornerRadius: 6,\n slotFill: 0.72,\n groupGap: 6,\n stroke: [],\n shadow: [],\n },\n // A category axis names its slots, so its captions are set like a caption\n // rather than like a numeric marker: same size, less weight than the line\n // chart's ticks, and centred on the slot.\n xAxis: {\n ...DEFAULT_X_AXIS,\n labelStyle: { ...DEFAULT_X_AXIS.labelStyle, fontWeight: 600 },\n tickPadding: 16,\n },\n yAxis: DEFAULT_Y_AXIS,\n plotArea: DEFAULT_PLOT_AREA,\n legend: DEFAULT_LEGEND,\n valueStyle: {\n fontSize: 24,\n fontWeight: 700,\n textAlign: \"center\",\n fill: CHART_PALETTE.markerColor,\n },\n spotlight: { dimOpacity: SPOTLIGHT_DIM },\n}\n\nfunction resolveBarStyle(\n bar: Partial<BarStyleConfig> | undefined,\n previous: BarStyleConfig\n): BarStyleConfig {\n return {\n cornerRadius: bar?.cornerRadius ?? previous.cornerRadius,\n slotFill: bar?.slotFill ?? previous.slotFill,\n groupGap: bar?.groupGap ?? previous.groupGap,\n stroke: strokeOps.resolve(bar?.stroke ?? previous.stroke),\n shadow: shadowOps.resolve(bar?.shadow ?? previous.shadow),\n }\n}\n\nfunction lerpBarStyle(\n from: BarStyleConfig,\n to: BarStyleConfig,\n t: number\n): BarStyleConfig {\n return {\n cornerRadius: lerpNumber(from.cornerRadius, to.cornerRadius, t),\n slotFill: lerpNumber(from.slotFill, to.slotFill, t),\n groupGap: lerpNumber(from.groupGap, to.groupGap, t),\n stroke: strokeOps.lerp(\n from.stroke as StrokeResolved[],\n to.stroke as StrokeResolved[],\n t\n ),\n shadow: shadowOps.lerp(\n from.shadow as ShadowResolved[],\n to.shadow as ShadowResolved[],\n t\n ),\n }\n}\n\n/**\n * {@link BarChart}'s `theme` entry's resolve: merges the author's partial\n * {@link BarChartTheme} onto `previous` (falling back to\n * {@link DEFAULT_BAR_CHART_THEME} on first resolution), fully resolving every\n * leaf so the tween below always has resolved data to interpolate.\n */\nexport function resolveBarChartTheme(\n theme: BarChartTheme | undefined,\n previous?: BarChartConfig\n): BarChartConfig {\n const base = previous ?? DEFAULT_BAR_CHART_THEME\n return {\n seriesFills: resolveSeriesFills(theme?.seriesFills, base.seriesFills),\n bar: resolveBarStyle(theme?.bar, base.bar),\n xAxis: resolveAxisConfig(theme?.xAxis, base.xAxis),\n yAxis: resolveAxisConfig(theme?.yAxis, base.yAxis),\n plotArea: resolvePlotArea(theme?.plotArea, base.plotArea),\n legend: resolveLegendConfig(theme?.legend, base.legend),\n valueStyle: textOps.resolveStyle(theme?.valueStyle, base.valueStyle),\n spotlight: {\n dimOpacity: theme?.spotlight?.dimOpacity ?? base.spotlight.dimOpacity,\n },\n }\n}\n\n/** {@link BarChart}'s `theme` entry's lerp — leaf by leaf. */\nexport function lerpBarChartTheme(\n from: BarChartConfig,\n to: BarChartConfig,\n t: number\n): BarChartConfig {\n return {\n seriesFills: lerpSeriesFills(from.seriesFills, to.seriesFills, t),\n bar: lerpBarStyle(from.bar, to.bar, t),\n xAxis: lerpAxisConfig(from.xAxis, to.xAxis, t),\n yAxis: lerpAxisConfig(from.yAxis, to.yAxis, t),\n plotArea: lerpPlotArea(from.plotArea, to.plotArea, t),\n legend: lerpLegendConfig(from.legend, to.legend, t),\n valueStyle: textOps.lerpStyle(from.valueStyle, to.valueStyle, t),\n spotlight: {\n dimOpacity: lerpNumber(\n from.spotlight.dimOpacity,\n to.spotlight.dimOpacity,\n t\n ),\n },\n }\n}\n\n/** The paint series `index` is drawn in: its own override, else the palette's entry. */\nexport function resolveBarFill(\n palette: Fill[],\n series: BarSeries,\n index: number\n): Fill {\n if (series.fill != null) return fillOps.resolve(series.fill)\n if (palette.length === 0) return \"white\"\n return palette[index % palette.length]\n}\n"]}
@@ -1,2 +1,2 @@
1
1
  import{fillOps as b,lerpNumber as y,shadowOps as M,strokeOps as g,insetsOps as w,textOps as S}from"@motionscript/sdk";import{niceStep as ft,enterBudget as Z}from"@motionscript/sdk/component";function ge(e,t){return{label:e?.label??t?.label??"",range:e?.range??t?.range,ticks:e?.ticks??t?.ticks,format:e?.format??t?.format}}function D(e,t,r){return{label:r<.5?e.label:t.label,range:xe(e.range,t.range,r),ticks:me(e.ticks,t.ticks,r),format:r<.5?e.format:t.format}}function xe(e,t,r){return!e||!t?r<.5?e:t:[y(e[0],t[0],r),y(e[1],t[1],r)]}function me(e,t,r){if(!e||!t)return r<.5?e:t;let n=Math.max(e.length,t.length),i=[];for(let o=0;o<n;o++)i.push(y(e[o]??t[o],t[o]??e[o],r));return i}function Be(e,t){if(t?.range===void 0&&t?.ticks===void 0)return;let r=ge(t,e);if(t.ticks===void 0&&t.range!==void 0){let n=Math.max(2,e.ticks?.length??2);r.ticks=Q(t.range,n-1)}return r}function B(e){return e.blend}function Ye(e,t,r){let n=t.range!==e.range,i=n&&t.range&&t.ticks&&t.ticks===e.ticks?{...t,ticks:Q(t.range,Math.max(2,t.ticks.length)-1)}:t,o=B(e)!==void 0,l=o||n||i.ticks!==e.ticks,s=o||e.range===void 0||e.ticks===void 0||i.range===void 0||i.ticks===void 0;return!l||!s?D(e,i,r):r===0?e:r===1?i:{...D(e,i,r),blend:{from:e,to:i,t:r}}}function X(e,t){let r=B(e);return r?D(X(r.from,t),X(r.to,t),r.t):t(e)}var E={plotFill:"#15130e",borderColor:"#4a4036",gridColor:"#2c2820",markerColor:"#8a7f70",regionColor:"#d99a3a"},Y=["#d9603b","#8194ad","#e0a93b","#5e9d6b","#9ec85a"],$=.2;function $e(e){return e<0?0:e>1?1:e}function He(e,t){if(t===void 0)return 0;let r=e[t];return typeof r=="number"&&Number.isFinite(r)?r:0}function Ve(e,t){if(t===void 0||t==="")return"";let r=e[t];return r==null?"":String(r)}function We(e,t=[]){if(e.length===0)return[];let r=new Set(t),n=new Set,i=[];for(let o of e)for(let l of Object.keys(o))!r.has(l)&&!n.has(l)&&(n.add(l),i.push(l));return i.filter(o=>{let l=!1;for(let s of e){let a=s[o];if(!(a===void 0||a==="")){if(typeof a!="number")return!1;l=!0}}return l})}var H={fontSize:24,fontWeight:700,textAlign:"center",fill:E.markerColor},V={fontSize:30,fontWeight:600,textAlign:"center",fill:E.markerColor},W={weight:2,fill:E.gridColor},j={labelStyle:H,titleStyle:V,lineStyle:W,tickPadding:20,titlePadding:4},O={labelStyle:{...H,fontWeight:500,textAlign:"right"},titleStyle:V,lineStyle:W,tickPadding:40,titlePadding:24},v={fill:[],stroke:{weight:6,fill:E.borderColor},shadow:[]},K={padding:0,textStyle:{fontSize:30,fontWeight:600,textAlign:"left",fill:E.markerColor},itemGap:44,markerStyle:{width:60,height:24,borderRadius:6,stroke:[],shadow:[]}},je=Y.map(e=>({weight:4,fill:e})),Ke=Y.map(e=>e);function Je(e,t){return{labelStyle:S.resolveStyle(e?.labelStyle,t.labelStyle),titleStyle:S.resolveStyle(e?.titleStyle,t.titleStyle),lineStyle:g.resolve(e?.lineStyle??t.lineStyle),tickPadding:w.resolve(e?.tickPadding??t.tickPadding,t.tickPadding),titlePadding:w.resolve(e?.titlePadding??t.titlePadding,t.titlePadding)}}function Qe(e,t,r){return{labelStyle:S.lerpStyle(e.labelStyle,t.labelStyle,r),titleStyle:S.lerpStyle(e.titleStyle,t.titleStyle,r),lineStyle:g.lerp(e.lineStyle,t.lineStyle,r),tickPadding:w.lerp(e.tickPadding,t.tickPadding,r),titlePadding:w.lerp(e.titlePadding,t.titlePadding,r)}}function Ze(e,t){return{fill:b.resolve(e?.fill??t.fill),stroke:g.resolve(e?.stroke??t.stroke),shadow:M.resolve(e?.shadow??t.shadow)}}function qe(e,t,r){return{fill:b.lerp(e.fill,t.fill,r),stroke:g.lerp(e.stroke,t.stroke,r),shadow:M.lerp(e.shadow,t.shadow,r)}}function ye(e,t){return{width:e?.width??t.width,height:e?.height??t.height,borderRadius:e?.borderRadius??t.borderRadius,stroke:g.resolve(e?.stroke??t.stroke),shadow:M.resolve(e?.shadow??t.shadow)}}function ke(e,t,r){return{width:y(e.width,t.width,r),height:y(e.height,t.height,r),borderRadius:y(e.borderRadius,t.borderRadius,r),stroke:g.lerp(e.stroke,t.stroke,r),shadow:M.lerp(e.shadow,t.shadow,r)}}function et(e,t){return{padding:w.resolve(e?.padding??t.padding,t.padding),textStyle:S.resolveStyle(e?.textStyle,t.textStyle),itemGap:e?.itemGap??t.itemGap,markerStyle:ye(e?.markerStyle,t.markerStyle)}}function tt(e,t,r){return{padding:w.lerp(e.padding,t.padding,r),textStyle:S.lerpStyle(e.textStyle,t.textStyle,r),itemGap:y(e.itemGap,t.itemGap,r),markerStyle:ke(e.markerStyle,t.markerStyle,r)}}function rt(e,t){return(e??t).map(r=>g.resolve(r))}function nt(e,t,r){let n=Math.max(e.length,t.length),i=[];for(let o=0;o<n;o++){let l=e[o]??t[o],s=t[o]??e[o];i.push(g.lerp(l,s,r))}return i}function ot(e,t){return(e??t).map(r=>b.resolve(r))}function it(e,t,r){let n=Math.max(e.length,t.length),i=[];for(let o=0;o<n;o++){let l=e[o]??t[o],s=t[o]??e[o];i.push(b.lerp(l,s,r))}return i}function we(e,t,r){let n=e.length>0?e[r%e.length]:{weight:4,fill:"white"};if(t.fill==null)return n;let i=g.resolve(n);if(i.length===0)return{weight:4,fill:t.fill};let o=[...i];return o[o.length-1]={...o[o.length-1],fill:b.resolve(t.fill)},o}function lt(e,t,r){let n=g.resolve(we(e,t,r));return n[n.length-1]?.fill??"white"}function st(e,t,r){return t??(e.length===0?"white":e[r%e.length])}function J(e,t){if(Number.isInteger(e))return String(e);let r=Math.max(1,t===void 0?1:Se(t));return e.toFixed(r).replace(/(\.\d*?)0+$/,"$1").replace(/\.$/,"")}function Se(e){if(!(e>0)||!Number.isFinite(e))return 1;for(let t=0;t<=6;t++){let r=e*10**t;if(Math.abs(r-Math.round(r))<1e-6*Math.max(1,r))return t}return Math.min(6,Math.max(1,Math.ceil(-Math.log10(e))+1))}function at(e){let t=[...e].filter(Number.isFinite).sort((n,i)=>n-i),r;for(let n=1;n<t.length;n++){let i=t[n]-t[n-1];i>1e-12&&(r===void 0||i<r)&&(r=i)}return r}function ct(e){let t=e*100;return Number.isInteger(t)?`${t}%`:`${t.toFixed(1).replace(/\.0$/,"")}%`}function Q([e,t],r){let n=Math.max(2,Math.round(r));return Array.from({length:n+1},(i,o)=>e+(t-e)/n*o)}import{Rect as T}from"@motionscript/sdk";import{Rect as re,RotatedBox as Te,Text as ne,Types as G,component2d as Le,insetsOps as oe,textOps as ie}from"@motionscript/sdk";import{Types as be}from"@motionscript/sdk";function h(e,t){return be.custom(e,t)}function Ee(e){return e}import{Types as ve,easeOut as Ae}from"@motionscript/sdk";var Pe=e=>e;function A(e){return e.easing?Pe:Ae()}function q(e,t){let r=e.duration,n=t.reduce((o,l)=>Math.max(o,l.at+l.duration),0),i=n>r&&n>0?r/n:1;return e.together(t.map(({at:o,duration:l,piece:s})=>{let a=o*i,f=l*i;return e.sequence([e.wait(a),s,e.wait(Math.max(0,r-a-f))])}))}var R={opacity:1,shift:0};function ee(){return ve.custom({},{private:!0,resolve:(e,t)=>({...t,...e})})}function P(e,t){return e[t]??R}function te(e,t){let r={};for(let n of e)r[n]=t;return r}function C(e,t,r,n){return e.animate(i=>{let o=n(i);return{arrivals:{[t]:{opacity:r.opacity+(R.opacity-r.opacity)*o,shift:r.shift+(R.shift-r.shift)*o}}}})}var _e=24;function Me(e,t){let r=e.markers.indexOf(t);return(r>=0?e.captions[r]:void 0)??J(t)}var le=Le({key:"verticalAxis",state:{range:h(()=>[0,1]),markers:h(()=>[]),captions:h(()=>[]),axis:G.custom(O),label:"",arrivals:ee()},defaults:{width:"fill",height:"fill"},children:e=>{let t=oe.resolve(e.axis.tickPadding).right,r=oe.resolve(e.axis.titlePadding).right,n=[],i=()=>n[0]?.parent?.layoutBounds??{width:0,height:0},o=()=>[...e.markers].sort((f,c)=>c-f),l=f=>{if(f===void 0)return 0;let[c,p]=e.range;return p<=c?0:(f-c)/(p-c)},s=e.markers.map((f,c)=>{let p=new ne({...ie.styleProps(e.axis.labelStyle),text:()=>{let u=o()[c];return u===void 0?"":Me(e,u)},y:()=>{let u=i().height,d=(l(o()[c])-.5)*u,m=u/2-p.layoutBounds.height/2;return Math.min(d,m)},opacity:()=>P(e.arrivals,c).opacity,x:()=>P(e.arrivals,c).shift});return p}),a=[];return e.label!==""&&a.push(new Te({angle:-90,width:"hug",height:"hug",children:new ne({...ie.styleProps(e.axis.titleStyle),text:e.label,opacity:()=>P(e.arrivals,"label").opacity,x:()=>P(e.arrivals,"label").shift})})),a.push(new re({height:"fill",width:"hug",padding:{right:t},children:s})),n.push(new re({flow:"horizontal",gap:r,children:a})),n},commands:{enter:{args:{stagger:G.custom(void 0),slide:G.number(_e)},run:(e,t)=>{let{stagger:r,slide:n}=e.args,i=t.markers.length,o=t.label!=="",l={opacity:0,shift:n},s=[...t.markers.keys(),...o?["label"]:[]],a=e.animate(()=>({arrivals:te(s,l)}));if(i===0)return a;let{step:f,per:c,caption:p}=Z(e.duration,i,o,r),u=A(e),d=t.markers.map((k,N)=>({at:(i-1-N)*f,duration:c,piece:C(e,N,l,u)})),m=o?[{at:(i-1)*f+c,duration:p,piece:C(e,"label",l,u)}]:[];return e.together([a,q(e,[...d,...m])])}}}});var Oe=24;function vt(e){return new T({flow:"vertical",width:"fill",height:"fill",padding:{top:Oe},children:[new T({flow:"horizontal",height:"fill",width:"fill",children:[e.axis,e.plot]}),new T({flow:"horizontal",width:"fill",children:[e.gutter,new T({flow:"vertical",width:"fill",gap:20,children:[...e.below]})]})]})}function At(e){for(let t=e.length-1;t>=0;t--){let r=e[t];if(!T.is(r))continue;let[n,i]=r.children,o=n?.children[0],l=n?.children[1],s=i?.children[1];if(!(!o||!le.is(o)||!l||!s))return{axis:o,plot:l.children,below:s.children}}return null}function Pt(e){let t=e?.layoutBounds;return{width:t?.width??0,height:t?.height??0}}import{Graphics2D as Fe,Types as De,component2d as Re,strokeOps as se}from"@motionscript/sdk";function F(e,t){for(let r of t.fills??[])e.fill(r);for(let r of t.strokes??[])for(let n of r)e.fill(n.fill);for(let r of t.shadows??[])for(let n of r)e.fill(n.fill)}var Ft=Re({key:"chartBorder",state:{stroke:De.custom(v.stroke,{resolve:se.resolve,lerp:se.lerp})},load:(e,t)=>F(e,{strokes:[t.stroke]}),render:(e,t)=>{let{width:r,height:n}=e.size;r<=0||n<=0||e.draw(new Fe().rect({x:0,y:0,width:r,height:n}).stroke(t.stroke))}});import{Graphics2D as I,Types as z,component2d as Ce,fillOps as ae,shadowOps as ce,strokeOps as de}from"@motionscript/sdk";var he=e=>z.custom(e,{resolve:de.resolve,lerp:de.lerp}),zt=Ce({key:"chartCanvas",state:{fill:z.custom(v.fill,{resolve:ae.resolve,lerp:ae.lerp}),shadow:z.custom(v.shadow,{resolve:ce.resolve,lerp:ce.lerp}),xGridStroke:he(j.lineStyle),yGridStroke:he(O.lineStyle),xMarkers:h(()=>[]),yMarkers:h(()=>[]),xExtent:h(()=>[0,1]),yExtent:h(()=>[0,1])},load:(e,t)=>F(e,{fills:[t.fill],shadows:[t.shadow],strokes:[t.xGridStroke,t.yGridStroke]}),render:(e,t)=>{let{width:r,height:n}=e.size;if(r<=0||n<=0)return;e.draw(new I().rect({x:0,y:0,width:r,height:n}).shadow(t.shadow).fill(t.fill));let[i,o]=t.xExtent,[l,s]=t.yExtent,a=d=>o<=i?0:(d-i)/(o-i),f=d=>s<=l?0:(d-l)/(s-l),c=d=>d>1e-6&&d<1-1e-6,p=t.xGridStroke[0];if(p){let d=new I;for(let m of t.xMarkers){let k=a(m);c(k)&&d.rect({x:-r/2+k*r,y:0,width:p.weight,height:n})}e.draw(d.fill(p.fill))}let u=t.yGridStroke[0];if(u){let d=new I;for(let m of t.yMarkers){let k=f(m);c(k)&&d.rect({x:0,y:-n/2+k*n,width:r,height:u.weight})}e.draw(d.fill(u.fill))}}});import{Flow as _,Paint as x,Rect as Ge,Shapes as U,Text as Ie,Types as L,component2d as ue,textOps as ze}from"@motionscript/sdk";var pe={...x.state,..._.state,...U.corners},Ue=ue({key:"legendEntry",state:{...pe,shown:L.number(1)},commands:{...x.commands},measure:(e,t,r)=>{let n=_.measure(e,t,r,x.inset(t));return t.shown>=1?n:{...n,width:t.shown*n.width}},layout:(e,t,r)=>_.layout(e,t,r,x.inset(t)),render:(e,t)=>x.draw(e,t,U.rect(e.size,t))});function Ne(e,t){if(Array.isArray(e))return e;let r=[...t??[]];for(let[n,i]of Object.entries(e))r[Number(n)]=i;return r}var $t=ue({key:"chartLegend",state:{...pe,series:h(()=>[]),swatches:h(()=>[]),legend:L.custom(K),reveal:h(()=>[],{resolve:Ne})},defaults:{flow:"horizontal",width:"hug",height:"hug",gap:e=>e.legend.itemGap,padding:e=>e.legend.padding,reveal:e=>e.series.map(t=>t.enabled??!0?1:0)},children:e=>{let t=e.legend.markerStyle;return e.series.map((r,n)=>Ue({flow:"horizontal",gap:12,align:"center",width:"hug",height:"hug",opacity:()=>e.reveal[n]??0,gapScale:()=>e.reveal[n]??0,shown:()=>e.reveal[n]??0,children:[Ie({...ze.styleProps(e.legend.textStyle),text:r.label}),Ge({width:t.width,height:t.height,cornerRadius:t.borderRadius,fill:e.swatches[n]??"white",stroke:t.stroke,shadow:t.shadow})]}))},commands:{...x.commands,showSeries:{args:{index:L.number(0,{min:0,step:1})},run:(e,t)=>e.args.index in t.series?e.animate(fe(t,e.args.index,1,A(e))):e.wait(e.duration)},hideSeries:{args:{index:L.number(0,{min:0,step:1})},run:(e,t)=>e.args.index in t.series?e.animate(fe(t,e.args.index,0,A(e))):e.wait(e.duration)},spotlightSeries:{args:{index:L.number(0,{min:0,step:1})},run:(e,t,r)=>e.together(r.flatMap((n,i)=>(t.reveal[i]??0)>0?[n.to({opacity:i===e.args.index?1:$})]:[]))}},measure:(e,t,r)=>_.measure(e,t,r,x.inset(t)),layout:(e,t,r)=>_.layout(e,t,r,x.inset(t)),render:(e,t)=>x.draw(e,t,U.rect(e.size,t))});function fe(e,t,r,n){let i=e.reveal[t]??0;return o=>({reveal:{[t]:i+(r-i)*n(o)}})}function Ht(e,t){return(e.reveal[t]??0)>0}export*from"@motionscript/sdk/component";export{h as a,Ee as b,A as c,q as d,R as e,ee as f,P as g,te as h,C as i,ge as j,D as k,Be as l,Ye as m,X as n,E as o,Y as p,$ as q,$e as r,He as s,Ve as t,We as u,j as v,O as w,v as x,K as y,je as z,Ke as A,Je as B,Qe as C,Ze as D,qe as E,et as F,tt as G,rt as H,nt as I,ot as J,it as K,we as L,lt as M,st as N,J as O,at as P,ct as Q,Q as R,ft as S,Z as T,Me as U,le as V,Oe as W,vt as X,At as Y,Pt as Z,F as _,Ft as $,zt as aa,$t as ba,Ht as ca};
2
- //# sourceMappingURL=chunk-XROBIRQK.js.map
2
+ //# sourceMappingURL=chunk-TJNHLQGV.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../src/kit/shared.ts", "../../../src/kit/cartesian.ts", "../../../src/kit/vertical-axis.ts", "../../../src/kit/entries.ts", "../../../src/kit/motion.ts", "../../../src/kit/chart-border.ts", "../../../src/kit/paints.ts", "../../../src/kit/chart-canvas.ts", "../../../src/kit/chart-legend.ts", "../../../src/engine.ts"],
4
+ "sourcesContent": ["/**\n * The vocabulary every chart node shares: what a *series* is, how an axis is\n * scaled and formatted, and the theme leaves each chart's own theme is assembled\n * from.\n *\n * This module exists because the three chart types are three ways of drawing the\n * same table. A line chart, a bar chart and a pie chart disagree about almost\n * everything visible — polylines against columns against wedges — and agree\n * about almost everything underneath: a series is a column with a caption and a\n * paint, a marker is a number formatted one way, a legend is a swatch beside a\n * label, and a theme is a tree of paints with one mapper and one tween. Writing\n * that three times would mean three answers to \"how does a chart round its axis\"\n * within one app.\n *\n * Kept renderer-agnostic (no `Node` imports) so a host's own inspector\n * mappings can build a partial theme without pulling the components in.\n *\n * **Two conventions run through every resolver and lerp below**, inherited from\n * the shape system's `resolveStroke`/`resolveShadow`:\n *\n * - A **mapper** merges the author's loose, partial input onto a `previous`\n * resolved value and resolves every leaf, so the tween always has resolved\n * data to interpolate. It is idempotent.\n * - A **tween** interpolates two resolved values leaf by leaf. Categorical\n * fields (`fontFamily`, `textAlign`) and fields that are `undefined` on either\n * side **step at the midpoint**, because there is no meaningful in-between and\n * defaulting the missing side to 0 would animate a font weight down to nothing\n * on the way to \"unset\".\n */\n\nimport {\n fillOps,\n lerpNumber,\n shadowOps,\n strokeOps,\n insetsOps,\n textOps,\n type DataRecord,\n type Fill,\n type FillResolved,\n type Insets,\n type InsetsResolved,\n type Shadow,\n type ShadowResolved,\n type Stroke,\n type StrokeResolved,\n type TextStyle,\n} from \"@motionscript/sdk\"\n\n/**\n * One series of a chart: a column of the table, captioned and painted.\n *\n * `field` names the column carrying this series' values. What that becomes is\n * the chart's business — a traced polyline, a column of bars, a wedge — but the\n * three questions asked of it are the same everywhere: which column, what to\n * call it, what to paint it, and whether it is currently drawn.\n *\n * `fill` is an **override**: omit it and the series takes the theme palette's\n * entry for its own index, which is what makes a chart nobody has themed come\n * out in five distinct colours rather than five identical ones.\n */\nexport interface ChartSeries {\n /** Key into each data row for this series' value. */\n field: string\n /** Legend caption for this series. */\n label: string\n /**\n * Paint override for this series' mark and its legend swatch. Omit to take\n * the theme palette's entry for this series' index.\n */\n fill?: Fill\n /** Whether the series shows by default. Defaults to `true`. */\n enabled?: boolean\n}\n\n/** A `[min, max]` numeric range in data units. */\nexport type Range = [min: number, max: number]\n\n/**\n * One axis's *data* domain — what it shows — as opposed to {@link AxisConfig},\n * which is how it's *styled*. Every field is optional: `range`/`ticks` derive\n * from the plotted data when omitted, and `label` defaults to `\"\"`.\n */\nexport interface AxisDomainConfig {\n /** Caption drawn alongside the axis. Pass `\"\"` (the default) to hide it. */\n label?: string\n /** `[min, max]` of the scale. Derived from the data bounds when omitted. */\n range?: Range\n /** Tick values (in data units). Derived (five evenly-spaced) when omitted. */\n ticks?: number[]\n /**\n * How a tick value is written down — a `number-format` pattern such as\n * `#,##0.00` or `$#,##0`.\n *\n * Here rather than in {@link AxisConfig} because it is a statement about the\n * *numbers*, not about the ink they are set in: `$` on a revenue axis says\n * what the column holds, and it has to survive a re-theme. It sits beside\n * `range` and `ticks` for the same reason those do.\n */\n format?: string\n}\n\n/**\n * An axis domain entry's resolve: merges the author's partial input onto\n * `previous`, so a later partial `.set({ yAxis: {...} })` patches\n * `label`/`range`/`ticks` independently. `range`/`ticks` staying `undefined`\n * after the merge is the auto-derive signal the scale getters read.\n */\nexport function resolveAxisDomain(\n domain: AxisDomainConfig | undefined,\n previous?: AxisDomainConfig\n): AxisDomainConfig {\n return {\n label: domain?.label ?? previous?.label ?? \"\",\n range: domain?.range ?? previous?.range,\n ticks: domain?.ticks ?? previous?.ticks,\n format: domain?.format ?? previous?.format,\n }\n}\n\n/**\n * An axis domain entry's lerp: interpolates `range`/`ticks` numerically\n * (pairwise by index for `ticks`), so a `to` command that restates a range\n * rescales the plot smoothly. `label`, and either field when one side is in\n * auto-derive mode, step at the midpoint.\n */\nexport function lerpAxisDomain(\n from: AxisDomainConfig,\n to: AxisDomainConfig,\n t: number\n): AxisDomainConfig {\n return {\n label: t < 0.5 ? from.label : to.label,\n range: lerpOptionalRange(from.range, to.range, t),\n ticks: lerpOptionalTicks(from.ticks, to.ticks, t),\n // Steps at the midpoint with the caption, for the same reason: there is no\n // halfway between `$#,##0` and `0.0%`.\n format: t < 0.5 ? from.format : to.format,\n }\n}\n\nfunction lerpOptionalRange(\n from: Range | undefined,\n to: Range | undefined,\n t: number\n): Range | undefined {\n if (!from || !to) return t < 0.5 ? from : to\n return [lerpNumber(from[0], to[0], t), lerpNumber(from[1], to[1], t)]\n}\n\nfunction lerpOptionalTicks(\n from: number[] | undefined,\n to: number[] | undefined,\n t: number\n): number[] | undefined {\n if (!from || !to) return t < 0.5 ? from : to\n const n = Math.max(from.length, to.length)\n const out: number[] = []\n for (let i = 0; i < n; i++) {\n out.push(lerpNumber(from[i] ?? to[i], to[i] ?? from[i], t))\n }\n return out\n}\n\n/**\n * Where a generic `to()` of an axis domain should land, given the domain as it\n * is *drawn* (`drawn`, with `range` and `ticks` resolved off the data), or\n * `undefined` when the request states neither a `range` nor `ticks` and the\n * plain property tween will do.\n *\n * The stored domain of an auto axis has no `range` and no `ticks`, and\n * {@link lerpAxisDomain} steps an unset side at the midpoint, so tweening the\n * stored value from an auto axis is a cut. The charts therefore tween between\n * resolved domains, as their zoom does; this builds the destination. A `range`\n * given without `ticks` keeps the tick *count* the axis already draws, so each\n * marker slides from its old value to its new one instead of the row popping.\n */\nexport function axisTweenTarget(\n drawn: AxisDomainConfig,\n requested: AxisDomainConfig | undefined\n): AxisDomainConfig | undefined {\n if (requested?.range === undefined && requested?.ticks === undefined) return undefined\n const target = resolveAxisDomain(requested, drawn)\n if (requested.ticks === undefined && requested.range !== undefined) {\n const count = Math.max(2, drawn.ticks?.length ?? 2)\n target.ticks = markerValues(requested.range, count - 1)\n }\n return target\n}\n\n/**\n * An axis domain part-way through a tween that leaves or reaches a `range` or\n * `ticks` left to the data: both ends as stored, and how far along. Only the\n * chart knows what an auto side draws as, so {@link settleAxisDomain} resolves\n * it there. Its own fields are the plain {@link lerpAxisDomain} of its ends, so\n * a mapper merging onto one still reads a domain.\n */\nexport interface AxisDomainBlend extends AxisDomainConfig {\n readonly blend: { readonly from: AxisDomainConfig; readonly to: AxisDomainConfig; readonly t: number }\n}\n\nfunction blendOf(domain: AxisDomainConfig): AxisDomainBlend[\"blend\"] | undefined {\n return (domain as Partial<AxisDomainBlend>).blend\n}\n\n/**\n * A chart's axis entry tween: what `to({ yAxis: { range } })` moves through.\n * Between two explicit domains it is {@link lerpAxisDomain}, and a restated\n * `range` without `ticks` spreads explicit ticks across it, keeping their count.\n * When either end leaves `range` or `ticks` to the data, the value between is an\n * {@link AxisDomainBlend}, which the chart draws as the blend of what each end\n * draws: a rescale from an auto axis is continuous, not a cut at the midpoint.\n */\nexport function lerpAxisEntry(from: AxisDomainConfig, to: AxisDomainConfig, t: number): AxisDomainConfig {\n // The mapper merged the request onto `from`, so a field it left unstated is `from`'s own, by identity.\n const rangeMoved = to.range !== from.range\n const target = rangeMoved && to.range && to.ticks && to.ticks === from.ticks\n ? { ...to, ticks: markerValues(to.range, Math.max(2, to.ticks.length) - 1) }\n : to\n const blended = blendOf(from) !== undefined\n const moved = blended || rangeMoved || target.ticks !== from.ticks\n const auto = blended || from.range === undefined || from.ticks === undefined || target.range === undefined || target.ticks === undefined\n if (!moved || !auto) return lerpAxisDomain(from, target, t)\n if (t === 0) return from\n if (t === 1) return target\n const blend: AxisDomainBlend = { ...lerpAxisDomain(from, target, t), blend: { from, to: target, t } }\n return blend\n}\n\n/**\n * `domain` as the chart draws it: `drawn` resolves a plain domain's auto `range`\n * and `ticks` off the data, and a blend is the lerp of what its two ends draw.\n */\nexport function settleAxisDomain(domain: AxisDomainConfig, drawn: (domain: AxisDomainConfig) => AxisDomainConfig): AxisDomainConfig {\n const blend = blendOf(domain)\n if (!blend) return drawn(domain)\n return lerpAxisDomain(settleAxisDomain(blend.from, drawn), settleAxisDomain(blend.to, drawn), blend.t)\n}\n\n/**\n * The low-level colour tokens the default themes are built from — a dark plot\n * panel with warm-grey axis furniture.\n */\nexport const CHART_PALETTE = {\n /** Fill of the plot panel behind the marks. */\n plotFill: \"#15130e\",\n /** Colour of the plot's thick border. */\n borderColor: \"#4a4036\",\n /** Colour of the faint grid lines spanning the plot. */\n gridColor: \"#2c2820\",\n /** Colour of the numeric axis markers, axis captions, and legend labels. */\n markerColor: \"#8a7f70\",\n /** Colour of a spotlit region's dashed rules and its wash. */\n regionColor: \"#d99a3a\",\n} as const\n\n/** The out-of-the-box series palette, cycled by series index. */\nexport const DEFAULT_SERIES_COLORS = [\n \"#d9603b\",\n \"#8194ad\",\n \"#e0a93b\",\n \"#5e9d6b\",\n \"#9ec85a\",\n] as const\n\n/**\n * Opacity an *un*-spotlighted series fades to — its mark and its legend entry\n * alike, so the two dim together.\n */\nexport const SPOTLIGHT_DIM = 0.2\n\n/** Clamp a number into `[0, 1]` — the range every reveal signal here runs in. */\nexport function clampUnit(value: number): number {\n return value < 0 ? 0 : value > 1 ? 1 : value\n}\n\n// ---- Row readers ---------------------------------------------------------\n\n/**\n * Read a numeric field off a row, defaulting missing, non-numeric and non-finite\n * values to 0.\n *\n * A column left as strings silently plots as 0, which is why the CSV parser's\n * numeric auto-coercion matters — a quoted or unit-suffixed column won't plot\n * until it's coerced.\n */\nexport function num(row: DataRecord, field: string | undefined): number {\n if (field === undefined) return 0\n const v = row[field]\n // A `NaN` or infinite cell is no more a measurement than a missing one: read\n // as 0, it cannot poison the scale and turn every point into `NaN`.\n return typeof v === \"number\" && Number.isFinite(v) ? v : 0\n}\n\n/**\n * Read a field off a row as its caption — the counterpart to {@link num} for\n * the columns a chart *names* things by rather than measures them by (a bar's\n * category, a wedge's slice). A missing cell reads as empty rather than as the\n * string `\"undefined\"`.\n */\nexport function text(row: DataRecord, field: string | undefined): string {\n if (field === undefined || field === \"\") return \"\"\n const v = row[field]\n return v === undefined || v === null ? \"\" : String(v)\n}\n\n/**\n * Every column that holds numbers across `data`, in header order, skipping the\n * names in `exclude` (typically the category column).\n *\n * A column counts as numeric only if every non-empty cell in it is a number, so\n * a stray text cell leaves the column out rather than plotting zeros. This is\n * what lets a chart plot \"every other column\" without the author naming twenty\n * fields.\n */\nexport function numericFields(\n data: DataRecord[],\n exclude: string[] = []\n): string[] {\n if (data.length === 0) return []\n const skip = new Set(exclude)\n const seen = new Set<string>()\n const order: string[] = []\n for (const row of data)\n for (const key of Object.keys(row))\n if (!skip.has(key) && !seen.has(key)) {\n seen.add(key)\n order.push(key)\n }\n\n return order.filter((key) => {\n let sawValue = false\n for (const row of data) {\n const v = row[key]\n if (v === undefined || v === \"\") continue\n if (typeof v !== \"number\") return false\n sawValue = true\n }\n return sawValue\n })\n}\n\n// ---- Theme branches ------------------------------------------------------\n\n/** One axis's (x or y) style vocabulary. */\nexport interface AxisConfig {\n /** Style of each marker along the axis. */\n labelStyle: TextStyle\n /** Style of the axis caption. */\n titleStyle: TextStyle\n /**\n * This axis's direction of the plot's internal grid — weight+fill honoured,\n * dash/cap/join ignored (grid lines are drawn as filled rects).\n */\n lineStyle: Stroke\n /**\n * Gap between the plot and the marker row. Only the side facing the plot is\n * read — `.top` for a horizontal axis, `.right` for a vertical one.\n */\n tickPadding: Insets\n /** Gap between the marker row and the caption, read the same side. */\n titlePadding: Insets\n}\n\n/** A legend's per-series colour swatch — a rounded chip, not the caption. */\nexport interface LegendMarkerConfig {\n width: number\n height: number\n borderRadius: number\n stroke: Stroke\n shadow: Shadow\n}\n\n/** A legend's visual theme. */\nexport interface LegendConfig {\n padding: Insets\n /** Caption style — only the swatch carries the series colour. */\n textStyle: TextStyle\n /** Gap between legend entries. */\n itemGap: number\n markerStyle: LegendMarkerConfig\n}\n\n/** The plot panel a cartesian chart draws its marks over. */\nexport interface PlotAreaConfig {\n stroke: Stroke\n fill: Fill\n shadow: Shadow\n}\n\nconst AXIS_LABEL_STYLE: TextStyle = {\n fontSize: 24,\n fontWeight: 700,\n textAlign: \"center\",\n fill: CHART_PALETTE.markerColor,\n}\nconst AXIS_TITLE_STYLE: TextStyle = {\n fontSize: 30,\n fontWeight: 600,\n textAlign: \"center\",\n fill: CHART_PALETTE.markerColor,\n}\nconst AXIS_LINE_STYLE: Stroke = { weight: 2, fill: CHART_PALETTE.gridColor }\n\n/** The default styling of a horizontal (marker row beneath the plot) axis. */\nexport const DEFAULT_X_AXIS: AxisConfig = {\n labelStyle: AXIS_LABEL_STYLE,\n titleStyle: AXIS_TITLE_STYLE,\n lineStyle: AXIS_LINE_STYLE,\n tickPadding: 20,\n titlePadding: 4,\n}\n\n/** The default styling of a vertical (marker column beside the plot) axis. */\nexport const DEFAULT_Y_AXIS: AxisConfig = {\n labelStyle: { ...AXIS_LABEL_STYLE, fontWeight: 500, textAlign: \"right\" },\n titleStyle: AXIS_TITLE_STYLE,\n lineStyle: AXIS_LINE_STYLE,\n tickPadding: 40,\n titlePadding: 24,\n}\n\n/**\n * The default plot panel: **unpainted**, framed. A chart reads against the\n * stage rather than punching a panel into it, which is the look the family was\n * designed at.\n */\nexport const DEFAULT_PLOT_AREA: PlotAreaConfig = {\n fill: [],\n stroke: { weight: 6, fill: CHART_PALETTE.borderColor },\n shadow: [],\n}\n\n/** The default legend styling, shared by every chart that draws one. */\nexport const DEFAULT_LEGEND: LegendConfig = {\n padding: 0,\n textStyle: {\n fontSize: 30,\n fontWeight: 600,\n textAlign: \"left\",\n fill: CHART_PALETTE.markerColor,\n },\n itemGap: 44,\n markerStyle: {\n width: 60,\n height: 24,\n borderRadius: 6,\n stroke: [],\n shadow: [],\n },\n}\n\n/** The default per-series stroke palette — {@link DEFAULT_SERIES_COLORS}, drawn 4px. */\nexport const DEFAULT_SERIES_STROKES: Stroke[] = DEFAULT_SERIES_COLORS.map(\n (fill) => ({ weight: 4, fill })\n)\n\n/** The default per-series fill palette — the same colours, as solid paint. */\nexport const DEFAULT_SERIES_FILLS: Fill[] = DEFAULT_SERIES_COLORS.map(\n (fill) => fill\n)\n\n// ---- Theme branch resolvers ----------------------------------------------\n// Each chart type assembles its own theme out of these, so \"the axis furniture\"\n// means one thing across the family and each chart still declares only the\n// branches it actually draws.\n\nexport function resolveAxisConfig(\n axis: Partial<AxisConfig> | undefined,\n previous: AxisConfig\n): AxisConfig {\n return {\n labelStyle: textOps.resolveStyle(axis?.labelStyle, previous.labelStyle),\n titleStyle: textOps.resolveStyle(axis?.titleStyle, previous.titleStyle),\n lineStyle: strokeOps.resolve(axis?.lineStyle ?? previous.lineStyle),\n tickPadding: insetsOps.resolve(\n axis?.tickPadding ?? previous.tickPadding,\n previous.tickPadding as InsetsResolved\n ),\n titlePadding: insetsOps.resolve(\n axis?.titlePadding ?? previous.titlePadding,\n previous.titlePadding as InsetsResolved\n ),\n }\n}\n\nexport function lerpAxisConfig(\n from: AxisConfig,\n to: AxisConfig,\n t: number\n): AxisConfig {\n return {\n labelStyle: textOps.lerpStyle(from.labelStyle, to.labelStyle, t),\n titleStyle: textOps.lerpStyle(from.titleStyle, to.titleStyle, t),\n lineStyle: strokeOps.lerp(\n from.lineStyle as StrokeResolved[],\n to.lineStyle as StrokeResolved[],\n t\n ),\n tickPadding: insetsOps.lerp(\n from.tickPadding as InsetsResolved,\n to.tickPadding as InsetsResolved,\n t\n ),\n titlePadding: insetsOps.lerp(\n from.titlePadding as InsetsResolved,\n to.titlePadding as InsetsResolved,\n t\n ),\n }\n}\n\nexport function resolvePlotArea(\n plotArea: Partial<PlotAreaConfig> | undefined,\n previous: PlotAreaConfig\n): PlotAreaConfig {\n return {\n fill: fillOps.resolve(plotArea?.fill ?? previous.fill),\n stroke: strokeOps.resolve(plotArea?.stroke ?? previous.stroke),\n shadow: shadowOps.resolve(plotArea?.shadow ?? previous.shadow),\n }\n}\n\nexport function lerpPlotArea(\n from: PlotAreaConfig,\n to: PlotAreaConfig,\n t: number\n): PlotAreaConfig {\n return {\n fill: fillOps.lerp(\n from.fill as FillResolved[],\n to.fill as FillResolved[],\n t\n ),\n stroke: strokeOps.lerp(\n from.stroke as StrokeResolved[],\n to.stroke as StrokeResolved[],\n t\n ),\n shadow: shadowOps.lerp(\n from.shadow as ShadowResolved[],\n to.shadow as ShadowResolved[],\n t\n ),\n }\n}\n\nfunction resolveLegendMarkerConfig(\n marker: Partial<LegendMarkerConfig> | undefined,\n previous: LegendMarkerConfig\n): LegendMarkerConfig {\n return {\n width: marker?.width ?? previous.width,\n height: marker?.height ?? previous.height,\n borderRadius: marker?.borderRadius ?? previous.borderRadius,\n stroke: strokeOps.resolve(marker?.stroke ?? previous.stroke),\n shadow: shadowOps.resolve(marker?.shadow ?? previous.shadow),\n }\n}\n\nfunction lerpLegendMarkerConfig(\n from: LegendMarkerConfig,\n to: LegendMarkerConfig,\n t: number\n): LegendMarkerConfig {\n return {\n width: lerpNumber(from.width, to.width, t),\n height: lerpNumber(from.height, to.height, t),\n borderRadius: lerpNumber(from.borderRadius, to.borderRadius, t),\n stroke: strokeOps.lerp(\n from.stroke as StrokeResolved[],\n to.stroke as StrokeResolved[],\n t\n ),\n shadow: shadowOps.lerp(\n from.shadow as ShadowResolved[],\n to.shadow as ShadowResolved[],\n t\n ),\n }\n}\n\n/** A legend theme as the author may state it — every branch optional. */\nexport type LegendTheme = Partial<Omit<LegendConfig, \"markerStyle\">> & {\n markerStyle?: Partial<LegendMarkerConfig>\n}\n\nexport function resolveLegendConfig(\n legend: LegendTheme | undefined,\n previous: LegendConfig\n): LegendConfig {\n return {\n padding: insetsOps.resolve(\n legend?.padding ?? previous.padding,\n previous.padding as InsetsResolved\n ),\n textStyle: textOps.resolveStyle(legend?.textStyle, previous.textStyle),\n itemGap: legend?.itemGap ?? previous.itemGap,\n markerStyle: resolveLegendMarkerConfig(\n legend?.markerStyle,\n previous.markerStyle\n ),\n }\n}\n\nexport function lerpLegendConfig(\n from: LegendConfig,\n to: LegendConfig,\n t: number\n): LegendConfig {\n return {\n padding: insetsOps.lerp(\n from.padding as InsetsResolved,\n to.padding as InsetsResolved,\n t\n ),\n textStyle: textOps.lerpStyle(from.textStyle, to.textStyle, t),\n itemGap: lerpNumber(from.itemGap, to.itemGap, t),\n markerStyle: lerpLegendMarkerConfig(from.markerStyle, to.markerStyle, t),\n }\n}\n\n/** A whole-palette override replaces the previous palette outright. */\nexport function resolveSeriesStrokes(\n styles: Stroke[] | undefined,\n previous: Stroke[]\n): Stroke[] {\n return (styles ?? previous).map((s) => strokeOps.resolve(s))\n}\n\n/** Pairwise by index; a palette that grows/shrinks holds the longer side's tail. */\nexport function lerpSeriesStrokes(\n from: Stroke[],\n to: Stroke[],\n t: number\n): Stroke[] {\n const n = Math.max(from.length, to.length)\n const out: Stroke[] = []\n for (let i = 0; i < n; i++) {\n const a = (from[i] ?? to[i]) as StrokeResolved[]\n const b = (to[i] ?? from[i]) as StrokeResolved[]\n out.push(strokeOps.lerp(a, b, t))\n }\n return out\n}\n\n/** The fill-palette twin of {@link resolveSeriesStrokes}, for filled marks. */\nexport function resolveSeriesFills(\n fills: Fill[] | undefined,\n previous: Fill[]\n): Fill[] {\n return (fills ?? previous).map((f) => fillOps.resolve(f))\n}\n\n/** The fill-palette twin of {@link lerpSeriesStrokes}. */\nexport function lerpSeriesFills(from: Fill[], to: Fill[], t: number): Fill[] {\n const n = Math.max(from.length, to.length)\n const out: Fill[] = []\n for (let i = 0; i < n; i++) {\n const a = (from[i] ?? to[i]) as FillResolved[]\n const b = (to[i] ?? from[i]) as FillResolved[]\n out.push(fillOps.lerp(a, b, t))\n }\n return out\n}\n\n// ---- Per-series paint ----------------------------------------------------\n\n/**\n * The effective stroke for series `index`: the palette's entry at\n * `index % palette.length`, with the series' own `fill`, if set, overriding just\n * the last (topmost) layer's colour.\n */\nexport function resolveSeriesStroke(\n palette: Stroke[],\n series: ChartSeries,\n index: number\n): Stroke {\n const entry: Stroke =\n palette.length > 0\n ? palette[index % palette.length]\n : { weight: 4, fill: \"white\" }\n if (series.fill == null) return entry\n const layers = strokeOps.resolve(entry)\n if (layers.length === 0) return { weight: 4, fill: series.fill }\n const next = [...layers]\n next[next.length - 1] = {\n ...next[next.length - 1],\n fill: fillOps.resolve(series.fill),\n }\n return next\n}\n\n/**\n * The effective *colour* for series `index` — the same colour\n * {@link resolveSeriesStroke} draws its line in — for contexts (a legend\n * swatch) that only need the fill.\n */\nexport function resolveSeriesFill(\n palette: Stroke[],\n series: ChartSeries,\n index: number\n): Fill {\n const layers = strokeOps.resolve(resolveSeriesStroke(palette, series, index))\n return layers[layers.length - 1]?.fill ?? \"white\"\n}\n\n/**\n * The effective paint for mark `index` of a **filled** chart (a bar, a wedge):\n * the series' own `fill` when it carries one, else the palette's entry for its\n * index, cycled.\n *\n * The stroke twin above has to reach inside a stroke to swap one layer's colour;\n * a filled mark has no such geometry wrapped around its paint, so an override is\n * simply the whole answer.\n */\nexport function resolvePalettePaint(\n palette: Fill[],\n override: Fill | undefined,\n index: number\n): Fill {\n if (override != null) return override\n if (palette.length === 0) return \"white\"\n return palette[index % palette.length]\n}\n\n// ---- Scales and formatting -----------------------------------------------\n\n\n/**\n * Format a marker value: integers plain, otherwise trimmed to one decimal, or to\n * as many as the axis's tick `step` needs to tell neighbouring ticks apart.\n *\n * One decimal is the floor, so a step of 0.1 or more reads exactly as it always\n * has; a finer step (a unit axis ticked every 0.05) gets two, instead of five\n * ticks captioned `0.1 0.1 0.2 0.2 0.3`. Trailing zeros are dropped either way.\n */\nexport function formatMarker(value: number, step?: number): string {\n if (Number.isInteger(value)) return String(value)\n const decimals = Math.max(1, step === undefined ? 1 : decimalsFor(step))\n return value.toFixed(decimals).replace(/(\\.\\d*?)0+$/, \"$1\").replace(/\\.$/, \"\")\n}\n\n/** Decimal places needed to write `step` exactly, capped at six. */\nfunction decimalsFor(step: number): number {\n if (!(step > 0) || !Number.isFinite(step)) return 1\n for (let d = 0; d <= 6; d++) {\n const scaled = step * 10 ** d\n if (Math.abs(scaled - Math.round(scaled)) < 1e-6 * Math.max(1, scaled)) return d\n }\n // An irrational-looking step (a third): as many places as separate the ticks.\n return Math.min(6, Math.max(1, Math.ceil(-Math.log10(step)) + 1))\n}\n\n/** The gap between neighbouring tick values, or `undefined` for fewer than two distinct ones. */\nexport function markerStep(markers: readonly number[]): number | undefined {\n const sorted = [...markers].filter(Number.isFinite).sort((a, b) => a - b)\n let step: number | undefined\n for (let i = 1; i < sorted.length; i++) {\n const gap = sorted[i] - sorted[i - 1]\n if (gap > 1e-12 && (step === undefined || gap < step)) step = gap\n }\n return step\n}\n\n/** Format a fraction in `[0, 1]` as a percentage: integers plain, else one decimal. */\nexport function formatPercent(fraction: number): string {\n const pct = fraction * 100\n if (Number.isInteger(pct)) return `${pct}%`\n return `${pct.toFixed(1).replace(/\\.0$/, \"\")}%`\n}\n\n/** `markerCount` evenly-spaced tick values across `[min, max]`, ends inclusive. */\nexport function markerValues([min, max]: Range, markerCount: number): number[] {\n const n = Math.max(2, Math.round(markerCount))\n return Array.from({ length: n + 1 }, (_, i) => min + ((max - min) / n) * i)\n}\n\n// Lifted into the engine, because a graph and a diagram want them too — see\n// `@motionscript/sdk/component`. Re-exported so a chart, and anything that\n// copied one, still reaches them where it always did.\nexport { niceStep, enterBudget } from \"@motionscript/sdk/component\"\n", "import { Rect, type ChildView, type Node2D, type Size2D } from \"@motionscript/sdk\"\n\nimport { VerticalAxis } from \"./vertical-axis\"\n\n/**\n * Headroom above the plot, in px.\n *\n * Two jobs, and the second is why it is real padding on the composition rather\n * than a number the point-mapping math imagines: a series peaking at the top of\n * the scale isn't flush to the top edge, **and** the top marker, which is\n * centred on that top grid line, has somewhere to be. Without it the marker's\n * upper half is drawn above the chart's own box.\n */\nexport const TOP_HEADROOM = 24\n\n/** The parts of a cartesian chart's frame, as {@link cartesianFrame} arranges them. */\nexport interface CartesianParts {\n /** The value axis, left of the plot. */\n readonly axis: Node2D\n /** The plot panel: the box the grid and the marks share. */\n readonly plot: Node2D\n /** An invisible twin of the value axis, reserving its gutter under the plot. */\n readonly gutter: Node2D\n /** What sits under the plot, top to bottom: the other axis, then the legend. */\n readonly below: readonly Node2D[]\n}\n\n/**\n * The frame the line and bar charts share: the value axis beside the plot, and\n * under them the gutter beside the rows below the plot, so those rows start at\n * the plot's left edge rather than the chart's.\n */\nexport function cartesianFrame(parts: CartesianParts): Node2D {\n return new Rect({\n flow: \"vertical\",\n width: \"fill\",\n height: \"fill\",\n padding: { top: TOP_HEADROOM },\n children: [\n new Rect({ flow: \"horizontal\", height: \"fill\", width: \"fill\", children: [parts.axis, parts.plot] }),\n new Rect({\n flow: \"horizontal\",\n width: \"fill\",\n children: [parts.gutter, new Rect({ flow: \"vertical\", width: \"fill\", gap: 20, children: [...parts.below] })],\n }),\n ],\n }) as unknown as Node2D\n}\n\n/** A cartesian frame's parts, as a command reaches them through its chart's children. */\nexport interface CartesianViews {\n readonly axis: ChildView\n /** The plot panel's own children: the grid, the marks, the border. */\n readonly plot: readonly ChildView[]\n readonly below: readonly ChildView[]\n}\n\n/**\n * The frame among a chart's children: the last child shaped as\n * {@link cartesianFrame} builds it. The chart builds it after the children it\n * was given, and a document adds its own later still, so it is found by shape.\n */\nexport function cartesianViews(children: readonly ChildView[]): CartesianViews | null {\n for (let i = children.length - 1; i >= 0; i--) {\n const frame = children[i]\n if (!Rect.is(frame)) continue\n const [top, bottom] = frame.children\n const axis = top?.children[0]\n const plot = top?.children[1]\n const below = bottom?.children[1]\n if (!axis || !VerticalAxis.is(axis) || !plot || !below) continue\n return { axis, plot: plot.children, below: below.children }\n }\n return null\n}\n\n/** A part's laid-out size, or nothing before it is built or laid out. */\nexport function laidOutSize(node: Node2D | undefined): Size2D {\n const bounds = node?.layoutBounds\n return { width: bounds?.width ?? 0, height: bounds?.height ?? 0 }\n}\n", "import {\n Rect,\n RotatedBox,\n Text,\n Types,\n component2d,\n insetsOps,\n textOps,\n type NodeOf,\n type Node2D,\n type Node2DProps,\n} from \"@motionscript/sdk\"\n\nimport { asParts, fresh } from \"./entries\"\nimport { armed, arrival, arrivalOf, arrivalsEntry, beats, frameEase, type Arrival } from \"./motion\"\nimport {\n DEFAULT_Y_AXIS,\n enterBudget,\n formatMarker,\n type AxisConfig,\n type Range,\n} from \"./shared\"\n\nexport interface VerticalAxisProps extends Node2DProps {\n /** `[min, max]` of the vertical scale the markers are positioned within. */\n range: Range\n /**\n * Tick values (in data units) to draw up the axis, each placed by its fraction\n * across {@link VerticalAxisProps.range}. Need not be evenly spaced or include\n * the ends.\n */\n markers: number[]\n /**\n * How each marker is captioned, one per entry of `markers` and in its order.\n * Empty (the default), or short, captions a marker with its bare numeric\n * reading; a chart with a number format on this axis writes them through it.\n */\n captions: string[]\n /** Marker/caption text style, grid stroke, and gutters — see {@link AxisConfig}. */\n axis: AxisConfig\n /**\n * Rotated caption drawn in the gutter to the left of the marker column. Empty\n * string (the default) hides it — no caption node is built and no label gutter\n * is reserved.\n */\n label: string\n}\n\n/** How far a marker starts to the right of its slot when it enters, in px. */\nconst SLIDE = 24\n\n/** How a tick value is captioned — see {@link VerticalAxisProps.captions}. */\nexport type MarkerLabel = (value: number) => string\n\n/** What a marker's caption reads: the one listed for its value, else its bare reading. */\nexport function markerCaption(s: { readonly markers: readonly number[]; readonly captions: readonly string[] }, value: number): string {\n const index = s.markers.indexOf(value)\n return (index >= 0 ? s.captions[index] : undefined) ?? formatMarker(value)\n}\n\n/**\n * The numeric vertical axis shared by the cartesian charts: a **column of\n * {@link Text} nodes**, one per marker value, running top-to-bottom (highest\n * value at the top). Pure layout — no `Graphics` — so the markers are real text\n * nodes the scene can measure, restyle, or animate individually.\n *\n * Each marker is positioned so its centre lands exactly on its value's fraction\n * across `range` within the axis's filled height, so unevenly-spaced markers\n * land where their values fall. Both the caption and the position are *bound*,\n * not snapshotted: a chart that rescales its axes (see `LineChart.zoomTo`)\n * tweens `range`/`markers` live. Only the marker **count** is fixed at\n * construction.\n *\n * `enter` staggers them in **bottom-to-top**: each marker starts pushed a\n * little to the right and transparent, then fades and slides left into place.\n * The resting state is the landed one, so a chart that is never told to enter\n * still reads as a finished chart.\n */\nexport const VerticalAxis = component2d({\n key: \"verticalAxis\",\n state: {\n range: fresh<Range>(() => [0, 1]),\n markers: fresh<number[]>(() => []),\n captions: fresh<string[]>(() => []),\n axis: Types.custom<AxisConfig>(DEFAULT_Y_AXIS),\n label: \"\",\n arrivals: arrivalsEntry(),\n },\n // A plain node filled its parent; one made of its markers would hug them.\n defaults: { width: \"fill\", height: \"fill\" },\n children: (state) => {\n // Composed from plain entries (range/markers/label), so it's built once here.\n const tickPad = insetsOps.resolve(state.axis.tickPadding).right\n const titlePad = insetsOps.resolve(state.axis.titlePadding).right\n\n const row: Node2D[] = []\n // The row's parent is the axis: its box is what the markers are placed in.\n const box = () => (row[0]?.parent as Node2D | null | undefined)?.layoutBounds ?? { width: 0, height: 0 }\n /** The live tick values, top → bottom (highest first) — re-read every frame. */\n const sorted = () => [...state.markers].sort((a, b) => b - a)\n /** Tick `i`'s fraction across the live `range`, 0 at the bottom edge. */\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 // One text node per marker, ordered top → bottom. A slot with no value (the\n // live list is shorter than the column built for it) renders as empty text\n // rather than repeating its neighbour.\n const markers = state.markers.map((_, i) => {\n const marker: Node2D = new Text({\n ...textOps.styleProps(state.axis.labelStyle),\n text: () => {\n const value = sorted()[i]\n return value === undefined ? \"\" : markerCaption(state, value)\n },\n y: () => {\n // Node2D space is y-up, so the fraction runs from the bottom.\n const height = box().height\n const centred = (fracAt(sorted()[i]) - 0.5) * height\n // Held inside the axis's top edge — the backstop for a chart whose\n // headroom is smaller than its marker text is tall, which a theme with\n // much larger markers can arrange. Only the top, because what lies\n // below the axis is the chart's own horizontal axis and legend.\n const limit = height / 2 - marker.layoutBounds.height / 2\n return Math.min(centred, limit)\n },\n opacity: () => arrivalOf(state.arrivals, i).opacity,\n x: () => arrivalOf(state.arrivals, i).shift,\n })\n return marker\n })\n\n const children: Node2D[] = []\n if (state.label !== \"\") {\n // Node2D space is y-up and rotation is clockwise, so -90° reads bottom-to-top.\n children.push(\n new RotatedBox({\n angle: -90,\n width: \"hug\",\n height: \"hug\",\n children: new Text({\n ...textOps.styleProps(state.axis.titleStyle),\n text: state.label,\n opacity: () => arrivalOf(state.arrivals, \"label\").opacity,\n x: () => arrivalOf(state.arrivals, \"label\").shift,\n }),\n })\n )\n }\n children.push(new Rect({ height: \"fill\", width: \"hug\", padding: { right: tickPad }, children: markers }))\n row.push(new Rect({ flow: \"horizontal\", gap: titlePad, children }))\n return asParts(row)\n },\n commands: {\n /**\n * Stagger the markers in bottom to top, then slide the caption in from the\n * right after them. Its first frame arms them all: shifted right by `slide`\n * and transparent.\n *\n * The timeline's duration is the **whole** entrance, not one marker's slide:\n * the stagger and the caption are carved out of it rather than added on top.\n * See {@link enterBudget} for why that distinction matters here.\n *\n * `stagger` is seconds between successive markers (derived when omitted);\n * `slide` is the starting rightward offset in px.\n */\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 // Bottom marker leads: the column is top → bottom, so walk it in reverse.\n const markers = state.markers.map((_, i) => ({ at: (count - 1 - i) * step, duration: per, piece: arrival(timeline, i, from, ease) }))\n // Once every marker has landed, the caption slides in from the right.\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 VerticalAxis = NodeOf<typeof VerticalAxis>\n", "import { Types, type Element, type Node2D, type Type } from \"@motionscript/sdk\"\n\n/**\n * An entry made fresh for every node: each node gets its own array or box\n * rather than one shared literal, and a host is told no default for it.\n */\nexport function fresh<T, In = T>(make: () => T, options?: { readonly resolve?: (value: In) => T; readonly kind?: string }): Type<In, T> {\n // A function default is a factory: the component calls it once per node.\n return Types.custom<In, T>(make as unknown as In, options)\n}\n\n/**\n * Nodes a `children` builder made with `new`, as the elements it returns. A\n * part built that way can be read in a binding — its own box, or its parent's,\n * which is the component it was built for — where a call would hand back only\n * the element.\n */\nexport function asParts(nodes: readonly Node2D[]): readonly Element[] {\n return nodes as unknown as readonly Element[]\n}\n", "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. */\ntype AnyTimeline = Timeline<any, any>\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\nexport const 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", "import {\n Graphics2D,\n Types,\n component2d,\n strokeOps,\n type NodeOf,\n // Named so this component's emitted type reaches `Node2D` through the sdk, not core.\n type Node2D as _Node2D,\n type Node2DProps,\n type Stroke,\n type StrokeResolved,\n} from \"@motionscript/sdk\"\n\nimport { loadPaints } from \"./paints\"\nimport { DEFAULT_PLOT_AREA } from \"./shared\"\n\nexport interface ChartBorderProps extends Node2DProps {\n /**\n * The plot's thick frame stroke. The stroke resolver's default `align` is\n * `'inside'`, so the whole weight sits within the layout box unless the theme\n * overrides it.\n */\n stroke: Stroke\n}\n\n/**\n * The thick **frame** around a cartesian chart's plot: an empty rect wearing an\n * inside stroke, sized from its box.\n *\n * Split out of {@link ChartCanvas} (the filled + gridded backdrop) so it can be\n * placed *after* the marks in the tree — the box then frames the plot **over**\n * them, so a line or a column running to the edge tucks cleanly under the border\n * rather than sitting on top of it.\n */\nexport const ChartBorder = component2d({\n key: \"chartBorder\",\n state: {\n stroke: Types.custom<Stroke, StrokeResolved[]>(DEFAULT_PLOT_AREA.stroke, { resolve: strokeOps.resolve, lerp: strokeOps.lerp }),\n },\n load: (loader, state) => loadPaints(loader, { strokes: [state.stroke] }),\n render: (ctx, state) => {\n const { width, height } = ctx.size\n if (width <= 0 || height <= 0) return\n // Empty rect (no fill) with the themed stroke, so the frame sits over the\n // marks but the box interior stays clear.\n ctx.draw(new Graphics2D().rect({ x: 0, y: 0, width, height }).stroke(state.stroke))\n },\n})\n\nexport type ChartBorder = NodeOf<typeof ChartBorder>\n", "import type { FillResolved, Loader, ShadowResolved, StrokeResolved } from \"@motionscript/sdk\"\n\n/** Paints a chart part holds in entries of its own, which no paint entry loads for it. */\nexport interface PartPaints {\n readonly fills?: readonly (readonly FillResolved[])[]\n readonly strokes?: readonly (readonly StrokeResolved[])[]\n readonly shadows?: readonly (readonly ShadowResolved[])[]\n}\n\n/** Declare `paints` from a component's `load`. */\nexport function loadPaints(loader: Loader, paints: PartPaints): void {\n for (const fill of paints.fills ?? []) loader.fill(fill)\n for (const strokes of paints.strokes ?? []) for (const stroke of strokes) loader.fill(stroke.fill)\n for (const shadows of paints.shadows ?? []) for (const shadow of shadows) loader.fill(shadow.fill)\n}\n", "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` through the sdk, not core.\n type Node2D as _Node2D,\n type 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\nexport interface ChartCanvasProps extends Node2DProps {\n /** Fill of the plot panel behind the marks. */\n fill: Fill\n /** Drop shadow of the panel. Default none. */\n shadow: Shadow\n /** Stroke of the vertical grid lines (one per {@link xMarkers}). */\n xGridStroke: Stroke\n /** Stroke of the horizontal grid lines (one per {@link yMarkers}). */\n yGridStroke: Stroke\n /**\n * Data-unit x positions to draw a vertical grid line at — one per horizontal\n * axis marker, so a line lands under every marker regardless of spacing.\n * Positions at (or past) an {@link xExtent} edge coincide with the border and\n * are skipped. Pass an empty list for a chart whose horizontal axis is\n * categorical and so has no numeric position to rule at.\n */\n xMarkers: number[]\n /** Data-unit y positions to draw a horizontal grid line at. */\n yMarkers: number[]\n /** `[min, max]` of the horizontal scale — maps {@link xMarkers} into the panel. */\n xExtent: Range\n /** `[min, max]` of the vertical scale — maps {@link yMarkers} into the panel. */\n yExtent: Range\n}\n\nconst strokeEntry = (initial: Stroke) =>\n Types.custom<Stroke, StrokeResolved[]>(initial, { resolve: strokeOps.resolve, lerp: strokeOps.lerp })\n\n/**\n * The plot **backdrop** of a cartesian chart: a filled panel with a grid of\n * faint lines spanning both directions across it, sized from its box and centred\n * on its origin.\n *\n * The thick frame is a **separate** {@link ChartBorder} node so it can be drawn\n * *over* the marks while this panel sits under them. Grid lines are placed from\n * the axis marker positions (in data units) mapped into the panel through\n * {@link ChartCanvasProps.xExtent}/`yExtent` — so each line lands under its\n * marker whether or not the markers are evenly 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 xMarkers: fresh<number[]>(() => []),\n yMarkers: fresh<number[]>(() => []),\n xExtent: fresh<Range>(() => [0, 1]),\n yExtent: fresh<Range>(() => [0, 1]),\n },\n // The grid strokes are named 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 // Plot panel, centred on the origin (node space is y-up, origin centre).\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 // Fraction of a marker across its extent; guard a zero-width extent.\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 // Skip markers landing on (or outside) the edges — they'd 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", "import {\n Flow,\n Paint,\n Rect,\n Shapes,\n Text,\n Types,\n component2d,\n textOps,\n type Fill,\n type NodeOf,\n type RectProps,\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\nexport interface ChartLegendProps extends RectProps {\n /**\n * One entry per series: legend `label`, its `enabled` default, and — through\n * {@link ChartLegendProps.swatches} — the colour its chip is painted. A series\n * that starts enabled (the default) is added to the row up front; one that\n * starts `false` is hidden until a later `showSeries`.\n */\n series: ChartSeries[]\n /**\n * The swatch colour for each series, by index — resolved by the chart, whose\n * business it is whether a series' colour comes out of a stroke palette (a\n * line) or a fill palette (a bar). Shorter than the series list means the\n * missing entries fall back to white.\n */\n swatches: Fill[]\n /** Padding, caption style, gaps, and swatch geometry — see {@link LegendConfig}. */\n legend: LegendConfig\n}\n\n/** A row's entries are a `Rect`'s: paint, the flow of its children, its corners. */\nconst row = { ...Paint.state, ...Flow.state, ...Shapes.corners }\n\n/**\n * One legend entry. It hugs its caption and swatch; `shown` scales that width\n * from nothing to the whole of it.\n *\n * The width is taken from the content on every measure, at any reveal, so a frame\n * never depends on whether the entry was once seen fully shown. A collapse needs a\n * number to scale, and `\"hug\"` is not one until something measures it — this is\n * the something.\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/**\n * How far each entry is shown, written whole (a document, a `set`) or one\n * entry at a time (a command, as `{ [index]: reveal }`), which leaves the others\n * as the frame left them — so two series moving at once each move their own.\n */\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 * The legend of a chart: a **horizontal-flow row of entries**, one per series, laid out\n * left-to-right with the theme's `itemGap`. Each entry pairs the series label\n * with a rounded colour swatch; only the swatch carries the series colour, the\n * caption reads in the legend's neutral ink.\n *\n * `showSeries`/`hideSeries` flip a series on or off — showing one grows its\n * entry back in at its stable series position, hiding one shrinks it away. So\n * the row only ever shows the currently-enabled series, in series order, and\n * closes the gap when one leaves.\n *\n * This node *is* the row, so the parent places it (centred below the chart) and\n * it hugs its entries.\n */\nexport const ChartLegend = component2d({\n key: \"chartLegend\",\n state: {\n ...row,\n series: fresh<ChartSeries[]>(() => []),\n swatches: fresh<Fill[]>(() => []),\n legend: Types.custom<LegendConfig>(DEFAULT_LEGEND),\n /**\n * How far each entry is shown, `0`–`1`. Drives its width, opacity and gap\n * together, so `0` is an entry that takes no room and `1` is one at its\n * natural size.\n *\n * This replaces adding and removing the entry from the row: a command is\n * asked what it looks like at a time, and \"is this node a child\" is not a\n * value a time can produce. Collapsed-but-present is the same picture reached\n * by a route that *is* a function of `t`.\n */\n reveal: fresh<number[], number[] | Readonly<Record<number, number>>>(() => [], { resolve: revealOf }),\n },\n // This node *is* the row, so the direction is its own answer. It has to be\n // stated — a `Rect` given no flow lays its children out `freeform`, which\n // stacks every entry on the same spot. The gap and padding are the theme's.\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 // One entry per series, in series order, every one a child for good: order is\n // fixed rather than depending on which siblings happen to be shown, so an entry\n // 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 // Bound to the reveal, so showing and hiding is a number moving rather\n // than a child arriving and leaving. The width follows it in `measure`,\n // and the flanking gap closes with the entry rather than snapping.\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 /**\n * Grow series `index`'s entry back in at its series-order position, the\n * siblings sliding over to make room. A reveal tween rather than an animated\n * `addChildAt`: the entry never left, so the whole thing is a function of `t`.\n */\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 /** Shrink series `index`'s entry away, the gap closing behind it — 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 /**\n * Dim every entry but series `index` to {@link SPOTLIGHT_DIM} and bring that\n * one back to full — the legend half of a chart's `spotlightSeries`. Entries\n * stay in place; only their opacity moves, so the row doesn't reflow. Only\n * the entries shown when it is built are touched: a hidden series' entry is\n * left as `hideSeries` left it.\n */\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/**\n * One entry's reveal ramping to `target` from where the command finds it — so a\n * hide that follows a show starts where the show left it.\n */\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\n/** Whether series `index` is showing at all on `legend`. */\nexport function legendShows(legend: { readonly reveal: readonly number[] }, index: number): boolean {\n return (legend.reveal[index] ?? 0) > 0\n}\n\nexport type ChartLegend = NodeOf<typeof ChartLegend>\n", "/**\n * The chart **kit**: everything the three charts share, and the one thing a\n * copied chart does not copy.\n *\n * A copy of the line chart takes `line-chart/*` into your project and rewrites its\n * reach for these to `@motionscript/charts`, so the plot panel, the value\n * axis, the legend and the number formatting stay a real dependency. That is\n * not a size decision — it is the only arrangement that works.\n *\n * A chart identifies its own parts by component identity (`VerticalAxis.is`).\n * Copy `VerticalAxis` alongside your fork and there are now two of it — two\n * components answering one name, and whichever one your chart did not get is\n * a part it never finds, with no error. Keeping the kit external makes that\n * impossible rather than unlikely.\n *\n * It also means a fix to the axis reaches a forked chart without a re-add,\n * which is the half of the bargain you get back for it.\n */\nexport * from \"./kit/shared\"\nexport * from \"./kit/cartesian\"\nexport * from \"./kit/chart-border\"\nexport * from \"./kit/chart-canvas\"\nexport * from \"./kit/chart-legend\"\nexport * from \"./kit/entries\"\nexport * from \"./kit/motion\"\nexport * from \"./kit/paints\"\nexport * from \"./kit/vertical-axis\"\nexport * from \"@motionscript/sdk/component\"\n"],
5
+ "mappings": "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",
6
+ "names": ["fillOps", "lerpNumber", "shadowOps", "strokeOps", "insetsOps", "textOps", "niceStep", "enterBudget", "resolveAxisDomain", "domain", "previous", "lerpAxisDomain", "from", "to", "t", "lerpOptionalRange", "lerpOptionalTicks", "out", "i", "axisTweenTarget", "drawn", "requested", "target", "count", "markerValues", "blendOf", "lerpAxisEntry", "rangeMoved", "blended", "moved", "auto", "settleAxisDomain", "blend", "CHART_PALETTE", "DEFAULT_SERIES_COLORS", "SPOTLIGHT_DIM", "clampUnit", "value", "num", "row", "field", "v", "text", "numericFields", "data", "exclude", "skip", "seen", "order", "key", "sawValue", "AXIS_LABEL_STYLE", "AXIS_TITLE_STYLE", "AXIS_LINE_STYLE", "DEFAULT_X_AXIS", "DEFAULT_Y_AXIS", "DEFAULT_PLOT_AREA", "DEFAULT_LEGEND", "DEFAULT_SERIES_STROKES", "fill", "DEFAULT_SERIES_FILLS", "resolveAxisConfig", "axis", "lerpAxisConfig", "resolvePlotArea", "plotArea", "lerpPlotArea", "resolveLegendMarkerConfig", "marker", "lerpLegendMarkerConfig", "resolveLegendConfig", "legend", "lerpLegendConfig", "resolveSeriesStrokes", "styles", "s", "lerpSeriesStrokes", "a", "b", "resolveSeriesFills", "fills", "f", "lerpSeriesFills", "resolveSeriesStroke", "palette", "series", "index", "entry", "layers", "next", "resolveSeriesFill", "resolvePalettePaint", "override", "formatMarker", "step", "decimals", "decimalsFor", "d", "scaled", "markerStep", "markers", "sorted", "gap", "formatPercent", "fraction", "pct", "min", "max", "markerCount", "_", "Rect", "Rect", "RotatedBox", "Text", "Types", "component2d", "insetsOps", "textOps", "Types", "fresh", "make", "options", "asParts", "nodes", "Types", "easeOut", "identity", "t", "frameEase", "timeline", "beats", "parts", "total", "natural", "end", "part", "k", "at", "duration", "piece", "start", "length", "LANDED", "arrivalsEntry", "next", "previous", "arrivalOf", "arrivals", "key", "armed", "keys", "from", "record", "arrival", "ease", "e", "SLIDE", "markerCaption", "s", "value", "index", "formatMarker", "VerticalAxis", "component2d", "fresh", "Types", "DEFAULT_Y_AXIS", "arrivalsEntry", "state", "tickPad", "insetsOps", "titlePad", "row", "box", "sorted", "a", "b", "fracAt", "min", "max", "markers", "_", "i", "marker", "Text", "textOps", "height", "centred", "limit", "arrivalOf", "children", "RotatedBox", "Rect", "timeline", "stagger", "slide", "count", "captioned", "from", "keys", "arm", "armed", "step", "per", "caption", "enterBudget", "ease", "frameEase", "arrival", "label", "beats", "TOP_HEADROOM", "cartesianFrame", "parts", "Rect", "cartesianViews", "children", "i", "frame", "top", "bottom", "axis", "plot", "below", "VerticalAxis", "laidOutSize", "node", "bounds", "Graphics2D", "Types", "component2d", "strokeOps", "loadPaints", "loader", "paints", "fill", "strokes", "stroke", "shadows", "shadow", "ChartBorder", "component2d", "Types", "DEFAULT_PLOT_AREA", "strokeOps", "loader", "state", "loadPaints", "ctx", "width", "height", "Graphics2D", "Graphics2D", "Types", "component2d", "fillOps", "shadowOps", "strokeOps", "strokeEntry", "initial", "Types", "strokeOps", "ChartCanvas", "component2d", "DEFAULT_PLOT_AREA", "fillOps", "shadowOps", "DEFAULT_X_AXIS", "DEFAULT_Y_AXIS", "fresh", "loader", "state", "loadPaints", "ctx", "W", "H", "Graphics2D", "xMin", "xMax", "yMin", "yMax", "xFrac", "v", "yFrac", "interior", "f", "xStroke", "xGrid", "yStroke", "yGrid", "Flow", "Paint", "Rect", "Shapes", "Text", "Types", "component2d", "textOps", "row", "Paint", "Flow", "Shapes", "LegendEntry", "component2d", "Types", "measurer", "state", "children", "size", "box", "ctx", "revealOf", "value", "previous", "next", "index", "reveal", "ChartLegend", "fresh", "DEFAULT_LEGEND", "s", "marker", "series", "Text", "textOps", "Rect", "timeline", "revealFrame", "frameEase", "entry", "SPOTLIGHT_DIM", "target", "ease", "from", "t", "legendShows", "legend"]
7
+ }