@amsterdam/design-system-react 3.0.0 → 3.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (55) hide show
  1. package/dist/Accordion/Accordion.js +1 -1
  2. package/dist/Accordion/AccordionContext.d.ts +1 -2
  3. package/dist/Accordion/AccordionContext.js +3 -4
  4. package/dist/Accordion/AccordionSection.js +1 -1
  5. package/dist/Badge/Badge.d.ts +5 -0
  6. package/dist/Badge/Badge.js +3 -2
  7. package/dist/Checkbox/Checkbox.d.ts +5 -4
  8. package/dist/ImageSlider/ImageSlider.d.ts +3 -22
  9. package/dist/ImageSlider/ImageSlider.js +31 -86
  10. package/dist/ImageSlider/ImageSliderControl.d.ts +2 -0
  11. package/dist/ImageSlider/ImageSliderControl.js +5 -0
  12. package/dist/ImageSlider/ImageSliderThumbnails.d.ts +5 -6
  13. package/dist/ImageSlider/ImageSliderThumbnails.js +7 -12
  14. package/dist/ImageSlider/index.d.ts +0 -1
  15. package/dist/ImageSlider/utils/debounce.d.ts +1 -0
  16. package/dist/ImageSlider/utils/debounce.js +8 -0
  17. package/dist/ImageSlider/utils/index.d.ts +4 -0
  18. package/dist/ImageSlider/utils/index.js +4 -0
  19. package/dist/ImageSlider/utils/scrollToCurrentSlideOnResize.d.ts +7 -0
  20. package/dist/ImageSlider/utils/scrollToCurrentSlideOnResize.js +11 -0
  21. package/dist/ImageSlider/utils/scrollToSlide.d.ts +2 -0
  22. package/dist/ImageSlider/utils/scrollToSlide.js +9 -0
  23. package/dist/ImageSlider/utils/setCurrentSlideIdToVisibleSlide.d.ts +8 -0
  24. package/dist/ImageSlider/utils/setCurrentSlideIdToVisibleSlide.js +10 -0
  25. package/dist/PageHeader/PageHeader.d.ts +6 -1
  26. package/dist/PageHeader/PageHeader.js +2 -2
  27. package/dist/ProgressList/ProgressList.d.ts +43 -0
  28. package/dist/ProgressList/ProgressList.js +19 -0
  29. package/dist/ProgressList/ProgressListContext.d.ts +10 -0
  30. package/dist/ProgressList/ProgressListContext.js +11 -0
  31. package/dist/ProgressList/ProgressListStep.d.ts +23 -0
  32. package/dist/ProgressList/ProgressListStep.js +12 -0
  33. package/dist/ProgressList/ProgressListSubstep.d.ts +16 -0
  34. package/dist/ProgressList/ProgressListSubstep.js +7 -0
  35. package/dist/ProgressList/ProgressListSubsteps.d.ts +9 -0
  36. package/dist/ProgressList/ProgressListSubsteps.js +5 -0
  37. package/dist/ProgressList/index.d.ts +9 -0
  38. package/dist/ProgressList/index.js +5 -0
  39. package/dist/Radio/Radio.d.ts +5 -4
  40. package/dist/index.cjs.js +671 -632
  41. package/dist/index.cjs.js.map +1 -1
  42. package/dist/index.d.ts +107 -57
  43. package/dist/index.esm.js +673 -635
  44. package/dist/index.esm.js.map +1 -1
  45. package/dist/index.js +1 -0
  46. package/dist/tsconfig.build.tsbuildinfo +1 -1
  47. package/package.json +9 -9
  48. package/dist/ImageSlider/ImageSliderContext.d.ts +0 -13
  49. package/dist/ImageSlider/ImageSliderContext.js +0 -14
  50. package/dist/ImageSlider/ImageSliderControls.d.ts +0 -17
  51. package/dist/ImageSlider/ImageSliderControls.js +0 -13
  52. package/dist/ImageSlider/ImageSliderItem.d.ts +0 -15
  53. package/dist/ImageSlider/ImageSliderItem.js +0 -11
  54. package/dist/ImageSlider/ImageSliderScroller.d.ts +0 -9
  55. package/dist/ImageSlider/ImageSliderScroller.js +0 -5
@@ -2,7 +2,7 @@ import { jsx as _jsx } from "react/jsx-runtime";
2
2
  import { clsx } from 'clsx';
3
3
  import { forwardRef, useImperativeHandle, useRef } from 'react';
4
4
  import { useKeyboardFocus } from '../common/useKeyboardFocus';
5
- import AccordionContext from './AccordionContext';
5
+ import { AccordionContext } from './AccordionContext';
6
6
  import { AccordionSection } from './AccordionSection';
7
7
  const AccordionRoot = forwardRef(({ children, className, headingLevel, sectionAs = 'section' }, ref) => {
8
8
  const innerRef = useRef(null);
@@ -7,5 +7,4 @@ export type AccordionContextValue = {
7
7
  headingLevel: AccordionProps['headingLevel'];
8
8
  sectionAs?: AccordionProps['sectionAs'];
9
9
  };
10
- declare const AccordionContext: import("react").Context<AccordionContextValue>;
11
- export default AccordionContext;
10
+ export declare const AccordionContext: import("react").Context<AccordionContextValue>;
@@ -4,9 +4,8 @@
4
4
  */
5
5
  import { createContext } from 'react';
6
6
  const defaultValues = {
7
- // Level 2 is set here, but it is never used.
8
- // headingLevel is a required prop in Accordion, which always overwrites it.
7
+ // Default value for type safety.
8
+ // The actual value is always provided via Accordion’s required headingLevel prop.
9
9
  headingLevel: 2,
10
10
  };
11
- const AccordionContext = createContext(defaultValues);
12
- export default AccordionContext;
11
+ export const AccordionContext = createContext(defaultValues);
@@ -4,7 +4,7 @@ import { clsx } from 'clsx';
4
4
  import { forwardRef, useContext, useId, useState } from 'react';
5
5
  import { Heading } from '../Heading';
6
6
  import { Icon } from '../Icon/Icon';
7
- import AccordionContext from './AccordionContext';
7
+ import { AccordionContext } from './AccordionContext';
8
8
  // The 'ams-accordion__header' class is @deprecated and will be removed in a future release.
9
9
  export const AccordionSection = forwardRef(({ children, className, expanded = false, label, ...restProps }, ref) => {
10
10
  const { headingLevel, sectionAs } = useContext(AccordionContext);
@@ -3,11 +3,14 @@
3
3
  * Copyright Gemeente Amsterdam
4
4
  */
5
5
  import type { HTMLAttributes } from 'react';
6
+ import type { IconProps } from '../Icon';
6
7
  export declare const badgeColors: readonly ["azure", "lime", "magenta", "orange", "purple", "red", "yellow"];
7
8
  type BadgeColor = (typeof badgeColors)[number];
8
9
  export type BadgeProps = {
9
10
  /** The background colour. */
10
11
  color?: BadgeColor;
12
+ /** The icon to show before the badge text. */
13
+ icon?: IconProps['svg'];
11
14
  /** The text content. */
12
15
  label: string | number;
13
16
  } & HTMLAttributes<HTMLElement>;
@@ -17,6 +20,8 @@ export type BadgeProps = {
17
20
  export declare const Badge: import("react").ForwardRefExoticComponent<{
18
21
  /** The background colour. */
19
22
  color?: BadgeColor;
23
+ /** The icon to show before the badge text. */
24
+ icon?: IconProps["svg"];
20
25
  /** The text content. */
21
26
  label: string | number;
22
27
  } & HTMLAttributes<HTMLElement> & import("react").RefAttributes<HTMLElement>>;
@@ -1,9 +1,10 @@
1
- import { jsx as _jsx } from "react/jsx-runtime";
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { clsx } from 'clsx';
3
3
  import { forwardRef } from 'react';
4
+ import { Icon } from '../Icon';
4
5
  export const badgeColors = ['azure', 'lime', 'magenta', 'orange', 'purple', 'red', 'yellow'];
5
6
  /**
6
7
  * @see {@link https://designsystem.amsterdam/?path=/docs/components-feedback-badge--docs Badge docs at Amsterdam Design System}
7
8
  */
8
- export const Badge = forwardRef(({ className, color, label, ...restProps }, ref) => (_jsx("span", { ...restProps, className: clsx('ams-badge', color && `ams-badge--${color}`, className), ref: ref, children: label })));
9
+ export const Badge = forwardRef(({ className, color, icon, label, ...restProps }, ref) => (_jsxs("span", { ...restProps, className: clsx('ams-badge', color && `ams-badge--${color}`, className), ref: ref, children: [icon && _jsx(Icon, { svg: icon }), label] })));
9
10
  Badge.displayName = 'Badge';
@@ -2,13 +2,14 @@
2
2
  * @license EUPL-1.2+
3
3
  * Copyright Gemeente Amsterdam
4
4
  */
5
- import type { InputHTMLAttributes, PropsWithChildren, ReactNode } from 'react';
5
+ import type { InputHTMLAttributes, PropsWithChildren } from 'react';
6
+ import type { IconProps } from '../Icon';
6
7
  export type CheckboxProps = {
7
8
  /**
8
9
  * An icon to display instead of the default icon.
9
10
  * @default CheckboxIcon
10
11
  */
11
- icon?: Function | ReactNode;
12
+ icon?: IconProps['svg'];
12
13
  /** Allows being neither checked nor unchecked. */
13
14
  indeterminate?: boolean;
14
15
  /** Whether the value fails a validation rule. */
@@ -22,11 +23,11 @@ export declare const Checkbox: import("react").ForwardRefExoticComponent<{
22
23
  * An icon to display instead of the default icon.
23
24
  * @default CheckboxIcon
24
25
  */
25
- icon?: Function | ReactNode;
26
+ icon?: IconProps["svg"];
26
27
  /** Allows being neither checked nor unchecked. */
27
28
  indeterminate?: boolean;
28
29
  /** Whether the value fails a validation rule. */
29
30
  invalid?: boolean;
30
31
  } & Omit<InputHTMLAttributes<HTMLInputElement>, "aria-invalid" | "type"> & {
31
- children?: ReactNode | undefined;
32
+ children?: import("react").ReactNode | undefined;
32
33
  } & import("react").RefAttributes<HTMLInputElement>>;
@@ -4,31 +4,18 @@
4
4
  */
5
5
  import type { HTMLAttributes } from 'react';
6
6
  import type { ImageProps } from '../Image/Image';
7
- export type ImageSliderImageProps = ImageProps;
8
7
  export type ImageSliderProps = {
9
8
  /** Display buttons to navigate to the previous or next image. */
10
9
  controls?: boolean;
11
10
  /** Label for the image if you need to translate the alt text. */
12
11
  imageLabel?: string;
13
12
  /** The set of images to display. */
14
- images: ImageSliderImageProps[];
13
+ images: ImageProps[];
15
14
  /** The label for the ‘next’ button */
16
15
  nextLabel?: string;
17
16
  /** The label for the ‘previous’ button */
18
17
  previousLabel?: string;
19
18
  } & HTMLAttributes<HTMLDivElement>;
20
- export declare const ImageSliderRoot: import("react").ForwardRefExoticComponent<{
21
- /** Display buttons to navigate to the previous or next image. */
22
- controls?: boolean;
23
- /** Label for the image if you need to translate the alt text. */
24
- imageLabel?: string;
25
- /** The set of images to display. */
26
- images: ImageSliderImageProps[];
27
- /** The label for the ‘next’ button */
28
- nextLabel?: string;
29
- /** The label for the ‘previous’ button */
30
- previousLabel?: string;
31
- } & HTMLAttributes<HTMLDivElement> & import("react").RefAttributes<HTMLDivElement>>;
32
19
  /**
33
20
  * @see {@link https://designsystem.amsterdam/?path=/docs/components-media-image-slider--docs Image Slider docs at Amsterdam Design System}
34
21
  */
@@ -38,15 +25,9 @@ export declare const ImageSlider: import("react").ForwardRefExoticComponent<{
38
25
  /** Label for the image if you need to translate the alt text. */
39
26
  imageLabel?: string;
40
27
  /** The set of images to display. */
41
- images: ImageSliderImageProps[];
28
+ images: ImageProps[];
42
29
  /** The label for the ‘next’ button */
43
30
  nextLabel?: string;
44
31
  /** The label for the ‘previous’ button */
45
32
  previousLabel?: string;
46
- } & HTMLAttributes<HTMLDivElement> & import("react").RefAttributes<HTMLDivElement>> & {
47
- Item: import("react").ForwardRefExoticComponent<{
48
- slideId: number;
49
- } & HTMLAttributes<HTMLDivElement> & {
50
- children?: import("react").ReactNode | undefined;
51
- } & import("react").RefAttributes<HTMLDivElement>>;
52
- };
33
+ } & HTMLAttributes<HTMLDivElement> & import("react").RefAttributes<HTMLDivElement>>;
@@ -1,97 +1,42 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { ChevronBackwardIcon, ChevronForwardIcon } from '@amsterdam/design-system-react-icons';
2
3
  import { clsx } from 'clsx';
3
- import { forwardRef, useCallback, useEffect, useMemo, useRef, useState } from 'react';
4
+ import { forwardRef, useEffect, useRef, useState } from 'react';
4
5
  import { Image } from '../Image/Image';
5
- import { ImageSliderContext } from './ImageSliderContext';
6
- import { ImageSliderControls } from './ImageSliderControls';
7
- import { ImageSliderItem } from './ImageSliderItem';
8
- import { ImageSliderScroller } from './ImageSliderScroller';
6
+ import { ImageSliderControl } from './ImageSliderControl';
9
7
  import { ImageSliderThumbnails } from './ImageSliderThumbnails';
10
- export const ImageSliderRoot = forwardRef(({ className, controls, imageLabel = 'Afbeelding', images, nextLabel = 'Volgende', previousLabel = 'Vorige', ...restProps }, ref) => {
8
+ import { debounce, scrollToCurrentSlideOnResize, scrollToSlide, setCurrentSlideIdToVisibleSlide } from './utils';
9
+ /**
10
+ * @see {@link https://designsystem.amsterdam/?path=/docs/components-media-image-slider--docs Image Slider docs at Amsterdam Design System}
11
+ */
12
+ export const ImageSlider = forwardRef(({ className, controls, imageLabel = 'Afbeelding', images, nextLabel = 'Volgende', previousLabel = 'Vorige', ...restProps }, ref) => {
13
+ if (images.length === 0)
14
+ return null;
11
15
  const [currentSlideId, setCurrentSlideId] = useState(0);
12
- const [isAtStart, setIsAtStart] = useState(true);
13
- const [isAtEnd, setIsAtEnd] = useState(false);
14
- const targetRef = useRef(null);
15
- const observerRef = useRef(null);
16
- const inView = useCallback((observations) => {
17
- const images = Array.from(targetRef.current?.children || []);
18
- observations.forEach((observation) => {
19
- if (observation.isIntersecting) {
20
- setCurrentSlideId(images.indexOf(observation.target));
21
- }
22
- });
23
- }, []);
24
- const observerOptions = useMemo(() => ({
25
- root: targetRef.current,
26
- threshold: 0.6,
27
- }), []);
28
- const updateControls = useCallback(() => {
29
- const sliderScrollerElement = targetRef.current;
30
- if (!sliderScrollerElement)
31
- return;
32
- const { firstElementChild: firstElement, lastElementChild: lastElement } = sliderScrollerElement;
33
- setIsAtStart(firstElement === sliderScrollerElement?.children[currentSlideId]);
34
- setIsAtEnd(lastElement === sliderScrollerElement?.children[currentSlideId]);
35
- }, [currentSlideId]);
16
+ const scrollerRef = useRef(null);
17
+ const isAtStart = currentSlideId === 0;
18
+ const isAtEnd = currentSlideId === images.length - 1;
36
19
  useEffect(() => {
37
- if (targetRef.current) {
38
- observerRef.current = new IntersectionObserver(inView, observerOptions);
39
- const observer = observerRef.current;
40
- const slides = Array.from(targetRef.current.children);
41
- slides.forEach((slide) => observer.observe(slide));
42
- targetRef.current.addEventListener('scrollend', synchronise);
43
- updateControls();
44
- return () => {
45
- slides.forEach((slide) => observer.unobserve(slide));
46
- targetRef.current?.removeEventListener('scrollend', synchronise);
47
- };
48
- }
49
- return undefined;
50
- }, [inView, observerOptions, updateControls]);
51
- const synchronise = useCallback(() => updateControls(), [updateControls]);
52
- const goToSlide = useCallback((element) => {
53
- const sliderScrollerElement = targetRef.current;
54
- if (!sliderScrollerElement || !element)
55
- return;
56
- sliderScrollerElement.scrollTo({
57
- left: element.offsetLeft,
58
- });
20
+ if (!scrollerRef.current)
21
+ return undefined;
22
+ const observerOptions = {
23
+ root: scrollerRef.current,
24
+ threshold: 0.6,
25
+ };
26
+ const observer = new IntersectionObserver((observations) => setCurrentSlideIdToVisibleSlide({ observations, ref: scrollerRef, setCurrentSlideId }), observerOptions);
27
+ const slides = Array.from(scrollerRef.current.children);
28
+ slides.forEach((slide) => observer.observe(slide));
29
+ return () => observer.disconnect();
59
30
  }, []);
60
- const goToSlideId = useCallback((id) => {
61
- const element = targetRef.current?.children[id];
62
- if (element)
63
- goToSlide(element);
64
- }, [goToSlide]);
65
- const goToNextSlide = useCallback(() => {
66
- const element = targetRef.current?.children[currentSlideId];
67
- const nextElement = element?.nextElementSibling;
68
- if (nextElement)
69
- goToSlide(nextElement);
70
- }, [currentSlideId, goToSlide]);
71
- const goToPreviousSlide = useCallback(() => {
72
- const element = targetRef.current?.children[currentSlideId];
73
- const previousElement = element?.previousElementSibling;
74
- if (previousElement)
75
- goToSlide(previousElement);
76
- }, [currentSlideId, goToSlide]);
77
31
  useEffect(() => {
78
- const handleResize = () => {
79
- const sliderScrollerElement = targetRef.current;
80
- const currentSlideElement = targetRef.current?.children[currentSlideId];
81
- if (!sliderScrollerElement || !currentSlideElement)
82
- return;
83
- const expectedScrollLeft = currentSlideElement.offsetLeft;
84
- if (Math.abs(sliderScrollerElement.scrollLeft - expectedScrollLeft) < 1)
85
- return;
86
- goToSlide(currentSlideElement);
87
- };
32
+ const handleResize = debounce(() => scrollToCurrentSlideOnResize({ currentSlideId, ref: scrollerRef }), 100);
88
33
  window.addEventListener('resize', handleResize);
89
34
  return () => window.removeEventListener('resize', handleResize);
90
- }, [currentSlideId, goToSlide]);
91
- 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 })] }) }));
35
+ }, [currentSlideId]);
36
+ return (_jsxs("div", { ...restProps, "aria-roledescription": "carousel", className: clsx('ams-image-slider',
37
+ // The 'ams-image-slider--controls' class is @deprecated and will be removed in a future release.
38
+ controls && 'ams-image-slider--controls', className), ref: ref, children: [controls && (_jsxs("div", { className: "ams-image-slider__controls", children: [_jsx(ImageSliderControl, { disabled: isAtStart, icon: ChevronBackwardIcon, iconOnly: true, onClick: () => scrollToSlide(currentSlideId - 1, scrollerRef), children: previousLabel }), _jsx(ImageSliderControl, { disabled: isAtEnd, icon: ChevronForwardIcon, iconOnly: true, onClick: () => scrollToSlide(currentSlideId + 1, scrollerRef), children: nextLabel })] })), _jsx("div", { "aria-live": "polite", className: "ams-image-slider__scroller", ref: scrollerRef, role: "group", tabIndex: 0, children: images.map(({ alt, aspectRatio, sizes, src, srcSet }, index) => (_jsx(Image, { alt: alt, "aria-hidden": index !== currentSlideId ? true : undefined, aspectRatio: aspectRatio, className: clsx('ams-image-slider__item',
39
+ // The 'ams-image-slider__item--in-view' class is @deprecated and will be removed in a future release.
40
+ index === currentSlideId && 'ams-image-slider__item--in-view'), sizes: sizes, src: src, srcSet: srcSet }, `${index}-${src}`))) }), _jsx(ImageSliderThumbnails, { currentSlideId: currentSlideId, imageLabel: imageLabel, scrollToSlide: (id) => scrollToSlide(id, scrollerRef), thumbnails: images })] }));
92
41
  });
93
- ImageSliderRoot.displayName = 'ImageSlider';
94
- /**
95
- * @see {@link https://designsystem.amsterdam/?path=/docs/components-media-image-slider--docs Image Slider docs at Amsterdam Design System}
96
- */
97
- export const ImageSlider = Object.assign(ImageSliderRoot, { Item: ImageSliderItem });
42
+ ImageSlider.displayName = 'ImageSlider';
@@ -0,0 +1,2 @@
1
+ import type { ButtonProps } from '../Button';
2
+ export declare const ImageSliderControl: ({ icon, ...restProps }: ButtonProps) => import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,5 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { Button } from '../Button';
3
+ export const ImageSliderControl = ({ icon, ...restProps }) => {
4
+ return _jsx(Button, { className: "ams-image-slider__control", color: "inverse", icon: icon, ...restProps });
5
+ };
@@ -3,12 +3,11 @@
3
3
  * Copyright Gemeente Amsterdam
4
4
  */
5
5
  import type { HTMLAttributes } from 'react';
6
- import type { ImageSliderImageProps } from './ImageSlider';
6
+ import type { ImageSliderProps } from './ImageSlider';
7
7
  export type ImageSliderThumbnailsProps = {
8
+ currentSlideId: number;
8
9
  imageLabel?: string;
9
- thumbnails: ImageSliderImageProps[];
10
+ scrollToSlide: (id: number) => void;
11
+ thumbnails: ImageSliderProps['images'];
10
12
  } & HTMLAttributes<HTMLElement>;
11
- export declare const ImageSliderThumbnails: import("react").ForwardRefExoticComponent<{
12
- imageLabel?: string;
13
- thumbnails: ImageSliderImageProps[];
14
- } & HTMLAttributes<HTMLElement> & import("react").RefAttributes<HTMLElement>>;
13
+ export declare const ImageSliderThumbnails: ({ currentSlideId, imageLabel, scrollToSlide, thumbnails, }: ImageSliderThumbnailsProps) => import("react/jsx-runtime").JSX.Element;
@@ -1,28 +1,23 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
2
  import { clsx } from 'clsx';
3
- import { forwardRef, useCallback, useContext, useMemo } from 'react';
4
3
  import { generateAspectRatioClass } from '../Image/generateAspectRatioClass';
5
- import { ImageSliderContext } from './ImageSliderContext';
6
- export const ImageSliderThumbnails = forwardRef(({ className, imageLabel, thumbnails, ...restProps }, ref) => {
7
- const { currentSlideId, goToNextSlide, goToPreviousSlide, goToSlideId } = useContext(ImageSliderContext);
8
- const handleKeyDown = useCallback((event) => {
4
+ export const ImageSliderThumbnails = ({ currentSlideId, imageLabel, scrollToSlide, thumbnails, }) => {
5
+ const handleKeyDown = (event) => {
9
6
  const element = event.currentTarget.children[currentSlideId];
10
7
  if (event.key === 'ArrowRight') {
11
8
  const nextElement = element?.nextElementSibling;
12
9
  if (nextElement) {
13
10
  nextElement.focus();
14
- goToNextSlide();
11
+ scrollToSlide(currentSlideId + 1);
15
12
  }
16
13
  }
17
14
  if (event.key === 'ArrowLeft') {
18
15
  const previousElement = element?.previousElementSibling;
19
16
  if (previousElement) {
20
17
  previousElement.focus();
21
- goToPreviousSlide();
18
+ scrollToSlide(currentSlideId - 1);
22
19
  }
23
20
  }
24
- }, [currentSlideId, goToNextSlide, goToPreviousSlide]);
25
- const renderThumbnails = useMemo(() => thumbnails.map(({ alt, aspectRatio, src }, index) => (_jsx("button", { "aria-label": `${imageLabel} ${index + 1}: ${alt}`, "aria-posinset": index + 1, "aria-selected": currentSlideId === index ? 'true' : 'false', "aria-setsize": thumbnails.length, className: clsx('ams-image-slider__thumbnail', currentSlideId === index && 'ams-image-slider__thumbnail--in-view', generateAspectRatioClass(aspectRatio)), onClick: () => goToSlideId(index), role: "tab", style: { backgroundImage: `url(${src})` }, tabIndex: currentSlideId === index ? 0 : -1, type: "button" }, index))), [currentSlideId, goToSlideId, imageLabel, thumbnails]);
26
- return (_jsx("nav", { ...restProps, className: clsx('ams-image-slider__thumbnails', className), onKeyDown: handleKeyDown, ref: ref, role: "tablist", children: renderThumbnails }));
27
- });
28
- ImageSliderThumbnails.displayName = 'ImageSlider.Thumbnails';
21
+ };
22
+ return (_jsx("nav", { className: "ams-image-slider__thumbnails", onKeyDown: handleKeyDown, role: "tablist", children: thumbnails.map(({ alt, aspectRatio, src }, index) => (_jsx("button", { "aria-label": `${imageLabel} ${index + 1}: ${alt}`, "aria-posinset": index + 1, "aria-selected": currentSlideId === index ? 'true' : 'false', "aria-setsize": thumbnails.length, className: clsx('ams-image-slider__thumbnail', currentSlideId === index && 'ams-image-slider__thumbnail--in-view', generateAspectRatioClass(aspectRatio)), onClick: () => scrollToSlide(index), role: "tab", style: { backgroundImage: `url(${src})` }, tabIndex: currentSlideId === index ? 0 : -1, type: "button" }, `${index}-${src}`))) }));
23
+ };
@@ -4,4 +4,3 @@
4
4
  */
5
5
  export { ImageSlider } from './ImageSlider';
6
6
  export type { ImageSliderProps } from './ImageSlider';
7
- export type { ImageSliderItemProps } from './ImageSliderItem';
@@ -0,0 +1 @@
1
+ export declare function debounce<F extends (...args: unknown[]) => void>(fn: F, delay: number): (...args: Parameters<F>) => void;
@@ -0,0 +1,8 @@
1
+ export function debounce(fn, delay) {
2
+ let timer = null;
3
+ return function (...args) {
4
+ if (timer)
5
+ clearTimeout(timer);
6
+ timer = setTimeout(() => fn.apply(this, args), delay);
7
+ };
8
+ }
@@ -0,0 +1,4 @@
1
+ export { debounce } from './debounce';
2
+ export { scrollToCurrentSlideOnResize } from './scrollToCurrentSlideOnResize';
3
+ export { scrollToSlide } from './scrollToSlide';
4
+ export { setCurrentSlideIdToVisibleSlide } from './setCurrentSlideIdToVisibleSlide';
@@ -0,0 +1,4 @@
1
+ export { debounce } from './debounce';
2
+ export { scrollToCurrentSlideOnResize } from './scrollToCurrentSlideOnResize';
3
+ export { scrollToSlide } from './scrollToSlide';
4
+ export { setCurrentSlideIdToVisibleSlide } from './setCurrentSlideIdToVisibleSlide';
@@ -0,0 +1,7 @@
1
+ import type { RefObject } from 'react';
2
+ type Args = {
3
+ currentSlideId: number;
4
+ ref: RefObject<HTMLDivElement>;
5
+ };
6
+ export declare const scrollToCurrentSlideOnResize: ({ currentSlideId, ref }: Args) => void;
7
+ export {};
@@ -0,0 +1,11 @@
1
+ import { scrollToSlide } from './scrollToSlide';
2
+ export const scrollToCurrentSlideOnResize = ({ currentSlideId, ref }) => {
3
+ const scrollerElement = ref.current;
4
+ const currentSlideElement = ref.current?.children[currentSlideId];
5
+ if (!scrollerElement || !currentSlideElement)
6
+ return;
7
+ const expectedScrollLeft = currentSlideElement.offsetLeft;
8
+ if (Math.abs(scrollerElement.scrollLeft - expectedScrollLeft) < 1)
9
+ return;
10
+ scrollToSlide(currentSlideId, ref);
11
+ };
@@ -0,0 +1,2 @@
1
+ import type { RefObject } from 'react';
2
+ export declare const scrollToSlide: (id: number, ref: RefObject<HTMLDivElement>) => void;
@@ -0,0 +1,9 @@
1
+ export const scrollToSlide = (id, ref) => {
2
+ const element = ref.current?.children[id];
3
+ const scrollerElement = ref.current;
4
+ if (!element || !scrollerElement)
5
+ return;
6
+ scrollerElement.scrollTo({
7
+ left: element.offsetLeft,
8
+ });
9
+ };
@@ -0,0 +1,8 @@
1
+ import type { RefObject } from 'react';
2
+ type Args = {
3
+ observations: IntersectionObserverEntry[];
4
+ ref: RefObject<HTMLDivElement>;
5
+ setCurrentSlideId: (id: number) => void;
6
+ };
7
+ export declare const setCurrentSlideIdToVisibleSlide: ({ observations, ref, setCurrentSlideId }: Args) => void;
8
+ export {};
@@ -0,0 +1,10 @@
1
+ export const setCurrentSlideIdToVisibleSlide = ({ observations, ref, setCurrentSlideId }) => {
2
+ const images = Array.from(ref.current?.children || []);
3
+ if (images.length === 0)
4
+ return;
5
+ observations.forEach((observation) => {
6
+ if (observation.isIntersecting) {
7
+ setCurrentSlideId(images.indexOf(observation.target));
8
+ }
9
+ });
10
+ };
@@ -3,6 +3,7 @@
3
3
  * Copyright Gemeente Amsterdam
4
4
  */
5
5
  import type { AnchorHTMLAttributes, ComponentType, HTMLAttributes, ReactNode } from 'react';
6
+ import type { IconProps } from '../Icon';
6
7
  import type { LogoBrand } from '../Logo';
7
8
  import type { LogoBrandConfig } from '../Logo/Logo';
8
9
  export type PageHeaderProps = {
@@ -18,6 +19,8 @@ export type PageHeaderProps = {
18
19
  logoLinkComponent?: ComponentType<AnchorHTMLAttributes<HTMLAnchorElement>>;
19
20
  /** The accessible text for the link on the logo. */
20
21
  logoLinkTitle?: string;
22
+ /** An icon to display instead of the default icon. */
23
+ menuButtonIcon?: IconProps['svg'];
21
24
  /** The visible text for the menu button. */
22
25
  menuButtonText?: string;
23
26
  /** The text for screen readers when the button hides the menu. */
@@ -47,6 +50,8 @@ export declare const PageHeader: import("react").ForwardRefExoticComponent<{
47
50
  logoLinkComponent?: ComponentType<AnchorHTMLAttributes<HTMLAnchorElement>>;
48
51
  /** The accessible text for the link on the logo. */
49
52
  logoLinkTitle?: string;
53
+ /** An icon to display instead of the default icon. */
54
+ menuButtonIcon?: IconProps["svg"];
50
55
  /** The visible text for the menu button. */
51
56
  menuButtonText?: string;
52
57
  /** The text for screen readers when the button hides the menu. */
@@ -63,7 +68,7 @@ export declare const PageHeader: import("react").ForwardRefExoticComponent<{
63
68
  GridCellNarrowWindowOnly: import("react").ForwardRefExoticComponent<import("..").GridCellProps & import("react").RefAttributes<HTMLElement>>;
64
69
  MenuLink: import("react").ForwardRefExoticComponent<{
65
70
  fixed?: boolean;
66
- icon?: import("../Icon").IconProps["svg"];
71
+ icon?: IconProps["svg"];
67
72
  } & AnchorHTMLAttributes<HTMLAnchorElement> & {
68
73
  children?: ReactNode | undefined;
69
74
  } & import("react").RefAttributes<HTMLAnchorElement>>;
@@ -8,7 +8,7 @@ import { PageHeaderGridCellNarrowWindowOnly } from './PageHeaderGridCellNarrowWi
8
8
  import { PageHeaderMenuIcon } from './PageHeaderMenuIcon';
9
9
  import { PageHeaderMenuLink } from './PageHeaderMenuLink';
10
10
  const LogoLinkContent = ({ brandName, logoAccessibleName, logoBrand, }) => (_jsxs(_Fragment, { children: [_jsx("span", { className: clsx(logoBrand === 'amsterdam' && Boolean(brandName) && 'ams-page-header__logo-container'), children: _jsx(Logo, { "aria-label": logoAccessibleName, brand: logoBrand }) }), brandName && (_jsx("span", { "aria-hidden": "true", className: "ams-page-header__brand-name", children: brandName }))] }));
11
- const PageHeaderRoot = forwardRef(({ brandName, children, className, logoAccessibleName, logoBrand = 'amsterdam', logoLink = '/', logoLinkComponent = (props) => _jsx("a", { ...props }), logoLinkTitle = `Ga naar de homepage${brandName ? ` van ${brandName}` : ''}`, menuButtonText = 'Menu', menuButtonTextForHide = 'Verberg navigatiemenu', menuButtonTextForShow = 'Laat navigatiemenu zien', menuItems, navigationLabel = 'Hoofdnavigatie', noMenuButtonOnWideWindow, ...restProps }, ref) => {
11
+ const PageHeaderRoot = forwardRef(({ brandName, children, className, logoAccessibleName, logoBrand = 'amsterdam', logoLink = '/', logoLinkComponent = (props) => _jsx("a", { ...props }), logoLinkTitle = `Ga naar de homepage${brandName ? ` van ${brandName}` : ''}`, menuButtonIcon, menuButtonText = 'Menu', menuButtonTextForHide = 'Verberg navigatiemenu', menuButtonTextForShow = 'Laat navigatiemenu zien', menuItems, navigationLabel = 'Hoofdnavigatie', noMenuButtonOnWideWindow, ...restProps }, ref) => {
12
12
  const [open, setOpen] = useState(false);
13
13
  const Link = logoLinkComponent;
14
14
  const hasMegaMenu = Boolean(children);
@@ -20,7 +20,7 @@ const PageHeaderRoot = forwardRef(({ brandName, children, className, logoAccessi
20
20
  }
21
21
  }, [isWideWindow]);
22
22
  return (_jsxs("header", { ...restProps, className: clsx('ams-page-header', className), ref: ref, children: [_jsxs(Link, { className: "ams-page-header__logo-link", href: logoLink, children: [_jsx(LogoLinkContent, { brandName: brandName, logoAccessibleName: logoAccessibleName, logoBrand: logoBrand }), _jsx("span", { className: "ams-visually-hidden", children: ` ${logoLinkTitle}` })] }), (hasMegaMenu || menuItems) && (_jsxs("nav", { "aria-labelledby": "primary-navigation", className: "ams-page-header__navigation", children: [_jsx("h2", { "aria-hidden": true, className: "ams-visually-hidden", id: "primary-navigation", children: navigationLabel }), _jsx("div", { "aria-hidden": true, className: "ams-page-header__logo-link ams-page-header__logo-link--hidden", hidden: true, children: _jsx(LogoLinkContent, { brandName: brandName, logoBrand: logoBrand }) }), _jsxs("ul", { className: "ams-page-header__menu", children: [menuItems, hasMegaMenu && (_jsx("li", { className: clsx('ams-page-header__mega-menu-button-item', noMenuButtonOnWideWindow && 'ams-page-header__mega-menu-button-item--hide-on-wide-window'), hidden // Hide the list item containing the menu button until its CSS loads. If it doesn't load, the menu will always be visible.
23
- : true, children: _jsxs("button", { "aria-controls": "ams-page-header-mega-menu", "aria-expanded": open, className: "ams-page-header__mega-menu-button", onClick: () => setOpen(!open), type: "button", children: [_jsx("span", { "aria-hidden": "true", className: "ams-page-header__mega-menu-button-label", children: menuButtonText }), _jsx("span", { className: "ams-visually-hidden", children: open ? menuButtonTextForHide : menuButtonTextForShow }), _jsx("span", { "aria-hidden": "true", className: "ams-page-header__mega-menu-button-hidden-label", children: menuButtonText }), _jsx(Icon, { svg: _jsx(PageHeaderMenuIcon, { className: clsx('ams-page-header__menu-icon', open && 'ams-page-header__menu-icon--open') }) })] }) }))] }), hasMegaMenu && (_jsx("div", { className: clsx('ams-page-header__mega-menu', !open && 'ams-page-header__mega-menu--closed'), id: "ams-page-header-mega-menu", children: children }))] }))] }));
23
+ : true, children: _jsxs("button", { "aria-controls": "ams-page-header-mega-menu", "aria-expanded": open, className: "ams-page-header__mega-menu-button", onClick: () => setOpen(!open), type: "button", children: [_jsx("span", { "aria-hidden": "true", className: "ams-page-header__mega-menu-button-label", children: menuButtonText }), _jsx("span", { className: "ams-visually-hidden", children: open ? menuButtonTextForHide : menuButtonTextForShow }), _jsx("span", { "aria-hidden": "true", className: "ams-page-header__mega-menu-button-hidden-label", children: menuButtonText }), _jsx(Icon, { svg: menuButtonIcon ?? (_jsx(PageHeaderMenuIcon, { className: clsx('ams-page-header__menu-icon', open && 'ams-page-header__menu-icon--open') })) })] }) }))] }), hasMegaMenu && (_jsx("div", { className: clsx('ams-page-header__mega-menu', !open && 'ams-page-header__mega-menu--closed'), id: "ams-page-header-mega-menu", children: children }))] }))] }));
24
24
  });
25
25
  PageHeaderRoot.displayName = 'PageHeader';
26
26
  /**
@@ -0,0 +1,43 @@
1
+ /**
2
+ * @license EUPL-1.2+
3
+ * Copyright Gemeente Amsterdam
4
+ */
5
+ import type { HTMLAttributes, PropsWithChildren } from 'react';
6
+ export declare const progressListHeadingSizes: (2 | 3 | 4)[];
7
+ type ProgressListHeadingSize = (typeof progressListHeadingSizes)[number];
8
+ export type ProgressListProps = {
9
+ /**
10
+ * The hierarchical level of this Progress List’s Headings within the document.
11
+ * There is no default value; determine the correct level for this instance.
12
+ */
13
+ headingLevel: ProgressListHeadingSize;
14
+ } & PropsWithChildren<HTMLAttributes<HTMLOListElement>>;
15
+ /**
16
+ * @see {@link https://designsystem.amsterdam/?path=/docs/components-containers-progress-list--docs Progress List docs at Amsterdam Design System}
17
+ */
18
+ export declare const ProgressList: import("react").ForwardRefExoticComponent<{
19
+ /**
20
+ * The hierarchical level of this Progress List’s Headings within the document.
21
+ * There is no default value; determine the correct level for this instance.
22
+ */
23
+ headingLevel: ProgressListHeadingSize;
24
+ } & HTMLAttributes<HTMLOListElement> & {
25
+ children?: import("react").ReactNode | undefined;
26
+ } & import("react").RefAttributes<HTMLOListElement>> & {
27
+ Step: import("react").ForwardRefExoticComponent<{
28
+ hasSubsteps?: boolean;
29
+ heading: string;
30
+ status?: "current" | "completed";
31
+ } & HTMLAttributes<HTMLElement> & {
32
+ children?: import("react").ReactNode | undefined;
33
+ } & import("react").RefAttributes<HTMLLIElement>>;
34
+ Substep: import("react").ForwardRefExoticComponent<{
35
+ status?: import("./ProgressListStep").ProgressListStepProps["status"];
36
+ } & HTMLAttributes<HTMLElement> & {
37
+ children?: import("react").ReactNode | undefined;
38
+ } & import("react").RefAttributes<HTMLLIElement>>;
39
+ Substeps: import("react").ForwardRefExoticComponent<HTMLAttributes<HTMLOListElement> & {
40
+ children?: import("react").ReactNode | undefined;
41
+ } & import("react").RefAttributes<HTMLOListElement>>;
42
+ };
43
+ export {};
@@ -0,0 +1,19 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { clsx } from 'clsx';
3
+ import { forwardRef } from 'react';
4
+ import { headingSizes } from '../Heading/Heading';
5
+ import { ProgressListContext } from './ProgressListContext';
6
+ import { ProgressListStep } from './ProgressListStep';
7
+ import { ProgressListSubstep } from './ProgressListSubstep';
8
+ import { ProgressListSubsteps } from './ProgressListSubsteps';
9
+ export const progressListHeadingSizes = headingSizes.filter((size) => size !== 1);
10
+ const ProgressListRoot = forwardRef(({ children, className, headingLevel, ...restProps }, ref) => (_jsx(ProgressListContext.Provider, { value: { headingLevel: headingLevel }, children: _jsx("ol", { ...restProps, className: clsx('ams-progress-list', `ams-progress-list--heading-${headingLevel}`, className), ref: ref, children: children }) })));
11
+ /**
12
+ * @see {@link https://designsystem.amsterdam/?path=/docs/components-containers-progress-list--docs Progress List docs at Amsterdam Design System}
13
+ */
14
+ export const ProgressList = Object.assign(ProgressListRoot, {
15
+ Step: ProgressListStep,
16
+ Substep: ProgressListSubstep,
17
+ Substeps: ProgressListSubsteps,
18
+ });
19
+ ProgressListRoot.displayName = 'ProgressList';
@@ -0,0 +1,10 @@
1
+ /**
2
+ * @license EUPL-1.2+
3
+ * Copyright Gemeente Amsterdam
4
+ */
5
+ import type { ProgressListProps } from './ProgressList';
6
+ type ProgressListContextValue = {
7
+ headingLevel: ProgressListProps['headingLevel'];
8
+ };
9
+ export declare const ProgressListContext: import("react").Context<ProgressListContextValue>;
10
+ export {};
@@ -0,0 +1,11 @@
1
+ /**
2
+ * @license EUPL-1.2+
3
+ * Copyright Gemeente Amsterdam
4
+ */
5
+ import { createContext } from 'react';
6
+ const defaultValues = {
7
+ // Default value for type safety.
8
+ // The actual value is always provided via ProgressList’s required headingLevel prop.
9
+ headingLevel: 2,
10
+ };
11
+ export const ProgressListContext = createContext(defaultValues);