Untitled

 avatar
unknown
plain_text
2 months ago
17 kB
3
Indexable
"use client"
import { motion } from "framer-motion"
import type { ClientConfig } from "@/lib/client-config"
import { getR2ImageUrl } from "@/lib/r2"
import { ChevronDown, Heart, MousePointer, ArrowDown } 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"

  // Visual settings
  const overlayOpacity = heroConfig.overlayOpacity ?? 0.3
  const overlayColor = heroConfig.overlayColor || "black"
  const overlayGradient = heroConfig.overlayGradient || "gradient-to-b from-black/40 via-transparent to-black/70"
  const textColor = heroConfig.textColor === "dark" ? "text-gray-800" : "text-white"
  
  // Animation settings
  const animationStyle = heroConfig.animationStyle || "fade"
  const animationSpeed = heroConfig.animationSpeed || "medium"
  const animationDuration = animationSpeed === "slow" ? 1.5 : animationSpeed === "fast" ? 0.6 : 1
  const animationDelay = animationSpeed === "slow" ? 0.3 : animationSpeed === "fast" ? 0.1 : 0.2
  
  // Typography settings
  const titleStyle = heroConfig.titleStyle || "elegant"
  const titleSize = heroConfig.titleSize || "medium"
  const titleFont = heroConfig.titleFont || ""
  const subtitleFont = heroConfig.subtitleFont || ""
  
  // Content layout settings
  const contentAlignment = heroConfig.contentAlignment || "center"
  const contentPosition = heroConfig.contentPosition || "center"
  const contentWidth = heroConfig.contentWidth || "medium"
  
  // Create dynamic classes based on settings
  const titleStyleClasses = {
    classic: "font-medium",
    elegant: "font-light tracking-wide",
    minimal: "font-thin tracking-wider",
    script: "font-serif italic",
    bold: "font-bold",
    outlined: "text-transparent bg-clip-text bg-gradient-to-r from-white to-gray-100 [-webkit-text-stroke:1px_rgba(255,255,255,0.7)]"
  }[titleStyle]
  
  const titleSizeClasses = {
    small: "text-2xl sm:text-3xl md:text-4xl lg:text-5xl",
    medium: "text-3xl sm:text-4xl md:text-5xl lg:text-6xl",
    large: "text-4xl sm:text-5xl md:text-6xl lg:text-7xl",
    xlarge: "text-5xl sm:text-6xl md:text-7xl lg:text-8xl"
  }[titleSize]
  
  const contentWidthClasses = {
    narrow: "max-w-xl",
    medium: "max-w-3xl",
    wide: "max-w-5xl", 
    full: "max-w-full px-6"
  }[contentWidth]
  
  const contentAlignmentClasses = {
    center: "items-center text-center",
    left: "items-start text-left",
    right: "items-end text-right"
  }[contentAlignment]
  
  const contentPositionClasses = {
    center: "justify-center",
    top: "justify-start pt-24",
    bottom: "justify-end pb-24"
  }[contentPosition]
  
  // Scroll indicator style
  const scrollIndicatorStyle = heroConfig.scrollIndicatorStyle || "arrow"
  
  // Logo settings
  const showLogo = heroConfig.showLogo ?? false
  const logoUrl = heroConfig.logoUrl || ""
  const logoPosition = heroConfig.logoPosition || "center"
  const logoSize = heroConfig.logoSize || "medium"
  
  const logoSizeClasses = {
    small: "h-10 w-auto",
    medium: "h-16 w-auto",
    large: "h-24 w-auto"
  }[logoSize]
  
  const logoPositionClasses = {
    center: "mx-auto mb-8",
    "top-left": "absolute top-8 left-8 z-30",
    "top-right": "absolute top-8 right-8 z-30",
    "bottom-left": "absolute bottom-8 left-8 z-30",
    "bottom-right": "absolute bottom-8 right-8 z-30"
  }[logoPosition]
  
  // Photographer info settings
  const showPhotographerInfo = heroConfig.showPhotographerInfo ?? true
  const photographerInfoPosition = heroConfig.photographerInfoPosition || "bottom"
  const photographerInfoStyle = heroConfig.photographerInfoStyle || "minimal"
  
  const photographerPositionClasses = {
    bottom: "bottom-8 left-0 right-0 text-center",
    top: "top-8 left-0 right-0 text-center",
    side: "bottom-8 right-8 text-right",
    overlay: "bottom-1/4 left-0 right-0 text-center"
  }[photographerInfoPosition]
  
  // Get decorative elements configuration
  const decorativeElements = heroConfig.decorativeElements || {}
  const showCorners = decorativeElements.corners !== false
  const showBorders = decorativeElements.borders !== false
  const showDividers = decorativeElements.dividers ?? true
  const showFloral = decorativeElements.floral ?? false
  const backgroundPattern = decorativeElements.backgroundPattern || "/decorative/floral-pattern.png"

  // Animation variants based on style
  const imageAnimationVariants = {
    fade: {
      initial: { opacity: 0, scale: 1.05 },
      animate: { opacity: 1, scale: 1 }
    },
    slide: {
      initial: { opacity: 0, x: -50 },
      animate: { opacity: 1, x: 0 }
    },
    reveal: {
      initial: { opacity: 1, clipPath: "polygon(0 0, 0 0, 0 100%, 0% 100%)" },
      animate: { opacity: 1, clipPath: "polygon(0 0, 100% 0, 100% 100%, 0 100%)" }
    },
    parallax: {
      initial: { opacity: 0, y: 50 },
      animate: { opacity: 1, y: 0 }
    },
    none: {
      initial: { opacity: 1 },
      animate: { opacity: 1 }
    }
  }[animationStyle]

  // Choose animation for title based on style
  const titleAnimationVariants = {
    fade: {
      initial: { opacity: 0, y: 20 },
      animate: { opacity: 1, y: 0 }
    },
    slide: {
      initial: { opacity: 0, x: -30 },
      animate: { opacity: 1, x: 0 }
    },
    reveal: {
      initial: { opacity: 0, y: 50 },
      animate: { opacity: 1, y: 0 }
    },
    typing: {
      initial: { width: "0%" },
      animate: { width: "100%" }
    },
    none: {
      initial: { opacity: 1 },
      animate: { opacity: 1 }
    }
  }[animationStyle === "typing" ? "typing" : animationStyle]

  return (
    <div className="relative h-screen w-full overflow-hidden bg-black">
      {/* Decorative background pattern */}
      {backgroundPattern && (
        <div 
          className="absolute inset-0 bg-repeat opacity-5" 
          style={{ backgroundImage: `url('${backgroundPattern}')` }}
        ></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={imageAnimationVariants.initial}
                animate={imageAnimationVariants.animate}
                transition={{ 
                  duration: animationDuration, 
                  delay: index * animationDelay 
                }}
                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-${overlayColor} bg-${overlayGradient}`}
                  style={{ opacity: overlayOpacity }}
                />
              </motion.div>
            </div>
          )
        })}
      </div>

      {/* Decorative frame - only show if borders are enabled */}
      {showBorders && (
        <motion.div
          className="absolute inset-4 border border-white/10 pointer-events-none z-10"
          initial={{ opacity: 0 }}
          animate={{ opacity: 1 }}
          transition={{ duration: animationDuration, delay: animationDelay }}
        />
      )}

      {/* Logo - show only if configured */}
      {showLogo && logoUrl && (
        <motion.div
          className={`z-30 ${logoPositionClasses}`}
          initial={{ opacity: 0, y: -20 }}
          animate={{ opacity: 1, y: 0 }}
          transition={{ duration: animationDuration, delay: animationDelay }}
        >
          <img
            src={logoUrl}
            alt="Logo"
            className={logoSizeClasses}
          />
        </motion.div>
      )}

      {/* Content overlay */}
      <div className={`absolute inset-0 flex flex-col ${contentAlignmentClasses} ${contentPositionClasses} z-20 px-4`}>
        <div className={contentWidthClasses}>
          {/* Decorative heart element */}
          {showFloral && (
            <motion.div
              className="mb-6 flex justify-center"
              initial={{ opacity: 0, y: -20 }}
              animate={{ opacity: 1, y: 0 }}
              transition={{ duration: animationDuration, delay: animationDelay * 3 }}
            >
              <Heart className="h-8 w-8 text-rose-300/80" />
            </motion.div>
          )}

          {/* Title with animation based on selected style */}
          {animationStyle === "typing" ? (
            <div className="overflow-hidden">
              <motion.h1
                className={`${textColor} ${titleSizeClasses} ${titleStyleClasses}`}
                initial={titleAnimationVariants.initial}
                animate={titleAnimationVariants.animate}
                transition={{ 
                  duration: animationDuration * 2, 
                  delay: animationDelay * 4,
                  ease: "easeInOut" 
                }}
                style={{ fontFamily: titleFont, whiteSpace: "nowrap", overflow: "hidden" }}
              >
                {config.title}
              </motion.h1>
            </div>
          ) : (
            <motion.h1
              className={`${textColor} ${titleSizeClasses} ${titleStyleClasses} mb-6`}
              initial={titleAnimationVariants.initial}
              animate={titleAnimationVariants.animate}
              transition={{ 
                duration: animationDuration, 
                delay: animationDelay * 4
              }}
              style={{ fontFamily: titleFont }}
            >
              {config.title}
            </motion.h1>
          )}

          {/* Elegant divider - show only if dividers are enabled */}
          {showDividers && (
            <motion.div
              className="my-6 flex items-center justify-center gap-4"
              initial={{ opacity: 0, scaleX: 0 }}
              animate={{ opacity: 1, scaleX: 1 }}
              transition={{ duration: animationDuration, delay: animationDelay * 5 }}
            >
              <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>
          )}

          {/* Subtitle */}
          {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: animationDuration, delay: animationDelay * 6 }}
              style={{ fontFamily: subtitleFont }}
            >
              {heroConfig.subtitle}
            </motion.p>
          )}

          {/* Date */}
          <motion.div
            className={`${textColor} mt-8 text-lg sm:text-xl tracking-widest`}
            initial={{ opacity: 0 }}
            animate={{ opacity: 1 }}
            transition={{ duration: animationDuration, delay: animationDelay * 7 }}
          >
            {config.date}
          </motion.div>
        </div>
      </div>

      {/* Photographer credit - based on position and style */}
      {showPhotographerInfo && (
        <motion.div
          className={`absolute ${photographerPositionClasses} z-20`}
          initial={{ opacity: 0 }}
          animate={{ opacity: 1 }}
          transition={{ duration: animationDuration, delay: animationDelay * 8 }}
        >
          {photographerInfoStyle === "signature" ? (
            <div className="inline-flex items-center px-3 py-1.5 bg-black/20 backdrop-blur-sm rounded-full">
              {logoUrl && (
                <img src={logoUrl} alt="Logo" className="w-4 h-4 mr-2" />
              )}
              <span className="text-white text-xs font-medium tracking-wide">
                {config.photographerName || config.photographerCredit}
              </span>
            </div>
          ) : photographerInfoStyle === "detailed" ? (
            <div className="flex items-center justify-center gap-2">
              <div className="px-4 py-2 bg-black/30 backdrop-blur-sm rounded-md">
                <div className="flex items-center gap-2">
                  {logoUrl && (
                    <img src={logoUrl} alt="Logo" className="w-5 h-5" />
                  )}
                  <div>
                    <p className="text-white text-xs font-medium">{config.photographerName || ""}</p>
                    <p className="text-white/80 text-xs">{config.photographerCredit}</p>
                  </div>
                </div>
              </div>
            </div>
          ) : (
            // Default minimal style
            <p className="text-white/70 text-xs tracking-widest">{config.photographerCredit}</p>
          )}
        </motion.div>
      )}

      {/* Scroll Indicator with style variants */}
      {(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: animationDuration * 0.8,
              delay: animationDelay * 9,
              repeat: Number.POSITIVE_INFINITY,
              repeatType: "reverse",
              repeatDelay: 0.5,
            },
          }}
        >
          {scrollIndicatorStyle === "dot" ? (
            <div className="flex flex-col items-center gap-1">
              <div className={`h-2 w-2 rounded-full ${textColor} opacity-80`}></div>
              <div className={`h-2 w-2 rounded-full ${textColor} opacity-60`}></div>
              <div className={`h-2 w-2 rounded-full ${textColor} opacity-40`}></div>
            </div>
          ) : scrollIndicatorStyle === "icon" ? (
            <div className="p-2 border border-white/20 rounded-full">
              <MousePointer className="h-4 w-4 text-white/70" />
            </div>
          ) : scrollIndicatorStyle === "text" ? (
            <p className="text-sm uppercase tracking-widest text-white/70">Scroll</p>
          ) : (
            // Default arrow style
            <div className="p-2 border border-white/20 rounded-full">
              <ChevronDown className="h-5 w-5 text-white/70" />
            </div>
          )}
        </motion.div>
      )}

      {/* Corner decorations - only show if corners are enabled */}
      {showCorners && (
        <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: animationDuration, delay: animationDelay * 6 }}
          />
          <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: animationDuration, delay: animationDelay * 7 }}
          />
          <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: animationDuration, delay: animationDelay * 8 }}
          />
          <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: animationDuration, delay: animationDelay * 9 }}
          />
        </div>
      )}
    </div>
  )
}
Editor is loading...
Leave a Comment