trptk/components/release/ReleaseCover.tsx
2026-02-24 17:14:07 +01:00

59 lines
1.8 KiB
TypeScript

"use client";
import Image from "next/image";
import { motion } from "framer-motion";
import { useState } from "react";
import { ARTIST_PLACEHOLDER_SRC } from "@/lib/constants";
type Props = {
src: string;
alt: string;
};
export function ReleaseCover({ src: initialSrc, alt }: Props) {
const [imgError, setImgError] = useState(false);
const [imgLoaded, setImgLoaded] = useState(false);
const src = imgError ? ARTIST_PLACEHOLDER_SRC : initialSrc;
return (
<motion.div
key={src}
className=""
initial={{ opacity: 0, filter: "blur(10px)" }}
animate={
imgLoaded ? { opacity: 1, filter: "blur(0px)" } : { opacity: 0, filter: "blur(10px)" }
}
transition={{ duration: 1, ease: "easeInOut" }}
>
<div className="mx-auto w-full">
<div className="group relative isolate mx-auto aspect-square w-full overflow-visible rounded-2xl">
<div
className="pointer-events-none absolute -inset-6 z-0"
style={{ "--cover-url": `url(${src})` } as React.CSSProperties}
>
<div
id="blurred-bg"
key={src}
aria-hidden="true"
className={`release-blur pointer-events-none absolute -inset-6 z-0 scale-105 rounded-3xl ${imgLoaded ? "loaded" : ""}`}
/>
</div>
<div className="absolute inset-0 z-10 overflow-hidden rounded-2xl">
<Image
src={src}
alt={alt}
fill
sizes="(max-width: 800px) calc(100vw - 32px), 600px"
priority
style={{ objectFit: "cover" }}
onLoad={() => setImgLoaded(true)}
onError={() => setImgError(true)}
/>
</div>
</div>
</div>
</motion.div>
);
}