/* NEKTAR — Section 02 · Le lieu
Deux modes : défilement (slideshow) + éditorial (photos décalées crème) */
const { useState, useEffect, useRef, useCallback } = React;
const TWEAK_DEFAULTS = { mode: "défilement" };
/* Photos — 4 sélectionnées pour l'éditorial, 8 pour le défilement */
const PHOTO_IDS = [
'1TEstUhVD3N47mYYylKHWpY4880UyyWpV', // LG1_9285
'1rMY-385QRZlJoUXb45qouWsjAAZqNe8A', // LG1_9363
'1urxtR7pHDDTrQ2PRgSr2SJ1d4OUfq_LW', // LG1_9379
'1MImo3cywdkv19Tt9aR45jW3FghqEUMny', // LG1_9383
'1x_gKGYXKSa4Aox4DNrn3rDrIZlRqXm5b', // LG1_9389
'1_SEoQHJaVdQiMMCWrqWzarTE7p_IiESf', // LG1_9430
'1fywRqv_02oeApJearFWXj11RoKhti14R', // LG1_9440
'1MfJ-e4ST1sH7hchWSmnKJPFWQi_M12-0', // LG1_9463
];
const PHOTOS = PHOTO_IDS.map(id =>
`https://drive.google.com/thumbnail?id=${id}&sz=w2400`
);
const AUTO_DELAY = 3000; // 3s entre chaque slide
const RESUME_DELAY = 4000; // ms avant reprise auto après action manuelle
/* -------------------------------------------------------
SVG Arrows — 1px or, no fill (design system)
------------------------------------------------------- */
function ArrowLeft() {
return (
);
}
function ArrowRight() {
return (
);
}
/* -------------------------------------------------------
Gallery
------------------------------------------------------- */
function Gallery() {
const [current, setCurrent] = useState(0);
const [paused, setPaused] = useState(false);
const [progKey, setProgKey] = useState(0);
const total = PHOTOS.length;
const autoRef = useRef(null);
const resumeRef = useRef(null);
const touchX = useRef(null);
/* Navigate to slide index */
const goTo = useCallback((idx) => {
setCurrent(((idx % total) + total) % total);
setProgKey(k => k + 1);
}, [total]);
const goNext = useCallback(() => goTo(current + 1), [goTo, current]);
const goPrev = useCallback(() => goTo(current - 1), [goTo, current]);
/* Manual action — pause, navigate, schedule resume */
const manual = useCallback((fn) => {
setPaused(true);
clearTimeout(resumeRef.current);
fn();
resumeRef.current = setTimeout(() => setPaused(false), RESUME_DELAY);
}, []);
/* Auto-advance */
useEffect(() => {
if (paused) { clearInterval(autoRef.current); return; }
autoRef.current = setInterval(() => {
setCurrent(c => (c + 1) % total);
setProgKey(k => k + 1);
}, AUTO_DELAY);
return () => clearInterval(autoRef.current);
}, [paused, total]);
/* Keyboard ← → */
useEffect(() => {
const onKey = (e) => {
if (e.key === 'ArrowLeft') manual(goPrev);
if (e.key === 'ArrowRight') manual(goNext);
};
window.addEventListener('keydown', onKey);
return () => window.removeEventListener('keydown', onKey);
}, [manual, goPrev, goNext]);
/* Touch swipe */
const onTouchStart = (e) => { touchX.current = e.touches[0].clientX; };
const onTouchEnd = (e) => {
if (touchX.current === null) return;
const dx = e.changedTouches[0].clientX - touchX.current;
touchX.current = null;
if (Math.abs(dx) < 44) return; // ignore micro-taps
manual(dx < 0 ? goNext : goPrev);
};
return (
setPaused(true)}
onMouseLeave={() => setPaused(false)}
onTouchStart={onTouchStart}
onTouchEnd={onTouchEnd}
>
{/* Slides */}
{PHOTOS.map((url, i) => (
))}
{/* Flèche gauche */}
{/* Flèche droite */}
{/* Label "GALERIE" centré sur la photo */}
Galerie
{/* Dots de pagination — centrés en bas, or */}
{PHOTOS.map((_, i) => (
{/* Barre de progression — key force le reset à chaque changement */}
);
}
/* -------------------------------------------------------
Editorial Carousel — paires décalées, flèches or, auto 3s
------------------------------------------------------- */
function EditorialCarousel() {
// Construire les paires : [photos 0+1], [photos 2+3], [photos 4+5], [photos 6+7]
const PAIRS = [];
for (let i = 0; i < PHOTO_IDS.length - 1; i += 2) {
PAIRS.push([
`https://drive.google.com/thumbnail?id=${PHOTO_IDS[i]}&sz=w2400`,
`https://drive.google.com/thumbnail?id=${PHOTO_IDS[i + 1]}&sz=w2400`,
]);
}
const [current, setCurrent] = useState(0);
const [paused, setPaused] = useState(false);
const [progKey, setProgKey] = useState(0);
const total = PAIRS.length;
const autoRef = useRef(null);
const resumeRef = useRef(null);
const goTo = useCallback((idx) => {
setCurrent(((idx % total) + total) % total);
setProgKey(k => k + 1);
}, [total]);
const goNext = useCallback(() => goTo(current + 1), [goTo, current]);
const goPrev = useCallback(() => goTo(current - 1), [goTo, current]);
const manual = useCallback((fn) => {
setPaused(true);
clearTimeout(resumeRef.current);
fn();
resumeRef.current = setTimeout(() => setPaused(false), 4000);
}, []);
// Auto-avance 3s
useEffect(() => {
if (paused) { clearInterval(autoRef.current); return; }
autoRef.current = setInterval(() => {
setCurrent(c => (c + 1) % total);
setProgKey(k => k + 1);
}, 3000);
return () => clearInterval(autoRef.current);
}, [paused, total]);
// Clavier ← →
useEffect(() => {
const onKey = (e) => {
if (e.key === 'ArrowLeft') manual(goPrev);
if (e.key === 'ArrowRight') manual(goNext);
};
window.addEventListener('keydown', onKey);
return () => window.removeEventListener('keydown', onKey);
}, [manual, goPrev, goNext]);
const [leftUrl, rightUrl] = PAIRS[current];
return (
setPaused(true)}
onMouseLeave={() => setPaused(false)}
>
{/* Paire courante — fade in à chaque changement */}
{/* Flèche gauche */}
{/* Flèche droite */}
{/* Barre de progression */}
);
}
/* -------------------------------------------------------
App
------------------------------------------------------- */
function App() {
const [t, setTweak] = useTweaks(TWEAK_DEFAULTS);
const isEditorial = t.mode === "éditorial";
return (
{/* Mode éditorial — desktop uniquement */}
{isEditorial && (
)}
{/* Mode défilement — toujours sur mobile, optionnel sur desktop */}
setTweak("mode", v)}
/>
);
}
ReactDOM.createRoot(document.getElementById('root')).render();