@voila.dev/chart 5.0.118 → 6.0.120

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 (43) hide show
  1. package/dist/core/motion/leaving.d.ts +2 -2
  2. package/dist/core/motion/leaving.d.ts.map +1 -1
  3. package/dist/core/motion/leaving.js +4 -11
  4. package/dist/core/motion/leaving.js.map +2 -2
  5. package/dist/core/motion/motion-store.d.ts.map +1 -1
  6. package/dist/core/motion/motion-store.js +58 -84
  7. package/dist/core/motion/motion-store.js.map +3 -3
  8. package/dist/core/motion/points-motion.d.ts.map +1 -1
  9. package/dist/core/motion/points-motion.js +9 -22
  10. package/dist/core/motion/points-motion.js.map +2 -2
  11. package/dist/core/motion/strings.d.ts +4 -2
  12. package/dist/core/motion/strings.d.ts.map +1 -1
  13. package/dist/core/motion/strings.js +5 -6
  14. package/dist/core/motion/strings.js.map +2 -2
  15. package/dist/core/motion/timing.d.ts +16 -18
  16. package/dist/core/motion/timing.d.ts.map +1 -1
  17. package/dist/core/motion/timing.js +9 -8
  18. package/dist/core/motion/timing.js.map +2 -2
  19. package/dist/core/motion/tween-timing.d.ts +17 -0
  20. package/dist/core/motion/tween-timing.d.ts.map +1 -0
  21. package/dist/core/motion/tween-timing.js +22 -0
  22. package/dist/core/motion/tween-timing.js.map +7 -0
  23. package/dist/index.d.ts +2 -1
  24. package/dist/index.d.ts.map +1 -1
  25. package/dist/index.js +2 -0
  26. package/dist/index.js.map +2 -2
  27. package/dist/react/chart.d.ts +3 -2
  28. package/dist/react/chart.d.ts.map +1 -1
  29. package/dist/react/chart.js +3 -1
  30. package/dist/react/chart.js.map +2 -2
  31. package/dist/react/use-animated-scene.d.ts.map +1 -1
  32. package/dist/react/use-animated-scene.js +4 -4
  33. package/dist/react/use-animated-scene.js.map +2 -2
  34. package/package.json +2 -2
  35. package/src/core/motion/leaving.ts +6 -12
  36. package/src/core/motion/motion-store.ts +65 -109
  37. package/src/core/motion/points-motion.ts +12 -24
  38. package/src/core/motion/strings.ts +9 -12
  39. package/src/core/motion/timing.ts +28 -33
  40. package/src/core/motion/tween-timing.ts +35 -0
  41. package/src/index.ts +2 -0
  42. package/src/react/chart.tsx +9 -4
  43. package/src/react/use-animated-scene.ts +6 -4
@@ -18,17 +18,16 @@ function stringMixer(node, channel, from, to) {
18
18
  const morph = node.kind === "path" ? node.morph : void 0;
19
19
  return morph ? morph(from, to) : (progress) => tweenPath(from, to, progress);
20
20
  }
21
- function withStrings(node, values) {
22
- if (node.kind === "group") return node;
23
- const { d, ...colours } = values;
24
- const shaped = d === void 0 || node.kind !== "path" ? node : { ...node, d };
25
- return Object.keys(colours).length === 0 ? shaped : { ...shaped, paint: { ...shaped.paint, ...colours } };
21
+ function withString(node, channel, value) {
22
+ if (channel !== "d")
23
+ return { ...node, paint: { ...node.paint, [channel]: value } };
24
+ return node.kind === "path" ? { ...node, d: value } : node;
26
25
  }
27
26
  export {
28
27
  STRING_CHANNELS,
29
28
  colourBetween,
30
29
  stringMixer,
31
30
  stringOf,
32
- withStrings
31
+ withString
33
32
  };
34
33
  //# sourceMappingURL=strings.js.map
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../src/core/motion/strings.ts"],
4
- "sourcesContent": ["import { tweenPath } from \"#/core/motion/tween.ts\";\nimport type { SceneNode } from \"#/core/types.ts\";\n\n/** The channels that move as a string mixed by progress, not as a number. */\nexport const STRING_CHANNELS = [\"d\", \"fill\", \"stroke\"] as const;\n\nexport type StringChannel = (typeof STRING_CHANNELS)[number];\n\n/** 2 % steps: at most 51 colours per pair, so the Canvas resolver's cache stays small. */\nconst COLOUR_STEPS = 50;\n\n/**\n * A colour part of the way to another, as CSS: `color-mix()` reads the\n * chart's `var(--chart-1)` tokens as they are, so nothing parses colours,\n * and the Canvas resolver computes the mix against the page like any\n * other colour.\n */\nexport function colourBetween(\n\tfrom: string,\n\tto: string,\n\tprogress: number,\n): string {\n\tconst step = Math.round(Math.min(1, Math.max(0, progress)) * COLOUR_STEPS);\n\tif (step === 0) return from;\n\tif (step === COLOUR_STEPS) return to;\n\treturn `color-mix(in oklab, ${to} ${(step * 100) / COLOUR_STEPS}%, ${from})`;\n}\n\n/** What a string channel of `node` reads now. */\nexport function stringOf(\n\tnode: SceneNode | undefined,\n\tchannel: StringChannel,\n): string | undefined {\n\tif (node === undefined || node.kind === \"group\") return undefined;\n\treturn channel === \"d\"\n\t\t? node.kind === \"path\"\n\t\t\t? node.d\n\t\t\t: undefined\n\t\t: node.paint[channel];\n}\n\n/** The mix from what was painted to what `node` asks for. */\nexport function stringMixer(\n\tnode: SceneNode,\n\tchannel: StringChannel,\n\tfrom: string,\n\tto: string,\n): (progress: number) => string {\n\tif (channel !== \"d\") {\n\t\treturn (progress) => colourBetween(from, to, progress);\n\t}\n\tconst morph = node.kind === \"path\" ? node.morph : undefined;\n\treturn morph ? morph(from, to) : (progress) => tweenPath(from, to, progress);\n}\n\n/** `node` with each string channel set to its moving value. */\nexport function withStrings(\n\tnode: SceneNode,\n\tvalues: Partial<Record<StringChannel, string>>,\n): SceneNode {\n\tif (node.kind === \"group\") return node;\n\tconst { d, ...colours } = values;\n\tconst shaped =\n\t\td === undefined || node.kind !== \"path\" ? node : { ...node, d };\n\treturn Object.keys(colours).length === 0\n\t\t? shaped\n\t\t: ({ ...shaped, paint: { ...shaped.paint, ...colours } } as SceneNode);\n}\n"],
5
- "mappings": "AAAA,SAAS,iBAAiB;AAInB,MAAM,kBAAkB,CAAC,KAAK,QAAQ,QAAQ;AAKrD,MAAM,eAAe;AAQd,SAAS,cACf,MACA,IACA,UACS;AACT,QAAM,OAAO,KAAK,MAAM,KAAK,IAAI,GAAG,KAAK,IAAI,GAAG,QAAQ,CAAC,IAAI,YAAY;AACzE,MAAI,SAAS,EAAG,QAAO;AACvB,MAAI,SAAS,aAAc,QAAO;AAClC,SAAO,uBAAuB,EAAE,IAAK,OAAO,MAAO,YAAY,MAAM,IAAI;AAC1E;AAGO,SAAS,SACf,MACA,SACqB;AACrB,MAAI,SAAS,UAAa,KAAK,SAAS,QAAS,QAAO;AACxD,SAAO,YAAY,MAChB,KAAK,SAAS,SACb,KAAK,IACL,SACD,KAAK,MAAM,OAAO;AACtB;AAGO,SAAS,YACf,MACA,SACA,MACA,IAC+B;AAC/B,MAAI,YAAY,KAAK;AACpB,WAAO,CAAC,aAAa,cAAc,MAAM,IAAI,QAAQ;AAAA,EACtD;AACA,QAAM,QAAQ,KAAK,SAAS,SAAS,KAAK,QAAQ;AAClD,SAAO,QAAQ,MAAM,MAAM,EAAE,IAAI,CAAC,aAAa,UAAU,MAAM,IAAI,QAAQ;AAC5E;AAGO,SAAS,YACf,MACA,QACY;AACZ,MAAI,KAAK,SAAS,QAAS,QAAO;AAClC,QAAM,EAAE,GAAG,GAAG,QAAQ,IAAI;AAC1B,QAAM,SACL,MAAM,UAAa,KAAK,SAAS,SAAS,OAAO,EAAE,GAAG,MAAM,EAAE;AAC/D,SAAO,OAAO,KAAK,OAAO,EAAE,WAAW,IACpC,SACC,EAAE,GAAG,QAAQ,OAAO,EAAE,GAAG,OAAO,OAAO,GAAG,QAAQ,EAAE;AACzD;",
4
+ "sourcesContent": ["import { tweenPath } from \"#/core/motion/tween.ts\";\nimport type { SceneNode } from \"#/core/types.ts\";\n\n/** The channels that move as a string mixed by progress, not as a number. */\nexport const STRING_CHANNELS = [\"d\", \"fill\", \"stroke\"] as const;\n\nexport type StringChannel = (typeof STRING_CHANNELS)[number];\n\n/** 2 % steps: at most 51 colours per pair, so the Canvas resolver's cache stays small. */\nconst COLOUR_STEPS = 50;\n\n/**\n * A colour part of the way to another, as CSS: `color-mix()` reads the\n * chart's `var(--chart-1)` tokens as they are, so nothing parses colours,\n * and the Canvas resolver computes the mix against the page like any\n * other colour.\n */\nexport function colourBetween(\n\tfrom: string,\n\tto: string,\n\tprogress: number,\n): string {\n\tconst step = Math.round(Math.min(1, Math.max(0, progress)) * COLOUR_STEPS);\n\tif (step === 0) return from;\n\tif (step === COLOUR_STEPS) return to;\n\treturn `color-mix(in oklab, ${to} ${(step * 100) / COLOUR_STEPS}%, ${from})`;\n}\n\n/** What a string channel of `node` reads now. */\nexport function stringOf(\n\tnode: SceneNode | undefined,\n\tchannel: StringChannel,\n): string | undefined {\n\tif (node === undefined || node.kind === \"group\") return undefined;\n\treturn channel === \"d\"\n\t\t? node.kind === \"path\"\n\t\t\t? node.d\n\t\t\t: undefined\n\t\t: node.paint[channel];\n}\n\n/** The mix from what was painted to what `node` asks for. */\nexport function stringMixer(\n\tnode: SceneNode,\n\tchannel: StringChannel,\n\tfrom: string,\n\tto: string,\n): (progress: number) => string {\n\tif (channel !== \"d\") {\n\t\treturn (progress) => colourBetween(from, to, progress);\n\t}\n\tconst morph = node.kind === \"path\" ? node.morph : undefined;\n\treturn morph ? morph(from, to) : (progress) => tweenPath(from, to, progress);\n}\n\n/** `node` with one string channel set to its moving value. */\nexport function withString<Node extends Exclude<SceneNode, { kind: \"group\" }>>(\n\tnode: Node,\n\tchannel: StringChannel,\n\tvalue: string,\n): Node {\n\tif (channel !== \"d\")\n\t\treturn { ...node, paint: { ...node.paint, [channel]: value } };\n\treturn node.kind === \"path\" ? { ...node, d: value } : node;\n}\n"],
5
+ "mappings": "AAAA,SAAS,iBAAiB;AAInB,MAAM,kBAAkB,CAAC,KAAK,QAAQ,QAAQ;AAKrD,MAAM,eAAe;AAQd,SAAS,cACf,MACA,IACA,UACS;AACT,QAAM,OAAO,KAAK,MAAM,KAAK,IAAI,GAAG,KAAK,IAAI,GAAG,QAAQ,CAAC,IAAI,YAAY;AACzE,MAAI,SAAS,EAAG,QAAO;AACvB,MAAI,SAAS,aAAc,QAAO;AAClC,SAAO,uBAAuB,EAAE,IAAK,OAAO,MAAO,YAAY,MAAM,IAAI;AAC1E;AAGO,SAAS,SACf,MACA,SACqB;AACrB,MAAI,SAAS,UAAa,KAAK,SAAS,QAAS,QAAO;AACxD,SAAO,YAAY,MAChB,KAAK,SAAS,SACb,KAAK,IACL,SACD,KAAK,MAAM,OAAO;AACtB;AAGO,SAAS,YACf,MACA,SACA,MACA,IAC+B;AAC/B,MAAI,YAAY,KAAK;AACpB,WAAO,CAAC,aAAa,cAAc,MAAM,IAAI,QAAQ;AAAA,EACtD;AACA,QAAM,QAAQ,KAAK,SAAS,SAAS,KAAK,QAAQ;AAClD,SAAO,QAAQ,MAAM,MAAM,EAAE,IAAI,CAAC,aAAa,UAAU,MAAM,IAAI,QAAQ;AAC5E;AAGO,SAAS,WACf,MACA,SACA,OACO;AACP,MAAI,YAAY;AACf,WAAO,EAAE,GAAG,MAAM,OAAO,EAAE,GAAG,KAAK,OAAO,CAAC,OAAO,GAAG,MAAM,EAAE;AAC9D,SAAO,KAAK,SAAS,SAAS,EAAE,GAAG,MAAM,GAAG,MAAM,IAAI;AACvD;",
6
6
  "names": []
7
7
  }
@@ -1,28 +1,26 @@
1
- import type { EasingName } from "@voila.dev/motion";
2
- /** How a data update moves: what `<Chart animate>` accepts beyond `true`, `false` and a duration. */
3
- export type ChartAnimation = {
4
- readonly type?: "spring";
1
+ import { type Generator } from "@voila.dev/motion";
2
+ /**
3
+ * How every channel of a chart moves on an update. The store only asks it
4
+ * for one channel's motion, so a chart that never tweens never loads the
5
+ * tween: `tween()` builds one, the default is a spring.
6
+ */
7
+ export interface ChartTiming {
8
+ /** Seconds between neighbours. */
9
+ readonly stagger: number;
10
+ /** One channel from `from` to `to`, leaving at `velocity` (units per second). */
11
+ motion(from: number, to: number, velocity: number): Generator;
12
+ }
13
+ /** The spring `<Chart animate>` takes beyond `true`, `false` and a duration. */
14
+ export interface ChartSpring {
5
15
  /** Perceived duration, in milliseconds. */
6
16
  readonly duration?: number;
7
17
  /** 0 never overshoots (the default: a bar past its value shows a number the data doesn't have); up to 0.5. */
8
18
  readonly bounce?: number;
9
19
  /** Milliseconds between marks in focus order, capped at 300 ms in all. */
10
20
  readonly stagger?: number;
11
- } | {
12
- readonly type: "tween";
13
- readonly duration?: number;
14
- readonly easing?: EasingName;
15
- readonly stagger?: number;
16
- };
17
- export interface ChartTiming {
18
- readonly type: "spring" | "tween";
19
- /** Seconds, as the motion package counts them. */
20
- readonly duration: number;
21
- readonly bounce: number;
22
- readonly easing: EasingName;
23
- /** Seconds between neighbours. */
24
- readonly stagger: number;
25
21
  }
22
+ export type ChartAnimation = ChartSpring | ChartTiming;
23
+ export declare const DEFAULT_DURATION = 300;
26
24
  /** The timing an `animate` prop means, or `null` to snap. */
27
25
  export declare function chartTiming(animate: boolean | number | ChartAnimation): ChartTiming | null;
28
26
  /** The delay of the `index`-th of `count` marks: the gap shrinks so the last one never waits more than 300 ms. */
@@ -1 +1 @@
1
- {"version":3,"file":"timing.d.ts","sourceRoot":"","sources":["../../../src/core/motion/timing.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAEpD,qGAAqG;AACrG,MAAM,MAAM,cAAc,GACvB;IACA,QAAQ,CAAC,IAAI,CAAC,EAAE,QAAQ,CAAC;IACzB,2CAA2C;IAC3C,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAC3B,8GAA8G;IAC9G,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IACzB,0EAA0E;IAC1E,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;CACzB,GACD;IACA,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC;IACvB,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,MAAM,CAAC,EAAE,UAAU,CAAC;IAC7B,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;CACzB,CAAC;AAEL,MAAM,WAAW,WAAW;IAC3B,QAAQ,CAAC,IAAI,EAAE,QAAQ,GAAG,OAAO,CAAC;IAClC,kDAAkD;IAClD,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,MAAM,EAAE,UAAU,CAAC;IAC5B,kCAAkC;IAClC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;CACzB;AAKD,6DAA6D;AAC7D,wBAAgB,WAAW,CAC1B,OAAO,EAAE,OAAO,GAAG,MAAM,GAAG,cAAc,GACxC,WAAW,GAAG,IAAI,CAgBpB;AAED,kHAAkH;AAClH,wBAAgB,YAAY,CAC3B,MAAM,EAAE,WAAW,EACnB,KAAK,EAAE,MAAM,EACb,KAAK,EAAE,MAAM,GACX,MAAM,CAGR"}
1
+ {"version":3,"file":"timing.d.ts","sourceRoot":"","sources":["../../../src/core/motion/timing.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,SAAS,EAAU,MAAM,mBAAmB,CAAC;AAE3D;;;;GAIG;AACH,MAAM,WAAW,WAAW;IAC3B,kCAAkC;IAClC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,iFAAiF;IACjF,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,SAAS,CAAC;CAC9D;AAED,gFAAgF;AAChF,MAAM,WAAW,WAAW;IAC3B,2CAA2C;IAC3C,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAC3B,8GAA8G;IAC9G,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IACzB,0EAA0E;IAC1E,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;CAC1B;AAED,MAAM,MAAM,cAAc,GAAG,WAAW,GAAG,WAAW,CAAC;AAEvD,eAAO,MAAM,gBAAgB,MAAM,CAAC;AAGpC,6DAA6D;AAC7D,wBAAgB,WAAW,CAC1B,OAAO,EAAE,OAAO,GAAG,MAAM,GAAG,cAAc,GACxC,WAAW,GAAG,IAAI,CAepB;AAED,kHAAkH;AAClH,wBAAgB,YAAY,CAC3B,MAAM,EAAE,WAAW,EACnB,KAAK,EAAE,MAAM,EACb,KAAK,EAAE,MAAM,GACX,MAAM,CAGR"}
@@ -1,17 +1,17 @@
1
+ import { spring } from "@voila.dev/motion";
1
2
  const DEFAULT_DURATION = 300;
2
3
  const MAX_STAGGER_TOTAL = 0.3;
3
4
  function chartTiming(animate) {
4
5
  if (animate === false || animate === 0) return null;
6
+ if (typeof animate === "object" && "motion" in animate) return animate;
5
7
  const options = typeof animate === "object" ? animate : { duration: animate === true ? DEFAULT_DURATION : animate };
8
+ const duration = (options.duration ?? DEFAULT_DURATION) / 1e3;
9
+ const bounce = Math.min(Math.max(options.bounce ?? 0, 0), 0.5);
6
10
  return {
7
- type: options.type ?? "spring",
8
- duration: (options.duration ?? DEFAULT_DURATION) / 1e3,
9
- bounce: Math.min(
10
- Math.max("bounce" in options ? options.bounce ?? 0 : 0, 0),
11
- 0.5
12
- ),
13
- easing: ("easing" in options ? options.easing : void 0) ?? "easeOut",
14
- stagger: (options.stagger ?? 0) / 1e3
11
+ stagger: (options.stagger ?? 0) / 1e3,
12
+ motion(from, to, velocity) {
13
+ return spring({ from, to, velocity, duration, bounce });
14
+ }
15
15
  };
16
16
  }
17
17
  function staggerDelay(timing, index, count) {
@@ -19,6 +19,7 @@ function staggerDelay(timing, index, count) {
19
19
  return index * Math.min(timing.stagger, MAX_STAGGER_TOTAL / (count - 1));
20
20
  }
21
21
  export {
22
+ DEFAULT_DURATION,
22
23
  chartTiming,
23
24
  staggerDelay
24
25
  };
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../src/core/motion/timing.ts"],
4
- "sourcesContent": ["import type { EasingName } from \"@voila.dev/motion\";\n\n/** How a data update moves: what `<Chart animate>` accepts beyond `true`, `false` and a duration. */\nexport type ChartAnimation =\n\t| {\n\t\t\treadonly type?: \"spring\";\n\t\t\t/** Perceived duration, in milliseconds. */\n\t\t\treadonly duration?: number;\n\t\t\t/** 0 never overshoots (the default: a bar past its value shows a number the data doesn't have); up to 0.5. */\n\t\t\treadonly bounce?: number;\n\t\t\t/** Milliseconds between marks in focus order, capped at 300 ms in all. */\n\t\t\treadonly stagger?: number;\n\t }\n\t| {\n\t\t\treadonly type: \"tween\";\n\t\t\treadonly duration?: number;\n\t\t\treadonly easing?: EasingName;\n\t\t\treadonly stagger?: number;\n\t };\n\nexport interface ChartTiming {\n\treadonly type: \"spring\" | \"tween\";\n\t/** Seconds, as the motion package counts them. */\n\treadonly duration: number;\n\treadonly bounce: number;\n\treadonly easing: EasingName;\n\t/** Seconds between neighbours. */\n\treadonly stagger: number;\n}\n\nconst DEFAULT_DURATION = 300;\nconst MAX_STAGGER_TOTAL = 0.3;\n\n/** The timing an `animate` prop means, or `null` to snap. */\nexport function chartTiming(\n\tanimate: boolean | number | ChartAnimation,\n): ChartTiming | null {\n\tif (animate === false || animate === 0) return null;\n\tconst options: ChartAnimation =\n\t\ttypeof animate === \"object\"\n\t\t\t? animate\n\t\t\t: { duration: animate === true ? DEFAULT_DURATION : animate };\n\treturn {\n\t\ttype: options.type ?? \"spring\",\n\t\tduration: (options.duration ?? DEFAULT_DURATION) / 1000,\n\t\tbounce: Math.min(\n\t\t\tMath.max(\"bounce\" in options ? (options.bounce ?? 0) : 0, 0),\n\t\t\t0.5,\n\t\t),\n\t\teasing: (\"easing\" in options ? options.easing : undefined) ?? \"easeOut\",\n\t\tstagger: (options.stagger ?? 0) / 1000,\n\t};\n}\n\n/** The delay of the `index`-th of `count` marks: the gap shrinks so the last one never waits more than 300 ms. */\nexport function staggerDelay(\n\ttiming: ChartTiming,\n\tindex: number,\n\tcount: number,\n): number {\n\tif (timing.stagger === 0 || count < 2) return 0;\n\treturn index * Math.min(timing.stagger, MAX_STAGGER_TOTAL / (count - 1));\n}\n"],
5
- "mappings": "AA8BA,MAAM,mBAAmB;AACzB,MAAM,oBAAoB;AAGnB,SAAS,YACf,SACqB;AACrB,MAAI,YAAY,SAAS,YAAY,EAAG,QAAO;AAC/C,QAAM,UACL,OAAO,YAAY,WAChB,UACA,EAAE,UAAU,YAAY,OAAO,mBAAmB,QAAQ;AAC9D,SAAO;AAAA,IACN,MAAM,QAAQ,QAAQ;AAAA,IACtB,WAAW,QAAQ,YAAY,oBAAoB;AAAA,IACnD,QAAQ,KAAK;AAAA,MACZ,KAAK,IAAI,YAAY,UAAW,QAAQ,UAAU,IAAK,GAAG,CAAC;AAAA,MAC3D;AAAA,IACD;AAAA,IACA,SAAS,YAAY,UAAU,QAAQ,SAAS,WAAc;AAAA,IAC9D,UAAU,QAAQ,WAAW,KAAK;AAAA,EACnC;AACD;AAGO,SAAS,aACf,QACA,OACA,OACS;AACT,MAAI,OAAO,YAAY,KAAK,QAAQ,EAAG,QAAO;AAC9C,SAAO,QAAQ,KAAK,IAAI,OAAO,SAAS,qBAAqB,QAAQ,EAAE;AACxE;",
4
+ "sourcesContent": ["import { type Generator, spring } from \"@voila.dev/motion\";\n\n/**\n * How every channel of a chart moves on an update. The store only asks it\n * for one channel's motion, so a chart that never tweens never loads the\n * tween: `tween()` builds one, the default is a spring.\n */\nexport interface ChartTiming {\n\t/** Seconds between neighbours. */\n\treadonly stagger: number;\n\t/** One channel from `from` to `to`, leaving at `velocity` (units per second). */\n\tmotion(from: number, to: number, velocity: number): Generator;\n}\n\n/** The spring `<Chart animate>` takes beyond `true`, `false` and a duration. */\nexport interface ChartSpring {\n\t/** Perceived duration, in milliseconds. */\n\treadonly duration?: number;\n\t/** 0 never overshoots (the default: a bar past its value shows a number the data doesn't have); up to 0.5. */\n\treadonly bounce?: number;\n\t/** Milliseconds between marks in focus order, capped at 300 ms in all. */\n\treadonly stagger?: number;\n}\n\nexport type ChartAnimation = ChartSpring | ChartTiming;\n\nexport const DEFAULT_DURATION = 300;\nconst MAX_STAGGER_TOTAL = 0.3;\n\n/** The timing an `animate` prop means, or `null` to snap. */\nexport function chartTiming(\n\tanimate: boolean | number | ChartAnimation,\n): ChartTiming | null {\n\tif (animate === false || animate === 0) return null;\n\tif (typeof animate === \"object\" && \"motion\" in animate) return animate;\n\tconst options: ChartSpring =\n\t\ttypeof animate === \"object\"\n\t\t\t? animate\n\t\t\t: { duration: animate === true ? DEFAULT_DURATION : animate };\n\tconst duration = (options.duration ?? DEFAULT_DURATION) / 1000;\n\tconst bounce = Math.min(Math.max(options.bounce ?? 0, 0), 0.5);\n\treturn {\n\t\tstagger: (options.stagger ?? 0) / 1000,\n\t\tmotion(from, to, velocity) {\n\t\t\treturn spring({ from, to, velocity, duration, bounce });\n\t\t},\n\t};\n}\n\n/** The delay of the `index`-th of `count` marks: the gap shrinks so the last one never waits more than 300 ms. */\nexport function staggerDelay(\n\ttiming: ChartTiming,\n\tindex: number,\n\tcount: number,\n): number {\n\tif (timing.stagger === 0 || count < 2) return 0;\n\treturn index * Math.min(timing.stagger, MAX_STAGGER_TOTAL / (count - 1));\n}\n"],
5
+ "mappings": "AAAA,SAAyB,cAAc;AA0BhC,MAAM,mBAAmB;AAChC,MAAM,oBAAoB;AAGnB,SAAS,YACf,SACqB;AACrB,MAAI,YAAY,SAAS,YAAY,EAAG,QAAO;AAC/C,MAAI,OAAO,YAAY,YAAY,YAAY,QAAS,QAAO;AAC/D,QAAM,UACL,OAAO,YAAY,WAChB,UACA,EAAE,UAAU,YAAY,OAAO,mBAAmB,QAAQ;AAC9D,QAAM,YAAY,QAAQ,YAAY,oBAAoB;AAC1D,QAAM,SAAS,KAAK,IAAI,KAAK,IAAI,QAAQ,UAAU,GAAG,CAAC,GAAG,GAAG;AAC7D,SAAO;AAAA,IACN,UAAU,QAAQ,WAAW,KAAK;AAAA,IAClC,OAAO,MAAM,IAAI,UAAU;AAC1B,aAAO,OAAO,EAAE,MAAM,IAAI,UAAU,UAAU,OAAO,CAAC;AAAA,IACvD;AAAA,EACD;AACD;AAGO,SAAS,aACf,QACA,OACA,OACS;AACT,MAAI,OAAO,YAAY,KAAK,QAAQ,EAAG,QAAO;AAC9C,SAAO,QAAQ,KAAK,IAAI,OAAO,SAAS,qBAAqB,QAAQ,EAAE;AACxE;",
6
6
  "names": []
7
7
  }
@@ -0,0 +1,17 @@
1
+ import { type EasingName } from "@voila.dev/motion";
2
+ import { type ChartTiming } from "./timing.js";
3
+ export interface ChartTweenOptions {
4
+ /** Milliseconds. */
5
+ readonly duration?: number;
6
+ readonly easing?: EasingName;
7
+ /** Milliseconds between marks in focus order, capped at 300 ms in all. */
8
+ readonly stagger?: number;
9
+ }
10
+ /**
11
+ * A fixed curve instead of the spring, for `<Chart animate={tween()} />`.
12
+ * Its own import, so the easings only load in a chart that asks for them.
13
+ * The options stay on the timing as plain fields: `<Chart>` reads an inline
14
+ * `animate` by value, and a function alone would not tell two tweens apart.
15
+ */
16
+ export declare function tween(options?: ChartTweenOptions): ChartTiming & ChartTweenOptions;
17
+ //# sourceMappingURL=tween-timing.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tween-timing.d.ts","sourceRoot":"","sources":["../../../src/core/motion/tween-timing.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,UAAU,EAA2B,MAAM,mBAAmB,CAAC;AAC7E,OAAO,EAAE,KAAK,WAAW,EAAoB,MAAM,yBAAyB,CAAC;AAE7E,MAAM,WAAW,iBAAiB;IACjC,oBAAoB;IACpB,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,MAAM,CAAC,EAAE,UAAU,CAAC;IAC7B,0EAA0E;IAC1E,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;CAC1B;AAED;;;;;GAKG;AACH,wBAAgB,KAAK,CACpB,OAAO,GAAE,iBAAsB,GAC7B,WAAW,GAAG,iBAAiB,CAejC"}
@@ -0,0 +1,22 @@
1
+ import { tween as tweenGenerator } from "@voila.dev/motion";
2
+ import { DEFAULT_DURATION } from "./timing.js";
3
+ function tween(options = {}) {
4
+ const duration = options.duration ?? DEFAULT_DURATION;
5
+ const easing = options.easing ?? "easeOut";
6
+ return {
7
+ duration,
8
+ easing,
9
+ stagger: (options.stagger ?? 0) / 1e3,
10
+ motion(from, to) {
11
+ return tweenGenerator({
12
+ keyframes: [from, to],
13
+ duration: duration / 1e3,
14
+ ease: easing
15
+ });
16
+ }
17
+ };
18
+ }
19
+ export {
20
+ tween
21
+ };
22
+ //# sourceMappingURL=tween-timing.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../src/core/motion/tween-timing.ts"],
4
+ "sourcesContent": ["import { type EasingName, tween as tweenGenerator } from \"@voila.dev/motion\";\nimport { type ChartTiming, DEFAULT_DURATION } from \"#/core/motion/timing.ts\";\n\nexport interface ChartTweenOptions {\n\t/** Milliseconds. */\n\treadonly duration?: number;\n\treadonly easing?: EasingName;\n\t/** Milliseconds between marks in focus order, capped at 300 ms in all. */\n\treadonly stagger?: number;\n}\n\n/**\n * A fixed curve instead of the spring, for `<Chart animate={tween()} />`.\n * Its own import, so the easings only load in a chart that asks for them.\n * The options stay on the timing as plain fields: `<Chart>` reads an inline\n * `animate` by value, and a function alone would not tell two tweens apart.\n */\nexport function tween(\n\toptions: ChartTweenOptions = {},\n): ChartTiming & ChartTweenOptions {\n\tconst duration = options.duration ?? DEFAULT_DURATION;\n\tconst easing = options.easing ?? \"easeOut\";\n\treturn {\n\t\tduration,\n\t\teasing,\n\t\tstagger: (options.stagger ?? 0) / 1000,\n\t\tmotion(from, to) {\n\t\t\treturn tweenGenerator({\n\t\t\t\tkeyframes: [from, to],\n\t\t\t\tduration: duration / 1000,\n\t\t\t\tease: easing,\n\t\t\t});\n\t\t},\n\t};\n}\n"],
5
+ "mappings": "AAAA,SAA0B,SAAS,sBAAsB;AACzD,SAA2B,wBAAwB;AAgB5C,SAAS,MACf,UAA6B,CAAC,GACI;AAClC,QAAM,WAAW,QAAQ,YAAY;AACrC,QAAM,SAAS,QAAQ,UAAU;AACjC,SAAO;AAAA,IACN;AAAA,IACA;AAAA,IACA,UAAU,QAAQ,WAAW,KAAK;AAAA,IAClC,OAAO,MAAM,IAAI;AAChB,aAAO,eAAe;AAAA,QACrB,WAAW,CAAC,MAAM,EAAE;AAAA,QACpB,UAAU,WAAW;AAAA,QACrB,MAAM;AAAA,MACP,CAAC;AAAA,IACF;AAAA,EACD;AACD;",
6
+ "names": []
7
+ }
package/dist/index.d.ts CHANGED
@@ -23,8 +23,9 @@ export { type WaffleOptions, waffleY } from "./core/marks/stats/waffle.js";
23
23
  export { type TextOptions, text } from "./core/marks/text.js";
24
24
  export { type TickOptions, tickX, tickY } from "./core/marks/tick.js";
25
25
  export { createMotionStore, type MotionStore, } from "./core/motion/motion-store.js";
26
- export { type ChartAnimation, type ChartTiming, chartTiming, } from "./core/motion/timing.js";
26
+ export { type ChartAnimation, type ChartSpring, type ChartTiming, chartTiming, } from "./core/motion/timing.js";
27
27
  export { easeOutCubic, tweenScene } from "./core/motion/tween.js";
28
+ export { type ChartTweenOptions, tween } from "./core/motion/tween-timing.js";
28
29
  export { type ChartFocusStop, findNearest, focusStops, } from "./core/nearest.js";
29
30
  export { estimateTextWidth } from "./core/text.js";
30
31
  export { DEFAULT_THEME } from "./core/theme.js";
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,cAAc,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AACtE,OAAO,EACN,KAAK,gBAAgB,EACrB,KAAK,iBAAiB,EACtB,KAAK,iBAAiB,EACtB,KAAK,eAAe,EACpB,KAAK,UAAU,EACf,KAAK,iBAAiB,EACtB,KAAK,yBAAyB,EAC9B,KAAK,SAAS,EACd,WAAW,GACX,MAAM,wBAAwB,CAAC;AAChC,OAAO,EAAE,KAAK,EAAE,MAAM,iBAAiB,CAAC;AACxC,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AACzE,OAAO,EAAE,KAAK,WAAW,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,sBAAsB,CAAC;AACtE,OAAO,EAAE,KAAK,UAAU,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,qBAAqB,CAAC;AAClE,OAAO,EAAE,KAAK,WAAW,EAAE,IAAI,EAAE,MAAM,sBAAsB,CAAC;AAC9D,OAAO,EAAE,KAAK,UAAU,EAAE,GAAG,EAAE,MAAM,qBAAqB,CAAC;AAC3D,OAAO,EAAE,KAAK,aAAa,EAAE,MAAM,EAAE,MAAM,wBAAwB,CAAC;AACpE,OAAO,EAAE,KAAK,WAAW,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,sBAAsB,CAAC;AACtE,OAAO,EACN,KAAK,UAAU,EACf,GAAG,EACH,KAAK,EACL,KAAK,YAAY,EACjB,KAAK,gBAAgB,EACrB,KAAK,EACL,SAAS,GACT,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EAAE,KAAK,WAAW,EAAE,KAAK,EAAE,MAAM,sBAAsB,CAAC;AAC/D,OAAO,EAAE,KAAK,WAAW,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,sBAAsB,CAAC;AACtE,YAAY,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAC/D,OAAO,EACN,KAAK,iBAAiB,EACtB,WAAW,GACX,MAAM,kCAAkC,CAAC;AAC1C,OAAO,EACN,KAAK,UAAU,EACf,IAAI,EACJ,KAAK,gBAAgB,EACrB,SAAS,EACT,OAAO,GACP,MAAM,oCAAoC,CAAC;AAC5C,OAAO,EAAE,KAAK,YAAY,EAAE,MAAM,EAAE,MAAM,6BAA6B,CAAC;AACxE,OAAO,EAAE,KAAK,aAAa,EAAE,MAAM,EAAE,MAAM,8BAA8B,CAAC;AAC1E,OAAO,EACN,KAAK,gBAAgB,EACrB,SAAS,GACT,MAAM,iCAAiC,CAAC;AACzC,OAAO,EACN,KAAK,iBAAiB,EACtB,WAAW,GACX,MAAM,kCAAkC,CAAC;AAC1C,OAAO,EACN,UAAU,EACV,aAAa,EACb,SAAS,EACT,QAAQ,GACR,MAAM,kCAAkC,CAAC;AAC1C,OAAO,EAAE,KAAK,aAAa,EAAE,OAAO,EAAE,MAAM,8BAA8B,CAAC;AAC3E,OAAO,EAAE,KAAK,WAAW,EAAE,IAAI,EAAE,MAAM,sBAAsB,CAAC;AAC9D,OAAO,EAAE,KAAK,WAAW,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,sBAAsB,CAAC;AACtE,OAAO,EACN,iBAAiB,EACjB,KAAK,WAAW,GAChB,MAAM,+BAA+B,CAAC;AACvC,OAAO,EACN,KAAK,cAAc,EACnB,KAAK,WAAW,EAChB,KAAK,WAAW,EAChB,WAAW,GACX,MAAM,yBAAyB,CAAC;AACjC,OAAO,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;AAClE,OAAO,EAAE,KAAK,iBAAiB,EAAE,KAAK,EAAE,MAAM,+BAA+B,CAAC;AAC9E,OAAO,EACN,KAAK,cAAc,EACnB,WAAW,EACX,UAAU,GACV,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AACnD,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAChD,mBAAmB,iBAAiB,CAAC"}
package/dist/index.js CHANGED
@@ -50,6 +50,7 @@ import {
50
50
  chartTiming
51
51
  } from "./core/motion/timing.js";
52
52
  import { easeOutCubic, tweenScene } from "./core/motion/tween.js";
53
+ import { tween } from "./core/motion/tween-timing.js";
53
54
  import {
54
55
  findNearest,
55
56
  focusStops
@@ -100,6 +101,7 @@ export {
100
101
  text,
101
102
  tickX,
102
103
  tickY,
104
+ tween,
103
105
  tweenScene,
104
106
  violinY,
105
107
  waffleY
package/dist/index.js.map CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../src/index.ts"],
4
- "sourcesContent": ["export { type CompileOptions, compileChart } from \"#/core/compile.ts\";\nexport {\n\ttype ChartAxisOptions,\n\ttype ChartBuildContext,\n\ttype ChartColorOptions,\n\ttype ChartDefinition,\n\ttype ChartFacet,\n\ttype ChartFacetOptions,\n\ttype ChartPositionScaleOptions,\n\ttype ChartSpec,\n\tdefineChart,\n} from \"#/core/define-chart.ts\";\nexport { facet } from \"#/core/facet.ts\";\nexport { formatDate, formatNumber, formatValue } from \"#/core/format.ts\";\nexport { type AreaOptions, areaX, areaY } from \"#/core/marks/area.ts\";\nexport { type BarOptions, barX, barY } from \"#/core/marks/bar.ts\";\nexport { type CellOptions, cell } from \"#/core/marks/cell.ts\";\nexport { type DotOptions, dot } from \"#/core/marks/dot.ts\";\nexport { type FunnelOptions, funnel } from \"#/core/marks/funnel.ts\";\nexport { type LineOptions, lineX, lineY } from \"#/core/marks/line.ts\";\nexport {\n\ttype ArcOptions,\n\tarc,\n\tdonut,\n\ttype RadarOptions,\n\ttype RadialBarOptions,\n\tradar,\n\tradialBar,\n} from \"#/core/marks/polar.ts\";\nexport { type RectOptions, rectY } from \"#/core/marks/rect.ts\";\nexport { type RuleOptions, ruleX, ruleY } from \"#/core/marks/rule.ts\";\nexport type { ChartMarkOptions } from \"#/core/marks/shared.ts\";\nexport {\n\ttype DifferenceOptions,\n\tdifferenceY,\n} from \"#/core/marks/stats/difference.ts\";\nexport {\n\ttype BoxOptions,\n\tboxY,\n\ttype RidgelineOptions,\n\tridgeline,\n\tviolinY,\n} from \"#/core/marks/stats/distribution.ts\";\nexport { type DodgeOptions, dodgeY } from \"#/core/marks/stats/dodge.ts\";\nexport { type HexbinOptions, hexbin } from \"#/core/marks/stats/hexbin.ts\";\nexport {\n\ttype HistogramOptions,\n\thistogram,\n} from \"#/core/marks/stats/histogram.ts\";\nexport {\n\ttype RegressionOptions,\n\tregressionY,\n} from \"#/core/marks/stats/regression.ts\";\nexport {\n\tboxSummary,\n\tkernelDensity,\n\tlinearFit,\n\tquantile,\n} from \"#/core/marks/stats/statistics.ts\";\nexport { type WaffleOptions, waffleY } from \"#/core/marks/stats/waffle.ts\";\nexport { type TextOptions, text } from \"#/core/marks/text.ts\";\nexport { type TickOptions, tickX, tickY } from \"#/core/marks/tick.ts\";\nexport {\n\tcreateMotionStore,\n\ttype MotionStore,\n} from \"#/core/motion/motion-store.ts\";\nexport {\n\ttype ChartAnimation,\n\ttype ChartTiming,\n\tchartTiming,\n} from \"#/core/motion/timing.ts\";\nexport { easeOutCubic, tweenScene } from \"#/core/motion/tween.ts\";\nexport {\n\ttype ChartFocusStop,\n\tfindNearest,\n\tfocusStops,\n} from \"#/core/nearest.ts\";\nexport { estimateTextWidth } from \"#/core/text.ts\";\nexport { DEFAULT_THEME } from \"#/core/theme.ts\";\nexport type * from \"#/core/types.ts\";\n"],
5
- "mappings": "AAAA,SAA8B,oBAAoB;AAClD;AAAA,EASC;AAAA,OACM;AACP,SAAS,aAAa;AACtB,SAAS,YAAY,cAAc,mBAAmB;AACtD,SAA2B,OAAO,aAAa;AAC/C,SAA0B,MAAM,YAAY;AAC5C,SAA2B,YAAY;AACvC,SAA0B,WAAW;AACrC,SAA6B,cAAc;AAC3C,SAA2B,OAAO,aAAa;AAC/C;AAAA,EAEC;AAAA,EACA;AAAA,EAGA;AAAA,EACA;AAAA,OACM;AACP,SAA2B,aAAa;AACxC,SAA2B,OAAO,aAAa;AAE/C;AAAA,EAEC;AAAA,OACM;AACP;AAAA,EAEC;AAAA,EAEA;AAAA,EACA;AAAA,OACM;AACP,SAA4B,cAAc;AAC1C,SAA6B,cAAc;AAC3C;AAAA,EAEC;AAAA,OACM;AACP;AAAA,EAEC;AAAA,OACM;AACP;AAAA,EACC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACM;AACP,SAA6B,eAAe;AAC5C,SAA2B,YAAY;AACvC,SAA2B,OAAO,aAAa;AAC/C;AAAA,EACC;AAAA,OAEM;AACP;AAAA,EAGC;AAAA,OACM;AACP,SAAS,cAAc,kBAAkB;AACzC;AAAA,EAEC;AAAA,EACA;AAAA,OACM;AACP,SAAS,yBAAyB;AAClC,SAAS,qBAAqB;",
4
+ "sourcesContent": ["export { type CompileOptions, compileChart } from \"#/core/compile.ts\";\nexport {\n\ttype ChartAxisOptions,\n\ttype ChartBuildContext,\n\ttype ChartColorOptions,\n\ttype ChartDefinition,\n\ttype ChartFacet,\n\ttype ChartFacetOptions,\n\ttype ChartPositionScaleOptions,\n\ttype ChartSpec,\n\tdefineChart,\n} from \"#/core/define-chart.ts\";\nexport { facet } from \"#/core/facet.ts\";\nexport { formatDate, formatNumber, formatValue } from \"#/core/format.ts\";\nexport { type AreaOptions, areaX, areaY } from \"#/core/marks/area.ts\";\nexport { type BarOptions, barX, barY } from \"#/core/marks/bar.ts\";\nexport { type CellOptions, cell } from \"#/core/marks/cell.ts\";\nexport { type DotOptions, dot } from \"#/core/marks/dot.ts\";\nexport { type FunnelOptions, funnel } from \"#/core/marks/funnel.ts\";\nexport { type LineOptions, lineX, lineY } from \"#/core/marks/line.ts\";\nexport {\n\ttype ArcOptions,\n\tarc,\n\tdonut,\n\ttype RadarOptions,\n\ttype RadialBarOptions,\n\tradar,\n\tradialBar,\n} from \"#/core/marks/polar.ts\";\nexport { type RectOptions, rectY } from \"#/core/marks/rect.ts\";\nexport { type RuleOptions, ruleX, ruleY } from \"#/core/marks/rule.ts\";\nexport type { ChartMarkOptions } from \"#/core/marks/shared.ts\";\nexport {\n\ttype DifferenceOptions,\n\tdifferenceY,\n} from \"#/core/marks/stats/difference.ts\";\nexport {\n\ttype BoxOptions,\n\tboxY,\n\ttype RidgelineOptions,\n\tridgeline,\n\tviolinY,\n} from \"#/core/marks/stats/distribution.ts\";\nexport { type DodgeOptions, dodgeY } from \"#/core/marks/stats/dodge.ts\";\nexport { type HexbinOptions, hexbin } from \"#/core/marks/stats/hexbin.ts\";\nexport {\n\ttype HistogramOptions,\n\thistogram,\n} from \"#/core/marks/stats/histogram.ts\";\nexport {\n\ttype RegressionOptions,\n\tregressionY,\n} from \"#/core/marks/stats/regression.ts\";\nexport {\n\tboxSummary,\n\tkernelDensity,\n\tlinearFit,\n\tquantile,\n} from \"#/core/marks/stats/statistics.ts\";\nexport { type WaffleOptions, waffleY } from \"#/core/marks/stats/waffle.ts\";\nexport { type TextOptions, text } from \"#/core/marks/text.ts\";\nexport { type TickOptions, tickX, tickY } from \"#/core/marks/tick.ts\";\nexport {\n\tcreateMotionStore,\n\ttype MotionStore,\n} from \"#/core/motion/motion-store.ts\";\nexport {\n\ttype ChartAnimation,\n\ttype ChartSpring,\n\ttype ChartTiming,\n\tchartTiming,\n} from \"#/core/motion/timing.ts\";\nexport { easeOutCubic, tweenScene } from \"#/core/motion/tween.ts\";\nexport { type ChartTweenOptions, tween } from \"#/core/motion/tween-timing.ts\";\nexport {\n\ttype ChartFocusStop,\n\tfindNearest,\n\tfocusStops,\n} from \"#/core/nearest.ts\";\nexport { estimateTextWidth } from \"#/core/text.ts\";\nexport { DEFAULT_THEME } from \"#/core/theme.ts\";\nexport type * from \"#/core/types.ts\";\n"],
5
+ "mappings": "AAAA,SAA8B,oBAAoB;AAClD;AAAA,EASC;AAAA,OACM;AACP,SAAS,aAAa;AACtB,SAAS,YAAY,cAAc,mBAAmB;AACtD,SAA2B,OAAO,aAAa;AAC/C,SAA0B,MAAM,YAAY;AAC5C,SAA2B,YAAY;AACvC,SAA0B,WAAW;AACrC,SAA6B,cAAc;AAC3C,SAA2B,OAAO,aAAa;AAC/C;AAAA,EAEC;AAAA,EACA;AAAA,EAGA;AAAA,EACA;AAAA,OACM;AACP,SAA2B,aAAa;AACxC,SAA2B,OAAO,aAAa;AAE/C;AAAA,EAEC;AAAA,OACM;AACP;AAAA,EAEC;AAAA,EAEA;AAAA,EACA;AAAA,OACM;AACP,SAA4B,cAAc;AAC1C,SAA6B,cAAc;AAC3C;AAAA,EAEC;AAAA,OACM;AACP;AAAA,EAEC;AAAA,OACM;AACP;AAAA,EACC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACM;AACP,SAA6B,eAAe;AAC5C,SAA2B,YAAY;AACvC,SAA2B,OAAO,aAAa;AAC/C;AAAA,EACC;AAAA,OAEM;AACP;AAAA,EAIC;AAAA,OACM;AACP,SAAS,cAAc,kBAAkB;AACzC,SAAiC,aAAa;AAC9C;AAAA,EAEC;AAAA,EACA;AAAA,OACM;AACP,SAAS,yBAAyB;AAClC,SAAS,qBAAqB;",
6
6
  "names": []
7
7
  }
@@ -26,8 +26,9 @@ interface Props extends Omit<React.ComponentProps<"div">, "children" | "onSelect
26
26
  readonly dataTable?: boolean;
27
27
  /**
28
28
  * How a data update moves: `true` (a spring with no bounce), a perceived
29
- * duration in milliseconds, or `{ duration, bounce, stagger }` and
30
- * `{ type: "tween", easing }`. `false` snaps; reduced motion always does.
29
+ * duration in milliseconds, `{ duration, bounce, stagger }`, or
30
+ * `tween({ duration, easing, stagger })`. `false` snaps; reduced motion
31
+ * always does.
31
32
  */
32
33
  readonly animate?: boolean | number | ChartAnimation;
33
34
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"chart.d.ts","sourceRoot":"","sources":["../../src/react/chart.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAE/B,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AAC9D,OAAO,EAAE,KAAK,cAAc,EAAe,MAAM,yBAAyB,CAAC;AAG3E,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAClD,OAAO,EACN,KAAK,UAAU,EAGf,MAAM,2BAA2B,CAAC;AAGnC,OAAO,EAGN,KAAK,uBAAuB,EAC5B,MAAM,2BAA2B,CAAC;AAGnC,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AA4BzD,UAAU,KACT,SAAQ,IAAI,CAAC,KAAK,CAAC,cAAc,CAAC,KAAK,CAAC,EAAE,UAAU,GAAG,UAAU,GAAG,OAAO,CAAC;IAC5E,QAAQ,CAAC,UAAU,EAAE,eAAe,CAAC;IACrC,sFAAsF;IACtF,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,4DAA4D;IAC5D,QAAQ,CAAC,eAAe,CAAC,EAAE,MAAM,CAAC;IAClC,iDAAiD;IACjD,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IACzB,sEAAsE;IACtE,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAC9B,kGAAkG;IAClG,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAC;IAC/B,QAAQ,CAAC,QAAQ,CAAC,EAAE,aAAa,CAAC;IAClC,sEAAsE;IACtE,QAAQ,CAAC,OAAO,CAAC,EACd,OAAO,GACP,CAAC,CAAC,KAAK,EAAE,uBAAuB,KAAK,KAAK,CAAC,SAAS,CAAC,CAAC;IACzD,2DAA2D;IAC3D,QAAQ,CAAC,MAAM,CAAC,EAAE,OAAO,CAAC;IAC1B,mHAAmH;IACnH,QAAQ,CAAC,SAAS,CAAC,EAAE,OAAO,CAAC;IAC7B;;;;OAIG;IACH,QAAQ,CAAC,OAAO,CAAC,EAAE,OAAO,GAAG,MAAM,GAAG,cAAc,CAAC;IACrD;;;OAGG;IACH,QAAQ,CAAC,IAAI,CAAC,EAAE,OAAO,CAAC;IACxB,iHAAiH;IACjH,QAAQ,CAAC,KAAK,CAAC,EAAE,UAAU,CAAC;IAC5B,QAAQ,CAAC,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,UAAU,GAAG,IAAI,KAAK,IAAI,CAAC;IAC5D,QAAQ,CAAC,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,UAAU,GAAG,IAAI,KAAK,IAAI,CAAC;CACvD;AAOD;;;;GAIG;AACH,wBAAgB,KAAK,CAAC,EACrB,UAAU,EACV,SAAS,EACT,eAAe,EACf,MAAuB,EACvB,WAAW,EACX,YAAoC,EACpC,QAAQ,EAAE,QAAsB,EAChC,OAAc,EACd,MAAM,EACN,SAAgB,EAChB,OAAc,EACd,IAAY,EACZ,KAAK,EAAE,aAAa,EACpB,aAAa,EACb,QAAQ,EACR,KAAK,EACL,GAAG,KAAK,EACR,EAAE,KAAK,qBAgYP"}
1
+ {"version":3,"file":"chart.d.ts","sourceRoot":"","sources":["../../src/react/chart.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAE/B,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AAC9D,OAAO,EAAE,KAAK,cAAc,EAAe,MAAM,yBAAyB,CAAC;AAG3E,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAClD,OAAO,EACN,KAAK,UAAU,EAGf,MAAM,2BAA2B,CAAC;AAGnC,OAAO,EAGN,KAAK,uBAAuB,EAC5B,MAAM,2BAA2B,CAAC;AAGnC,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AA4BzD,UAAU,KACT,SAAQ,IAAI,CAAC,KAAK,CAAC,cAAc,CAAC,KAAK,CAAC,EAAE,UAAU,GAAG,UAAU,GAAG,OAAO,CAAC;IAC5E,QAAQ,CAAC,UAAU,EAAE,eAAe,CAAC;IACrC,sFAAsF;IACtF,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,4DAA4D;IAC5D,QAAQ,CAAC,eAAe,CAAC,EAAE,MAAM,CAAC;IAClC,iDAAiD;IACjD,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IACzB,sEAAsE;IACtE,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAC9B,kGAAkG;IAClG,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAC;IAC/B,QAAQ,CAAC,QAAQ,CAAC,EAAE,aAAa,CAAC;IAClC,sEAAsE;IACtE,QAAQ,CAAC,OAAO,CAAC,EACd,OAAO,GACP,CAAC,CAAC,KAAK,EAAE,uBAAuB,KAAK,KAAK,CAAC,SAAS,CAAC,CAAC;IACzD,2DAA2D;IAC3D,QAAQ,CAAC,MAAM,CAAC,EAAE,OAAO,CAAC;IAC1B,mHAAmH;IACnH,QAAQ,CAAC,SAAS,CAAC,EAAE,OAAO,CAAC;IAC7B;;;;;OAKG;IACH,QAAQ,CAAC,OAAO,CAAC,EAAE,OAAO,GAAG,MAAM,GAAG,cAAc,CAAC;IACrD;;;OAGG;IACH,QAAQ,CAAC,IAAI,CAAC,EAAE,OAAO,CAAC;IACxB,iHAAiH;IACjH,QAAQ,CAAC,KAAK,CAAC,EAAE,UAAU,CAAC;IAC5B,QAAQ,CAAC,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,UAAU,GAAG,IAAI,KAAK,IAAI,CAAC;IAC5D,QAAQ,CAAC,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,UAAU,GAAG,IAAI,KAAK,IAAI,CAAC;CACvD;AAOD;;;;GAIG;AACH,wBAAgB,KAAK,CAAC,EACrB,UAAU,EACV,SAAS,EACT,eAAe,EACf,MAAuB,EACvB,WAAW,EACX,YAAoC,EACpC,QAAQ,EAAE,QAAsB,EAChC,OAAc,EACd,MAAM,EACN,SAAgB,EAChB,OAAc,EACd,IAAY,EACZ,KAAK,EAAE,aAAa,EACpB,aAAa,EACb,QAAQ,EACR,KAAK,EACL,GAAG,KAAK,EACR,EAAE,KAAK,qBAoYP"}
@@ -107,9 +107,11 @@ function Chart({
107
107
  () => [definition, hidden, keyboardZooms],
108
108
  [definition, hidden, keyboardZooms]
109
109
  );
110
+ const latestAnimate = React.useRef(animate);
111
+ latestAnimate.current = animate;
110
112
  const animateKey = JSON.stringify(animate);
111
113
  const timing = React.useMemo(
112
- () => chartTiming(JSON.parse(animateKey)),
114
+ () => chartTiming(latestAnimate.current),
113
115
  [animateKey]
114
116
  );
115
117
  const shown = useAnimatedScene(scene, animationTrigger, timing);
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/react/chart.tsx"],
4
- "sourcesContent": ["import * as React from \"react\";\nimport { compileChart } from \"#/core/compile.ts\";\nimport type { ChartDefinition } from \"#/core/define-chart.ts\";\nimport { type ChartAnimation, chartTiming } from \"#/core/motion/timing.ts\";\nimport { findNearest, focusStops } from \"#/core/nearest.ts\";\nimport { toNumber } from \"#/core/scales/continuous.ts\";\nimport type { ChartPoint } from \"#/core/types.ts\";\nimport {\n\ttype ChartBrush,\n\ttype ChartBrushRange,\n\tordered,\n} from \"#/react/brush-contract.ts\";\nimport { ChartDataTable } from \"#/react/chart-data-table.tsx\";\nimport { ChartColorRamp, ChartLegend } from \"#/react/chart-legend.tsx\";\nimport {\n\tChartTooltip,\n\tChartTooltipContent,\n\ttype ChartTooltipRenderProps,\n} from \"#/react/chart-tooltip.tsx\";\nimport { FocusOverlay } from \"#/react/focus-overlay.tsx\";\nimport { messagesFor } from \"#/react/messages.ts\";\nimport type { ChartRenderer } from \"#/react/renderer.ts\";\nimport { SR_ONLY } from \"#/react/sr-only.ts\";\nimport { SvgRenderer } from \"#/react/svg-renderer.tsx\";\nimport { useAnimatedScene } from \"#/react/use-animated-scene.ts\";\nimport { useChartFocus } from \"#/react/use-chart-focus.ts\";\nimport { useChartWidth } from \"#/react/use-chart-width.ts\";\nimport { useChartZoom } from \"#/react/use-chart-zoom.ts\";\nimport { useTextMeasurer } from \"#/react/use-text-measurer.ts\";\n\nconst DEFAULT_HEIGHT = 300;\nconst DEFAULT_INITIAL_WIDTH = 640;\n\n/** Focus ring, forced colours and reduced motion: what inline styles cannot say. */\nconst CHART_CSS = `\n[data-slot=\"chart-surface\"]:focus-visible{outline:2px solid var(--ring,Highlight);outline-offset:2px;border-radius:4px}\n@media (forced-colors:active){\n[data-slot=\"chart-svg\"] [data-role=\"axis\"],[data-slot=\"chart-svg\"] [data-role=\"axis-title\"]{fill:CanvasText}\n[data-slot=\"chart-svg\"] [data-role=\"grid\"]{stroke:GrayText}\n[data-slot=\"chart-focus-ring\"]{stroke:Highlight}\n[data-slot=\"chart-tooltip\"]{border-color:CanvasText}\n}\n@media (prefers-reduced-motion:no-preference){\n[data-slot=\"chart-tooltip\"]{transition:transform 80ms ease-out}\n}\n`;\n\n// `title` would put a native tooltip on the whole chart; the props carry the\n// accessible name as `ariaLabel` instead.\ninterface Props\n\textends Omit<React.ComponentProps<\"div\">, \"children\" | \"onSelect\" | \"title\"> {\n\treadonly definition: ChartDefinition;\n\t/** What the chart shows, in a sentence: the screen reader's name for it. Required. */\n\treadonly ariaLabel: string;\n\t/** The takeaway, when the label alone does not carry it. */\n\treadonly ariaDescription?: string;\n\t/** Pixels. Ignored when `aspectRatio` is set. */\n\treadonly height?: number;\n\t/** Height as a share of the width: `0.5` is twice as wide as tall. */\n\treadonly aspectRatio?: number;\n\t/** Width used on the server and for the first client render, before the container is measured. */\n\treadonly initialWidth?: number;\n\treadonly renderer?: ChartRenderer;\n\t/** `false` turns the tooltip off; a function replaces its content. */\n\treadonly tooltip?:\n\t\t| boolean\n\t\t| ((props: ChartTooltipRenderProps) => React.ReactNode);\n\t/** Shown by default when there is more than one series. */\n\treadonly legend?: boolean;\n\t/** The visually hidden table of the values. On by default; turn it off only when the page shows the same table. */\n\treadonly dataTable?: boolean;\n\t/**\n\t * How a data update moves: `true` (a spring with no bounce), a perceived\n\t * duration in milliseconds, or `{ duration, bounce, stagger }` and\n\t * `{ type: \"tween\", easing }`. `false` snaps; reduced motion always does.\n\t */\n\treadonly animate?: boolean | number | ChartAnimation;\n\t/**\n\t * Zoom a continuous x: the wheel while the chart has focus, + and \u2212,\n\t * Shift and the arrows to pan, a drag to pan when zoomed, 0 to reset.\n\t */\n\treadonly zoom?: boolean;\n\t/** Select a range of x by dragging or from the keyboard: `brushX({ onBrush })` from `@voila.dev/chart/brush`. */\n\treadonly brush?: ChartBrush;\n\treadonly onFocusChange?: (point: ChartPoint | null) => void;\n\treadonly onSelect?: (point: ChartPoint | null) => void;\n}\n\n/** A pointer that moved less than this between down and up clicked rather than dragged. */\nconst DRAG_THRESHOLD = 3;\nconst ZOOM_STEP = 0.8;\nconst PAN_SHARE = 0.1;\n\n/**\n * Draws a definition. Everything a reader needs beyond the picture comes with\n * it: keyboard focus on every value, a live region that reads the focused\n * value, a legend that toggles series, and the values as a hidden table.\n */\nexport function Chart({\n\tdefinition,\n\tariaLabel,\n\tariaDescription,\n\theight = DEFAULT_HEIGHT,\n\taspectRatio,\n\tinitialWidth = DEFAULT_INITIAL_WIDTH,\n\trenderer: Renderer = SvgRenderer,\n\ttooltip = true,\n\tlegend,\n\tdataTable = true,\n\tanimate = true,\n\tzoom = false,\n\tbrush: brushBehavior,\n\tonFocusChange,\n\tonSelect,\n\tstyle,\n\t...props\n}: Props) {\n\tconst chartId = React.useId().replaceAll(\":\", \"\");\n\tconst { ref, width, node } = useChartWidth(initialWidth);\n\tconst measureText = useTextMeasurer(node);\n\tconst [hidden, setHidden] = React.useState<ReadonlySet<string>>(\n\t\t() => new Set(),\n\t);\n\tconst chartHeight =\n\t\taspectRatio === undefined ? height : Math.round(width * aspectRatio);\n\n\tconst baseScene = React.useMemo(\n\t\t() =>\n\t\t\tcompileChart(definition, {\n\t\t\t\twidth,\n\t\t\t\theight: chartHeight,\n\t\t\t\tmeasureText,\n\t\t\t\thiddenSeries: hidden,\n\t\t\t}),\n\t\t[definition, width, chartHeight, measureText, hidden],\n\t);\n\tconst baseX = baseScene.scales.x;\n\tconst fullX = React.useMemo<readonly [number, number] | undefined>(\n\t\t() =>\n\t\t\tbaseX && baseX.kind !== \"band\" && baseX.kind !== \"point\"\n\t\t\t\t? [toNumber(baseX.domain[0]), toNumber(baseX.domain[1])]\n\t\t\t\t: undefined,\n\t\t[baseX],\n\t);\n\tconst viewport = useChartZoom(zoom ? fullX : undefined, definition);\n\tconst scene = React.useMemo(\n\t\t() =>\n\t\t\tviewport.domain === null\n\t\t\t\t? baseScene\n\t\t\t\t: compileChart(definition, {\n\t\t\t\t\t\twidth,\n\t\t\t\t\t\theight: chartHeight,\n\t\t\t\t\t\tmeasureText,\n\t\t\t\t\t\thiddenSeries: hidden,\n\t\t\t\t\t\txDomain: viewport.domain,\n\t\t\t\t\t}),\n\t\t[\n\t\t\tbaseScene,\n\t\t\tviewport.domain,\n\t\t\tdefinition,\n\t\t\twidth,\n\t\t\tchartHeight,\n\t\t\tmeasureText,\n\t\t\thidden,\n\t\t],\n\t);\n\tconst [brush, setBrush] = React.useState<ChartBrushRange | null>(null);\n\tconst [zoomAnnouncement, setZoomAnnouncement] = React.useState(\"\");\n\tconst drag = React.useRef<{\n\t\treadonly startX: number;\n\t\treadonly domain: readonly [number, number] | null;\n\t\tmoved: boolean;\n\t} | null>(null);\n\tconst stops = React.useMemo(() => focusStops(scene), [scene]);\n\t// A step zoom from the keyboard moves like a data update; the wheel and a\n\t// drag are direct manipulation, so they snap.\n\tconst [keyboardZooms, setKeyboardZooms] = React.useState(0);\n\tconst animationTrigger = React.useMemo(\n\t\t() => [definition, hidden, keyboardZooms],\n\t\t[definition, hidden, keyboardZooms],\n\t);\n\t// An inline `animate={{ \u2026 }}` object is read by value, not identity.\n\tconst animateKey = JSON.stringify(animate);\n\tconst timing = React.useMemo(\n\t\t() => chartTiming(JSON.parse(animateKey)),\n\t\t[animateKey],\n\t);\n\tconst shown = useAnimatedScene(scene, animationTrigger, timing);\n\tconst messages = messagesFor(scene.locale);\n\tconst { active, handlers } = useChartFocus({\n\t\tstops,\n\t\torder: scene.focusOrder,\n\t\thitTest: (x, y) => findNearest(scene, stops, x, y),\n\t\tonFocusChange,\n\t\tonSelect,\n\t});\n\n\tconst xScale = scene.scales.x;\n\tconst xContinuous =\n\t\txScale !== undefined && xScale.kind !== \"band\" && xScale.kind !== \"point\";\n\n\tfunction updateBrush(range: ChartBrushRange | null) {\n\t\tsetBrush(range);\n\t\tbrushBehavior?.onBrush(range);\n\t}\n\n\tfunction localX(event: React.PointerEvent<HTMLElement>): number {\n\t\treturn event.clientX - event.currentTarget.getBoundingClientRect().left;\n\t}\n\n\tfunction onPointerDown(event: React.PointerEvent<HTMLDivElement>) {\n\t\tif (!brushBehavior && !(zoom && viewport.domain !== null)) {\n\t\t\treturn;\n\t\t}\n\t\tevent.currentTarget.setPointerCapture(event.pointerId);\n\t\t// A drag selects a range, not a value: the hover tooltip gives way.\n\t\thandlers.onPointerLeave();\n\t\tdrag.current = {\n\t\t\tstartX: localX(event),\n\t\t\tdomain: viewport.domain,\n\t\t\tmoved: false,\n\t\t};\n\t}\n\n\tfunction onPointerMove(event: React.PointerEvent<HTMLDivElement>) {\n\t\tconst current = drag.current;\n\t\tif (current === null || xScale === undefined) {\n\t\t\thandlers.onPointerMove(event);\n\t\t\treturn;\n\t\t}\n\t\tconst x = localX(event);\n\t\tif (Math.abs(x - current.startX) > DRAG_THRESHOLD) {\n\t\t\tcurrent.moved = true;\n\t\t}\n\t\tif (!current.moved) return;\n\t\tif (brushBehavior) {\n\t\t\tupdateBrush(\n\t\t\t\tordered(xScale, [xScale.invert(current.startX), xScale.invert(x)]),\n\t\t\t);\n\t\t} else if (current.domain !== null) {\n\t\t\tconst [from, to] = current.domain;\n\t\t\tconst perPixel = (to - from) / Math.max(1, scene.plot.width);\n\t\t\tviewport.pan(\n\t\t\t\t(current.startX - x) * perPixel -\n\t\t\t\t\t((viewport.domain?.[0] ?? from) - from),\n\t\t\t);\n\t\t}\n\t}\n\n\tfunction onPointerUp(event: React.PointerEvent<HTMLDivElement>) {\n\t\tif (\n\t\t\tdrag.current !== null &&\n\t\t\tevent.currentTarget.hasPointerCapture(event.pointerId)\n\t\t) {\n\t\t\tevent.currentTarget.releasePointerCapture(event.pointerId);\n\t\t}\n\t}\n\n\tfunction onClick(event: React.MouseEvent<HTMLDivElement>) {\n\t\tconst dragged = drag.current?.moved === true;\n\t\tdrag.current = null;\n\t\tif (!dragged) {\n\t\t\thandlers.onClick(event);\n\t\t}\n\t}\n\n\tfunction announceViewport(next: readonly [number, number] | null) {\n\t\tif (xScale === undefined) return;\n\t\tconst [from, to] = next ?? fullX ?? [0, 0];\n\t\tconst asValue = (value: number) =>\n\t\t\txScale.kind === \"time\" ? new Date(value) : value;\n\t\tsetZoomAnnouncement(\n\t\t\tmessages.showing(\n\t\t\t\txScale.format(asValue(from)),\n\t\t\t\txScale.format(asValue(to)),\n\t\t\t),\n\t\t);\n\t}\n\n\tfunction onKeyDown(event: React.KeyboardEvent<HTMLDivElement>) {\n\t\tif (zoom && xContinuous && fullX !== undefined) {\n\t\t\tconst focused = active?.point.xValue;\n\t\t\tconst [from, to] = viewport.domain ?? fullX;\n\t\t\tconst center =\n\t\t\t\tfocused === undefined ? (from + to) / 2 : toNumber(focused);\n\t\t\tif (event.key === \"+\" || event.key === \"=\") {\n\t\t\t\tevent.preventDefault();\n\t\t\t\tviewport.zoomAround(center, ZOOM_STEP);\n\t\t\t\tsetKeyboardZooms((count) => count + 1);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (event.key === \"-\" || event.key === \"_\") {\n\t\t\t\tevent.preventDefault();\n\t\t\t\tviewport.zoomAround(center, 1 / ZOOM_STEP);\n\t\t\t\tsetKeyboardZooms((count) => count + 1);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (event.key === \"0\") {\n\t\t\t\tevent.preventDefault();\n\t\t\t\tviewport.reset();\n\t\t\t\tsetKeyboardZooms((count) => count + 1);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (\n\t\t\t\tevent.shiftKey &&\n\t\t\t\t(event.key === \"ArrowLeft\" || event.key === \"ArrowRight\")\n\t\t\t) {\n\t\t\t\tevent.preventDefault();\n\t\t\t\tviewport.pan(\n\t\t\t\t\t(event.key === \"ArrowRight\" ? 1 : -1) * viewport.span() * PAN_SHARE,\n\t\t\t\t);\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t\thandlers.onKeyDown(event);\n\t}\n\n\t// Moving the focus says the new value; the last zoom's message gives way.\n\tconst activeKey = active?.point.key;\n\t// biome-ignore lint/correctness/useExhaustiveDependencies: clears on a change of focused point only\n\tReact.useEffect(() => {\n\t\tsetZoomAnnouncement(\"\");\n\t}, [activeKey]);\n\n\tconst viewportKey = viewport.domain?.join(\":\") ?? \"full\";\n\tconst previousViewport = React.useRef(viewportKey);\n\t// biome-ignore lint/correctness/useExhaustiveDependencies: announce a change of viewport, once\n\tReact.useEffect(() => {\n\t\tif (previousViewport.current !== viewportKey) {\n\t\t\tpreviousViewport.current = viewportKey;\n\t\t\tannounceViewport(viewport.domain);\n\t\t}\n\t}, [viewportKey]);\n\n\t// The wheel zooms only while the chart has focus: scrolling past a chart\n\t// must keep scrolling the page.\n\tconst zoomRef = React.useRef({\n\t\tviewport,\n\t\txScale,\n\t\tenabled: zoom && xContinuous,\n\t});\n\tzoomRef.current = { viewport, xScale, enabled: zoom && xContinuous };\n\tReact.useEffect(() => {\n\t\tif (node === null) return;\n\t\tfunction onWheel(event: WheelEvent) {\n\t\t\tconst { viewport: current, xScale: scale, enabled } = zoomRef.current;\n\t\t\tif (\n\t\t\t\t!enabled ||\n\t\t\t\tscale === undefined ||\n\t\t\t\tdocument.activeElement !== node ||\n\t\t\t\tnode === null\n\t\t\t)\n\t\t\t\treturn;\n\t\t\tevent.preventDefault();\n\t\t\tconst x = event.clientX - node.getBoundingClientRect().left;\n\t\t\tcurrent.zoomAround(\n\t\t\t\ttoNumber(scale.invert(x)),\n\t\t\t\tevent.deltaY > 0 ? 1 / ZOOM_STEP : ZOOM_STEP,\n\t\t\t);\n\t\t}\n\t\tnode.addEventListener(\"wheel\", onWheel, { passive: false });\n\t\treturn () => node.removeEventListener(\"wheel\", onWheel);\n\t}, [node]);\n\n\tconst announcement =\n\t\tactive?.source === \"keyboard\"\n\t\t\t? `${active.point.title} : ${(active.stop.points.length > 1\n\t\t\t\t\t? active.stop.points\n\t\t\t\t\t: [active.point]\n\t\t\t\t)\n\t\t\t\t\t.map((point) => `${point.seriesLabel ?? scene.yLabel} ${point.value}`)\n\t\t\t\t\t.join(\", \")}`\n\t\t\t: \"\";\n\tconst showLegend = legend ?? scene.legend.length > 1;\n\tconst descriptionId = `${chartId}-description`;\n\tconst renderTooltip =\n\t\ttypeof tooltip === \"function\" ? tooltip : ChartTooltipContent;\n\n\tfunction toggle(key: string) {\n\t\tsetHidden((current) => {\n\t\t\tconst next = new Set(current);\n\t\t\tif (!next.delete(key)) {\n\t\t\t\tnext.add(key);\n\t\t\t}\n\t\t\treturn next;\n\t\t});\n\t}\n\n\treturn (\n\t\t<div\n\t\t\tdata-slot=\"chart\"\n\t\t\tstyle={{ display: \"grid\", gap: 8, ...style }}\n\t\t\t{...props}\n\t\t>\n\t\t\t<style href=\"voila-chart\" precedence=\"default\">\n\t\t\t\t{CHART_CSS}\n\t\t\t</style>\n\t\t\t<div\n\t\t\t\tdata-slot=\"chart-frame\"\n\t\t\t\tstyle={{ position: \"relative\", height: chartHeight }}\n\t\t\t>\n\t\t\t\t<div\n\t\t\t\t\tref={ref}\n\t\t\t\t\trole=\"img\"\n\t\t\t\t\taria-roledescription={messages.roleDescription}\n\t\t\t\t\taria-label={ariaLabel}\n\t\t\t\t\taria-describedby={descriptionId}\n\t\t\t\t\t// The chart is one focus stop; the arrow keys move inside it, the way\n\t\t\t\t\t// a reader walks the values. role=\"img\" keeps it a single named\n\t\t\t\t\t// graphic, the live region speaks the focused value.\n\t\t\t\t\t// biome-ignore lint/a11y/noNoninteractiveTabindex: keyboard exploration of the values\n\t\t\t\t\ttabIndex={0}\n\t\t\t\t\tdata-slot=\"chart-surface\"\n\t\t\t\t\tstyle={{\n\t\t\t\t\t\tposition: \"absolute\",\n\t\t\t\t\t\tinset: 0,\n\t\t\t\t\t\ttouchAction: brushBehavior || zoom ? \"none\" : \"pan-y\",\n\t\t\t\t\t\t// Dragging across the plot brushes or pans; without this it\n\t\t\t\t\t\t// also selected the tick labels and the tooltip as text.\n\t\t\t\t\t\t...(brushBehavior || zoom\n\t\t\t\t\t\t\t? { userSelect: \"none\", WebkitUserSelect: \"none\" }\n\t\t\t\t\t\t\t: {}),\n\t\t\t\t\t}}\n\t\t\t\t\t{...handlers}\n\t\t\t\t\tonKeyDown={onKeyDown}\n\t\t\t\t\tonPointerDown={onPointerDown}\n\t\t\t\t\tonPointerMove={onPointerMove}\n\t\t\t\t\tonPointerUp={onPointerUp}\n\t\t\t\t\tonClick={onClick}\n\t\t\t\t>\n\t\t\t\t\t<Renderer scene={shown} chartId={chartId} />\n\t\t\t\t\t<FocusOverlay scene={scene} active={active} theme={scene.theme} />\n\t\t\t\t\t{tooltip !== false && active !== null ? (\n\t\t\t\t\t\t<ChartTooltip\n\t\t\t\t\t\t\tactive={active}\n\t\t\t\t\t\t\tscene={scene}\n\t\t\t\t\t\t\trender={renderTooltip}\n\t\t\t\t\t\t/>\n\t\t\t\t\t) : null}\n\t\t\t\t</div>\n\t\t\t\t{brushBehavior && xScale ? (\n\t\t\t\t\t<brushBehavior.Layer\n\t\t\t\t\t\tscale={xScale}\n\t\t\t\t\t\tplot={scene.plot}\n\t\t\t\t\t\trange={brush}\n\t\t\t\t\t\tmessages={messages}\n\t\t\t\t\t\tonChange={updateBrush}\n\t\t\t\t\t/>\n\t\t\t\t) : null}\n\t\t\t</div>\n\t\t\t<div id={descriptionId} style={SR_ONLY}>\n\t\t\t\t{ariaDescription ? `${ariaDescription} ` : \"\"}\n\t\t\t\t{messages.keyboardHint}\n\t\t\t\t{zoom && xContinuous ? messages.zoomHint : \"\"}\n\t\t\t</div>\n\t\t\t<div aria-live=\"polite\" aria-atomic=\"true\" style={SR_ONLY}>\n\t\t\t\t{zoomAnnouncement || announcement}\n\t\t\t</div>\n\t\t\t{viewport.domain !== null ? (\n\t\t\t\t<button\n\t\t\t\t\ttype=\"button\"\n\t\t\t\t\tdata-slot=\"chart-reset-zoom\"\n\t\t\t\t\tonClick={() => viewport.reset()}\n\t\t\t\t\tstyle={{\n\t\t\t\t\t\tjustifySelf: \"end\",\n\t\t\t\t\t\tborder:\n\t\t\t\t\t\t\t\"1px solid var(--border, color-mix(in oklab, currentColor 20%, transparent))\",\n\t\t\t\t\t\tborderRadius: 6,\n\t\t\t\t\t\tbackground: \"transparent\",\n\t\t\t\t\t\tcolor: \"inherit\",\n\t\t\t\t\t\tfont: \"inherit\",\n\t\t\t\t\t\tfontSize: 12,\n\t\t\t\t\t\tpadding: \"2px 8px\",\n\t\t\t\t\t\tcursor: \"pointer\",\n\t\t\t\t\t}}\n\t\t\t\t>\n\t\t\t\t\t{messages.resetZoom}\n\t\t\t\t</button>\n\t\t\t) : null}\n\t\t\t{showLegend && scene.legend.length > 0 ? (\n\t\t\t\t<ChartLegend\n\t\t\t\t\titems={scene.legend}\n\t\t\t\t\thidden={hidden}\n\t\t\t\t\tlabel={messages.legend}\n\t\t\t\t\tonToggle={toggle}\n\t\t\t\t/>\n\t\t\t) : null}\n\t\t\t{scene.colorRamp && legend !== false ? (\n\t\t\t\t<ChartColorRamp ramp={scene.colorRamp} label={messages.legend} />\n\t\t\t) : null}\n\t\t\t{dataTable ? (\n\t\t\t\t<ChartDataTable\n\t\t\t\t\tscene={scene}\n\t\t\t\t\tstops={stops}\n\t\t\t\t\tcaption={messages.dataTable(ariaLabel)}\n\t\t\t\t\tseriesHeading={messages.series}\n\t\t\t\t/>\n\t\t\t) : null}\n\t\t</div>\n\t);\n}\n"],
5
- "mappings": "AA0YG,cAOC,YAPD;AA1YH,YAAY,WAAW;AACvB,SAAS,oBAAoB;AAE7B,SAA8B,mBAAmB;AACjD,SAAS,aAAa,kBAAkB;AACxC,SAAS,gBAAgB;AAEzB;AAAA,EAGC;AAAA,OACM;AACP,SAAS,sBAAsB;AAC/B,SAAS,gBAAgB,mBAAmB;AAC5C;AAAA,EACC;AAAA,EACA;AAAA,OAEM;AACP,SAAS,oBAAoB;AAC7B,SAAS,mBAAmB;AAE5B,SAAS,eAAe;AACxB,SAAS,mBAAmB;AAC5B,SAAS,wBAAwB;AACjC,SAAS,qBAAqB;AAC9B,SAAS,qBAAqB;AAC9B,SAAS,oBAAoB;AAC7B,SAAS,uBAAuB;AAEhC,MAAM,iBAAiB;AACvB,MAAM,wBAAwB;AAG9B,MAAM,YAAY;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAuDlB,MAAM,iBAAiB;AACvB,MAAM,YAAY;AAClB,MAAM,YAAY;AAOX,SAAS,MAAM;AAAA,EACrB;AAAA,EACA;AAAA,EACA;AAAA,EACA,SAAS;AAAA,EACT;AAAA,EACA,eAAe;AAAA,EACf,UAAU,WAAW;AAAA,EACrB,UAAU;AAAA,EACV;AAAA,EACA,YAAY;AAAA,EACZ,UAAU;AAAA,EACV,OAAO;AAAA,EACP,OAAO;AAAA,EACP;AAAA,EACA;AAAA,EACA;AAAA,EACA,GAAG;AACJ,GAAU;AACT,QAAM,UAAU,MAAM,MAAM,EAAE,WAAW,KAAK,EAAE;AAChD,QAAM,EAAE,KAAK,OAAO,KAAK,IAAI,cAAc,YAAY;AACvD,QAAM,cAAc,gBAAgB,IAAI;AACxC,QAAM,CAAC,QAAQ,SAAS,IAAI,MAAM;AAAA,IACjC,MAAM,oBAAI,IAAI;AAAA,EACf;AACA,QAAM,cACL,gBAAgB,SAAY,SAAS,KAAK,MAAM,QAAQ,WAAW;AAEpE,QAAM,YAAY,MAAM;AAAA,IACvB,MACC,aAAa,YAAY;AAAA,MACxB;AAAA,MACA,QAAQ;AAAA,MACR;AAAA,MACA,cAAc;AAAA,IACf,CAAC;AAAA,IACF,CAAC,YAAY,OAAO,aAAa,aAAa,MAAM;AAAA,EACrD;AACA,QAAM,QAAQ,UAAU,OAAO;AAC/B,QAAM,QAAQ,MAAM;AAAA,IACnB,MACC,SAAS,MAAM,SAAS,UAAU,MAAM,SAAS,UAC9C,CAAC,SAAS,MAAM,OAAO,CAAC,CAAC,GAAG,SAAS,MAAM,OAAO,CAAC,CAAC,CAAC,IACrD;AAAA,IACJ,CAAC,KAAK;AAAA,EACP;AACA,QAAM,WAAW,aAAa,OAAO,QAAQ,QAAW,UAAU;AAClE,QAAM,QAAQ,MAAM;AAAA,IACnB,MACC,SAAS,WAAW,OACjB,YACA,aAAa,YAAY;AAAA,MACzB;AAAA,MACA,QAAQ;AAAA,MACR;AAAA,MACA,cAAc;AAAA,MACd,SAAS,SAAS;AAAA,IACnB,CAAC;AAAA,IACJ;AAAA,MACC;AAAA,MACA,SAAS;AAAA,MACT;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACD;AAAA,EACD;AACA,QAAM,CAAC,OAAO,QAAQ,IAAI,MAAM,SAAiC,IAAI;AACrE,QAAM,CAAC,kBAAkB,mBAAmB,IAAI,MAAM,SAAS,EAAE;AACjE,QAAM,OAAO,MAAM,OAIT,IAAI;AACd,QAAM,QAAQ,MAAM,QAAQ,MAAM,WAAW,KAAK,GAAG,CAAC,KAAK,CAAC;AAG5D,QAAM,CAAC,eAAe,gBAAgB,IAAI,MAAM,SAAS,CAAC;AAC1D,QAAM,mBAAmB,MAAM;AAAA,IAC9B,MAAM,CAAC,YAAY,QAAQ,aAAa;AAAA,IACxC,CAAC,YAAY,QAAQ,aAAa;AAAA,EACnC;AAEA,QAAM,aAAa,KAAK,UAAU,OAAO;AACzC,QAAM,SAAS,MAAM;AAAA,IACpB,MAAM,YAAY,KAAK,MAAM,UAAU,CAAC;AAAA,IACxC,CAAC,UAAU;AAAA,EACZ;AACA,QAAM,QAAQ,iBAAiB,OAAO,kBAAkB,MAAM;AAC9D,QAAM,WAAW,YAAY,MAAM,MAAM;AACzC,QAAM,EAAE,QAAQ,SAAS,IAAI,cAAc;AAAA,IAC1C;AAAA,IACA,OAAO,MAAM;AAAA,IACb,SAAS,CAAC,GAAG,MAAM,YAAY,OAAO,OAAO,GAAG,CAAC;AAAA,IACjD;AAAA,IACA;AAAA,EACD,CAAC;AAED,QAAM,SAAS,MAAM,OAAO;AAC5B,QAAM,cACL,WAAW,UAAa,OAAO,SAAS,UAAU,OAAO,SAAS;AAEnE,WAAS,YAAY,OAA+B;AACnD,aAAS,KAAK;AACd,mBAAe,QAAQ,KAAK;AAAA,EAC7B;AAEA,WAAS,OAAO,OAAgD;AAC/D,WAAO,MAAM,UAAU,MAAM,cAAc,sBAAsB,EAAE;AAAA,EACpE;AAEA,WAAS,cAAc,OAA2C;AACjE,QAAI,CAAC,iBAAiB,EAAE,QAAQ,SAAS,WAAW,OAAO;AAC1D;AAAA,IACD;AACA,UAAM,cAAc,kBAAkB,MAAM,SAAS;AAErD,aAAS,eAAe;AACxB,SAAK,UAAU;AAAA,MACd,QAAQ,OAAO,KAAK;AAAA,MACpB,QAAQ,SAAS;AAAA,MACjB,OAAO;AAAA,IACR;AAAA,EACD;AAEA,WAAS,cAAc,OAA2C;AACjE,UAAM,UAAU,KAAK;AACrB,QAAI,YAAY,QAAQ,WAAW,QAAW;AAC7C,eAAS,cAAc,KAAK;AAC5B;AAAA,IACD;AACA,UAAM,IAAI,OAAO,KAAK;AACtB,QAAI,KAAK,IAAI,IAAI,QAAQ,MAAM,IAAI,gBAAgB;AAClD,cAAQ,QAAQ;AAAA,IACjB;AACA,QAAI,CAAC,QAAQ,MAAO;AACpB,QAAI,eAAe;AAClB;AAAA,QACC,QAAQ,QAAQ,CAAC,OAAO,OAAO,QAAQ,MAAM,GAAG,OAAO,OAAO,CAAC,CAAC,CAAC;AAAA,MAClE;AAAA,IACD,WAAW,QAAQ,WAAW,MAAM;AACnC,YAAM,CAAC,MAAM,EAAE,IAAI,QAAQ;AAC3B,YAAM,YAAY,KAAK,QAAQ,KAAK,IAAI,GAAG,MAAM,KAAK,KAAK;AAC3D,eAAS;AAAA,SACP,QAAQ,SAAS,KAAK,aACpB,SAAS,SAAS,CAAC,KAAK,QAAQ;AAAA,MACpC;AAAA,IACD;AAAA,EACD;AAEA,WAAS,YAAY,OAA2C;AAC/D,QACC,KAAK,YAAY,QACjB,MAAM,cAAc,kBAAkB,MAAM,SAAS,GACpD;AACD,YAAM,cAAc,sBAAsB,MAAM,SAAS;AAAA,IAC1D;AAAA,EACD;AAEA,WAAS,QAAQ,OAAyC;AACzD,UAAM,UAAU,KAAK,SAAS,UAAU;AACxC,SAAK,UAAU;AACf,QAAI,CAAC,SAAS;AACb,eAAS,QAAQ,KAAK;AAAA,IACvB;AAAA,EACD;AAEA,WAAS,iBAAiB,MAAwC;AACjE,QAAI,WAAW,OAAW;AAC1B,UAAM,CAAC,MAAM,EAAE,IAAI,QAAQ,SAAS,CAAC,GAAG,CAAC;AACzC,UAAM,UAAU,CAAC,UAChB,OAAO,SAAS,SAAS,IAAI,KAAK,KAAK,IAAI;AAC5C;AAAA,MACC,SAAS;AAAA,QACR,OAAO,OAAO,QAAQ,IAAI,CAAC;AAAA,QAC3B,OAAO,OAAO,QAAQ,EAAE,CAAC;AAAA,MAC1B;AAAA,IACD;AAAA,EACD;AAEA,WAAS,UAAU,OAA4C;AAC9D,QAAI,QAAQ,eAAe,UAAU,QAAW;AAC/C,YAAM,UAAU,QAAQ,MAAM;AAC9B,YAAM,CAAC,MAAM,EAAE,IAAI,SAAS,UAAU;AACtC,YAAM,SACL,YAAY,UAAa,OAAO,MAAM,IAAI,SAAS,OAAO;AAC3D,UAAI,MAAM,QAAQ,OAAO,MAAM,QAAQ,KAAK;AAC3C,cAAM,eAAe;AACrB,iBAAS,WAAW,QAAQ,SAAS;AACrC,yBAAiB,CAAC,UAAU,QAAQ,CAAC;AACrC;AAAA,MACD;AACA,UAAI,MAAM,QAAQ,OAAO,MAAM,QAAQ,KAAK;AAC3C,cAAM,eAAe;AACrB,iBAAS,WAAW,QAAQ,IAAI,SAAS;AACzC,yBAAiB,CAAC,UAAU,QAAQ,CAAC;AACrC;AAAA,MACD;AACA,UAAI,MAAM,QAAQ,KAAK;AACtB,cAAM,eAAe;AACrB,iBAAS,MAAM;AACf,yBAAiB,CAAC,UAAU,QAAQ,CAAC;AACrC;AAAA,MACD;AACA,UACC,MAAM,aACL,MAAM,QAAQ,eAAe,MAAM,QAAQ,eAC3C;AACD,cAAM,eAAe;AACrB,iBAAS;AAAA,WACP,MAAM,QAAQ,eAAe,IAAI,MAAM,SAAS,KAAK,IAAI;AAAA,QAC3D;AACA;AAAA,MACD;AAAA,IACD;AACA,aAAS,UAAU,KAAK;AAAA,EACzB;AAGA,QAAM,YAAY,QAAQ,MAAM;AAEhC,QAAM,UAAU,MAAM;AACrB,wBAAoB,EAAE;AAAA,EACvB,GAAG,CAAC,SAAS,CAAC;AAEd,QAAM,cAAc,SAAS,QAAQ,KAAK,GAAG,KAAK;AAClD,QAAM,mBAAmB,MAAM,OAAO,WAAW;AAEjD,QAAM,UAAU,MAAM;AACrB,QAAI,iBAAiB,YAAY,aAAa;AAC7C,uBAAiB,UAAU;AAC3B,uBAAiB,SAAS,MAAM;AAAA,IACjC;AAAA,EACD,GAAG,CAAC,WAAW,CAAC;AAIhB,QAAM,UAAU,MAAM,OAAO;AAAA,IAC5B;AAAA,IACA;AAAA,IACA,SAAS,QAAQ;AAAA,EAClB,CAAC;AACD,UAAQ,UAAU,EAAE,UAAU,QAAQ,SAAS,QAAQ,YAAY;AACnE,QAAM,UAAU,MAAM;AACrB,QAAI,SAAS,KAAM;AACnB,aAAS,QAAQ,OAAmB;AACnC,YAAM,EAAE,UAAU,SAAS,QAAQ,OAAO,QAAQ,IAAI,QAAQ;AAC9D,UACC,CAAC,WACD,UAAU,UACV,SAAS,kBAAkB,QAC3B,SAAS;AAET;AACD,YAAM,eAAe;AACrB,YAAM,IAAI,MAAM,UAAU,KAAK,sBAAsB,EAAE;AACvD,cAAQ;AAAA,QACP,SAAS,MAAM,OAAO,CAAC,CAAC;AAAA,QACxB,MAAM,SAAS,IAAI,IAAI,YAAY;AAAA,MACpC;AAAA,IACD;AACA,SAAK,iBAAiB,SAAS,SAAS,EAAE,SAAS,MAAM,CAAC;AAC1D,WAAO,MAAM,KAAK,oBAAoB,SAAS,OAAO;AAAA,EACvD,GAAG,CAAC,IAAI,CAAC;AAET,QAAM,eACL,QAAQ,WAAW,aAChB,GAAG,OAAO,MAAM,KAAK,OAAO,OAAO,KAAK,OAAO,SAAS,IACtD,OAAO,KAAK,SACZ,CAAC,OAAO,KAAK,GAEd,IAAI,CAAC,UAAU,GAAG,MAAM,eAAe,MAAM,MAAM,IAAI,MAAM,KAAK,EAAE,EACpE,KAAK,IAAI,CAAC,KACX;AACJ,QAAM,aAAa,UAAU,MAAM,OAAO,SAAS;AACnD,QAAM,gBAAgB,GAAG,OAAO;AAChC,QAAM,gBACL,OAAO,YAAY,aAAa,UAAU;AAE3C,WAAS,OAAO,KAAa;AAC5B,cAAU,CAAC,YAAY;AACtB,YAAM,OAAO,IAAI,IAAI,OAAO;AAC5B,UAAI,CAAC,KAAK,OAAO,GAAG,GAAG;AACtB,aAAK,IAAI,GAAG;AAAA,MACb;AACA,aAAO;AAAA,IACR,CAAC;AAAA,EACF;AAEA,SACC;AAAA,IAAC;AAAA;AAAA,MACA,aAAU;AAAA,MACV,OAAO,EAAE,SAAS,QAAQ,KAAK,GAAG,GAAG,MAAM;AAAA,MAC1C,GAAG;AAAA,MAEJ;AAAA,4BAAC,WAAM,MAAK,eAAc,YAAW,WACnC,qBACF;AAAA,QACA;AAAA,UAAC;AAAA;AAAA,YACA,aAAU;AAAA,YACV,OAAO,EAAE,UAAU,YAAY,QAAQ,YAAY;AAAA,YAEnD;AAAA;AAAA,gBAAC;AAAA;AAAA,kBACA;AAAA,kBACA,MAAK;AAAA,kBACL,wBAAsB,SAAS;AAAA,kBAC/B,cAAY;AAAA,kBACZ,oBAAkB;AAAA,kBAKlB,UAAU;AAAA,kBACV,aAAU;AAAA,kBACV,OAAO;AAAA,oBACN,UAAU;AAAA,oBACV,OAAO;AAAA,oBACP,aAAa,iBAAiB,OAAO,SAAS;AAAA;AAAA;AAAA,oBAG9C,GAAI,iBAAiB,OAClB,EAAE,YAAY,QAAQ,kBAAkB,OAAO,IAC/C,CAAC;AAAA,kBACL;AAAA,kBACC,GAAG;AAAA,kBACJ;AAAA,kBACA;AAAA,kBACA;AAAA,kBACA;AAAA,kBACA;AAAA,kBAEA;AAAA,wCAAC,YAAS,OAAO,OAAO,SAAkB;AAAA,oBAC1C,oBAAC,gBAAa,OAAc,QAAgB,OAAO,MAAM,OAAO;AAAA,oBAC/D,YAAY,SAAS,WAAW,OAChC;AAAA,sBAAC;AAAA;AAAA,wBACA;AAAA,wBACA;AAAA,wBACA,QAAQ;AAAA;AAAA,oBACT,IACG;AAAA;AAAA;AAAA,cACL;AAAA,cACC,iBAAiB,SACjB;AAAA,gBAAC,cAAc;AAAA,gBAAd;AAAA,kBACA,OAAO;AAAA,kBACP,MAAM,MAAM;AAAA,kBACZ,OAAO;AAAA,kBACP;AAAA,kBACA,UAAU;AAAA;AAAA,cACX,IACG;AAAA;AAAA;AAAA,QACL;AAAA,QACA,qBAAC,SAAI,IAAI,eAAe,OAAO,SAC7B;AAAA,4BAAkB,GAAG,eAAe,MAAM;AAAA,UAC1C,SAAS;AAAA,UACT,QAAQ,cAAc,SAAS,WAAW;AAAA,WAC5C;AAAA,QACA,oBAAC,SAAI,aAAU,UAAS,eAAY,QAAO,OAAO,SAChD,8BAAoB,cACtB;AAAA,QACC,SAAS,WAAW,OACpB;AAAA,UAAC;AAAA;AAAA,YACA,MAAK;AAAA,YACL,aAAU;AAAA,YACV,SAAS,MAAM,SAAS,MAAM;AAAA,YAC9B,OAAO;AAAA,cACN,aAAa;AAAA,cACb,QACC;AAAA,cACD,cAAc;AAAA,cACd,YAAY;AAAA,cACZ,OAAO;AAAA,cACP,MAAM;AAAA,cACN,UAAU;AAAA,cACV,SAAS;AAAA,cACT,QAAQ;AAAA,YACT;AAAA,YAEC,mBAAS;AAAA;AAAA,QACX,IACG;AAAA,QACH,cAAc,MAAM,OAAO,SAAS,IACpC;AAAA,UAAC;AAAA;AAAA,YACA,OAAO,MAAM;AAAA,YACb;AAAA,YACA,OAAO,SAAS;AAAA,YAChB,UAAU;AAAA;AAAA,QACX,IACG;AAAA,QACH,MAAM,aAAa,WAAW,QAC9B,oBAAC,kBAAe,MAAM,MAAM,WAAW,OAAO,SAAS,QAAQ,IAC5D;AAAA,QACH,YACA;AAAA,UAAC;AAAA;AAAA,YACA;AAAA,YACA;AAAA,YACA,SAAS,SAAS,UAAU,SAAS;AAAA,YACrC,eAAe,SAAS;AAAA;AAAA,QACzB,IACG;AAAA;AAAA;AAAA,EACL;AAEF;",
4
+ "sourcesContent": ["import * as React from \"react\";\nimport { compileChart } from \"#/core/compile.ts\";\nimport type { ChartDefinition } from \"#/core/define-chart.ts\";\nimport { type ChartAnimation, chartTiming } from \"#/core/motion/timing.ts\";\nimport { findNearest, focusStops } from \"#/core/nearest.ts\";\nimport { toNumber } from \"#/core/scales/continuous.ts\";\nimport type { ChartPoint } from \"#/core/types.ts\";\nimport {\n\ttype ChartBrush,\n\ttype ChartBrushRange,\n\tordered,\n} from \"#/react/brush-contract.ts\";\nimport { ChartDataTable } from \"#/react/chart-data-table.tsx\";\nimport { ChartColorRamp, ChartLegend } from \"#/react/chart-legend.tsx\";\nimport {\n\tChartTooltip,\n\tChartTooltipContent,\n\ttype ChartTooltipRenderProps,\n} from \"#/react/chart-tooltip.tsx\";\nimport { FocusOverlay } from \"#/react/focus-overlay.tsx\";\nimport { messagesFor } from \"#/react/messages.ts\";\nimport type { ChartRenderer } from \"#/react/renderer.ts\";\nimport { SR_ONLY } from \"#/react/sr-only.ts\";\nimport { SvgRenderer } from \"#/react/svg-renderer.tsx\";\nimport { useAnimatedScene } from \"#/react/use-animated-scene.ts\";\nimport { useChartFocus } from \"#/react/use-chart-focus.ts\";\nimport { useChartWidth } from \"#/react/use-chart-width.ts\";\nimport { useChartZoom } from \"#/react/use-chart-zoom.ts\";\nimport { useTextMeasurer } from \"#/react/use-text-measurer.ts\";\n\nconst DEFAULT_HEIGHT = 300;\nconst DEFAULT_INITIAL_WIDTH = 640;\n\n/** Focus ring, forced colours and reduced motion: what inline styles cannot say. */\nconst CHART_CSS = `\n[data-slot=\"chart-surface\"]:focus-visible{outline:2px solid var(--ring,Highlight);outline-offset:2px;border-radius:4px}\n@media (forced-colors:active){\n[data-slot=\"chart-svg\"] [data-role=\"axis\"],[data-slot=\"chart-svg\"] [data-role=\"axis-title\"]{fill:CanvasText}\n[data-slot=\"chart-svg\"] [data-role=\"grid\"]{stroke:GrayText}\n[data-slot=\"chart-focus-ring\"]{stroke:Highlight}\n[data-slot=\"chart-tooltip\"]{border-color:CanvasText}\n}\n@media (prefers-reduced-motion:no-preference){\n[data-slot=\"chart-tooltip\"]{transition:transform 80ms ease-out}\n}\n`;\n\n// `title` would put a native tooltip on the whole chart; the props carry the\n// accessible name as `ariaLabel` instead.\ninterface Props\n\textends Omit<React.ComponentProps<\"div\">, \"children\" | \"onSelect\" | \"title\"> {\n\treadonly definition: ChartDefinition;\n\t/** What the chart shows, in a sentence: the screen reader's name for it. Required. */\n\treadonly ariaLabel: string;\n\t/** The takeaway, when the label alone does not carry it. */\n\treadonly ariaDescription?: string;\n\t/** Pixels. Ignored when `aspectRatio` is set. */\n\treadonly height?: number;\n\t/** Height as a share of the width: `0.5` is twice as wide as tall. */\n\treadonly aspectRatio?: number;\n\t/** Width used on the server and for the first client render, before the container is measured. */\n\treadonly initialWidth?: number;\n\treadonly renderer?: ChartRenderer;\n\t/** `false` turns the tooltip off; a function replaces its content. */\n\treadonly tooltip?:\n\t\t| boolean\n\t\t| ((props: ChartTooltipRenderProps) => React.ReactNode);\n\t/** Shown by default when there is more than one series. */\n\treadonly legend?: boolean;\n\t/** The visually hidden table of the values. On by default; turn it off only when the page shows the same table. */\n\treadonly dataTable?: boolean;\n\t/**\n\t * How a data update moves: `true` (a spring with no bounce), a perceived\n\t * duration in milliseconds, `{ duration, bounce, stagger }`, or\n\t * `tween({ duration, easing, stagger })`. `false` snaps; reduced motion\n\t * always does.\n\t */\n\treadonly animate?: boolean | number | ChartAnimation;\n\t/**\n\t * Zoom a continuous x: the wheel while the chart has focus, + and \u2212,\n\t * Shift and the arrows to pan, a drag to pan when zoomed, 0 to reset.\n\t */\n\treadonly zoom?: boolean;\n\t/** Select a range of x by dragging or from the keyboard: `brushX({ onBrush })` from `@voila.dev/chart/brush`. */\n\treadonly brush?: ChartBrush;\n\treadonly onFocusChange?: (point: ChartPoint | null) => void;\n\treadonly onSelect?: (point: ChartPoint | null) => void;\n}\n\n/** A pointer that moved less than this between down and up clicked rather than dragged. */\nconst DRAG_THRESHOLD = 3;\nconst ZOOM_STEP = 0.8;\nconst PAN_SHARE = 0.1;\n\n/**\n * Draws a definition. Everything a reader needs beyond the picture comes with\n * it: keyboard focus on every value, a live region that reads the focused\n * value, a legend that toggles series, and the values as a hidden table.\n */\nexport function Chart({\n\tdefinition,\n\tariaLabel,\n\tariaDescription,\n\theight = DEFAULT_HEIGHT,\n\taspectRatio,\n\tinitialWidth = DEFAULT_INITIAL_WIDTH,\n\trenderer: Renderer = SvgRenderer,\n\ttooltip = true,\n\tlegend,\n\tdataTable = true,\n\tanimate = true,\n\tzoom = false,\n\tbrush: brushBehavior,\n\tonFocusChange,\n\tonSelect,\n\tstyle,\n\t...props\n}: Props) {\n\tconst chartId = React.useId().replaceAll(\":\", \"\");\n\tconst { ref, width, node } = useChartWidth(initialWidth);\n\tconst measureText = useTextMeasurer(node);\n\tconst [hidden, setHidden] = React.useState<ReadonlySet<string>>(\n\t\t() => new Set(),\n\t);\n\tconst chartHeight =\n\t\taspectRatio === undefined ? height : Math.round(width * aspectRatio);\n\n\tconst baseScene = React.useMemo(\n\t\t() =>\n\t\t\tcompileChart(definition, {\n\t\t\t\twidth,\n\t\t\t\theight: chartHeight,\n\t\t\t\tmeasureText,\n\t\t\t\thiddenSeries: hidden,\n\t\t\t}),\n\t\t[definition, width, chartHeight, measureText, hidden],\n\t);\n\tconst baseX = baseScene.scales.x;\n\tconst fullX = React.useMemo<readonly [number, number] | undefined>(\n\t\t() =>\n\t\t\tbaseX && baseX.kind !== \"band\" && baseX.kind !== \"point\"\n\t\t\t\t? [toNumber(baseX.domain[0]), toNumber(baseX.domain[1])]\n\t\t\t\t: undefined,\n\t\t[baseX],\n\t);\n\tconst viewport = useChartZoom(zoom ? fullX : undefined, definition);\n\tconst scene = React.useMemo(\n\t\t() =>\n\t\t\tviewport.domain === null\n\t\t\t\t? baseScene\n\t\t\t\t: compileChart(definition, {\n\t\t\t\t\t\twidth,\n\t\t\t\t\t\theight: chartHeight,\n\t\t\t\t\t\tmeasureText,\n\t\t\t\t\t\thiddenSeries: hidden,\n\t\t\t\t\t\txDomain: viewport.domain,\n\t\t\t\t\t}),\n\t\t[\n\t\t\tbaseScene,\n\t\t\tviewport.domain,\n\t\t\tdefinition,\n\t\t\twidth,\n\t\t\tchartHeight,\n\t\t\tmeasureText,\n\t\t\thidden,\n\t\t],\n\t);\n\tconst [brush, setBrush] = React.useState<ChartBrushRange | null>(null);\n\tconst [zoomAnnouncement, setZoomAnnouncement] = React.useState(\"\");\n\tconst drag = React.useRef<{\n\t\treadonly startX: number;\n\t\treadonly domain: readonly [number, number] | null;\n\t\tmoved: boolean;\n\t} | null>(null);\n\tconst stops = React.useMemo(() => focusStops(scene), [scene]);\n\t// A step zoom from the keyboard moves like a data update; the wheel and a\n\t// drag are direct manipulation, so they snap.\n\tconst [keyboardZooms, setKeyboardZooms] = React.useState(0);\n\tconst animationTrigger = React.useMemo(\n\t\t() => [definition, hidden, keyboardZooms],\n\t\t[definition, hidden, keyboardZooms],\n\t);\n\t// An inline `animate={{ \u2026 }}` or `tween({ \u2026 })` is read by value, not\n\t// identity: a new object each render must not restart the motion.\n\tconst latestAnimate = React.useRef(animate);\n\tlatestAnimate.current = animate;\n\tconst animateKey = JSON.stringify(animate);\n\t// biome-ignore lint/correctness/useExhaustiveDependencies: keyed on the value of `animate`.\n\tconst timing = React.useMemo(\n\t\t() => chartTiming(latestAnimate.current),\n\t\t[animateKey],\n\t);\n\tconst shown = useAnimatedScene(scene, animationTrigger, timing);\n\tconst messages = messagesFor(scene.locale);\n\tconst { active, handlers } = useChartFocus({\n\t\tstops,\n\t\torder: scene.focusOrder,\n\t\thitTest: (x, y) => findNearest(scene, stops, x, y),\n\t\tonFocusChange,\n\t\tonSelect,\n\t});\n\n\tconst xScale = scene.scales.x;\n\tconst xContinuous =\n\t\txScale !== undefined && xScale.kind !== \"band\" && xScale.kind !== \"point\";\n\n\tfunction updateBrush(range: ChartBrushRange | null) {\n\t\tsetBrush(range);\n\t\tbrushBehavior?.onBrush(range);\n\t}\n\n\tfunction localX(event: React.PointerEvent<HTMLElement>): number {\n\t\treturn event.clientX - event.currentTarget.getBoundingClientRect().left;\n\t}\n\n\tfunction onPointerDown(event: React.PointerEvent<HTMLDivElement>) {\n\t\tif (!brushBehavior && !(zoom && viewport.domain !== null)) {\n\t\t\treturn;\n\t\t}\n\t\tevent.currentTarget.setPointerCapture(event.pointerId);\n\t\t// A drag selects a range, not a value: the hover tooltip gives way.\n\t\thandlers.onPointerLeave();\n\t\tdrag.current = {\n\t\t\tstartX: localX(event),\n\t\t\tdomain: viewport.domain,\n\t\t\tmoved: false,\n\t\t};\n\t}\n\n\tfunction onPointerMove(event: React.PointerEvent<HTMLDivElement>) {\n\t\tconst current = drag.current;\n\t\tif (current === null || xScale === undefined) {\n\t\t\thandlers.onPointerMove(event);\n\t\t\treturn;\n\t\t}\n\t\tconst x = localX(event);\n\t\tif (Math.abs(x - current.startX) > DRAG_THRESHOLD) {\n\t\t\tcurrent.moved = true;\n\t\t}\n\t\tif (!current.moved) return;\n\t\tif (brushBehavior) {\n\t\t\tupdateBrush(\n\t\t\t\tordered(xScale, [xScale.invert(current.startX), xScale.invert(x)]),\n\t\t\t);\n\t\t} else if (current.domain !== null) {\n\t\t\tconst [from, to] = current.domain;\n\t\t\tconst perPixel = (to - from) / Math.max(1, scene.plot.width);\n\t\t\tviewport.pan(\n\t\t\t\t(current.startX - x) * perPixel -\n\t\t\t\t\t((viewport.domain?.[0] ?? from) - from),\n\t\t\t);\n\t\t}\n\t}\n\n\tfunction onPointerUp(event: React.PointerEvent<HTMLDivElement>) {\n\t\tif (\n\t\t\tdrag.current !== null &&\n\t\t\tevent.currentTarget.hasPointerCapture(event.pointerId)\n\t\t) {\n\t\t\tevent.currentTarget.releasePointerCapture(event.pointerId);\n\t\t}\n\t}\n\n\tfunction onClick(event: React.MouseEvent<HTMLDivElement>) {\n\t\tconst dragged = drag.current?.moved === true;\n\t\tdrag.current = null;\n\t\tif (!dragged) {\n\t\t\thandlers.onClick(event);\n\t\t}\n\t}\n\n\tfunction announceViewport(next: readonly [number, number] | null) {\n\t\tif (xScale === undefined) return;\n\t\tconst [from, to] = next ?? fullX ?? [0, 0];\n\t\tconst asValue = (value: number) =>\n\t\t\txScale.kind === \"time\" ? new Date(value) : value;\n\t\tsetZoomAnnouncement(\n\t\t\tmessages.showing(\n\t\t\t\txScale.format(asValue(from)),\n\t\t\t\txScale.format(asValue(to)),\n\t\t\t),\n\t\t);\n\t}\n\n\tfunction onKeyDown(event: React.KeyboardEvent<HTMLDivElement>) {\n\t\tif (zoom && xContinuous && fullX !== undefined) {\n\t\t\tconst focused = active?.point.xValue;\n\t\t\tconst [from, to] = viewport.domain ?? fullX;\n\t\t\tconst center =\n\t\t\t\tfocused === undefined ? (from + to) / 2 : toNumber(focused);\n\t\t\tif (event.key === \"+\" || event.key === \"=\") {\n\t\t\t\tevent.preventDefault();\n\t\t\t\tviewport.zoomAround(center, ZOOM_STEP);\n\t\t\t\tsetKeyboardZooms((count) => count + 1);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (event.key === \"-\" || event.key === \"_\") {\n\t\t\t\tevent.preventDefault();\n\t\t\t\tviewport.zoomAround(center, 1 / ZOOM_STEP);\n\t\t\t\tsetKeyboardZooms((count) => count + 1);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (event.key === \"0\") {\n\t\t\t\tevent.preventDefault();\n\t\t\t\tviewport.reset();\n\t\t\t\tsetKeyboardZooms((count) => count + 1);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (\n\t\t\t\tevent.shiftKey &&\n\t\t\t\t(event.key === \"ArrowLeft\" || event.key === \"ArrowRight\")\n\t\t\t) {\n\t\t\t\tevent.preventDefault();\n\t\t\t\tviewport.pan(\n\t\t\t\t\t(event.key === \"ArrowRight\" ? 1 : -1) * viewport.span() * PAN_SHARE,\n\t\t\t\t);\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t\thandlers.onKeyDown(event);\n\t}\n\n\t// Moving the focus says the new value; the last zoom's message gives way.\n\tconst activeKey = active?.point.key;\n\t// biome-ignore lint/correctness/useExhaustiveDependencies: clears on a change of focused point only\n\tReact.useEffect(() => {\n\t\tsetZoomAnnouncement(\"\");\n\t}, [activeKey]);\n\n\tconst viewportKey = viewport.domain?.join(\":\") ?? \"full\";\n\tconst previousViewport = React.useRef(viewportKey);\n\t// biome-ignore lint/correctness/useExhaustiveDependencies: announce a change of viewport, once\n\tReact.useEffect(() => {\n\t\tif (previousViewport.current !== viewportKey) {\n\t\t\tpreviousViewport.current = viewportKey;\n\t\t\tannounceViewport(viewport.domain);\n\t\t}\n\t}, [viewportKey]);\n\n\t// The wheel zooms only while the chart has focus: scrolling past a chart\n\t// must keep scrolling the page.\n\tconst zoomRef = React.useRef({\n\t\tviewport,\n\t\txScale,\n\t\tenabled: zoom && xContinuous,\n\t});\n\tzoomRef.current = { viewport, xScale, enabled: zoom && xContinuous };\n\tReact.useEffect(() => {\n\t\tif (node === null) return;\n\t\tfunction onWheel(event: WheelEvent) {\n\t\t\tconst { viewport: current, xScale: scale, enabled } = zoomRef.current;\n\t\t\tif (\n\t\t\t\t!enabled ||\n\t\t\t\tscale === undefined ||\n\t\t\t\tdocument.activeElement !== node ||\n\t\t\t\tnode === null\n\t\t\t)\n\t\t\t\treturn;\n\t\t\tevent.preventDefault();\n\t\t\tconst x = event.clientX - node.getBoundingClientRect().left;\n\t\t\tcurrent.zoomAround(\n\t\t\t\ttoNumber(scale.invert(x)),\n\t\t\t\tevent.deltaY > 0 ? 1 / ZOOM_STEP : ZOOM_STEP,\n\t\t\t);\n\t\t}\n\t\tnode.addEventListener(\"wheel\", onWheel, { passive: false });\n\t\treturn () => node.removeEventListener(\"wheel\", onWheel);\n\t}, [node]);\n\n\tconst announcement =\n\t\tactive?.source === \"keyboard\"\n\t\t\t? `${active.point.title} : ${(active.stop.points.length > 1\n\t\t\t\t\t? active.stop.points\n\t\t\t\t\t: [active.point]\n\t\t\t\t)\n\t\t\t\t\t.map((point) => `${point.seriesLabel ?? scene.yLabel} ${point.value}`)\n\t\t\t\t\t.join(\", \")}`\n\t\t\t: \"\";\n\tconst showLegend = legend ?? scene.legend.length > 1;\n\tconst descriptionId = `${chartId}-description`;\n\tconst renderTooltip =\n\t\ttypeof tooltip === \"function\" ? tooltip : ChartTooltipContent;\n\n\tfunction toggle(key: string) {\n\t\tsetHidden((current) => {\n\t\t\tconst next = new Set(current);\n\t\t\tif (!next.delete(key)) {\n\t\t\t\tnext.add(key);\n\t\t\t}\n\t\t\treturn next;\n\t\t});\n\t}\n\n\treturn (\n\t\t<div\n\t\t\tdata-slot=\"chart\"\n\t\t\tstyle={{ display: \"grid\", gap: 8, ...style }}\n\t\t\t{...props}\n\t\t>\n\t\t\t<style href=\"voila-chart\" precedence=\"default\">\n\t\t\t\t{CHART_CSS}\n\t\t\t</style>\n\t\t\t<div\n\t\t\t\tdata-slot=\"chart-frame\"\n\t\t\t\tstyle={{ position: \"relative\", height: chartHeight }}\n\t\t\t>\n\t\t\t\t<div\n\t\t\t\t\tref={ref}\n\t\t\t\t\trole=\"img\"\n\t\t\t\t\taria-roledescription={messages.roleDescription}\n\t\t\t\t\taria-label={ariaLabel}\n\t\t\t\t\taria-describedby={descriptionId}\n\t\t\t\t\t// The chart is one focus stop; the arrow keys move inside it, the way\n\t\t\t\t\t// a reader walks the values. role=\"img\" keeps it a single named\n\t\t\t\t\t// graphic, the live region speaks the focused value.\n\t\t\t\t\t// biome-ignore lint/a11y/noNoninteractiveTabindex: keyboard exploration of the values\n\t\t\t\t\ttabIndex={0}\n\t\t\t\t\tdata-slot=\"chart-surface\"\n\t\t\t\t\tstyle={{\n\t\t\t\t\t\tposition: \"absolute\",\n\t\t\t\t\t\tinset: 0,\n\t\t\t\t\t\ttouchAction: brushBehavior || zoom ? \"none\" : \"pan-y\",\n\t\t\t\t\t\t// Dragging across the plot brushes or pans; without this it\n\t\t\t\t\t\t// also selected the tick labels and the tooltip as text.\n\t\t\t\t\t\t...(brushBehavior || zoom\n\t\t\t\t\t\t\t? { userSelect: \"none\", WebkitUserSelect: \"none\" }\n\t\t\t\t\t\t\t: {}),\n\t\t\t\t\t}}\n\t\t\t\t\t{...handlers}\n\t\t\t\t\tonKeyDown={onKeyDown}\n\t\t\t\t\tonPointerDown={onPointerDown}\n\t\t\t\t\tonPointerMove={onPointerMove}\n\t\t\t\t\tonPointerUp={onPointerUp}\n\t\t\t\t\tonClick={onClick}\n\t\t\t\t>\n\t\t\t\t\t<Renderer scene={shown} chartId={chartId} />\n\t\t\t\t\t<FocusOverlay scene={scene} active={active} theme={scene.theme} />\n\t\t\t\t\t{tooltip !== false && active !== null ? (\n\t\t\t\t\t\t<ChartTooltip\n\t\t\t\t\t\t\tactive={active}\n\t\t\t\t\t\t\tscene={scene}\n\t\t\t\t\t\t\trender={renderTooltip}\n\t\t\t\t\t\t/>\n\t\t\t\t\t) : null}\n\t\t\t\t</div>\n\t\t\t\t{brushBehavior && xScale ? (\n\t\t\t\t\t<brushBehavior.Layer\n\t\t\t\t\t\tscale={xScale}\n\t\t\t\t\t\tplot={scene.plot}\n\t\t\t\t\t\trange={brush}\n\t\t\t\t\t\tmessages={messages}\n\t\t\t\t\t\tonChange={updateBrush}\n\t\t\t\t\t/>\n\t\t\t\t) : null}\n\t\t\t</div>\n\t\t\t<div id={descriptionId} style={SR_ONLY}>\n\t\t\t\t{ariaDescription ? `${ariaDescription} ` : \"\"}\n\t\t\t\t{messages.keyboardHint}\n\t\t\t\t{zoom && xContinuous ? messages.zoomHint : \"\"}\n\t\t\t</div>\n\t\t\t<div aria-live=\"polite\" aria-atomic=\"true\" style={SR_ONLY}>\n\t\t\t\t{zoomAnnouncement || announcement}\n\t\t\t</div>\n\t\t\t{viewport.domain !== null ? (\n\t\t\t\t<button\n\t\t\t\t\ttype=\"button\"\n\t\t\t\t\tdata-slot=\"chart-reset-zoom\"\n\t\t\t\t\tonClick={() => viewport.reset()}\n\t\t\t\t\tstyle={{\n\t\t\t\t\t\tjustifySelf: \"end\",\n\t\t\t\t\t\tborder:\n\t\t\t\t\t\t\t\"1px solid var(--border, color-mix(in oklab, currentColor 20%, transparent))\",\n\t\t\t\t\t\tborderRadius: 6,\n\t\t\t\t\t\tbackground: \"transparent\",\n\t\t\t\t\t\tcolor: \"inherit\",\n\t\t\t\t\t\tfont: \"inherit\",\n\t\t\t\t\t\tfontSize: 12,\n\t\t\t\t\t\tpadding: \"2px 8px\",\n\t\t\t\t\t\tcursor: \"pointer\",\n\t\t\t\t\t}}\n\t\t\t\t>\n\t\t\t\t\t{messages.resetZoom}\n\t\t\t\t</button>\n\t\t\t) : null}\n\t\t\t{showLegend && scene.legend.length > 0 ? (\n\t\t\t\t<ChartLegend\n\t\t\t\t\titems={scene.legend}\n\t\t\t\t\thidden={hidden}\n\t\t\t\t\tlabel={messages.legend}\n\t\t\t\t\tonToggle={toggle}\n\t\t\t\t/>\n\t\t\t) : null}\n\t\t\t{scene.colorRamp && legend !== false ? (\n\t\t\t\t<ChartColorRamp ramp={scene.colorRamp} label={messages.legend} />\n\t\t\t) : null}\n\t\t\t{dataTable ? (\n\t\t\t\t<ChartDataTable\n\t\t\t\t\tscene={scene}\n\t\t\t\t\tstops={stops}\n\t\t\t\t\tcaption={messages.dataTable(ariaLabel)}\n\t\t\t\t\tseriesHeading={messages.series}\n\t\t\t\t/>\n\t\t\t) : null}\n\t\t</div>\n\t);\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -1 +1 @@
1
- {"version":3,"file":"use-animated-scene.d.ts","sourceRoot":"","sources":["../../src/react/use-animated-scene.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAC;AAC3D,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAElD;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAC/B,KAAK,EAAE,UAAU,EACjB,OAAO,EAAE,OAAO,EAChB,MAAM,EAAE,WAAW,GAAG,IAAI,GACxB,UAAU,CAoDZ"}
1
+ {"version":3,"file":"use-animated-scene.d.ts","sourceRoot":"","sources":["../../src/react/use-animated-scene.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAC;AAC3D,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAElD;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAC/B,KAAK,EAAE,UAAU,EACjB,OAAO,EAAE,OAAO,EAChB,MAAM,EAAE,WAAW,GAAG,IAAI,GACxB,UAAU,CAsDZ"}
@@ -1,4 +1,4 @@
1
- import { cancelFrame, frame, shouldReduceMotion } from "@voila.dev/motion";
1
+ import { shouldReduceMotion } from "@voila.dev/motion";
2
2
  import * as React from "react";
3
3
  import {
4
4
  createMotionStore
@@ -31,19 +31,19 @@ function useAnimatedScene(scene, trigger, timing) {
31
31
  return;
32
32
  }
33
33
  if (changed) store.retarget(scene, performance.now());
34
+ let request = 0;
34
35
  function tick() {
35
36
  const now = performance.now();
36
37
  if (store?.settled(now)) {
37
38
  introduced.current = true;
38
- cancelFrame(tick);
39
39
  setShown(scene);
40
40
  return;
41
41
  }
42
42
  setShown(store?.frame(now) ?? scene);
43
+ request = requestAnimationFrame(tick);
43
44
  }
44
45
  tick();
45
- frame.update(tick, true);
46
- return () => cancelFrame(tick);
46
+ return () => cancelAnimationFrame(request);
47
47
  }, [scene, trigger, timing]);
48
48
  return shown;
49
49
  }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/react/use-animated-scene.ts"],
4
- "sourcesContent": ["import { cancelFrame, frame, shouldReduceMotion } from \"@voila.dev/motion\";\nimport * as React from \"react\";\nimport {\n\tcreateMotionStore,\n\ttype MotionStore,\n} from \"#/core/motion/motion-store.ts\";\nimport type { ChartTiming } from \"#/core/motion/timing.ts\";\nimport type { ChartScene } from \"#/core/types.ts\";\n\n/**\n * The scene to draw this frame. A data change (`trigger` changed: a new\n * definition, a series toggled) moves from what is on screen to the new\n * scene on springs; one landing mid-flight keeps every node's speed. A\n * resize snaps, since watching bars slide while a window is dragged helps no\n * one. Reduced motion always snaps.\n */\nexport function useAnimatedScene(\n\tscene: ChartScene,\n\ttrigger: unknown,\n\ttiming: ChartTiming | null,\n): ChartScene {\n\tconst [shown, setShown] = React.useState(scene);\n\tconst triggerRef = React.useRef(trigger);\n\t// The first render may play once: lines that `enter: \"draw\"` trace themselves in.\n\tconst introduced = React.useRef(false);\n\tconst introStart = React.useRef<number | null>(null);\n\tconst storeRef = React.useRef<{\n\t\tstore: MotionStore;\n\t\ttiming: ChartTiming;\n\t} | null>(null);\n\n\tReact.useLayoutEffect(() => {\n\t\tconst changed = triggerRef.current !== trigger;\n\t\ttriggerRef.current = trigger;\n\t\tif (timing && storeRef.current?.timing !== timing) {\n\t\t\tstoreRef.current = { store: createMotionStore(scene, timing), timing };\n\t\t}\n\t\tconst store = storeRef.current?.store;\n\t\tconst still = !timing || !store || shouldReduceMotion();\n\t\tlet intro = false;\n\t\tif (!still && !changed && !introduced.current) {\n\t\t\tconst start = introStart.current ?? performance.now();\n\t\t\t// A re-render mid-intro (the width measured after mount, fonts loaded): the\n\t\t\t// new geometry, drawn from where the intro already is, never from scratch.\n\t\t\tif (introStart.current !== null) store.snap(scene);\n\t\t\tintro = store.introduce(start);\n\t\t\tif (intro) introStart.current = start;\n\t\t}\n\t\tif (still || (!changed && !intro)) {\n\t\t\tintroduced.current = true;\n\t\t\tstore?.snap(scene);\n\t\t\tsetShown(scene);\n\t\t\treturn;\n\t\t}\n\t\tif (changed) store.retarget(scene, performance.now());\n\t\tfunction tick() {\n\t\t\tconst now = performance.now();\n\t\t\tif (store?.settled(now)) {\n\t\t\t\t// Only once it has played: StrictMode's rerun of the effect starts it again.\n\t\t\t\tintroduced.current = true;\n\t\t\t\tcancelFrame(tick);\n\t\t\t\tsetShown(scene);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tsetShown(store?.frame(now) ?? scene);\n\t\t}\n\t\ttick();\n\t\tframe.update(tick, true);\n\t\treturn () => cancelFrame(tick);\n\t}, [scene, trigger, timing]);\n\n\treturn shown;\n}\n"],
5
- "mappings": "AAAA,SAAS,aAAa,OAAO,0BAA0B;AACvD,YAAY,WAAW;AACvB;AAAA,EACC;AAAA,OAEM;AAWA,SAAS,iBACf,OACA,SACA,QACa;AACb,QAAM,CAAC,OAAO,QAAQ,IAAI,MAAM,SAAS,KAAK;AAC9C,QAAM,aAAa,MAAM,OAAO,OAAO;AAEvC,QAAM,aAAa,MAAM,OAAO,KAAK;AACrC,QAAM,aAAa,MAAM,OAAsB,IAAI;AACnD,QAAM,WAAW,MAAM,OAGb,IAAI;AAEd,QAAM,gBAAgB,MAAM;AAC3B,UAAM,UAAU,WAAW,YAAY;AACvC,eAAW,UAAU;AACrB,QAAI,UAAU,SAAS,SAAS,WAAW,QAAQ;AAClD,eAAS,UAAU,EAAE,OAAO,kBAAkB,OAAO,MAAM,GAAG,OAAO;AAAA,IACtE;AACA,UAAM,QAAQ,SAAS,SAAS;AAChC,UAAM,QAAQ,CAAC,UAAU,CAAC,SAAS,mBAAmB;AACtD,QAAI,QAAQ;AACZ,QAAI,CAAC,SAAS,CAAC,WAAW,CAAC,WAAW,SAAS;AAC9C,YAAM,QAAQ,WAAW,WAAW,YAAY,IAAI;AAGpD,UAAI,WAAW,YAAY,KAAM,OAAM,KAAK,KAAK;AACjD,cAAQ,MAAM,UAAU,KAAK;AAC7B,UAAI,MAAO,YAAW,UAAU;AAAA,IACjC;AACA,QAAI,SAAU,CAAC,WAAW,CAAC,OAAQ;AAClC,iBAAW,UAAU;AACrB,aAAO,KAAK,KAAK;AACjB,eAAS,KAAK;AACd;AAAA,IACD;AACA,QAAI,QAAS,OAAM,SAAS,OAAO,YAAY,IAAI,CAAC;AACpD,aAAS,OAAO;AACf,YAAM,MAAM,YAAY,IAAI;AAC5B,UAAI,OAAO,QAAQ,GAAG,GAAG;AAExB,mBAAW,UAAU;AACrB,oBAAY,IAAI;AAChB,iBAAS,KAAK;AACd;AAAA,MACD;AACA,eAAS,OAAO,MAAM,GAAG,KAAK,KAAK;AAAA,IACpC;AACA,SAAK;AACL,UAAM,OAAO,MAAM,IAAI;AACvB,WAAO,MAAM,YAAY,IAAI;AAAA,EAC9B,GAAG,CAAC,OAAO,SAAS,MAAM,CAAC;AAE3B,SAAO;AACR;",
4
+ "sourcesContent": ["import { shouldReduceMotion } from \"@voila.dev/motion\";\nimport * as React from \"react\";\nimport {\n\tcreateMotionStore,\n\ttype MotionStore,\n} from \"#/core/motion/motion-store.ts\";\nimport type { ChartTiming } from \"#/core/motion/timing.ts\";\nimport type { ChartScene } from \"#/core/types.ts\";\n\n/**\n * The scene to draw this frame. A data change (`trigger` changed: a new\n * definition, a series toggled) moves from what is on screen to the new\n * scene on springs; one landing mid-flight keeps every node's speed. A\n * resize snaps, since watching bars slide while a window is dragged helps no\n * one. Reduced motion always snaps.\n */\nexport function useAnimatedScene(\n\tscene: ChartScene,\n\ttrigger: unknown,\n\ttiming: ChartTiming | null,\n): ChartScene {\n\tconst [shown, setShown] = React.useState(scene);\n\tconst triggerRef = React.useRef(trigger);\n\t// The first render may play once: lines that `enter: \"draw\"` trace themselves in.\n\tconst introduced = React.useRef(false);\n\tconst introStart = React.useRef<number | null>(null);\n\tconst storeRef = React.useRef<{\n\t\tstore: MotionStore;\n\t\ttiming: ChartTiming;\n\t} | null>(null);\n\n\tReact.useLayoutEffect(() => {\n\t\tconst changed = triggerRef.current !== trigger;\n\t\ttriggerRef.current = trigger;\n\t\tif (timing && storeRef.current?.timing !== timing) {\n\t\t\tstoreRef.current = { store: createMotionStore(scene, timing), timing };\n\t\t}\n\t\tconst store = storeRef.current?.store;\n\t\tconst still = !timing || !store || shouldReduceMotion();\n\t\tlet intro = false;\n\t\tif (!still && !changed && !introduced.current) {\n\t\t\tconst start = introStart.current ?? performance.now();\n\t\t\t// A re-render mid-intro (the width measured after mount, fonts loaded): the\n\t\t\t// new geometry, drawn from where the intro already is, never from scratch.\n\t\t\tif (introStart.current !== null) store.snap(scene);\n\t\t\tintro = store.introduce(start);\n\t\t\tif (intro) introStart.current = start;\n\t\t}\n\t\tif (still || (!changed && !intro)) {\n\t\t\tintroduced.current = true;\n\t\t\tstore?.snap(scene);\n\t\t\tsetShown(scene);\n\t\t\treturn;\n\t\t}\n\t\tif (changed) store.retarget(scene, performance.now());\n\t\t// One chart needs one callback a frame: a plain requestAnimationFrame\n\t\t// keeps motion's shared frame loop out of every chart bundle.\n\t\tlet request = 0;\n\t\tfunction tick() {\n\t\t\tconst now = performance.now();\n\t\t\tif (store?.settled(now)) {\n\t\t\t\t// Only once it has played: StrictMode's rerun of the effect starts it again.\n\t\t\t\tintroduced.current = true;\n\t\t\t\tsetShown(scene);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tsetShown(store?.frame(now) ?? scene);\n\t\t\trequest = requestAnimationFrame(tick);\n\t\t}\n\t\ttick();\n\t\treturn () => cancelAnimationFrame(request);\n\t}, [scene, trigger, timing]);\n\n\treturn shown;\n}\n"],
5
+ "mappings": "AAAA,SAAS,0BAA0B;AACnC,YAAY,WAAW;AACvB;AAAA,EACC;AAAA,OAEM;AAWA,SAAS,iBACf,OACA,SACA,QACa;AACb,QAAM,CAAC,OAAO,QAAQ,IAAI,MAAM,SAAS,KAAK;AAC9C,QAAM,aAAa,MAAM,OAAO,OAAO;AAEvC,QAAM,aAAa,MAAM,OAAO,KAAK;AACrC,QAAM,aAAa,MAAM,OAAsB,IAAI;AACnD,QAAM,WAAW,MAAM,OAGb,IAAI;AAEd,QAAM,gBAAgB,MAAM;AAC3B,UAAM,UAAU,WAAW,YAAY;AACvC,eAAW,UAAU;AACrB,QAAI,UAAU,SAAS,SAAS,WAAW,QAAQ;AAClD,eAAS,UAAU,EAAE,OAAO,kBAAkB,OAAO,MAAM,GAAG,OAAO;AAAA,IACtE;AACA,UAAM,QAAQ,SAAS,SAAS;AAChC,UAAM,QAAQ,CAAC,UAAU,CAAC,SAAS,mBAAmB;AACtD,QAAI,QAAQ;AACZ,QAAI,CAAC,SAAS,CAAC,WAAW,CAAC,WAAW,SAAS;AAC9C,YAAM,QAAQ,WAAW,WAAW,YAAY,IAAI;AAGpD,UAAI,WAAW,YAAY,KAAM,OAAM,KAAK,KAAK;AACjD,cAAQ,MAAM,UAAU,KAAK;AAC7B,UAAI,MAAO,YAAW,UAAU;AAAA,IACjC;AACA,QAAI,SAAU,CAAC,WAAW,CAAC,OAAQ;AAClC,iBAAW,UAAU;AACrB,aAAO,KAAK,KAAK;AACjB,eAAS,KAAK;AACd;AAAA,IACD;AACA,QAAI,QAAS,OAAM,SAAS,OAAO,YAAY,IAAI,CAAC;AAGpD,QAAI,UAAU;AACd,aAAS,OAAO;AACf,YAAM,MAAM,YAAY,IAAI;AAC5B,UAAI,OAAO,QAAQ,GAAG,GAAG;AAExB,mBAAW,UAAU;AACrB,iBAAS,KAAK;AACd;AAAA,MACD;AACA,eAAS,OAAO,MAAM,GAAG,KAAK,KAAK;AACnC,gBAAU,sBAAsB,IAAI;AAAA,IACrC;AACA,SAAK;AACL,WAAO,MAAM,qBAAqB,OAAO;AAAA,EAC1C,GAAG,CAAC,OAAO,SAAS,MAAM,CAAC;AAE3B,SAAO;AACR;",
6
6
  "names": []
7
7
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@voila.dev/chart",
3
- "version": "5.0.118",
3
+ "version": "6.0.120",
4
4
  "type": "module",
5
5
  "description": "Charts as data: a definition of marks, channels and scales, drawn to SVG or Canvas, keyboard and screen-reader accessible. The core has no dependencies; the heavy layouts (geo, hierarchy, sankey…) sit behind their own subpaths.",
6
6
  "license": "MIT",
@@ -64,7 +64,7 @@
64
64
  },
65
65
  "sideEffects": false,
66
66
  "dependencies": {
67
- "@voila.dev/motion": "5.0.118",
67
+ "@voila.dev/motion": "6.0.120",
68
68
  "d3-contour": "4.0.2",
69
69
  "d3-delaunay": "6.0.4",
70
70
  "d3-force": "3.0.0",
@@ -33,23 +33,17 @@ export function leavingGuides(
33
33
  return into;
34
34
  }
35
35
 
36
- /** `children` with the leaving guides of `parent` back at their places, faded to `opacity`. */
36
+ /** `children` with the leaving guides of `parent` back at their places, as `paint` draws them. */
37
37
  export function withLeaving(
38
38
  children: readonly SceneNode[],
39
39
  parent: string | null,
40
- leaving: ReadonlyArray<readonly [LeavingGuide, number]>,
40
+ leaving: Iterable<LeavingGuide>,
41
+ paint: (node: SceneNode) => SceneNode,
41
42
  ): SceneNode[] {
42
43
  const placed = [...children];
43
- for (const [guide, opacity] of leaving) {
44
- if (guide.parent !== parent || guide.node.kind === "group") continue;
45
- const faded = {
46
- ...guide.node,
47
- paint: {
48
- ...guide.node.paint,
49
- opacity: (guide.node.paint.opacity ?? 1) * opacity,
50
- },
51
- } as SceneNode;
52
- placed.splice(Math.min(guide.index, placed.length), 0, faded);
44
+ for (const guide of leaving) {
45
+ if (guide.parent !== parent) continue;
46
+ placed.splice(Math.min(guide.index, placed.length), 0, paint(guide.node));
53
47
  }
54
48
  return placed;
55
49
  }