Untitled
unknown
plain_text
2 months ago
22 kB
4
Indexable
// /components/ScrollToolbar.tsx "use client" import React, { useState, useRef } from "react" import { motion, useScroll, useMotionValueEvent } from "framer-motion" import { Slider } from "@/components/ui/slider" import { Button } from "@/components/ui/button" import { Search, List, X, ChevronUp, ZoomIn, ZoomOut, UserRound, Mail, Instagram, Filter, Heart, Check, MessageCircle, Download, Trash2, AlertTriangle, Share2 } from "lucide-react" import { Popover, PopoverContent, PopoverTrigger, } from "@/components/ui/popover" import { AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle, } from "@/components/ui/alert-dialog" import { ShareDialog } from "./share-dialog" interface ToolbarProps { gridSize: number; setGridSize: (value: number) => void; sections: { title: string; description?: string; }[]; inView?: boolean; clientConfig: any; favorites: {[key: string]: boolean}; showFavoritesOnly: boolean; setShowFavoritesOnly: (value: boolean) => void; commentCounts: {[key: string]: number}; showCommentsOnly: boolean; setShowCommentsOnly: (value: boolean) => void; handleDownloadAllFavorites: () => void; handleResetAllFavorites: () => void; handleCreateSharedLink: (password: string, requirePassword: boolean) => Promise<string>; clientId: string; } export const ScrollToolbar = ({ gridSize, setGridSize, sections, inView = false, clientConfig, favorites, showFavoritesOnly, setShowFavoritesOnly, commentCounts, showCommentsOnly, setShowCommentsOnly, handleDownloadAllFavorites, handleResetAllFavorites, handleCreateSharedLink, clientId }: ToolbarProps) => { const [variant, setVariant] = useState("hidden"); const { scrollY } = useScroll(); const [isResetDialogOpen, setIsResetDialogOpen] = useState(false); const [isShareDialogOpen, setIsShareDialogOpen] = useState(false); const scrollToSection = (sectionTitle: string) => { const element = document.getElementById(`section-${sectionTitle}`) if (element) { element.scrollIntoView({ behavior: "smooth" }) } } const scrollToTop = () => { window.scrollTo({ top: 0, behavior: "smooth" }) } useMotionValueEvent(scrollY, "change", (latest) => { if (inView) return setVariant("absolute") if (latest > 300) setVariant("visible") else setVariant("hidden") }) // Calculate the number of favorited images const favoriteCount = Object.values(favorites).filter(Boolean).length; return ( <> {/* Alert Dialog for Reset Confirmation */} <AlertDialog open={isResetDialogOpen} onOpenChange={setIsResetDialogOpen}> <AlertDialogContent className="st-popover-bg st-border st-text"> <AlertDialogHeader> <AlertDialogTitle className="flex items-center gap-2"> <AlertTriangle className="h-5 w-5 st-amber" /> Favoriten zurücksetzen </AlertDialogTitle> <AlertDialogDescription className="st-text-secondary"> Sind Sie sicher, dass Sie alle Favoriten zurücksetzen möchten? Diese Aktion kann nicht rückgängig gemacht werden und alle markierten Favoriten gehen verloren. </AlertDialogDescription> </AlertDialogHeader> <AlertDialogFooter> <AlertDialogCancel className="st-item-bg st-text st-hover-bg st-border"> Abbrechen </AlertDialogCancel> <AlertDialogAction className="st-red-bg st-red-hover-bg text-white border-0" onClick={() => { handleResetAllFavorites(); setIsResetDialogOpen(false); }} > Ja, alle zurücksetzen </AlertDialogAction> </AlertDialogFooter> </AlertDialogContent> </AlertDialog> {/* Share Dialog */} <ShareDialog open={isShareDialogOpen} onOpenChange={setIsShareDialogOpen} favoriteCount={favoriteCount} clientId={clientId} handleCreateSharedLink={handleCreateSharedLink} /> <motion.div variants={{ hidden: { position: 'fixed', bottom: "-100px", left: 0, right: 0 }, visible: { position: 'fixed', bottom: '20px', left: 0, right: 0 }, absolute: { position: 'absolute', bottom: '20px', left: 0, right: 0 } }} animate={variant} transition={{ duration: 0.3, ease: "easeInOut" }} className="z-40 flex w-full justify-center pointer-events-none" > <div className="rounded-full st-bg backdrop-blur-sm flex px-2 sm:px-4 py-4 st-border border shadow-lg pointer-events-auto"> {/* Image Size Adjustment */} <Popover> <PopoverTrigger asChild> <div className="px-2 sm:px-3 md:px-4 flex items-center justify-center cursor-pointer st-hover-bg rounded-full transition-colors"> <Search className="h-5 w-5 st-text" /> </div> </PopoverTrigger> <PopoverContent className="w-72 p-4 st-popover-bg st-border st-text" sideOffset={16} side="top" align="center" > <div className="space-y-4"> <h4 className="font-medium text-sm">Fotogröße anpassen</h4> <div className="flex items-center gap-3"> <ZoomIn className="h-4 w-4 st-text-secondary" /> <Slider value={[gridSize]} min={1} max={6} step={1} onValueChange={(value) => setGridSize(value[0])} className="flex-1" /> <ZoomOut className="h-4 w-4 st-text-secondary" /> </div> </div> </PopoverContent> </Popover> <div className="h-6 w-px st-divider mx-1 sm:mx-2" /> {/* Filter Options */} <Popover> <PopoverTrigger asChild> <div className={` px-2 sm:px-3 md:px-4 flex items-center justify-center cursor-pointer ${showFavoritesOnly || showCommentsOnly ? 'st-item-bg st-text' : 'st-hover-bg st-text'} rounded-full transition-colors relative `}> <Filter className="h-5 w-5" /> {(favoriteCount > 0 && showFavoritesOnly) || (Object.keys(commentCounts).length > 0 && showCommentsOnly) ? ( <span className="absolute -top-1 -right-1 flex h-3 w-3"> <span className="relative inline-flex rounded-full h-3 w-3 bg-zinc-200"></span> </span> ) : null} </div> </PopoverTrigger> <PopoverContent className="w-72 p-4 st-popover-bg st-border st-text" sideOffset={16} side="top" align="center" > <div className="space-y-4"> <h4 className="font-medium text-sm">Filter Optionen</h4> <div className="space-y-2"> <div className="flex items-center gap-3 p-2 rounded-md st-hover-bg cursor-pointer transition-colors" onClick={() => { setShowFavoritesOnly(!showFavoritesOnly); if (!showFavoritesOnly) setShowCommentsOnly(false); }} > <div className={`p-1.5 rounded-full ${showFavoritesOnly ? 'st-pink-bg' : 'st-item-bg'}`}> <Heart className={`h-4 w-4 ${showFavoritesOnly ? 'st-pink' : 'st-text-secondary'}`} fill={showFavoritesOnly ? 'currentColor' : 'none'} /> </div> <span className="text-sm flex-1">Nur Favoriten anzeigen</span> <div className={`w-4 h-4 rounded-full border ${showFavoritesOnly ? 'bg-pink-500 border-pink-600' : 'st-border'} flex items-center justify-center`}> {showFavoritesOnly && <Check className="h-3 w-3 text-white" />} </div> </div> <div className="flex items-center gap-3 p-2 rounded-md st-hover-bg cursor-pointer transition-colors" onClick={() => { setShowCommentsOnly(!showCommentsOnly); if (!showCommentsOnly) setShowFavoritesOnly(false); }} > <div className={`p-1.5 rounded-full ${showCommentsOnly ? 'bg-blue-500/20' : 'st-item-bg'}`}> <MessageCircle className={`h-4 w-4 ${showCommentsOnly ? 'st-blue' : 'st-text-secondary'}`} /> </div> <span className="text-sm flex-1">Nur Bilder mit Kommentaren anzeigen</span> <div className={`w-4 h-4 rounded-full border ${showCommentsOnly ? 'bg-blue-500 border-blue-600' : 'st-border'} flex items-center justify-center`}> {showCommentsOnly && <Check className="h-3 w-3 text-white" />} </div> </div> {favoriteCount > 0 && showFavoritesOnly && ( <div className="flex items-center gap-3 p-2 text-xs st-text-secondary italic"> {favoriteCount} {favoriteCount === 1 ? 'Favorit' : 'Favoriten'} ausgewählt </div> )} {Object.keys(commentCounts).length > 0 && showCommentsOnly && ( <div className="flex items-center gap-3 p-2 text-xs st-text-secondary italic"> {Object.keys(commentCounts).length} {Object.keys(commentCounts).length === 1 ? 'Bild' : 'Bilder'} mit Kommentaren </div> )} </div> </div> </PopoverContent> </Popover> <div className="h-6 w-px st-divider mx-1 sm:mx-2" /> {/* Favorites Heart with Popover */} <Popover> <PopoverTrigger asChild> <div className={` px-2 sm:px-3 md:px-4 flex items-center justify-center cursor-pointer ${favoriteCount > 0 ? 'st-pink-hover-bg st-pink' : 'hover:bg-zinc-800/50 st-text-tertiary'} rounded-full transition-colors `}> <Heart className="h-5 w-5" fill={favoriteCount > 0 ? "currentColor" : "none"} /> {favoriteCount > 0 && ( <span className="ml-1 text-xs hidden sm:inline-block">{favoriteCount}</span> )} </div> </PopoverTrigger> <PopoverContent className="w-72 p-0 st-popover-bg st-border st-text shadow-xl" sideOffset={16} side="top" align="center" > <div className="p-3 border-b st-border"> <h4 className="font-medium">Favoriten</h4> </div> <div className="p-4"> {favoriteCount > 0 ? ( <div className="space-y-3"> <p className="text-sm st-text"> Sie haben {favoriteCount} {favoriteCount === 1 ? 'Bild' : 'Bilder'} als Favorit markiert. </p> <Button className="w-full st-pink-gradient st-pink-gradient-hover text-white border-0" onClick={handleDownloadAllFavorites} > <Download className="mr-2 h-4 w-4" /> Alle Favoriten herunterladen </Button> <Button className="w-full st-blue-gradient st-blue-gradient-hover text-white border-0" onClick={() => setIsShareDialogOpen(true)} > <Share2 className="mr-2 h-4 w-4" /> Favoriten teilen </Button> <div className="pt-2 border-t st-border"> <Button variant="ghost" className="w-full st-red-text st-red-hover-text st-red-hover-light-bg" onClick={() => setIsResetDialogOpen(true)} > <Trash2 className="mr-2 h-4 w-4" /> Alle Favoriten zurücksetzen </Button> </div> </div> ) : ( <div className="py-2 text-center"> <Heart className="h-8 w-8 st-text-tertiary mx-auto mb-2" /> <p className="text-sm st-text-secondary max-w-[200px] mx-auto"> Klicken Sie auf das Herz-Symbol bei den Bildern, um Favoriten hinzuzufügen. </p> </div> )} </div> </PopoverContent> </Popover> <div className="h-6 w-px st-divider mx-1 sm:mx-2" /> {/* Table of Contents */} <Popover> <PopoverTrigger asChild> <div className="px-2 sm:px-3 md:px-4 flex items-center justify-center cursor-pointer st-hover-bg rounded-full transition-colors"> <List className="h-5 w-5 st-text" /> </div> </PopoverTrigger> <PopoverContent className="w-72 p-0 st-popover-bg st-border st-text max-h-96 overflow-y-auto" sideOffset={16} side="top" align="center" > <div className="p-3 border-b st-border"> <h4 className="font-medium">Inhaltsverzeichnis</h4> </div> <div className="p-2"> {sections.map((section, index) => ( <div key={index} onClick={() => scrollToSection(section.title)} className="p-2 st-hover-bg rounded cursor-pointer transition-colors" > {section.title} </div> ))} </div> </PopoverContent> </Popover> <div className="h-6 w-px st-divider mx-1 sm:mx-2" /> {/* Personal Info */} <Popover> <PopoverTrigger asChild> <div className="px-2 sm:px-3 md:px-4 flex items-center justify-center cursor-pointer st-hover-bg rounded-full transition-colors"> <UserRound className="h-5 w-5 st-text" /> </div> </PopoverTrigger> <PopoverContent className="w-80 p-0 st-popover-bg st-border st-text shadow-xl" sideOffset={16} side="top" align="center" > <div className="overflow-hidden rounded-md"> {/* Card header with background image */} <div className="relative p-6"> {/* Background image - dark overlay applied */} <div className="absolute inset-0 z-0"> <div className="absolute inset-0 bg-gradient-to-r from-zinc-900/80 to-zinc-900/10"></div> <img src="/background-image.jpg" alt="Background" className="w-full h-full object-cover object-center opacity-90" /> </div> {/* Circular avatar image */} <div className="w-16 h-16 rounded-full st-item-bg flex items-center justify-center mb-3 ring-2 ring-white/20 shadow-lg overflow-hidden relative z-10"> <img src="/avatar.jpg" alt="Avatar" className="w-full h-full object-cover object-center" onError={(e) => { // Fallback if image fails to load e.currentTarget.style.display = 'none'; e.currentTarget.parentElement.innerHTML = '<span class="text-white text-xl font-bold">P</span>'; }} /> </div> <h3 className="font-bold text-white text-xl relative z-10"> {clientConfig?.photographerName || "Fotograf"} </h3> <div className="flex items-center gap-2 mt-1 relative z-10"> <div className="h-1 w-6 bg-indigo-400 rounded-full"></div> <p className="text-white/90 text-sm font-medium"> {clientConfig?.photographerCredit || "Professionelle Fotografie"} </p> </div> </div> {/* Card body */} <div className="p-4 space-y-4 bg-gradient-to-b from-zinc-900 to-zinc-950"> {/* Contact details with null checks */} <div className="space-y-3 pt-1"> <div className="flex items-center gap-3 st-text p-2 rounded-md st-hover-bg-light transition-colors"> <div className="st-indigo-bg p-2 rounded-md"> <Mail className="h-4 w-4 st-indigo-text" /> </div> <a href={`mailto:${clientConfig?.photographerEmail || "info@example.com"}`} className="text-sm hover:text-indigo-300 transition-colors flex-1" > {clientConfig?.photographerEmail || "info@example.com"} </a> </div> <div className="flex items-center gap-3 st-text p-2 rounded-md st-hover-bg-light transition-colors"> <div className="st-indigo-bg p-2 rounded-md"> <Instagram className="h-4 w-4 st-indigo-text" /> </div> <a href={clientConfig?.photographerInstagram || "#"} target="_blank" rel="noopener noreferrer" className="text-sm hover:text-indigo-300 transition-colors flex-1" > Instagram </a> </div> </div> {/* Decorated separator */} <div className="relative flex items-center py-2"> <div className="flex-grow border-t st-border"></div> <span className="flex-shrink mx-3 st-text-tertiary text-xs">FOTOGRAF</span> <div className="flex-grow border-t st-border"></div> </div> {/* Brief description */} <p className="text-sm st-text-secondary"> Hallo, mein Name ist Justin Faltus, ich bin ein perfektionistischer Fotograf, mit Sitz in Heidenheim, Baden-Württemberg. Meine Liebe zur Fotografie wurde durch magische Momente und wertvolle Erinnerungen geprägt. Ich arbeite stets mit höchster Professionalität und Perfektionismus, um Ihnen atemberaubende Bilder zu liefern, die unvergesslich bleiben. </p> {/* Contact button with gradient */} <Button className="w-full mt-2 st-indigo-gradient st-indigo-gradient-hover st-indigo-light-text border-0" onClick={() => { window.scrollTo({ top: document.body.scrollHeight, behavior: 'smooth' }); // Close the popover const popoverTrigger = document.querySelector('[data-state="open"]') as HTMLElement; if (popoverTrigger) popoverTrigger.click(); }} > Kontaktieren </Button> </div> </div> </PopoverContent> </Popover> <div className="h-6 w-px st-divider mx-1 sm:mx-2" /> {/* Scroll to Top */} <div className="px-2 sm:px-3 md:px-4 flex items-center justify-center cursor-pointer st-hover-bg rounded-full transition-colors" onClick={scrollToTop} > <ChevronUp className="h-5 w-5 st-text" /> </div> </div> </motion.div> </> ) } export default ScrollToolbar
Editor is loading...
Leave a Comment