@jcsoftdev/ui 0.1.2 → 0.1.4
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/charts/core/distribution/xy-points.js +3 -3
- package/dist/charts/core/motion.d.ts +6 -0
- package/dist/charts/core/motion.js +9 -3
- package/dist/charts/core/use-drill.js +1 -1
- package/dist/charts/donut-chart/donut-chart.js +1 -1
- package/dist/charts/gauge-chart/gauge-chart.js +9 -3
- package/dist/charts/heatmap-chart/heatmap-grid.js +2 -2
- package/dist/charts/histogram/histogram.js +2 -2
- package/dist/charts/maps/geo/use-map-view.js +1 -1
- package/dist/charts/tree-chart/tree-chart.js +2 -2
- package/dist/charts/tree-chart/use-tree-transition.js +1 -1
- package/package.json +1 -1
- package/src/charts/core/distribution/xy-points.tsx +3 -3
- package/src/charts/core/motion.ts +18 -4
- package/src/charts/core/use-drill.ts +1 -1
- package/src/charts/donut-chart/donut-chart.tsx +1 -1
- package/src/charts/gauge-chart/gauge-chart.tsx +11 -3
- package/src/charts/heatmap-chart/heatmap-grid.tsx +2 -2
- package/src/charts/histogram/histogram.tsx +2 -2
- package/src/charts/maps/geo/use-map-view.ts +1 -1
- package/src/charts/tree-chart/tree-chart.tsx +2 -2
- package/src/charts/tree-chart/use-tree-transition.ts +1 -1
|
@@ -14,7 +14,7 @@ import { ChartFrame } from "../chart-frame.js";
|
|
|
14
14
|
import { tint } from "../color-scale.js";
|
|
15
15
|
import { ChartDataTable } from "../data-table.js";
|
|
16
16
|
import { formatNumber, resolveFormat } from "../format.js";
|
|
17
|
-
import { useEntryAnimation } from "../motion.js";
|
|
17
|
+
import { fadeKeyframes, useEntryAnimation } from "../motion.js";
|
|
18
18
|
import { TONE_COLORS } from "../palette.js";
|
|
19
19
|
import { formatDate } from "../scales/time.js";
|
|
20
20
|
import { ChartTooltip } from "../tooltip.js";
|
|
@@ -633,7 +633,7 @@ function XYPointPlot({
|
|
|
633
633
|
);
|
|
634
634
|
});
|
|
635
635
|
root.querySelectorAll("[data-trend]").forEach((line) => {
|
|
636
|
-
line.animate(
|
|
636
|
+
line.animate(fadeKeyframes(line), {
|
|
637
637
|
duration: 500,
|
|
638
638
|
delay: 500,
|
|
639
639
|
easing: "ease-out",
|
|
@@ -647,7 +647,7 @@ function XYPointPlot({
|
|
|
647
647
|
wrapRef,
|
|
648
648
|
(root) => {
|
|
649
649
|
root.querySelectorAll("[data-canvas]").forEach((layer) => {
|
|
650
|
-
layer.animate(
|
|
650
|
+
layer.animate(fadeKeyframes(layer), {
|
|
651
651
|
duration: 450,
|
|
652
652
|
easing: "ease-out",
|
|
653
653
|
fill: "backwards"
|
|
@@ -19,6 +19,12 @@ export interface EntryAnimationOptions {
|
|
|
19
19
|
export declare function useEntryAnimation<T extends Element>(ref: RefObject<T | null>, play: (root: T) => void, { disabled, replayKey }?: EntryAnimationOptions): void;
|
|
20
20
|
/** Reveals children left to right by scaling a clip rectangle. */
|
|
21
21
|
export declare function revealClip(element: Element, duration?: number, delay?: number): void;
|
|
22
|
+
/**
|
|
23
|
+
* Fade-in keyframes that end at the element's own opacity. Ending at 1 would
|
|
24
|
+
* show a translucent element at full strength, then snap it back when the
|
|
25
|
+
* animation finishes.
|
|
26
|
+
*/
|
|
27
|
+
export declare function fadeKeyframes(element: Element): Keyframe[];
|
|
22
28
|
/** Fades an element in. */
|
|
23
29
|
export declare function fadeIn(element: Element, duration?: number, delay?: number): void;
|
|
24
30
|
/**
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { useEffect, useRef, useState } from "react";
|
|
3
|
+
import { useIsomorphicLayoutEffect } from "./use-chart-size.js";
|
|
3
4
|
const QUERY = "(prefers-reduced-motion: reduce)";
|
|
4
5
|
function useReducedMotion() {
|
|
5
6
|
const [reduced, setReduced] = useState(false);
|
|
@@ -21,7 +22,7 @@ function prefersReducedMotion() {
|
|
|
21
22
|
return typeof window !== "undefined" && !!window.matchMedia?.(QUERY).matches;
|
|
22
23
|
}
|
|
23
24
|
function useEntryAnimation(ref, play, { disabled = false, replayKey } = {}) {
|
|
24
|
-
|
|
25
|
+
useIsomorphicLayoutEffect(() => {
|
|
25
26
|
const root = ref.current;
|
|
26
27
|
if (disabled || !root || typeof root.animate !== "function" || prefersReducedMotion()) return;
|
|
27
28
|
play(root);
|
|
@@ -35,8 +36,12 @@ function revealClip(element, duration = 900, delay = 0) {
|
|
|
35
36
|
fill: "backwards"
|
|
36
37
|
});
|
|
37
38
|
}
|
|
39
|
+
function fadeKeyframes(element) {
|
|
40
|
+
const resting = typeof getComputedStyle === "function" ? getComputedStyle(element).opacity : "1";
|
|
41
|
+
return [{ opacity: 0 }, { opacity: resting || "1" }];
|
|
42
|
+
}
|
|
38
43
|
function fadeIn(element, duration = 400, delay = 0) {
|
|
39
|
-
element.animate(
|
|
44
|
+
element.animate(fadeKeyframes(element), {
|
|
40
45
|
duration,
|
|
41
46
|
delay,
|
|
42
47
|
easing: "ease-out",
|
|
@@ -60,7 +65,7 @@ function useAnimatedNumber(target, {
|
|
|
60
65
|
const startedAt = performance.now();
|
|
61
66
|
let frame = 0;
|
|
62
67
|
const tick = (now) => {
|
|
63
|
-
const progress = Math.min(1, (now - startedAt) / duration);
|
|
68
|
+
const progress = Math.min(1, Math.max(0, (now - startedAt) / duration));
|
|
64
69
|
const eased = 1 - (1 - progress) ** 3;
|
|
65
70
|
setValue(start + (target - start) * eased);
|
|
66
71
|
if (progress < 1) frame = requestAnimationFrame(tick);
|
|
@@ -74,6 +79,7 @@ export {
|
|
|
74
79
|
EASE_OUT,
|
|
75
80
|
easeOutCubic,
|
|
76
81
|
fadeIn,
|
|
82
|
+
fadeKeyframes,
|
|
77
83
|
revealClip,
|
|
78
84
|
useAnimatedNumber,
|
|
79
85
|
useEntryAnimation,
|
|
@@ -50,7 +50,7 @@ function useAnimatedView(target, { disabled = false, duration = 650 } = {}) {
|
|
|
50
50
|
let frame = 0;
|
|
51
51
|
const start = performance.now();
|
|
52
52
|
const step = (now) => {
|
|
53
|
-
const t = Math.min(1, (now - start) / duration);
|
|
53
|
+
const t = Math.min(1, Math.max(0, (now - start) / duration));
|
|
54
54
|
const next = interpolateView(from, to, easeOutCubic(t));
|
|
55
55
|
current.current = next;
|
|
56
56
|
setView(next);
|
|
@@ -620,7 +620,7 @@ function useTweenedValues(items, disabled) {
|
|
|
620
620
|
const started = performance.now();
|
|
621
621
|
let frame = 0;
|
|
622
622
|
const step = (now) => {
|
|
623
|
-
const t = easeOutCubic(Math.min(1, (now - started) / 700));
|
|
623
|
+
const t = easeOutCubic(Math.min(1, Math.max(0, (now - started) / 700)));
|
|
624
624
|
const next = {};
|
|
625
625
|
for (const id of Object.keys(target)) {
|
|
626
626
|
const start = from[id] ?? 0;
|
|
@@ -6,7 +6,13 @@ import { DECORATIVE } from "../core/a11y.js";
|
|
|
6
6
|
import { ChartFrame } from "../core/chart-frame.js";
|
|
7
7
|
import { ChartDataTable } from "../core/data-table.js";
|
|
8
8
|
import { resolveFormat } from "../core/format.js";
|
|
9
|
-
import {
|
|
9
|
+
import {
|
|
10
|
+
EASE_OUT,
|
|
11
|
+
fadeIn,
|
|
12
|
+
fadeKeyframes,
|
|
13
|
+
useAnimatedNumber,
|
|
14
|
+
useEntryAnimation
|
|
15
|
+
} from "../core/motion.js";
|
|
10
16
|
import { TONE_COLORS } from "../core/palette.js";
|
|
11
17
|
import { arcLine, degToRad, polarPoint, radToDeg } from "../core/polar.js";
|
|
12
18
|
import { ChartTooltip } from "../core/tooltip.js";
|
|
@@ -164,7 +170,7 @@ function GaugePlot({
|
|
|
164
170
|
);
|
|
165
171
|
const capAngle = roundedCaps ? band / 2 / mid : 0;
|
|
166
172
|
const fraction = toFraction(shown);
|
|
167
|
-
const active = bands.find((item) =>
|
|
173
|
+
const active = bands.find((item) => value >= item.from && value <= item.to);
|
|
168
174
|
const valueColor = active ? rangeColor(active) : "var(--accent-word)";
|
|
169
175
|
const progressEnd = a0 + capAngle + (span - 2 * capAngle) * fraction;
|
|
170
176
|
const bandPaths = useMemo(
|
|
@@ -202,7 +208,7 @@ function GaugePlot({
|
|
|
202
208
|
svgRef,
|
|
203
209
|
(root) => {
|
|
204
210
|
root.querySelectorAll("[data-band]").forEach((node, i) => {
|
|
205
|
-
node.animate(
|
|
211
|
+
node.animate(fadeKeyframes(node), {
|
|
206
212
|
duration: 500,
|
|
207
213
|
delay: i * 90,
|
|
208
214
|
easing: EASE_OUT,
|
|
@@ -8,7 +8,7 @@ import {
|
|
|
8
8
|
rangeColor,
|
|
9
9
|
scaleGradient
|
|
10
10
|
} from "../core/color-scale.js";
|
|
11
|
-
import { EASE_OUT, useEntryAnimation } from "../core/motion.js";
|
|
11
|
+
import { EASE_OUT, fadeKeyframes, useEntryAnimation } from "../core/motion.js";
|
|
12
12
|
import { ChartTooltip } from "../core/tooltip.js";
|
|
13
13
|
import { DEFAULT_LABELS } from "../core/types.js";
|
|
14
14
|
import { useCanvasPaint } from "../core/use-canvas.js";
|
|
@@ -99,7 +99,7 @@ function HeatmapGrid({
|
|
|
99
99
|
svgRef,
|
|
100
100
|
(element) => {
|
|
101
101
|
element.querySelectorAll("[data-column]").forEach((group, index) => {
|
|
102
|
-
group.animate(
|
|
102
|
+
group.animate(fadeKeyframes(group), {
|
|
103
103
|
duration: 420,
|
|
104
104
|
delay: Math.min(index * (360 / Math.max(1, columns)), 360),
|
|
105
105
|
easing: EASE_OUT,
|
|
@@ -18,7 +18,7 @@ import {
|
|
|
18
18
|
} from "../core/distribution/stats.js";
|
|
19
19
|
import { useDistributionLegend } from "../core/distribution/use-legend.js";
|
|
20
20
|
import { formatNumber, formatPercent, resolveFormat } from "../core/format.js";
|
|
21
|
-
import { EASE_OUT, useEntryAnimation } from "../core/motion.js";
|
|
21
|
+
import { EASE_OUT, fadeKeyframes, useEntryAnimation } from "../core/motion.js";
|
|
22
22
|
import { barPath, linePath } from "../core/path.js";
|
|
23
23
|
import { ChartTooltip } from "../core/tooltip.js";
|
|
24
24
|
function Histogram({
|
|
@@ -285,7 +285,7 @@ function HistogramPlot({
|
|
|
285
285
|
});
|
|
286
286
|
});
|
|
287
287
|
root.querySelectorAll("[data-curve]").forEach((curve) => {
|
|
288
|
-
curve.animate(
|
|
288
|
+
curve.animate(fadeKeyframes(curve), {
|
|
289
289
|
duration: 500,
|
|
290
290
|
delay: 420,
|
|
291
291
|
easing: "ease-out",
|
|
@@ -59,7 +59,7 @@ function useMapView({
|
|
|
59
59
|
const from = viewRef.current;
|
|
60
60
|
const start = performance.now();
|
|
61
61
|
const step = (now) => {
|
|
62
|
-
const t = Math.min(1, (now - start) / DURATION);
|
|
62
|
+
const t = Math.min(1, Math.max(0, (now - start) / DURATION));
|
|
63
63
|
const e = easeOut(t);
|
|
64
64
|
const k = from.k * (goal.k / from.k) ** e;
|
|
65
65
|
apply({ k, x: from.x + (goal.x - from.x) * e, y: from.y + (goal.y - from.y) * e });
|
|
@@ -10,7 +10,7 @@ import {
|
|
|
10
10
|
import { cn } from "../../utils/cn.js";
|
|
11
11
|
import { ChartFrame } from "../core/chart-frame.js";
|
|
12
12
|
import { ChartDataTable } from "../core/data-table.js";
|
|
13
|
-
import { EASE_OUT, useEntryAnimation } from "../core/motion.js";
|
|
13
|
+
import { EASE_OUT, fadeKeyframes, useEntryAnimation } from "../core/motion.js";
|
|
14
14
|
import { treeFitView } from "../core/tree-fit.js";
|
|
15
15
|
import {
|
|
16
16
|
treeEdgePath,
|
|
@@ -388,7 +388,7 @@ function TreePlot({
|
|
|
388
388
|
);
|
|
389
389
|
});
|
|
390
390
|
root.querySelectorAll("[data-tree-edges]").forEach((element) => {
|
|
391
|
-
element.animate(
|
|
391
|
+
element.animate(fadeKeyframes(element), {
|
|
392
392
|
duration: 500,
|
|
393
393
|
delay: 160,
|
|
394
394
|
easing: "ease-out",
|
|
@@ -58,7 +58,7 @@ function useTreeTransition(layout, parentOf, { disabled = false, duration = 360
|
|
|
58
58
|
let frame = 0;
|
|
59
59
|
const start = performance.now();
|
|
60
60
|
const step = (now) => {
|
|
61
|
-
const t = Math.min(1, (now - start) / duration);
|
|
61
|
+
const t = Math.min(1, Math.max(0, (now - start) / duration));
|
|
62
62
|
if (t >= 1) {
|
|
63
63
|
show(final);
|
|
64
64
|
return;
|
package/package.json
CHANGED
|
@@ -17,7 +17,7 @@ import { ChartFrame } from "../chart-frame.js";
|
|
|
17
17
|
import { tint } from "../color-scale.js";
|
|
18
18
|
import { ChartDataTable } from "../data-table.js";
|
|
19
19
|
import { formatNumber, resolveFormat, type ValueFormat } from "../format.js";
|
|
20
|
-
import { useEntryAnimation } from "../motion.js";
|
|
20
|
+
import { fadeKeyframes, useEntryAnimation } from "../motion.js";
|
|
21
21
|
import { type ChartTone, TONE_COLORS } from "../palette.js";
|
|
22
22
|
import { formatDate } from "../scales/time.js";
|
|
23
23
|
import { ChartTooltip, type TooltipRow } from "../tooltip.js";
|
|
@@ -858,7 +858,7 @@ function XYPointPlot({
|
|
|
858
858
|
);
|
|
859
859
|
});
|
|
860
860
|
root.querySelectorAll("[data-trend]").forEach((line) => {
|
|
861
|
-
line.animate(
|
|
861
|
+
line.animate(fadeKeyframes(line), {
|
|
862
862
|
duration: 500,
|
|
863
863
|
delay: 500,
|
|
864
864
|
easing: "ease-out",
|
|
@@ -873,7 +873,7 @@ function XYPointPlot({
|
|
|
873
873
|
wrapRef,
|
|
874
874
|
(root) => {
|
|
875
875
|
root.querySelectorAll("[data-canvas]").forEach((layer) => {
|
|
876
|
-
layer.animate(
|
|
876
|
+
layer.animate(fadeKeyframes(layer), {
|
|
877
877
|
duration: 450,
|
|
878
878
|
easing: "ease-out",
|
|
879
879
|
fill: "backwards",
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
|
|
3
3
|
import { type RefObject, useEffect, useRef, useState } from "react";
|
|
4
|
+
import { useIsomorphicLayoutEffect } from "./use-chart-size.js";
|
|
4
5
|
|
|
5
6
|
const QUERY = "(prefers-reduced-motion: reduce)";
|
|
6
7
|
|
|
@@ -47,8 +48,10 @@ export function useEntryAnimation<T extends Element>(
|
|
|
47
48
|
play: (root: T) => void,
|
|
48
49
|
{ disabled = false, replayKey }: EntryAnimationOptions = {},
|
|
49
50
|
): void {
|
|
50
|
-
//
|
|
51
|
-
|
|
51
|
+
// A layout effect starts the animations before the first paint. With a plain
|
|
52
|
+
// effect the final state paints for one frame, then `fill: "backwards"` hides
|
|
53
|
+
// it again, which reads as a flicker. Replays only when the key changes.
|
|
54
|
+
useIsomorphicLayoutEffect(() => {
|
|
52
55
|
const root = ref.current;
|
|
53
56
|
if (disabled || !root || typeof root.animate !== "function" || prefersReducedMotion()) return;
|
|
54
57
|
play(root);
|
|
@@ -65,9 +68,19 @@ export function revealClip(element: Element, duration = 900, delay = 0): void {
|
|
|
65
68
|
});
|
|
66
69
|
}
|
|
67
70
|
|
|
71
|
+
/**
|
|
72
|
+
* Fade-in keyframes that end at the element's own opacity. Ending at 1 would
|
|
73
|
+
* show a translucent element at full strength, then snap it back when the
|
|
74
|
+
* animation finishes.
|
|
75
|
+
*/
|
|
76
|
+
export function fadeKeyframes(element: Element): Keyframe[] {
|
|
77
|
+
const resting = typeof getComputedStyle === "function" ? getComputedStyle(element).opacity : "1";
|
|
78
|
+
return [{ opacity: 0 }, { opacity: resting || "1" }];
|
|
79
|
+
}
|
|
80
|
+
|
|
68
81
|
/** Fades an element in. */
|
|
69
82
|
export function fadeIn(element: Element, duration = 400, delay = 0): void {
|
|
70
|
-
element.animate(
|
|
83
|
+
element.animate(fadeKeyframes(element), {
|
|
71
84
|
duration,
|
|
72
85
|
delay,
|
|
73
86
|
easing: "ease-out",
|
|
@@ -100,7 +113,8 @@ export function useAnimatedNumber(
|
|
|
100
113
|
const startedAt = performance.now();
|
|
101
114
|
let frame = 0;
|
|
102
115
|
const tick = (now: number) => {
|
|
103
|
-
|
|
116
|
+
// rAF timestamps are the frame start and can precede `startedAt`.
|
|
117
|
+
const progress = Math.min(1, Math.max(0, (now - startedAt) / duration));
|
|
104
118
|
const eased = 1 - (1 - progress) ** 3;
|
|
105
119
|
setValue(start + (target - start) * eased);
|
|
106
120
|
if (progress < 1) frame = requestAnimationFrame(tick);
|
|
@@ -86,7 +86,7 @@ export function useAnimatedView(
|
|
|
86
86
|
let frame = 0;
|
|
87
87
|
const start = performance.now();
|
|
88
88
|
const step = (now: number) => {
|
|
89
|
-
const t = Math.min(1, (now - start) / duration);
|
|
89
|
+
const t = Math.min(1, Math.max(0, (now - start) / duration));
|
|
90
90
|
const next = interpolateView(from, to, easeOutCubic(t));
|
|
91
91
|
current.current = next;
|
|
92
92
|
setView(next);
|
|
@@ -810,7 +810,7 @@ function useTweenedValues(items: readonly Resolved[], disabled: boolean): number
|
|
|
810
810
|
const started = performance.now();
|
|
811
811
|
let frame = 0;
|
|
812
812
|
const step = (now: number) => {
|
|
813
|
-
const t = easeOutCubic(Math.min(1, (now - started) / 700));
|
|
813
|
+
const t = easeOutCubic(Math.min(1, Math.max(0, (now - started) / 700)));
|
|
814
814
|
const next: Record<string, number> = {};
|
|
815
815
|
for (const id of Object.keys(target)) {
|
|
816
816
|
const start = from[id] ?? 0;
|
|
@@ -6,7 +6,13 @@ import { DECORATIVE } from "../core/a11y.js";
|
|
|
6
6
|
import { ChartFrame } from "../core/chart-frame.js";
|
|
7
7
|
import { ChartDataTable } from "../core/data-table.js";
|
|
8
8
|
import { resolveFormat, type ValueFormat } from "../core/format.js";
|
|
9
|
-
import {
|
|
9
|
+
import {
|
|
10
|
+
EASE_OUT,
|
|
11
|
+
fadeIn,
|
|
12
|
+
fadeKeyframes,
|
|
13
|
+
useAnimatedNumber,
|
|
14
|
+
useEntryAnimation,
|
|
15
|
+
} from "../core/motion.js";
|
|
10
16
|
import { type ChartTone, TONE_COLORS } from "../core/palette.js";
|
|
11
17
|
import { arcLine, degToRad, polarPoint, radToDeg } from "../core/polar.js";
|
|
12
18
|
import { ChartTooltip } from "../core/tooltip.js";
|
|
@@ -264,7 +270,9 @@ function GaugePlot({
|
|
|
264
270
|
const capAngle = roundedCaps ? band / 2 / mid : 0;
|
|
265
271
|
|
|
266
272
|
const fraction = toFraction(shown);
|
|
267
|
-
|
|
273
|
+
// Colour and label follow the target value, so they do not flip band by band
|
|
274
|
+
// while the arc or needle sweeps towards it.
|
|
275
|
+
const active = bands.find((item) => value >= item.from && value <= item.to);
|
|
268
276
|
const valueColor = active ? rangeColor(active) : "var(--accent-word)";
|
|
269
277
|
const progressEnd = a0 + capAngle + (span - 2 * capAngle) * fraction;
|
|
270
278
|
|
|
@@ -307,7 +315,7 @@ function GaugePlot({
|
|
|
307
315
|
svgRef,
|
|
308
316
|
(root) => {
|
|
309
317
|
root.querySelectorAll("[data-band]").forEach((node, i) => {
|
|
310
|
-
node.animate(
|
|
318
|
+
node.animate(fadeKeyframes(node), {
|
|
311
319
|
duration: 500,
|
|
312
320
|
delay: i * 90,
|
|
313
321
|
easing: EASE_OUT,
|
|
@@ -11,7 +11,7 @@ import {
|
|
|
11
11
|
scaleGradient,
|
|
12
12
|
} from "../core/color-scale.js";
|
|
13
13
|
import type { ValueFormatter } from "../core/format.js";
|
|
14
|
-
import { EASE_OUT, useEntryAnimation } from "../core/motion.js";
|
|
14
|
+
import { EASE_OUT, fadeKeyframes, useEntryAnimation } from "../core/motion.js";
|
|
15
15
|
import { ChartTooltip } from "../core/tooltip.js";
|
|
16
16
|
import { type ChartLabels, DEFAULT_LABELS } from "../core/types.js";
|
|
17
17
|
import { useCanvasPaint } from "../core/use-canvas.js";
|
|
@@ -172,7 +172,7 @@ export function HeatmapGrid({
|
|
|
172
172
|
svgRef,
|
|
173
173
|
(element) => {
|
|
174
174
|
element.querySelectorAll("[data-column]").forEach((group, index) => {
|
|
175
|
-
group.animate(
|
|
175
|
+
group.animate(fadeKeyframes(group), {
|
|
176
176
|
duration: 420,
|
|
177
177
|
delay: Math.min(index * (360 / Math.max(1, columns)), 360),
|
|
178
178
|
easing: EASE_OUT,
|
|
@@ -20,7 +20,7 @@ import {
|
|
|
20
20
|
} from "../core/distribution/stats.js";
|
|
21
21
|
import { type ColoredSeries, useDistributionLegend } from "../core/distribution/use-legend.js";
|
|
22
22
|
import { formatNumber, formatPercent, resolveFormat, type ValueFormat } from "../core/format.js";
|
|
23
|
-
import { EASE_OUT, useEntryAnimation } from "../core/motion.js";
|
|
23
|
+
import { EASE_OUT, fadeKeyframes, useEntryAnimation } from "../core/motion.js";
|
|
24
24
|
import { barPath, linePath } from "../core/path.js";
|
|
25
25
|
import { ChartTooltip, type TooltipRow } from "../core/tooltip.js";
|
|
26
26
|
|
|
@@ -400,7 +400,7 @@ function HistogramPlot({
|
|
|
400
400
|
});
|
|
401
401
|
});
|
|
402
402
|
root.querySelectorAll("[data-curve]").forEach((curve) => {
|
|
403
|
-
curve.animate(
|
|
403
|
+
curve.animate(fadeKeyframes(curve), {
|
|
404
404
|
duration: 500,
|
|
405
405
|
delay: 420,
|
|
406
406
|
easing: "ease-out",
|
|
@@ -90,7 +90,7 @@ export function useMapView<E extends Element>({
|
|
|
90
90
|
const from = viewRef.current;
|
|
91
91
|
const start = performance.now();
|
|
92
92
|
const step = (now: number) => {
|
|
93
|
-
const t = Math.min(1, (now - start) / DURATION);
|
|
93
|
+
const t = Math.min(1, Math.max(0, (now - start) / DURATION));
|
|
94
94
|
const e = easeOut(t);
|
|
95
95
|
// Interpolate zoom geometrically so the speed feels constant.
|
|
96
96
|
const k = from.k * (goal.k / from.k) ** e;
|
|
@@ -12,7 +12,7 @@ import {
|
|
|
12
12
|
import { cn } from "../../utils/cn.js";
|
|
13
13
|
import { ChartFrame } from "../core/chart-frame.js";
|
|
14
14
|
import { ChartDataTable } from "../core/data-table.js";
|
|
15
|
-
import { EASE_OUT, useEntryAnimation } from "../core/motion.js";
|
|
15
|
+
import { EASE_OUT, fadeKeyframes, useEntryAnimation } from "../core/motion.js";
|
|
16
16
|
import { type TreeFitMode, treeFitView } from "../core/tree-fit.js";
|
|
17
17
|
import {
|
|
18
18
|
type TreeDirection,
|
|
@@ -568,7 +568,7 @@ function TreePlot({
|
|
|
568
568
|
);
|
|
569
569
|
});
|
|
570
570
|
root.querySelectorAll("[data-tree-edges]").forEach((element) => {
|
|
571
|
-
element.animate(
|
|
571
|
+
element.animate(fadeKeyframes(element), {
|
|
572
572
|
duration: 500,
|
|
573
573
|
delay: 160,
|
|
574
574
|
easing: "ease-out",
|
|
@@ -92,7 +92,7 @@ export function useTreeTransition(
|
|
|
92
92
|
let frame = 0;
|
|
93
93
|
const start = performance.now();
|
|
94
94
|
const step = (now: number) => {
|
|
95
|
-
const t = Math.min(1, (now - start) / duration);
|
|
95
|
+
const t = Math.min(1, Math.max(0, (now - start) / duration));
|
|
96
96
|
if (t >= 1) {
|
|
97
97
|
show(final);
|
|
98
98
|
return;
|