@amsterdam/design-system-react
Version:
All React components from the Amsterdam Design System. Use it to compose pages in your website or application.
98 lines (97 loc) • 5.54 kB
JavaScript
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
import { clsx } from 'clsx';
import { forwardRef, useCallback, useEffect, useMemo, useRef, useState } from 'react';
import { Image } from '../Image/Image';
import { ImageSliderContext } from './ImageSliderContext';
import { ImageSliderControls } from './ImageSliderControls';
import { ImageSliderItem } from './ImageSliderItem';
import { ImageSliderScroller } from './ImageSliderScroller';
import { ImageSliderThumbnails } from './ImageSliderThumbnails';
export const ImageSliderRoot = forwardRef(({ className, controls, imageLabel = 'Afbeelding', images, nextLabel = 'Volgende', previousLabel = 'Vorige', ...restProps }, ref) => {
const [currentSlideId, setCurrentSlideId] = useState(0);
const [isAtStart, setIsAtStart] = useState(true);
const [isAtEnd, setIsAtEnd] = useState(false);
const targetRef = useRef(null);
const observerRef = useRef(null);
const inView = useCallback((observations) => {
const images = Array.from(targetRef.current?.children || []);
observations.forEach((observation) => {
if (observation.isIntersecting) {
setCurrentSlideId(images.indexOf(observation.target));
}
});
}, []);
const observerOptions = useMemo(() => ({
root: targetRef.current,
threshold: 0.6,
}), []);
const updateControls = useCallback(() => {
const sliderScrollerElement = targetRef.current;
if (!sliderScrollerElement)
return;
const { firstElementChild: firstElement, lastElementChild: lastElement } = sliderScrollerElement;
setIsAtStart(firstElement === sliderScrollerElement?.children[currentSlideId]);
setIsAtEnd(lastElement === sliderScrollerElement?.children[currentSlideId]);
}, [currentSlideId]);
useEffect(() => {
if (targetRef.current) {
observerRef.current = new IntersectionObserver(inView, observerOptions);
const observer = observerRef.current;
const slides = Array.from(targetRef.current.children);
slides.forEach((slide) => observer.observe(slide));
targetRef.current.addEventListener('scrollend', synchronise);
updateControls();
return () => {
slides.forEach((slide) => observer.unobserve(slide));
targetRef.current?.removeEventListener('scrollend', synchronise);
};
}
return undefined;
}, [inView, observerOptions, updateControls]);
const synchronise = useCallback(() => updateControls(), [updateControls]);
const goToSlide = useCallback((element) => {
const sliderScrollerElement = targetRef.current;
if (!sliderScrollerElement || !element)
return;
sliderScrollerElement.scrollTo({
left: sliderScrollerElement.scrollLeft + element.offsetLeft - sliderScrollerElement.scrollLeft,
});
}, []);
const goToSlideId = useCallback((id) => {
const element = targetRef.current?.children[id];
if (element)
goToSlide(element);
}, [goToSlide]);
const goToNextSlide = useCallback(() => {
const element = targetRef.current?.children[currentSlideId];
const nextElement = element?.nextElementSibling;
if (nextElement)
goToSlide(nextElement);
}, [currentSlideId, goToSlide]);
const goToPreviousSlide = useCallback(() => {
const element = targetRef.current?.children[currentSlideId];
const previousElement = element?.previousElementSibling;
if (previousElement)
goToSlide(previousElement);
}, [currentSlideId, goToSlide]);
useEffect(() => {
const handleResize = () => {
const sliderScrollerElement = targetRef.current;
const currentSlideElement = targetRef.current?.children[currentSlideId];
if (!sliderScrollerElement || !currentSlideElement)
return;
const expectedScrollLeft = currentSlideElement.offsetLeft;
if (Math.abs(sliderScrollerElement.scrollLeft - expectedScrollLeft) < 1)
return;
goToSlide(currentSlideElement);
};
window.addEventListener('resize', handleResize);
return () => window.removeEventListener('resize', handleResize);
}, [currentSlideId, goToSlide]);
return (_jsx(ImageSliderContext.Provider, { value: { currentSlideId, goToNextSlide, goToPreviousSlide, goToSlideId, isAtEnd, isAtStart }, children: _jsxs("div", { ...restProps, "aria-roledescription": "carousel", className: clsx('ams-image-slider', controls && 'ams-image-slider--controls', className), ref: ref, tabIndex: -1, children: [controls && _jsx(ImageSliderControls, { nextLabel: nextLabel, previousLabel: previousLabel }), _jsx(ImageSliderScroller, { "aria-live": "polite", ref: targetRef, role: "group", tabIndex: 0, children: images.map(({ alt, aspectRatio, sizes, src, srcSet }, index) => (_jsx(ImageSliderItem, { slideId: index, children: _jsx(Image, { alt: alt, aspectRatio: aspectRatio, sizes: sizes, src: src, srcSet: srcSet }) }, index))) }), _jsx(ImageSliderThumbnails, { imageLabel: imageLabel, thumbnails: images })] }) }));
});
ImageSliderRoot.displayName = 'ImageSlider';
/**
* @see {@link https://designsystem.amsterdam/?path=/docs/components-media-image-slider--docs Image Slider docs at Amsterdam Design System}
*/
export const ImageSlider = Object.assign(ImageSliderRoot, { Item: ImageSliderItem });