Untitled
unknown
plain_text
8 months ago
7.5 kB
6
Indexable
import { useState, useEffect } from "react";
import { motion, AnimatePresence } from "framer-motion";
import { ClientConfig } from "@/lib/client-config";
import { getR2ImageUrl } from "@/lib/r2";
import { ChevronDown, Heart } from 'lucide-react';
interface PortraitGalleryHeroProps {
config: ClientConfig;
}
export function PortraitGalleryHero({ config }: PortraitGalleryHeroProps) {
const heroConfig = config.welcomeHero || {};
const images = heroConfig.images || [config.highlightImage];
// Limit to 3 images maximum
const displayImages = images.slice(0, 3);
// Determine layout based on number of images
const layoutClass =
displayImages.length === 1 ? "grid-cols-1" :
displayImages.length === 2 ? "grid-cols-1 md:grid-cols-2" :
"grid-cols-1 md:grid-cols-3";
const overlayOpacity = heroConfig.overlayOpacity ?? 0.3;
const textColor = heroConfig.textColor === 'dark' ? 'text-gray-800' : 'text-white';
return (
<div className="relative h-screen w-full overflow-hidden bg-black">
{/* Decorative background pattern */}
<div className="absolute inset-0 bg-[url('/decorative/floral-pattern.png')] bg-repeat opacity-5"></div>
{/* Image Grid */}
<div className={`grid ${layoutClass} h-full w-full`}>
{displayImages.map((image, index) => {
const imageUrl = image.startsWith('http')
? image
: getR2ImageUrl(config.id, image.split('/').pop() || '');
return (
<div key={index} className="relative h-full w-full overflow-hidden">
<motion.div
initial={{ scale: 1.1, opacity: 0 }}
animate={{ scale: 1, opacity: 1 }}
transition={{ duration: 1.5, delay: index * 0.2 }}
className="absolute inset-0"
>
<img
src={imageUrl || "/placeholder.svg"}
alt={`Wedding portrait ${index + 1}`}
className="h-full w-full object-cover object-center"
/>
<div
className="absolute inset-0 bg-gradient-to-b from-black/40 via-transparent to-black/70"
style={{ opacity: overlayOpacity + 0.2 }}
/>
</motion.div>
</div>
);
})}
</div>
{/* Decorative frame */}
<motion.div
className="absolute inset-4 border border-white/10 pointer-events-none z-10"
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
transition={{ duration: 1, delay: 0.5 }}
/>
{/* Content overlay */}
<div className="absolute inset-0 flex flex-col items-center justify-center z-20 px-4">
<div className="max-w-4xl text-center">
{/* Decorative element */}
<motion.div
className="mb-6 flex justify-center"
initial={{ opacity: 0, y: -20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 1, delay: 0.6 }}
>
<Heart className="h-8 w-8 text-rose-300/80" />
</motion.div>
<motion.h1
className={`${textColor} text-3xl sm:text-5xl md:text-6xl lg:text-7xl font-light mb-6 tracking-wide`}
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 1, delay: 0.8 }}
>
{config.title}
</motion.h1>
{/* Elegant divider */}
<motion.div
className="my-6 flex items-center justify-center gap-4"
initial={{ opacity: 0, scaleX: 0 }}
animate={{ opacity: 1, scaleX: 1 }}
transition={{ duration: 1.2, delay: 1 }}
>
<div className="h-px w-16 bg-white/60"></div>
<div className="w-2 h-2 rounded-full bg-rose-300/80"></div>
<div className="h-px w-16 bg-white/60"></div>
</motion.div>
{heroConfig.subtitle && (
<motion.p
className={`${textColor} max-w-xl mx-auto text-lg sm:text-xl tracking-wide`}
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
transition={{ duration: 1, delay: 1.2 }}
>
{heroConfig.subtitle}
</motion.p>
)}
<motion.div
className={`${textColor} mt-8 text-lg sm:text-xl tracking-widest`}
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
transition={{ duration: 1, delay: 1.4 }}
>
{config.date}
</motion.div>
</div>
</div>
{/* Photographer credit */}
<motion.div
className="absolute bottom-8 left-0 right-0 text-center z-20"
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
transition={{ duration: 1, delay: 1.6 }}
>
<p className="text-white/70 text-xs tracking-widest">
{config.photographerCredit}
</p>
</motion.div>
{/* Scroll Indicator */}
{(heroConfig.scrollIndicator ?? true) && (
<motion.div
className="absolute bottom-20 left-0 right-0 flex justify-center z-20"
initial={{ opacity: 0, y: -10 }}
animate={{
opacity: 1,
y: 0,
transition: {
duration: 0.8,
delay: 1.8,
repeat: Infinity,
repeatType: "reverse",
repeatDelay: 0.5
}
}}
>
<div className="p-2 border border-white/20 rounded-full">
<ChevronDown className="h-5 w-5 text-white/70" />
</div>
</motion.div>
)}
{/* Corner decorations */}
<div className="absolute inset-0 pointer-events-none z-10">
<motion.img
src="/decorative/floral-corner-1.png"
alt="Decorative corner"
className="absolute top-8 left-8 w-16 h-16 opacity-40"
initial={{ opacity: 0, scale: 0.8 }}
animate={{ opacity: 0.4, scale: 1 }}
transition={{ duration: 1, delay: 1.2 }}
/>
<motion.img
src="/decorative/floral-corner-1.png"
alt="Decorative corner"
className="absolute top-8 right-8 w-16 h-16 opacity-40 transform -scale-x-100"
initial={{ opacity: 0, scale: 0.8 }}
animate={{ opacity: 0.4, scale: 1 }}
transition={{ duration: 1, delay: 1.4 }}
/>
<motion.img
src="/decorative/floral-corner-1.png"
alt="Decorative corner"
className="absolute bottom-8 left-8 w-16 h-16 opacity-40 transform -scale-y-100"
initial={{ opacity: 0, scale: 0.8 }}
animate={{ opacity: 0.4, scale: 1 }}
transition={{ duration: 1, delay: 1.6 }}
/>
<motion.img
src="/decorative/floral-corner-1.png"
alt="Decorative corner"
className="absolute bottom-8 right-8 w-16 h-16 opacity-40 transform -scale-x-100 -scale-y-100"
initial={{ opacity: 0, scale: 0.8 }}
animate={{ opacity: 0.4, scale: 1 }}
transition={{ duration: 1, delay: 1.8 }}
/>
</div>
</div>
);
}
Editor is loading...
Leave a Comment