"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 (
{
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) => (
)}
);
}