radiquum.github.io/app/components/ProjectLink.tsx
Radiquum 3fb066fb1b
Some checks failed
Deploy Next.js site to Pages / build (push) Has been cancelled
Deploy Next.js site to Pages / deploy (push) Has been cancelled
feat: add random timeout before autoplay
2025-08-06 02:16:15 +05:00

100 lines
2.9 KiB
TypeScript

/* eslint-disable @next/next/no-img-element */
"use client";
import Link from "next/link";
import { IconWithText } from "../components/IconWithText";
import { useEffect, useRef, useState } from "react";
import useEmblaCarousel from "embla-carousel-react";
import Autoplay from "embla-carousel-autoplay";
import { useInView } from "motion/react";
type ProjectLinkProps = {
icon: string;
text: string;
desc: string;
url: string;
preview: string[];
};
export const ProjectLink = ({
icon,
text,
desc,
url,
preview,
}: ProjectLinkProps) => {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const [timeoutId, setTimeoutId] = useState<any>(null);
const [shouldUseCarousel] = useState(preview ? preview.length > 1 : false);
const [emblaRef, emblaApi] = useEmblaCarousel({ loop: true }, [
Autoplay({ delay: 5000, playOnInit: false }),
]);
const ref = useRef(null);
const isInView = useInView(ref);
useEffect(() => {
if (!shouldUseCarousel) return;
if (!emblaApi) return;
const autoplay = emblaApi?.plugins()?.autoplay;
if (!autoplay) return;
if (timeoutId) {
clearTimeout(timeoutId);
}
if (!isInView) {
autoplay.stop();
} else {
const randTimeout = Math.floor(Math.random() * 2500);
setTimeoutId(
setTimeout(() => {
autoplay.play();
}, randTimeout)
);
}
}, [shouldUseCarousel, emblaApi, isInView]);
return (
<Link href={url} key={`projects.link.${text}`} className="relative group">
<div className="px-2 overflow-hidden">
{shouldUseCarousel ? (
<div className="embla embla--projects" ref={ref}>
<div className="embla__viewport" ref={emblaRef}>
<div className="embla__container">
{preview.map((item, index) => (
<div
className="embla__slide"
key={`embla.project.${text}.slide.${index}`}
>
<img
className="embla__slide__img rounded-xl! aspect-video border-white/5 border-1 group-hover:scale-105 duration-100 ease-in-out origin-bottom"
src={item}
alt=""
/>
</div>
))}
</div>
</div>
</div>
) : (
<img
src={preview?.[0] || "/images/projects/no-preview.png"}
alt={text}
className="w-full aspect-video object-cover rounded-xl border-white/5 border-1 group-hover:scale-105 duration-100 ease-in-out origin-bottom "
/>
)}
</div>
<div className="absolute bottom-0 left-0 right-0">
<IconWithText
icon={icon}
text={text}
desc={desc}
backgroundColor={"#0a0a0a"}
backgroundOpacity={"100%"}
isGroup={true}
/>
</div>
</Link>
);
};