import cn from "classnames";
import {
animate,
motion,
useInView,
useAnimation,
AnimationPlaybackControls,
} from "framer-motion";
import Image from "next/image";
import { useEffect, useRef, useState } from "react";
import benchmarkData from "./benchmark-data/data.json";
import { Gradient } from "../home-shared/Gradient";
import gradients from "../home-shared/gradients.module.css";
import {
BenchmarkBar,
BenchmarkCategory,
BenchmarkData,
BenchmarkNumberOfModules,
} from "./PackBenchmarks";
interface BenchmarksGraphProps {
category: BenchmarkCategory;
numberOfModules: BenchmarkNumberOfModules;
bars: BenchmarkBar[];
pinTime?: true;
}
export function BenchmarksGraph({
category,
numberOfModules,
bars,
pinTime,
}: BenchmarksGraphProps) {
const data: BenchmarkData = benchmarkData[category][numberOfModules];
const keys = bars.map((bar) => bar.key);
const longestTime = Math.max(...keys.map((key) => data[key])) * 1000;
const longestTimeWithPadding = longestTime * 1.15;
const graphRef = useRef(null);
const graphInView = useInView(graphRef, { once: true, margin: "-128px" });
return (
{bars.map((bar) => {
return (
}
duration={data[bar.key] * 1000}
longestTime={longestTimeWithPadding}
inView={graphInView}
pinTime={pinTime}
>
);
})}
);
}
const START_DELAY = 0.0;
const graphBarVariants = {
initial: {
width: 0,
},
progress: {
width: "100%",
},
};
const graphBarWrapperVariants = {
hidden: {
opacity: 0,
},
show: {
opacity: 1,
},
};
function GraphBar({
turbo,
duration,
longestTime,
inView,
Label,
pinTime,
}: {
turbo?: boolean;
duration: number;
longestTime: number;
Label: JSX.Element;
inView?: boolean;
// Pin the time
pinTime?: true;
}) {
const controls = useAnimation();
const [timer, setTimer] = useState(0);
const [timerAnimation, setTimerAnimation] =
useState();
const [barWidth, setBarWidth] = useState(0);
const [, setFinished] = useState(false);
async function stopAnimation() {
timerAnimation && timerAnimation.stop();
controls.stop();
}
async function resetAnimation() {
setTimer(0);
setFinished(false);
await controls.start("initial");
}
async function startAnimation() {
const transition = {
duration: duration / 1000,
delay: START_DELAY,
};
setBarWidth((duration / longestTime) * 100);
await controls.start("show");
controls
.start("progress", {
...transition,
ease: "linear",
})
.then(() => {
setFinished(true);
});
const timerAnimationRef = animate(0, duration, {
...transition,
ease: "linear",
onUpdate(value) {
setTimer(value);
},
});
setTimerAnimation(timerAnimationRef);
}
async function playFullAnimation() {
await stopAnimation();
await controls.start("hidden");
await resetAnimation();
await startAnimation();
}
useEffect(() => {
if (inView) {
void startAnimation();
} else {
void stopAnimation();
void resetAnimation();
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [inView]);
useEffect(() => {
if (!inView) return;
void playFullAnimation();
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [duration, longestTime]);
return (
);
}
const GraphTimer = ({
turbo,
timer,
duration,
}: {
turbo: boolean;
timer: number;
duration: number;
}) => {
return (
);
};
function roundTo(num: number, decimals: number) {
const factor = Math.pow(10, decimals);
return Math.round(num * factor) / factor;
}
const Time = ({
value,
maxValue,
}: {
value: number;
maxValue: number;
}): JSX.Element => {
let unitValue: string;
let unit: string;
if (maxValue < 1000) {
unitValue = Math.round(value).toFixed(0);
unit = "ms";
} else {
const roundedValue = roundTo(value / 1000, 1);
unitValue = roundedValue.toFixed(1);
unit = "s";
}
return (
<>
{unitValue}
{unit}
>
);
};
function GraphLabel({
label,
turbo,
swc,
mobileOnly,
esbuild,
}: {
label: string;
turbo?: boolean;
swc?: boolean;
mobileOnly?: boolean;
esbuild?: boolean;
}) {
return (
{label}
{turbo && (
turbo
)}
{swc && (
with SWC
)}
{esbuild && (
esbuild
)}
);
}