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 (
{Label}
); } const GraphTimer = ({ turbo, timer, duration, }: { turbo: boolean; timer: number; duration: number; }) => { return (
{turbo && (
Turbopack Turbopack
)}

); }; 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

)}
); }