diff --git a/src/assets/staff/braz_romi.jpg b/src/assets/staff/braz_romi.jpg new file mode 100644 index 0000000..1c64bef Binary files /dev/null and b/src/assets/staff/braz_romi.jpg differ diff --git a/src/assets/staff/milu.jpg b/src/assets/staff/milu.jpg new file mode 100644 index 0000000..1fbbaf1 Binary files /dev/null and b/src/assets/staff/milu.jpg differ diff --git a/src/assets/staff/safira.jpg b/src/assets/staff/safira.jpg new file mode 100644 index 0000000..0f664f7 Binary files /dev/null and b/src/assets/staff/safira.jpg differ diff --git a/src/components/GallerySection.tsx b/src/components/GallerySection.tsx index d6da036..0e29f06 100644 --- a/src/components/GallerySection.tsx +++ b/src/components/GallerySection.tsx @@ -2,9 +2,18 @@ import { motion } from "framer-motion"; import { GALLERY_IMAGES } from "@/data/event-data"; import { ImageIcon } from "lucide-react"; import { useTranslation } from "react-i18next"; +import { useState } from "react"; +import Lightbox from "./Lightbox"; const GallerySection = () => { const { t } = useTranslation(); + const [isOpen, setIsOpen] = useState(false); + const [currentIndex, setCurrentIndex] = useState(0); + + const openLightbox = (i: number) => { + setCurrentIndex(i); + setIsOpen(true); + }; return (
{ whileInView={{ opacity: 1, scale: 1 }} viewport={{ once: false, amount: 0.15 }} transition={{ delay: i * 0.08 }} - className="aspect-square rounded-xl overflow-hidden bg-muted flex items-center justify-center" + className="aspect-square rounded-xl overflow-hidden bg-muted flex items-center justify-center cursor-pointer" + onClick={() => openLightbox(i)} > {img.src ? ( { ))} + {isOpen && ( + setIsOpen(false)} /> + )}
); diff --git a/src/components/Lightbox.tsx b/src/components/Lightbox.tsx new file mode 100644 index 0000000..c132693 --- /dev/null +++ b/src/components/Lightbox.tsx @@ -0,0 +1,148 @@ +import React, { useEffect, useState, useRef } from "react"; +import { ImageIcon, X, ChevronLeft, ChevronRight, Play, Pause, ZoomIn, ZoomOut } from "lucide-react"; + +type ImageItem = { src?: string; alt?: string }; + +type LightboxProps = { + images: ImageItem[]; + initialIndex?: number; + onClose?: () => void; +}; + +const Lightbox: React.FC = ({ images, initialIndex = 0, onClose }) => { + const [index, setIndex] = useState(initialIndex); + const [scale, setScale] = useState(1); + const [playing, setPlaying] = useState(false); + const timerRef = useRef(null); + + useEffect(() => { + setIndex(initialIndex); + }, [initialIndex]); + + useEffect(() => { + const onKey = (e: KeyboardEvent) => { + if (e.key === "Escape") handleClose(); + if (e.key === "ArrowRight") next(); + if (e.key === "ArrowLeft") prev(); + if (e.key === "+") zoomIn(); + if (e.key === "-") zoomOut(); + if (e.key === " ") setPlaying((p) => !p); + }; + window.addEventListener("keydown", onKey); + return () => window.removeEventListener("keydown", onKey); + }, [index]); + + useEffect(() => { + if (playing) { + timerRef.current = window.setInterval(() => setIndex((i) => (i + 1) % images.length), 3500); + } else if (timerRef.current) { + window.clearInterval(timerRef.current); + timerRef.current = null; + } + return () => { + if (timerRef.current) window.clearInterval(timerRef.current); + }; + }, [playing, images.length]); + + const prev = () => setIndex((i) => (i - 1 + images.length) % images.length); + const next = () => setIndex((i) => (i + 1) % images.length); + const handleClose = () => { + setPlaying(false); + setScale(1); + onClose?.(); + }; + const zoomIn = () => setScale((s) => Math.min(4, +(s + 0.25).toFixed(2))); + const zoomOut = () => setScale((s) => Math.max(0.5, +(s - 0.25).toFixed(2))); + + if (!images || images.length === 0) return null; + + const img = images[index]; + + return ( +
+
+ +
+ +
+ +
+ +
+ +
+ +
+ {img?.src ? ( + {img.alt + ) : ( +
+ +
{img?.alt}
+
+ )} +
+ +
+ + + + + +
+ +
+ {index + 1} / {images.length} +
+
+ ); +}; + +export default Lightbox; diff --git a/src/data/event-data.ts b/src/data/event-data.ts index 8fe9bad..da65eac 100644 --- a/src/data/event-data.ts +++ b/src/data/event-data.ts @@ -21,6 +21,9 @@ import djwinx from "@/assets/staff/djwinx.jpg"; import djklebynho from "@/assets/staff/djklebynho.jpg"; import letialex from "@/assets/staff/letialex.jpg"; import djcathie from "@/assets/staff/djcathie.jpg"; +import milu from "@/assets/staff/milu.jpg"; +import safira from "@/assets/staff/safira.jpg"; +import brazromi from "@/assets/staff/braz_romi.jpg"; import gal1 from "@/assets/gallery/gal1.jpg"; import gal2 from "@/assets/gallery/gal2.jpg"; @@ -189,6 +192,36 @@ export const STAFF = [ soundcloud: "", }, }, + { + id: "11", + name: "[Milu]", + role: "Instructor" as const, + description: "[Breve biografía del instructor]", + image: milu, + socials: { + instagram: "", + }, + }, + { + id: "12", + name: "[Safira]", + role: "Instructor" as const, + description: "[Breve biografía del instructor]", + image: safira, + socials: { + instagram: "", + }, + }, + { + id: "13", + name: "[Braz & Romina]", + role: "Instructor" as const, + description: "[Breve biografía del instructor]", + image: brazromi, + socials: { + instagram: "", + }, + }, ]; // ---- PROGRAMA DEL EVENTO ----