@amsterdam/design-system-react 2.2.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 (71) hide show
  1. package/README.md +1 -1
  2. package/dist/Accordion/Accordion.js +1 -1
  3. package/dist/Accordion/AccordionContext.d.ts +1 -2
  4. package/dist/Accordion/AccordionContext.js +3 -4
  5. package/dist/Accordion/AccordionSection.js +1 -1
  6. package/dist/Badge/Badge.d.ts +5 -0
  7. package/dist/Badge/Badge.js +3 -2
  8. package/dist/Checkbox/Checkbox.d.ts +5 -4
  9. package/dist/Column/Column.d.ts +1 -1
  10. package/dist/Grid/Grid.d.ts +1 -1
  11. package/dist/Grid/GridCell.d.ts +1 -1
  12. package/dist/ImageSlider/ImageSlider.d.ts +3 -22
  13. package/dist/ImageSlider/ImageSlider.js +31 -86
  14. package/dist/ImageSlider/ImageSliderControl.d.ts +2 -0
  15. package/dist/ImageSlider/ImageSliderControl.js +5 -0
  16. package/dist/ImageSlider/ImageSliderThumbnails.d.ts +5 -6
  17. package/dist/ImageSlider/ImageSliderThumbnails.js +7 -12
  18. package/dist/ImageSlider/index.d.ts +0 -1
  19. package/dist/ImageSlider/utils/debounce.d.ts +1 -0
  20. package/dist/ImageSlider/utils/debounce.js +8 -0
  21. package/dist/ImageSlider/utils/index.d.ts +4 -0
  22. package/dist/ImageSlider/utils/index.js +4 -0
  23. package/dist/ImageSlider/utils/scrollToCurrentSlideOnResize.d.ts +7 -0
  24. package/dist/ImageSlider/utils/scrollToCurrentSlideOnResize.js +11 -0
  25. package/dist/ImageSlider/utils/scrollToSlide.d.ts +2 -0
  26. package/dist/ImageSlider/utils/scrollToSlide.js +9 -0
  27. package/dist/ImageSlider/utils/setCurrentSlideIdToVisibleSlide.d.ts +8 -0
  28. package/dist/ImageSlider/utils/setCurrentSlideIdToVisibleSlide.js +10 -0
  29. package/dist/Logo/Logo.d.ts +10 -5
  30. package/dist/Logo/Logo.js +17 -9
  31. package/dist/PageFooter/PageFooter.d.ts +4 -1
  32. package/dist/PageFooter/PageFooterMenu.d.ts +27 -2
  33. package/dist/PageFooter/PageFooterMenu.js +6 -2
  34. package/dist/PageHeader/PageHeader.d.ts +19 -5
  35. package/dist/PageHeader/PageHeader.js +3 -2
  36. package/dist/PageHeader/PageHeaderGridCellNarrowWindowOnly.js +2 -1
  37. package/dist/Paragraph/Paragraph.d.ts +2 -2
  38. package/dist/Paragraph/Paragraph.js +8 -1
  39. package/dist/ProgressList/ProgressList.d.ts +43 -0
  40. package/dist/ProgressList/ProgressList.js +19 -0
  41. package/dist/ProgressList/ProgressListContext.d.ts +10 -0
  42. package/dist/ProgressList/ProgressListContext.js +11 -0
  43. package/dist/ProgressList/ProgressListStep.d.ts +23 -0
  44. package/dist/ProgressList/ProgressListStep.js +12 -0
  45. package/dist/ProgressList/ProgressListSubstep.d.ts +16 -0
  46. package/dist/ProgressList/ProgressListSubstep.js +7 -0
  47. package/dist/ProgressList/ProgressListSubsteps.d.ts +9 -0
  48. package/dist/ProgressList/ProgressListSubsteps.js +5 -0
  49. package/dist/ProgressList/index.d.ts +9 -0
  50. package/dist/ProgressList/index.js +5 -0
  51. package/dist/Radio/Radio.d.ts +5 -4
  52. package/dist/Row/Row.d.ts +1 -1
  53. package/dist/Spotlight/Spotlight.d.ts +1 -1
  54. package/dist/common/useIsAfterBreakpoint.d.ts +4 -4
  55. package/dist/common/useIsAfterBreakpoint.js +12 -13
  56. package/dist/index.cjs.js +734 -663
  57. package/dist/index.cjs.js.map +1 -1
  58. package/dist/index.d.ts +150 -77
  59. package/dist/index.esm.js +736 -666
  60. package/dist/index.esm.js.map +1 -1
  61. package/dist/index.js +1 -0
  62. package/dist/tsconfig.build.tsbuildinfo +1 -1
  63. package/package.json +13 -18
  64. package/dist/ImageSlider/ImageSliderContext.d.ts +0 -13
  65. package/dist/ImageSlider/ImageSliderContext.js +0 -14
  66. package/dist/ImageSlider/ImageSliderControls.d.ts +0 -17
  67. package/dist/ImageSlider/ImageSliderControls.js +0 -13
  68. package/dist/ImageSlider/ImageSliderItem.d.ts +0 -15
  69. package/dist/ImageSlider/ImageSliderItem.js +0 -11
  70. package/dist/ImageSlider/ImageSliderScroller.d.ts +0 -9
  71. package/dist/ImageSlider/ImageSliderScroller.js +0 -5
package/README.md CHANGED
@@ -3,7 +3,7 @@
3
3
  # Amsterdam Design System: React components
4
4
 
5
5
  This package provides all React components from the [Amsterdam Design System](https://designsystem.amsterdam).
6
- Use it to compose pages in your website or application.
6
+ Use it to compose pages in your websites.
7
7
 
8
8
  ## Introduction
9
9
 
@@ -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>>;
@@ -56,5 +56,5 @@ export declare const Column: import("react").ForwardRefExoticComponent<{
56
56
  gap?: ColumnGap;
57
57
  } & HTMLAttributes<HTMLElement> & {
58
58
  children?: import("react").ReactNode | undefined;
59
- } & import("react").RefAttributes<unknown>>;
59
+ } & import("react").RefAttributes<any>>;
60
60
  export {};
@@ -38,6 +38,6 @@ export type GridProps = {
38
38
  * @see {@link https://designsystem.amsterdam/?path=/docs/components-layout-grid--docs Grid docs at Amsterdam Design System}
39
39
  */
40
40
  export declare const Grid: import("react").ForwardRefExoticComponent<GridProps & import("react").RefAttributes<any>> & {
41
- Cell: import("react").ForwardRefExoticComponent<import("./GridCell").GridCellProps & import("react").RefAttributes<unknown>>;
41
+ Cell: import("react").ForwardRefExoticComponent<import("./GridCell").GridCellProps & import("react").RefAttributes<any>>;
42
42
  };
43
43
  export {};
@@ -21,5 +21,5 @@ export type GridCellProps = {
21
21
  /** The HTML tag to use. */
22
22
  as?: GridCellTag;
23
23
  } & PropsWithChildren<HTMLAttributes<HTMLElement>> & (GridCellSpanAllProp | GridCellSpanAndStartProps);
24
- export declare const GridCell: import("react").ForwardRefExoticComponent<GridCellProps & import("react").RefAttributes<unknown>>;
24
+ export declare const GridCell: import("react").ForwardRefExoticComponent<GridCellProps & import("react").RefAttributes<any>>;
25
25
  export {};
@@ -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
+ };
@@ -2,13 +2,18 @@
2
2
  * @license EUPL-1.2+
3
3
  * Copyright Gemeente Amsterdam
4
4
  */
5
- import type { ForwardRefExoticComponent, RefAttributes, SVGProps } from 'react';
6
- export type LogoBrand = 'amsterdam' | 'ggd-amsterdam' | 'museum-weesp' | 'stadsarchief' | 'stadsbank-van-lening' | 'vga-verzekeringen';
5
+ import type { ComponentType, SVGProps } from 'react';
6
+ export declare const logoBrands: readonly ["amsterdam", "ggd-amsterdam", "museum-weesp", "stadsarchief", "stadsbank-van-lening", "vga-verzekeringen"];
7
+ export type LogoBrand = (typeof logoBrands)[number];
7
8
  export type LogoProps = {
8
- /** The name of the brand for which to display the logo. */
9
- brand?: LogoBrand;
9
+ /** The name of the brand for which to display the logo, or configuration for a custom logo. */
10
+ brand?: LogoBrand | LogoBrandConfig;
10
11
  } & SVGProps<SVGSVGElement>;
12
+ export type LogoBrandConfig = {
13
+ label: string;
14
+ svg: ComponentType<SVGProps<SVGSVGElement>>;
15
+ };
11
16
  /**
12
17
  * @see {@link https://designsystem.amsterdam/?path=/docs/components-media-logo--docs Logo docs at Amsterdam Design System}
13
18
  */
14
- export declare const Logo: ForwardRefExoticComponent<Omit<LogoProps, "ref"> & RefAttributes<SVGSVGElement>>;
19
+ export declare const Logo: import("react").ForwardRefExoticComponent<Omit<LogoProps, "ref"> & import("react").RefAttributes<SVGSVGElement>>;
package/dist/Logo/Logo.js CHANGED
@@ -2,38 +2,46 @@ import { jsx as _jsx } from "react/jsx-runtime";
2
2
  import { clsx } from 'clsx';
3
3
  import { forwardRef } from 'react';
4
4
  import { LogoAmsterdam, LogoGgdAmsterdam, LogoMuseumWeesp, LogoStadsarchief, LogoStadsbankVanLening, LogoVgaVerzekeringen, } from './brand';
5
+ export const logoBrands = [
6
+ 'amsterdam',
7
+ 'ggd-amsterdam',
8
+ 'museum-weesp',
9
+ 'stadsarchief',
10
+ 'stadsbank-van-lening',
11
+ 'vga-verzekeringen',
12
+ ];
5
13
  const logoConfig = {
6
14
  amsterdam: {
7
15
  label: 'Gemeente Amsterdam logo',
8
- logo: LogoAmsterdam,
16
+ svg: LogoAmsterdam,
9
17
  },
10
18
  'ggd-amsterdam': {
11
19
  label: 'GGD Amsterdam logo',
12
- logo: LogoGgdAmsterdam,
20
+ svg: LogoGgdAmsterdam,
13
21
  },
14
22
  'museum-weesp': {
15
23
  label: 'Gemeente Amsterdam Museum Weesp logo',
16
- logo: LogoMuseumWeesp,
24
+ svg: LogoMuseumWeesp,
17
25
  },
18
26
  stadsarchief: {
19
27
  label: 'Gemeente Amsterdam Stadsarchief logo',
20
- logo: LogoStadsarchief,
28
+ svg: LogoStadsarchief,
21
29
  },
22
30
  'stadsbank-van-lening': {
23
31
  label: 'Gemeente Amsterdam Stadsbank van Lening logo',
24
- logo: LogoStadsbankVanLening,
32
+ svg: LogoStadsbankVanLening,
25
33
  },
26
34
  'vga-verzekeringen': {
27
35
  label: 'Gemeente Amsterdam VGA Verzekeringen logo',
28
- logo: LogoVgaVerzekeringen,
36
+ svg: LogoVgaVerzekeringen,
29
37
  },
30
38
  };
31
39
  /**
32
40
  * @see {@link https://designsystem.amsterdam/?path=/docs/components-media-logo--docs Logo docs at Amsterdam Design System}
33
41
  */
34
42
  export const Logo = forwardRef(({ 'aria-label': ariaLabel, brand = 'amsterdam', className, ...restProps }, ref) => {
35
- const LogoComponent = logoConfig[brand].logo;
36
- const logoLabel = logoConfig[brand].label;
37
- return (_jsx(LogoComponent, { ...restProps, "aria-label": ariaLabel || logoLabel, className: clsx('ams-logo', className), ref: ref }));
43
+ const config = typeof brand === 'string' ? logoConfig[brand] : brand;
44
+ const { label, svg: LogoComponent } = config;
45
+ return (_jsx(LogoComponent, { ...restProps, "aria-label": ariaLabel || label, className: clsx('ams-logo', className), ref: ref }));
38
46
  });
39
47
  Logo.displayName = 'Logo';
@@ -10,7 +10,10 @@ export type PageFooterProps = PropsWithChildren<HTMLAttributes<HTMLElement>>;
10
10
  export declare const PageFooter: import("react").ForwardRefExoticComponent<HTMLAttributes<HTMLElement> & {
11
11
  children?: import("react").ReactNode | undefined;
12
12
  } & import("react").RefAttributes<HTMLElement>> & {
13
- Menu: import("react").ForwardRefExoticComponent<HTMLAttributes<HTMLUListElement> & {
13
+ Menu: import("react").ForwardRefExoticComponent<{
14
+ heading?: string;
15
+ headingLevel?: import("..").HeadingProps["level"];
16
+ } & HTMLAttributes<HTMLUListElement> & {
14
17
  children?: import("react").ReactNode | undefined;
15
18
  } & import("react").RefAttributes<HTMLUListElement>>;
16
19
  MenuLink: import("react").ForwardRefExoticComponent<import("react").AnchorHTMLAttributes<HTMLAnchorElement> & {
@@ -3,7 +3,32 @@
3
3
  * Copyright Gemeente Amsterdam
4
4
  */
5
5
  import type { HTMLAttributes, PropsWithChildren } from 'react';
6
- export type PageFooterMenuProps = PropsWithChildren<HTMLAttributes<HTMLUListElement>>;
7
- export declare const PageFooterMenu: import("react").ForwardRefExoticComponent<HTMLAttributes<HTMLUListElement> & {
6
+ import type { HeadingProps } from '../Heading';
7
+ export type PageFooterMenuProps = {
8
+ /**
9
+ * Describes the menu for users of assistive technology.
10
+ * The heading gets visually hidden – sighted users can infer the meaning of the menu from its appearance.
11
+ * @default Over deze website
12
+ */
13
+ heading?: string;
14
+ /**
15
+ * The hierarchical level of the Footer Menu’s Heading within the document.
16
+ * The default value is 2. This will almost always be correct – but verify this yourself.
17
+ */
18
+ headingLevel?: HeadingProps['level'];
19
+ } & PropsWithChildren<HTMLAttributes<HTMLUListElement>>;
20
+ export declare const PageFooterMenu: import("react").ForwardRefExoticComponent<{
21
+ /**
22
+ * Describes the menu for users of assistive technology.
23
+ * The heading gets visually hidden – sighted users can infer the meaning of the menu from its appearance.
24
+ * @default Over deze website
25
+ */
26
+ heading?: string;
27
+ /**
28
+ * The hierarchical level of the Footer Menu’s Heading within the document.
29
+ * The default value is 2. This will almost always be correct – but verify this yourself.
30
+ */
31
+ headingLevel?: HeadingProps["level"];
32
+ } & HTMLAttributes<HTMLUListElement> & {
8
33
  children?: import("react").ReactNode | undefined;
9
34
  } & import("react").RefAttributes<HTMLUListElement>>;