"use client"; import Image from "next/image"; import { useCallback, useEffect, useMemo, useRef, useState } from "react"; import type { CSSProperties } from "react"; // Definição do tipo para garantir 100% de compatibilidade TS export interface SliderPhoto { src: string; alt: string; id?: string | number; } const ROTATE_MS = 5500; // --- 1. COMPONENTE DE SLIDE INDIVIDUAL --- interface SlideProps { photo: SliderPhoto; onRatioLoad: (src: string, ratio: number) => void; } function PromoStripSingleSlide({ photo, onRatioLoad }: SlideProps) { return (
{photo.alt} { const target = e.currentTarget; if (target.naturalHeight > 0) { onRatioLoad(photo.src, target.naturalWidth / target.naturalHeight); } }} /> {/* Label Interna - Estilo Premium/iOS */}
Destaque
); } // --- 2. HOOK DE DADOS (Simulação de Fetch) --- function useSliderPhotos(): { photos: SliderPhoto[]; loading: boolean } { const [photos, setPhotos] = useState([]); const [loading, setLoading] = useState(true); useEffect(() => { let cancelled = false; fetch("/api/slider-photos", { cache: "no-store" }) .then((r) => (r.ok ? r.json() : [])) .then((data: unknown) => { if (cancelled) return; if (Array.isArray(data)) { const validated = data.filter( (x): x is SliderPhoto => typeof x === "object" && x !== null && "src" in x && "alt" in x ); setPhotos(validated); } }) .catch(() => { if (!cancelled) setPhotos([]); }) .finally(() => { if (!cancelled) setLoading(false); }); return () => { cancelled = true; }; }, []); return { photos, loading }; } // --- 3. COMPONENTE PRINCIPAL (O CARROSSEL) --- export function TvonePromoStrip() { const { photos, loading } = useSliderPhotos(); const [index, setIndex] = useState(0); const [ratios, setRatios] = useState>({}); const [reduceMotion, setReduceMotion] = useState(false); // Detetar preferência de movimento do sistema (iOS/MacOS/Windows) useEffect(() => { const mq = window.matchMedia("(prefers-reduced-motion: reduce)"); const sync = () => setReduceMotion(mq.matches); sync(); mq.addEventListener("change", sync); return () => mq.removeEventListener("change", sync); }, []); // Lógica de avanço automático const advance = useCallback(() => { if (photos.length <= 1) return; setIndex((i: number) => (i + 1) % photos.length); }, [photos.length]); useEffect(() => { if (photos.length <= 1 || reduceMotion) return; const id = window.setInterval(advance, ROTATE_MS); return () => window.clearInterval(id); }, [photos.length, reduceMotion, advance]); // Handler para atualizar rácios de imagem const handleRatio = useCallback((src: string, ratio: number) => { setRatios((prev) => (prev[src] === ratio ? prev : { ...prev, [src]: ratio })); }, []); // Calcula a proporção do contentor baseado no slide ativo const currentRatio = useMemo(() => { const activePhoto = photos[index]; return activePhoto ? (ratios[activePhoto.src] || 16 / 9) : 16 / 9; }, [photos, index, ratios]); if (loading && photos.length === 0) { return
; } if (photos.length === 0) return null; return (
{/* O "ROLO" (A fita flexível) */}
{photos.map((photo: SliderPhoto, i: number) => (
))}
{/* INDICADORES (iOS Dot Style) */} {photos.length > 1 && (
{photos.map((_, i: number) => (
)}
); }