UNPKG

aura-glass

Version:

A comprehensive glassmorphism design system for React applications with 142+ production-ready components

441 lines (438 loc) 18.4 kB
'use client'; import { jsx, jsxs, Fragment } from 'react/jsx-runtime'; import { cn } from '../../lib/utilsComprehensive.js'; import { Grid3X3, List, ZoomIn, Heart, Share2, Eye, X, ChevronLeft, ChevronRight } from 'lucide-react'; import { useState, useCallback } from 'react'; import '../../primitives/GlassCore.js'; import '../../primitives/glass/GlassAdvanced.js'; import '../../primitives/OptimizedGlassCore.js'; import '../../primitives/glass/OptimizedGlassAdvanced.js'; import '../../primitives/MotionNative.js'; import { MotionFramer } from '../../primitives/motion/MotionFramer.js'; import { GlassButton } from '../button/GlassButton.js'; import '../button/GlassFab.js'; import '../button/GlassMagneticButton.js'; import { CardContent } from '../card/index.js'; import '../data-display/GlassAccordion.js'; import '../data-display/GlassAlert.js'; import '../data-display/GlassAvatar.js'; import { GlassBadge } from '../data-display/GlassBadge.js'; import '../data-display/GlassBadgeLine.js'; import '../data-display/GlassDataGrid.js'; import '../data-display/GlassDataTable.js'; import '../data-display/GlassHeatmap.js'; import '../data-display/GlassLoadingSkeleton.js'; import '../data-display/GlassProgress.js'; import '../data-display/GlassTimeline.js'; import '../data-display/GlassSkeleton.js'; import '../data-display/GlassNotificationCenter.js'; import '../data-display/GlassAnimatedNumber.js'; import { GlassImageViewer } from './GlassImageViewer.js'; import { GlassCard } from '../card/GlassCard.js'; /** * GlassGallery component * A responsive image gallery with grid, masonry, and list layouts */ const GlassGallery = ({ images, layout = "grid", columns = 3, aspectRatio = "square", showInfo = true, showActions = true, enableLightbox = true, showFilters = false, enableSelection = false, className, onImageClick, onSelectionChange, ...props }) => { const [lightboxIndex, setLightboxIndex] = useState(null); const [selectedImages, setSelectedImages] = useState(new Set()); const [viewMode, setViewMode] = useState("grid"); const [filterCategory, setFilterCategory] = useState("all"); // Get aspect ratio classes const getAspectRatioClass = useCallback(() => { switch (aspectRatio) { case "square": return "aspect-square"; case "portrait": return "aspect-[3/4]"; case "landscape": return "aspect-[4/3]"; case "auto": default: return "aspect-auto"; } }, [aspectRatio]); // Get grid columns classes const getGridColumnsClass = useCallback(() => { const cols = Math.min(Math.max(columns, 1), 6); return `grid-cols-1 sm:grid-cols-2 md:grid-cols-${Math.min(cols, 3)} lg:grid-cols-${Math.min(cols, 4)} xl:grid-cols-${cols}`; }, [columns]); // Handle image click const handleImageClick = useCallback((image, index) => { if (enableSelection) { const newSelected = new Set(selectedImages); if (newSelected.has(image.id)) { newSelected.delete(image.id); } else { newSelected.add(image.id); } setSelectedImages(newSelected); onSelectionChange?.(images.filter(img => newSelected.has(img.id))); } else if (enableLightbox) { setLightboxIndex(index); } onImageClick?.(image, index); }, [enableSelection, enableLightbox, selectedImages, images, onImageClick, onSelectionChange]); // Handle lightbox navigation const handleLightboxNav = useCallback(direction => { if (lightboxIndex === null) return; let newIndex = lightboxIndex; if (direction === "prev") { newIndex = lightboxIndex > 0 ? lightboxIndex - 1 : images.length - 1; } else { newIndex = lightboxIndex < images.length - 1 ? lightboxIndex + 1 : 0; } setLightboxIndex(newIndex); }, [lightboxIndex, images.length]); // Filter images by category const filteredImages = images.filter(image => filterCategory === "all" || image.category === filterCategory); // Get unique categories const categories = Array.from(new Set(images.map(img => img.category).filter(Boolean))); // Grid Layout const renderGridLayout = () => jsx("div", { className: cn("grid glass-gap-4", getGridColumnsClass()), children: filteredImages.map((image, index) => jsx("div", { className: 'group relative animate-fade-in', style: { animationDelay: `${Math.min(index, 20) * 50}ms`, animationFillMode: "both" }, children: jsx(GlassCard, { variant: "elevated", interactive: true, onClick: e => handleImageClick(image, index), className: cn("overflow-hidden cursor-pointer hover:scale-[1.02] transition-all duration-200 glass-sheen glass-tilt", selectedImages.has(image.id) && "ring-2 ring-primary", getAspectRatioClass()), children: jsxs(CardContent, { className: "glass-p-0", children: [jsxs("div", { className: 'relative overflow-hidden', children: [jsx("img", { src: image.thumbnail || image.src, alt: image.alt || image.title, className: 'glass-w-full glass-h-full object-cover transition-transform duration-300 group-hover:scale-105', loading: "lazy" }), enableSelection && jsx("div", { className: 'absolute glass-top-2 left-2', children: jsx("div", { className: cn("w-5 h-5 glass-radius-md border-2 transition-all", selectedImages.has(image.id) ? "bg-primary border-primary" : "bg-white/20 border-white/40"), children: selectedImages.has(image.id) && jsx("div", { className: "glass-w-full glass-h-full glass-flex glass-items-center glass-justify-center", children: jsx("div", { className: 'w-2 h-2 glass-surface-subtle glass-radius-full' }) }) }) }), jsx("div", { className: 'absolute inset-0 glass-surface-dark/0 group-hover:glass-surface-dark/40 transition-all duration-200', children: jsx("div", { className: 'absolute inset-0 glass-flex glass-items-center glass-justify-center opacity-0 group-hover:opacity-100 transition-opacity', children: jsxs("div", { className: "glass-flex glass-gap-2", children: [jsx(GlassButton, { variant: "secondary", size: "sm", onClick: e => { e.stopPropagation(); if (enableLightbox) setLightboxIndex(index); }, className: "glass-p-2", children: jsx(ZoomIn, { className: 'w-4 h-4' }) }), showActions && jsxs(Fragment, { children: [jsx(GlassButton, { variant: "secondary", size: "sm", onClick: e => e.stopPropagation(), className: "glass-p-2", children: jsx(Heart, { className: 'w-4 h-4' }) }), jsx(GlassButton, { variant: "secondary", size: "sm", onClick: e => e.stopPropagation(), className: "glass-p-2", children: jsx(Share2, { className: 'w-4 h-4' }) })] })] }) }) }), showInfo && image.title && jsxs("div", { className: 'absolute bottom-0 left-0 right-0 glass-p-3 glass-gradient-primary glass-gradient-primary glass-gradient-primary', children: [jsx("h3", { className: 'text-primary font-medium glass-text-sm truncate', children: image.title }), image.description && jsx("p", { className: 'text-primary/80 glass-text-xs glass-mt-1 line-clamp-2', children: image.description })] })] }), (image.likes || image.views || image.tags) && jsx("div", { className: "glass-p-3 glass-border-t glass-border-white/10", children: jsxs("div", { className: 'glass-flex glass-items-center glass-justify-between glass-text-xs text-primary/60', children: [jsxs("div", { className: "glass-flex glass-items-center glass-gap-3", children: [image.likes && jsxs("span", { className: "glass-flex glass-items-center glass-gap-1", children: [jsx(Heart, { className: 'w-3 h-3' }), image.likes] }), image.views && jsxs("span", { className: "glass-flex glass-items-center glass-gap-1", children: [jsx(Eye, { className: 'w-3 h-3' }), image.views] })] }), image.tags && image.tags.length > 0 && jsx("div", { className: "glass-flex glass-gap-1", children: image.tags.slice(0, 2).map((tag, tagIndex) => jsx(GlassBadge, { variant: "outline", size: "sm", children: tag }, tagIndex)) })] }) })] }) }) }, image.id)) }); // List Layout const renderListLayout = () => jsx("div", { className: "glass-auto-gap glass-auto-gap-lg", children: filteredImages.map((image, index) => jsx("div", { className: 'animate-slide-in-up', style: { animationDelay: `${Math.min(index, 20) * 50}ms`, animationFillMode: "both" }, children: jsx(GlassCard, { variant: "elevated", interactive: true, onClick: e => handleImageClick(image, index), className: 'overflow-hidden cursor-pointer hover:scale-[1.01] transition-transform', children: jsx(CardContent, { className: "glass-p-0", children: jsxs("div", { className: "glass-flex", children: [jsx("div", { className: 'w-32 h-32 glass-flex-shrink-0', children: jsx("img", { src: image.thumbnail || image.src, alt: image.alt || image.title, className: 'glass-w-full glass-h-full object-cover', loading: "lazy" }) }), jsxs("div", { className: "glass-flex-1 glass-p-4", children: [jsxs("div", { className: "glass-flex glass-items-start glass-justify-between", children: [jsxs("div", { className: "glass-flex-1", children: [jsx("h3", { className: 'text-primary font-medium mb-1', children: image.title }), image.description && jsx("p", { className: 'text-primary/70 glass-text-sm mb-2', children: image.description }), jsxs("div", { className: 'glass-flex glass-items-center glass-gap-4 glass-text-xs text-primary/60', children: [image.likes && jsxs("span", { className: "glass-flex glass-items-center glass-gap-1", children: [jsx(Heart, { className: 'w-3 h-3' }), image.likes] }), image.views && jsxs("span", { className: "glass-flex glass-items-center glass-gap-1", children: [jsx(Eye, { className: 'w-3 h-3' }), image.views] }), image.createdAt && jsx("span", { children: image.createdAt.toLocaleDateString() })] })] }), showActions && jsxs("div", { className: "glass-flex glass-gap-1 glass-ml-4", children: [jsx(GlassButton, { variant: "ghost", size: "sm", onClick: e => { e.stopPropagation(); if (enableLightbox) setLightboxIndex(index); }, className: "glass-p-2", children: jsx(ZoomIn, { className: 'w-4 h-4' }) }), jsx(GlassButton, { variant: "ghost", size: "sm", onClick: e => e.stopPropagation(), className: "glass-p-2", children: jsx(Heart, { className: 'w-4 h-4' }) }), jsx(GlassButton, { variant: "ghost", size: "sm", onClick: e => e.stopPropagation(), className: "glass-p-2", children: jsx(Share2, { className: 'w-4 h-4' }) })] })] }), image.tags && image.tags.length > 0 && jsx("div", { className: 'glass-flex glass-gap-1 mt-3', children: image.tags.map((tag, tagIndex) => jsx(GlassBadge, { variant: "outline", size: "sm", children: tag }, tagIndex)) })] })] }) }) }) }, image.id)) }); // Lightbox Modal const renderLightbox = () => { if (lightboxIndex === null) return null; const image = images[lightboxIndex]; return jsx("div", { "data-glass-component": true, className: 'fixed inset-0 z-50 glass-surface-dark/90 glass-flex glass-items-center glass-justify-center glass-p-4', children: jsxs("div", { className: 'relative max-w-4xl max-h-full', children: [jsx(GlassButton, { variant: "secondary", size: "sm", onClick: e => setLightboxIndex(null), className: 'absolute top-4 right-4 z-10', children: jsx(X, { className: 'w-4 h-4' }) }), images.length > 1 && jsxs(Fragment, { children: [jsx(GlassButton, { variant: "secondary", size: "lg", onClick: e => handleLightboxNav("prev"), className: 'absolute left-4 glass-top-1/2 transform -translate-y-1/2', children: jsx(ChevronLeft, { className: 'w-6 h-6' }) }), jsx(GlassButton, { variant: "secondary", size: "lg", onClick: e => handleLightboxNav("next"), className: 'absolute right-4 glass-top-1/2 transform -translate-y-1/2', children: jsx(ChevronRight, { className: 'w-6 h-6' }) })] }), jsx(GlassImageViewer, { images: images.map(img => ({ src: img.src, alt: img.alt, title: img.title })), initialIndex: lightboxIndex, enableZoom: true, enablePan: true, enableFullscreen: false, onImageChange: setLightboxIndex, className: 'max-h-[80vh]' }), image && (image.title || image.description) && jsxs("div", { className: 'glass-mt-4 text-center', children: [image.title && jsx("h3", { className: 'text-primary glass-text-lg font-medium mb-1', children: image.title }), image.description && jsx("p", { className: 'text-primary/80', children: image.description })] })] }) }); }; return jsx(MotionFramer, { preset: "fadeIn", className: "glass-w-full", children: jsxs("div", { className: cn("glass-auto-gap glass-auto-gap-2xl", className), ...props, children: [jsxs("div", { className: "glass-flex glass-items-center glass-justify-between", children: [jsxs("div", { className: "glass-flex glass-items-center glass-gap-4", children: [showFilters && categories.length > 0 && jsxs("select", { value: filterCategory, onChange: e => setFilterCategory(e.target.value), className: 'bg-glass-fill ring-1 ring-white/10 glass-radius-md glass-px-3 glass-py-1 glass-text-sm text-primary focus:outline-none focus:ring-white/30', children: [jsx("option", { value: "all", children: "All Categories" }), categories.map(category => jsx("option", { value: category, children: category }, category))] }), enableSelection && selectedImages.size > 0 && jsxs(GlassBadge, { variant: "primary", children: [selectedImages.size, " selected"] })] }), jsxs("div", { className: "glass-flex glass-items-center glass-gap-2", children: [jsx(GlassButton, { variant: viewMode === "grid" ? "primary" : "ghost", size: "sm", onClick: e => setViewMode("grid"), className: "glass-p-2", children: jsx(Grid3X3, { className: 'w-4 h-4' }) }), jsx(GlassButton, { variant: viewMode === "list" ? "primary" : "ghost", size: "sm", onClick: e => setViewMode("list"), className: "glass-p-2", children: jsx(List, { className: 'w-4 h-4' }) })] })] }), filteredImages.length === 0 ? jsxs("div", { className: 'text-center glass-py-12', children: [jsx("div", { className: 'text-primary/60 mb-4', children: jsx(Grid3X3, { className: 'w-12 h-12 glass-mx-auto' }) }), jsx("p", { className: 'text-primary/60', children: filterCategory === "all" ? "No images to display" : `No images in ${filterCategory} category` })] }) : viewMode === "grid" ? renderGridLayout() : renderListLayout(), renderLightbox()] }) }); }; export { GlassGallery, GlassGallery as default }; //# sourceMappingURL=GlassGallery.js.map