@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.
- package/dist/core/motion/leaving.d.ts +2 -2
- package/dist/core/motion/leaving.d.ts.map +1 -1
- package/dist/core/motion/leaving.js +4 -11
- package/dist/core/motion/leaving.js.map +2 -2
- package/dist/core/motion/motion-store.d.ts.map +1 -1
- package/dist/core/motion/motion-store.js +58 -84
- package/dist/core/motion/motion-store.js.map +3 -3
- package/dist/core/motion/points-motion.d.ts.map +1 -1
- package/dist/core/motion/points-motion.js +9 -22
- package/dist/core/motion/points-motion.js.map +2 -2
- package/dist/core/motion/strings.d.ts +4 -2
- package/dist/core/motion/strings.d.ts.map +1 -1
- package/dist/core/motion/strings.js +5 -6
- package/dist/core/motion/strings.js.map +2 -2
- package/dist/core/motion/timing.d.ts +16 -18
- package/dist/core/motion/timing.d.ts.map +1 -1
- package/dist/core/motion/timing.js +9 -8
- package/dist/core/motion/timing.js.map +2 -2
- package/dist/core/motion/tween-timing.d.ts +17 -0
- package/dist/core/motion/tween-timing.d.ts.map +1 -0
- package/dist/core/motion/tween-timing.js +22 -0
- package/dist/core/motion/tween-timing.js.map +7 -0
- package/dist/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/index.js.map +2 -2
- package/dist/react/chart.d.ts +3 -2
- package/dist/react/chart.d.ts.map +1 -1
- package/dist/react/chart.js +3 -1
- package/dist/react/chart.js.map +2 -2
- package/dist/react/use-animated-scene.d.ts.map +1 -1
- package/dist/react/use-animated-scene.js +4 -4
- package/dist/react/use-animated-scene.js.map +2 -2
- package/package.json +2 -2
- package/src/core/motion/leaving.ts +6 -12
- package/src/core/motion/motion-store.ts +65 -109
- package/src/core/motion/points-motion.ts +12 -24
- package/src/core/motion/strings.ts +9 -12
- package/src/core/motion/timing.ts +28 -33
- package/src/core/motion/tween-timing.ts +35 -0
- package/src/index.ts +2 -0
- package/src/react/chart.tsx +9 -4
- 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
|
|
22
|
-
if (
|
|
23
|
-
|
|
24
|
-
|
|
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
|
-
|
|
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
|
|
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,
|
|
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
|
|
2
|
-
/**
|
|
3
|
-
|
|
4
|
-
|
|
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,
|
|
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
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
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
|
|
5
|
-
"mappings": "
|
|
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";
|
package/dist/index.d.ts.map
CHANGED
|
@@ -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":"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"}
|
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,
|
|
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
|
}
|
package/dist/react/chart.d.ts
CHANGED
|
@@ -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,
|
|
30
|
-
* `{
|
|
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
|
|
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"}
|
package/dist/react/chart.js
CHANGED
|
@@ -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(
|
|
114
|
+
() => chartTiming(latestAnimate.current),
|
|
113
115
|
[animateKey]
|
|
114
116
|
);
|
|
115
117
|
const shown = useAnimatedScene(scene, animationTrigger, timing);
|
package/dist/react/chart.js.map
CHANGED
|
@@ -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": "
|
|
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,
|
|
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 {
|
|
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
|
-
|
|
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 {
|
|
5
|
-
"mappings": "AAAA,SAAS,
|
|
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": "
|
|
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": "
|
|
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,
|
|
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:
|
|
40
|
+
leaving: Iterable<LeavingGuide>,
|
|
41
|
+
paint: (node: SceneNode) => SceneNode,
|
|
41
42
|
): SceneNode[] {
|
|
42
43
|
const placed = [...children];
|
|
43
|
-
for (const
|
|
44
|
-
if (guide.parent !== parent
|
|
45
|
-
|
|
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
|
}
|