mirror of
https://github.com/PeterMaquiran/tvone.git
synced 2026-04-18 15:27:52 +00:00
This commit is contained in:
@@ -1,7 +1,8 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import Image from "next/image";
|
import Image from "next/image";
|
||||||
import { useCallback, useEffect, useState } from "react";
|
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
||||||
|
import type { CSSProperties } from "react";
|
||||||
|
|
||||||
import type { SliderPhoto } from "@/lib/slider-photos";
|
import type { SliderPhoto } from "@/lib/slider-photos";
|
||||||
|
|
||||||
@@ -14,10 +15,47 @@ function PromoStripSingleSlide({
|
|||||||
photos: SliderPhoto[];
|
photos: SliderPhoto[];
|
||||||
activeIndex: number;
|
activeIndex: number;
|
||||||
}) {
|
}) {
|
||||||
|
const [ratioBySrc, setRatioBySrc] = useState<Record<string, number>>({});
|
||||||
|
const lastKnownRatioRef = useRef(16 / 9);
|
||||||
|
|
||||||
|
const active = photos[activeIndex];
|
||||||
|
const activeRatio = active ? ratioBySrc[active.src] : undefined;
|
||||||
|
|
||||||
|
const displayRatio =
|
||||||
|
activeRatio != null && activeRatio > 0 ? activeRatio : photos.length > 0 ? lastKnownRatioRef.current : undefined;
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (activeRatio != null && activeRatio > 0) {
|
||||||
|
lastKnownRatioRef.current = activeRatio;
|
||||||
|
}
|
||||||
|
}, [activeRatio]);
|
||||||
|
|
||||||
|
const containerStyle = useMemo((): CSSProperties => {
|
||||||
|
if (photos.length === 0) {
|
||||||
|
return { minHeight: "10rem" };
|
||||||
|
}
|
||||||
|
if (displayRatio != null) {
|
||||||
|
return { aspectRatio: displayRatio };
|
||||||
|
}
|
||||||
|
return { minHeight: "10rem" };
|
||||||
|
}, [photos.length, displayRatio]);
|
||||||
|
|
||||||
|
const handleImageLoad = useCallback((src: string, naturalWidth: number, naturalHeight: number) => {
|
||||||
|
if (naturalHeight <= 0) return;
|
||||||
|
setRatioBySrc((prev) => {
|
||||||
|
const next = naturalWidth / naturalHeight;
|
||||||
|
if (prev[src] === next) return prev;
|
||||||
|
return { ...prev, [src]: next };
|
||||||
|
});
|
||||||
|
}, []);
|
||||||
|
|
||||||
const n = photos.length;
|
const n = photos.length;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="relative min-h-[140px] w-full sm:min-h-[168px] md:min-h-[190px] lg:min-h-[210px]">
|
<div
|
||||||
|
className="relative w-full bg-neutral-100 dark:bg-neutral-900"
|
||||||
|
style={containerStyle}
|
||||||
|
>
|
||||||
{n > 0 ? (
|
{n > 0 ? (
|
||||||
photos.map((photo, i) => (
|
photos.map((photo, i) => (
|
||||||
<Image
|
<Image
|
||||||
@@ -25,11 +63,13 @@ function PromoStripSingleSlide({
|
|||||||
src={photo.src}
|
src={photo.src}
|
||||||
alt={photo.alt}
|
alt={photo.alt}
|
||||||
fill
|
fill
|
||||||
|
priority={i === 0}
|
||||||
className={`object-cover object-center transition-opacity duration-[900ms] ease-in-out ${
|
className={`object-cover object-center transition-opacity duration-[900ms] ease-in-out ${
|
||||||
i === activeIndex ? "z-[1] opacity-100" : "pointer-events-none z-0 opacity-0"
|
i === activeIndex ? "z-[1] opacity-100" : "pointer-events-none z-0 opacity-0"
|
||||||
}`}
|
}`}
|
||||||
sizes="100vw"
|
sizes="100vw"
|
||||||
aria-hidden={i !== activeIndex}
|
aria-hidden={i !== activeIndex}
|
||||||
|
onLoad={(e) => handleImageLoad(photo.src, e.currentTarget.naturalWidth, e.currentTarget.naturalHeight)}
|
||||||
/>
|
/>
|
||||||
))
|
))
|
||||||
) : (
|
) : (
|
||||||
@@ -79,7 +119,7 @@ function useSliderPhotos(): { photos: SliderPhoto[]; loading: boolean } {
|
|||||||
return { photos, loading };
|
return { photos, loading };
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Slider: one image at a time from `public/slider` (see `lib/slider-photos.ts`). */
|
/** Slider: one image at a time; container height follows each image aspect ratio (no fixed empty band). */
|
||||||
export function TvonePromoStrip() {
|
export function TvonePromoStrip() {
|
||||||
const { photos, loading } = useSliderPhotos();
|
const { photos, loading } = useSliderPhotos();
|
||||||
const [index, setIndex] = useState(0);
|
const [index, setIndex] = useState(0);
|
||||||
@@ -122,7 +162,7 @@ export function TvonePromoStrip() {
|
|||||||
{current ? current.alt : loading ? "A carregar" : "Sem imagens"}
|
{current ? current.alt : loading ? "A carregar" : "Sem imagens"}
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
<div className="relative flex min-h-[140px] w-full flex-col sm:min-h-[168px] md:min-h-[190px] lg:min-h-[210px]">
|
<div className="relative w-full">
|
||||||
<PromoStripSingleSlide photos={photos} activeIndex={index} />
|
<PromoStripSingleSlide photos={photos} activeIndex={index} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user