feat: carousel keyboard keys

This commit is contained in:
trafficlunar 2025-04-18 16:54:38 +01:00
parent 10f1fc2fb3
commit 63f82c3c18
2 changed files with 51 additions and 1 deletions

View file

@ -15,6 +15,7 @@ export default function Carousel({ images, className }: Props) {
const [emblaRef, emblaApi] = useEmblaCarousel(); const [emblaRef, emblaApi] = useEmblaCarousel();
const [selectedIndex, setSelectedIndex] = useState(0); const [selectedIndex, setSelectedIndex] = useState(0);
const [scrollSnaps, setScrollSnaps] = useState<number[]>([]); const [scrollSnaps, setScrollSnaps] = useState<number[]>([]);
const [isFocused, setIsFocused] = useState(false);
useEffect(() => { useEffect(() => {
if (!emblaApi) return; if (!emblaApi) return;
@ -22,8 +23,31 @@ export default function Carousel({ images, className }: Props) {
emblaApi.on("select", () => setSelectedIndex(emblaApi.selectedScrollSnap())); emblaApi.on("select", () => setSelectedIndex(emblaApi.selectedScrollSnap()));
}, [emblaApi]); }, [emblaApi]);
// Handle keyboard events
useEffect(() => {
if (!isFocused || !emblaApi) return;
const handleKeyDown = (event: KeyboardEvent) => {
switch (event.key) {
case "ArrowLeft":
emblaApi.scrollPrev();
break;
case "ArrowRight":
emblaApi.scrollNext();
break;
default:
break;
}
};
window.addEventListener("keydown", handleKeyDown);
return () => {
window.removeEventListener("keydown", handleKeyDown);
};
}, [isFocused, emblaApi]);
return ( return (
<div className="relative w-full h-fit"> <div className="relative w-full h-fit" tabIndex={0} onMouseEnter={() => setIsFocused(true)} onMouseLeave={() => setIsFocused(false)}>
<div className={`overflow-hidden rounded-xl bg-zinc-300 border-2 border-zinc-300 ${className ?? ""}`} ref={emblaRef}> <div className={`overflow-hidden rounded-xl bg-zinc-300 border-2 border-zinc-300 ${className ?? ""}`} ref={emblaRef}>
<div className="flex"> <div className="flex">
{images.map((src, index) => ( {images.map((src, index) => (

View file

@ -53,6 +53,32 @@ export default function ImageViewer({ src, alt, width, height, className, images
emblaApi.on("select", () => setSelectedIndex(emblaApi.selectedScrollSnap())); emblaApi.on("select", () => setSelectedIndex(emblaApi.selectedScrollSnap()));
}, [emblaApi, images, src]); }, [emblaApi, images, src]);
// Handle keyboard events
useEffect(() => {
if (!isOpen || !emblaApi) return;
const handleKeyDown = (event: KeyboardEvent) => {
switch (event.key) {
case "ArrowLeft":
emblaApi.scrollPrev();
break;
case "ArrowRight":
emblaApi.scrollNext();
break;
case "Escape":
close();
break;
default:
break;
}
};
window.addEventListener("keydown", handleKeyDown);
return () => {
window.removeEventListener("keydown", handleKeyDown);
};
}, [isOpen, emblaApi]);
return ( return (
<> <>
<Image src={src} alt={alt} width={width} height={height} className={`cursor-pointer ${className}`} onClick={() => setIsOpen(true)} /> <Image src={src} alt={alt} width={width} height={height} className={`cursor-pointer ${className}`} onClick={() => setIsOpen(true)} />