← Components/Media

ImageGallery

imagegallery-1779379195705.tsx
'use client'
import { useState, useEffect } from 'react'

const IMAGES = [
  { id: 1, src: null, color: 'linear-gradient(135deg, #C9A84C, #6366f1)', caption: 'GlowButton Component', w: 1, h: 2 },
  { id: 2, src: null, color: 'linear-gradient(135deg, #6366f1, #22c55e)', caption: 'HoverTiltCard', w: 1, h: 1 },
  { id: 3, src: null, color: 'linear-gradient(135deg, #22c55e, #ef4444)', caption: 'CommandMenu', w: 1, h: 1 },
  { id: 4, src: null, color: 'linear-gradient(135deg, #ef4444, #f59e0b)', caption: 'DataTable', w: 2, h: 1 },
  { id: 5, src: null, color: 'linear-gradient(135deg, #f59e0b, #ec4899)', caption: 'ChatInterface', w: 1, h: 1 },
  { id: 6, src: null, color: 'linear-gradient(135deg, #ec4899, #C9A84C)', caption: 'RadialProgress', w: 1, h: 2 },
]

export default function ImageGallery() {
  const [lightbox, setLightbox] = useState<number | null>(null)

  useEffect(() => {
    if (lightbox === null) return
    const handler = (e: KeyboardEvent) => {
      if (e.key === 'Escape') setLightbox(null)
      if (e.key === 'ArrowRight') setLightbox(i => i !== null ? (i + 1) % IMAGES.length : null)
      if (e.key === 'ArrowLeft') setLightbox(i => i !== null ? (i - 1 + IMAGES.length) % IMAGES.length : null)
    }
    document.addEventListener('keydown', handler)
    return () => document.removeEventListener('keydown', handler)
  }, [lightbox])

  return (
    <div style={{ padding: 24, background: '#0A0A0A' }}>
      <div style={{ display: 'grid', gridTemplateColumns: 'repeat(3, 1fr)', gridAutoRows: '120px', gap: 8 }}>
        {IMAGES.map((img, i) => (
          <div key={img.id} onClick={() => setLightbox(i)}
            style={{ gridColumn: `span ${img.w}`, gridRow: `span ${img.h}`, background: img.color, borderRadius: 12, cursor: 'pointer', position: 'relative', overflow: 'hidden', transition: 'transform 0.2s' }}
            onMouseEnter={e => (e.currentTarget.style.transform = 'scale(0.97)')}
            onMouseLeave={e => (e.currentTarget.style.transform = 'scale(1)')}>
            <div style={{ position: 'absolute', inset: 0, display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
              <span style={{ color: 'rgba(255,255,255,0.8)', fontSize: 24 }}>⬡</span>
            </div>
            <div style={{ position: 'absolute', bottom: 0, left: 0, right: 0, padding: '20px 12px 10px', background: 'linear-gradient(transparent, rgba(0,0,0,0.6))' }}>
              <span style={{ color: '#fff', fontSize: 11, fontWeight: 600 }}>{img.caption}</span>
            </div>
          </div>
        ))}
      </div>

      {lightbox !== null && (
        <div style={{ position: 'fixed', inset: 0, background: 'rgba(0,0,0,0.92)', backdropFilter: 'blur(12px)', display: 'flex', alignItems: 'center', justifyContent: 'center', zIndex: 9999 }}
          onClick={e => { if (e.target === e.currentTarget) setLightbox(null) }}>
          <button onClick={() => setLightbox(i => i !== null ? (i - 1 + IMAGES.length) % IMAGES.length : null)} style={{ position: 'absolute', left: 24, background: 'rgba(255,255,255,0.1)', border: '1px solid rgba(255,255,255,0.2)', color: '#F5F5F0', borderRadius: '50%', width: 44, height: 44, cursor: 'pointer', fontSize: 18 }}>←</button>

          <div style={{ width: 480, height: 320, background: IMAGES[lightbox].color, borderRadius: 16, display: 'flex', alignItems: 'center', justifyContent: 'center', animation: 'popIn 0.25s ease' }}>
            <span style={{ color: 'rgba(255,255,255,0.8)', fontSize: 64 }}>⬡</span>
          </div>

          <button onClick={() => setLightbox(i => i !== null ? (i + 1) % IMAGES.length : null)} style={{ position: 'absolute', right: 24, background: 'rgba(255,255,255,0.1)', border: '1px solid rgba(255,255,255,0.2)', color: '#F5F5F0', borderRadius: '50%', width: 44, height: 44, cursor: 'pointer', fontSize: 18 }}>→</button>

          <div style={{ position: 'absolute', bottom: 32, textAlign: 'center' }}>
            <div style={{ color: '#F5F5F0', fontSize: 16, fontWeight: 600, marginBottom: 6 }}>{IMAGES[lightbox].caption}</div>
            <div style={{ color: 'rgba(255,255,255,0.4)', fontSize: 12 }}>{lightbox + 1} / {IMAGES.length} · ESC to close</div>
          </div>

          <button onClick={() => setLightbox(null)} style={{ position: 'absolute', top: 20, right: 20, background: 'rgba(255,255,255,0.1)', border: '1px solid rgba(255,255,255,0.15)', color: '#F5F5F0', borderRadius: '50%', width: 36, height: 36, cursor: 'pointer', fontSize: 18, display: 'flex', alignItems: 'center', justifyContent: 'center' }}>×</button>
        </div>
      )}
      <style>{`@keyframes popIn { from { opacity:0; transform:scale(0.9) } to { opacity:1; transform:scale(1) } }`}</style>
    </div>
  )
}

Component info

CategoryMedia
Frameworkreact
TierFREE
Views0
Copies0

About

Masonry image gallery with lightbox, keyboard navigation, zoom, and caption display