feat(DirectorySlideshow): add navigation controls and pause functionality for image slideshow
This commit is contained in:
parent
290cdb8ae9
commit
096f13d926
|
|
@ -1,4 +1,5 @@
|
|||
import { useEffect, useState } from 'react';
|
||||
import { useEffect, useRef, useState } from 'react';
|
||||
import { ChevronLeft, ChevronRight, Pause, Play } from 'lucide-react';
|
||||
import { listDirectory, getWebDAVBaseUrl, type EntryInfo } from '../webdav';
|
||||
import { IMAGE_EXTS } from './MediaEntry';
|
||||
|
||||
|
|
@ -7,8 +8,11 @@ interface Props {
|
|||
}
|
||||
|
||||
export default function DirectorySlideshow({ path }: Props) {
|
||||
const [images, setImages] = useState<EntryInfo[]>([]);
|
||||
const [idx, setIdx] = useState(0);
|
||||
const [images, setImages] = useState<EntryInfo[]>([]);
|
||||
const [idx, setIdx] = useState(0);
|
||||
const [paused, setPaused] = useState(false);
|
||||
const [touched, setTouched] = useState(false);
|
||||
const touchTimer = useRef<ReturnType<typeof setTimeout> | undefined>(undefined);
|
||||
|
||||
useEffect(() => {
|
||||
if (!path) { setImages([]); return; }
|
||||
|
|
@ -26,34 +30,70 @@ export default function DirectorySlideshow({ path }: Props) {
|
|||
}, [path]);
|
||||
|
||||
useEffect(() => {
|
||||
if (images.length <= 1) return;
|
||||
if (images.length <= 1 || paused) return;
|
||||
const t = setInterval(() => setIdx(i => (i + 1) % images.length), 4000);
|
||||
return () => clearInterval(t);
|
||||
}, [images.length]);
|
||||
}, [images.length, paused]);
|
||||
|
||||
useEffect(() => () => clearTimeout(touchTimer.current), []);
|
||||
|
||||
if (images.length === 0) return null;
|
||||
|
||||
const current = images[idx];
|
||||
const prev = () => setIdx(i => (i - 1 + images.length) % images.length);
|
||||
const next = () => setIdx(i => (i + 1) % images.length);
|
||||
|
||||
const handleTouch = () => {
|
||||
setTouched(true);
|
||||
clearTimeout(touchTimer.current);
|
||||
touchTimer.current = setTimeout(() => setTouched(false), 3000);
|
||||
};
|
||||
|
||||
const controlsVisible = touched ? 'opacity-100' : 'opacity-0 group-hover:opacity-100';
|
||||
|
||||
return (
|
||||
<div className="mb-3 rounded-lg overflow-hidden">
|
||||
<img
|
||||
key={current.path}
|
||||
src={getWebDAVBaseUrl() + current.path}
|
||||
alt={current.name}
|
||||
className="w-full h-48 object-contain"
|
||||
/>
|
||||
{images.length > 1 && (
|
||||
<div className="flex justify-center gap-1.5 py-1.5">
|
||||
{images.map((_, i) => (
|
||||
<button
|
||||
key={i}
|
||||
onClick={() => setIdx(i)}
|
||||
className={`w-1.5 h-1.5 rounded-full transition-colors ${i === idx ? 'bg-white' : 'bg-white/40'}`}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
<div className="relative group h-48" onPointerDown={handleTouch}>
|
||||
<img
|
||||
key={current.path}
|
||||
src={getWebDAVBaseUrl() + current.path}
|
||||
alt={current.name}
|
||||
className="w-full h-full object-contain"
|
||||
/>
|
||||
|
||||
{images.length > 1 && (
|
||||
<>
|
||||
{/* Prev / next */}
|
||||
<div className={`absolute inset-0 flex items-center justify-between px-2 pointer-events-none transition-opacity duration-200 ${controlsVisible}`}>
|
||||
<button onClick={prev} className="pointer-events-auto p-1 rounded-full bg-black/50 text-white hover:bg-black/70">
|
||||
<ChevronLeft className="w-5 h-5" />
|
||||
</button>
|
||||
<button onClick={next} className="pointer-events-auto p-1 rounded-full bg-black/50 text-white hover:bg-black/70">
|
||||
<ChevronRight className="w-5 h-5" />
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{/* Dots + pause */}
|
||||
<div className={`absolute bottom-0 left-0 right-0 flex items-center justify-center gap-2 py-1.5 transition-opacity duration-200 ${controlsVisible}`}>
|
||||
<button
|
||||
onClick={() => setPaused(p => !p)}
|
||||
className="p-0.5 rounded-full bg-black/50 text-white hover:bg-black/70"
|
||||
>
|
||||
{paused ? <Play className="w-3 h-3" /> : <Pause className="w-3 h-3" />}
|
||||
</button>
|
||||
<div className="flex gap-1.5">
|
||||
{images.map((_, i) => (
|
||||
<button
|
||||
key={i}
|
||||
onClick={() => setIdx(i)}
|
||||
className={`w-1.5 h-1.5 rounded-full transition-colors ${i === idx ? 'bg-white' : 'bg-white/40'}`}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user