@lumx/react 2.0.0-alpha.0 → 2.0.1

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 (102) hide show
  1. package/esm/_internal/AlertDialog.js +15 -5
  2. package/esm/_internal/AlertDialog.js.map +1 -1
  3. package/esm/_internal/AutocompleteMultiple.js +6 -2
  4. package/esm/_internal/AutocompleteMultiple.js.map +1 -1
  5. package/esm/_internal/ButtonRoot.js +1 -0
  6. package/esm/_internal/ButtonRoot.js.map +1 -1
  7. package/esm/_internal/ClickAwayProvider.js +31 -30
  8. package/esm/_internal/ClickAwayProvider.js.map +1 -1
  9. package/esm/_internal/Dialog2.js +6 -3
  10. package/esm/_internal/Dialog2.js.map +1 -1
  11. package/esm/_internal/Dropdown2.js +6 -2
  12. package/esm/_internal/Dropdown2.js.map +1 -1
  13. package/esm/_internal/Icon2.js +1 -1
  14. package/esm/_internal/Icon2.js.map +1 -1
  15. package/esm/_internal/Lightbox2.js +4 -2
  16. package/esm/_internal/Lightbox2.js.map +1 -1
  17. package/esm/_internal/Message2.js +4 -3
  18. package/esm/_internal/Message2.js.map +1 -1
  19. package/esm/_internal/Popover2.js +4 -2
  20. package/esm/_internal/Popover2.js.map +1 -1
  21. package/esm/_internal/SelectMultiple.js +39 -61
  22. package/esm/_internal/SelectMultiple.js.map +1 -1
  23. package/esm/_internal/SlideshowControls.js +93 -44
  24. package/esm/_internal/SlideshowControls.js.map +1 -1
  25. package/esm/_internal/Switch2.js +2 -0
  26. package/esm/_internal/Switch2.js.map +1 -1
  27. package/esm/_internal/alert-dialog.js +1 -1
  28. package/esm/_internal/autocomplete.js +1 -2
  29. package/esm/_internal/autocomplete.js.map +1 -1
  30. package/esm/_internal/button.js +1 -2
  31. package/esm/_internal/button.js.map +1 -1
  32. package/esm/_internal/date-picker.js +1 -2
  33. package/esm/_internal/date-picker.js.map +1 -1
  34. package/esm/_internal/dialog.js +1 -1
  35. package/esm/_internal/dropdown.js +1 -1
  36. package/esm/_internal/expansion-panel.js +1 -2
  37. package/esm/_internal/expansion-panel.js.map +1 -1
  38. package/esm/_internal/getRootClassName.js +78 -68
  39. package/esm/_internal/getRootClassName.js.map +1 -1
  40. package/esm/_internal/lightbox.js +1 -2
  41. package/esm/_internal/lightbox.js.map +1 -1
  42. package/esm/_internal/popover.js +1 -1
  43. package/esm/_internal/select.js +1 -2
  44. package/esm/_internal/select.js.map +1 -1
  45. package/esm/_internal/side-navigation.js +1 -2
  46. package/esm/_internal/side-navigation.js.map +1 -1
  47. package/esm/_internal/slideshow.js +1 -2
  48. package/esm/_internal/slideshow.js.map +1 -1
  49. package/esm/_internal/text-field.js +1 -2
  50. package/esm/_internal/text-field.js.map +1 -1
  51. package/esm/_internal/tooltip.js +1 -2
  52. package/esm/_internal/tooltip.js.map +1 -1
  53. package/esm/_internal/useDelayedVisibility.js +16 -2
  54. package/esm/_internal/useDelayedVisibility.js.map +1 -1
  55. package/esm/_internal/useDisableBodyScroll.js +205 -7
  56. package/esm/_internal/useDisableBodyScroll.js.map +1 -1
  57. package/esm/index.js +1 -2
  58. package/esm/index.js.map +1 -1
  59. package/package.json +6 -6
  60. package/src/components/alert-dialog/AlertDialog.stories.tsx +29 -0
  61. package/src/components/alert-dialog/AlertDialog.tsx +29 -9
  62. package/src/components/alert-dialog/__snapshots__/AlertDialog.test.tsx.snap +91 -0
  63. package/src/components/autocomplete/Autocomplete.test.tsx +2 -0
  64. package/src/components/autocomplete/Autocomplete.tsx +7 -0
  65. package/src/components/autocomplete/AutocompleteMultiple.test.tsx +2 -0
  66. package/src/components/autocomplete/AutocompleteMultiple.tsx +2 -0
  67. package/src/components/autocomplete/__snapshots__/Autocomplete.test.tsx.snap +2 -0
  68. package/src/components/autocomplete/__snapshots__/AutocompleteMultiple.test.tsx.snap +2 -0
  69. package/src/components/button/Button.stories.tsx +8 -2
  70. package/src/components/button/ButtonRoot.tsx +1 -1
  71. package/src/components/comment-block/CommentBlock.stories.tsx +3 -2
  72. package/src/components/comment-block/__snapshots__/CommentBlock.test.tsx.snap +1 -1
  73. package/src/components/dialog/Dialog.stories.tsx +62 -22
  74. package/src/components/dialog/Dialog.tsx +8 -2
  75. package/src/components/dialog/__snapshots__/Dialog.test.tsx.snap +99 -35
  76. package/src/components/icon/Icon.tsx +1 -0
  77. package/src/components/icon/__snapshots__/Icon.test.tsx.snap +2 -2
  78. package/src/components/lightbox/Lightbox.tsx +4 -1
  79. package/src/components/lightbox/__snapshots__/Lightbox.test.tsx.snap +7 -5
  80. package/src/components/message/Message.stories.tsx +8 -0
  81. package/src/components/message/Message.tsx +4 -2
  82. package/src/components/popover/Popover.tsx +4 -1
  83. package/src/components/popover/__snapshots__/Popover.test.tsx.snap +100 -80
  84. package/src/components/select/WithSelectContext.tsx +3 -60
  85. package/src/components/slideshow/Slideshow.stories.tsx +47 -7
  86. package/src/components/slideshow/Slideshow.test.tsx +4 -1
  87. package/src/components/slideshow/Slideshow.tsx +51 -26
  88. package/src/components/slideshow/SlideshowControls.stories.tsx +0 -4
  89. package/src/components/slideshow/SlideshowControls.tsx +23 -8
  90. package/src/components/slideshow/__snapshots__/Slideshow.test.tsx.snap +0 -5
  91. package/src/components/slideshow/useKeyNavigate.ts +28 -0
  92. package/src/components/slideshow/useSwipeNavigate.ts +18 -0
  93. package/src/components/switch/Switch.tsx +2 -0
  94. package/src/components/switch/__snapshots__/Switch.test.tsx.snap +8 -0
  95. package/src/hooks/useClickAway.tsx +6 -5
  96. package/src/hooks/useDelayedVisibility.tsx +22 -2
  97. package/src/hooks/useDisableBodyScroll.ts +16 -1
  98. package/src/hooks/useInfiniteScroll.tsx +14 -3
  99. package/src/hooks/useListenFocus.tsx +26 -0
  100. package/src/utils/ClickAwayProvider/ClickAwayProvider.tsx +23 -32
  101. package/types.d.ts +17 -8
  102. package/src/components/slideshow/useKeyOrSwipeNavigate.ts +0 -37
@@ -1,4 +1,4 @@
1
- import React, { Ref, useCallback, useEffect, useMemo, useRef, useState } from 'react';
1
+ import React, { Ref, useCallback, useMemo, useRef } from 'react';
2
2
 
3
3
  import classNames from 'classnames';
4
4
  import { uid } from 'uid';
@@ -10,7 +10,7 @@ import { Placement } from '@lumx/react/components/popover/Popover';
10
10
 
11
11
  import { getRootClassName, handleBasicClasses } from '@lumx/react/utils';
12
12
  import { mergeRefs } from '@lumx/react/utils/mergeRefs';
13
-
13
+ import { useListenFocus } from '@lumx/react/hooks/useListenFocus';
14
14
  import { CoreSelectProps, SelectVariant } from './constants';
15
15
 
16
16
  /** The display name of the component. */
@@ -25,57 +25,6 @@ export const DEFAULT_PROPS: Partial<CoreSelectProps> = {
25
25
  variant: SelectVariant.input,
26
26
  };
27
27
 
28
- /**
29
- * Listen on element focus to store the focus status.
30
- *
31
- * @param element Element to focus.
32
- * @param setIsFocus Setter used to store the focus status of the element.
33
- * @param isOpen Is the list opened
34
- * @param wasBlurred is it blurred
35
- * @param setWasBlurred set blurred
36
- * @param onBlur when its blurred
37
- */
38
- function useHandleElementFocus(
39
- element: HTMLElement | null,
40
- setIsFocus: (b: boolean) => void,
41
- isOpen: boolean,
42
- wasBlurred: boolean,
43
- setWasBlurred: (b: boolean) => void,
44
- onBlur?: () => void,
45
- ) {
46
- useEffect((): VoidFunction | void => {
47
- if (!element) {
48
- return undefined;
49
- }
50
-
51
- const setFocus = () => {
52
- if (!isOpen) {
53
- setIsFocus(true);
54
- }
55
- };
56
-
57
- const setBlur = () => {
58
- if (!isOpen) {
59
- setIsFocus(false);
60
-
61
- if (onBlur) {
62
- onBlur();
63
- }
64
- }
65
-
66
- setWasBlurred(true);
67
- };
68
-
69
- element.addEventListener('focus', setFocus);
70
- element.addEventListener('blur', setBlur);
71
-
72
- return () => {
73
- element.removeEventListener('focus', setFocus);
74
- element.removeEventListener('blur', setBlur);
75
- };
76
- }, [element, isOpen, onBlur, setIsFocus, setWasBlurred, wasBlurred]);
77
- }
78
-
79
28
  export const WithSelectContext = (
80
29
  SelectElement: React.FC<any>,
81
30
  {
@@ -94,7 +43,6 @@ export const WithSelectContext = (
94
43
  isRequired,
95
44
  isValid,
96
45
  label,
97
- onBlur,
98
46
  onClear,
99
47
  onDropdownClose,
100
48
  onInfiniteScroll,
@@ -110,10 +58,7 @@ export const WithSelectContext = (
110
58
  const selectId = useMemo(() => id || `select-${uid()}`, [id]);
111
59
  const anchorRef = useRef<HTMLElement>(null);
112
60
  const selectRef = useRef<HTMLDivElement>(null);
113
- const [isFocus, setIsFocus] = useState(Boolean(isOpen));
114
- const [wasBlurred, setWasBlurred] = useState(false);
115
-
116
- useHandleElementFocus(anchorRef.current, setIsFocus, Boolean(isOpen), wasBlurred, setWasBlurred, onBlur);
61
+ const isFocus = useListenFocus(anchorRef);
117
62
 
118
63
  const handleKeyboardNav = useCallback(
119
64
  (evt: React.KeyboardEvent<HTMLElement>) => {
@@ -129,8 +74,6 @@ export const WithSelectContext = (
129
74
  if (onDropdownClose) {
130
75
  onDropdownClose();
131
76
  }
132
-
133
- setWasBlurred(false);
134
77
  anchorRef?.current?.blur();
135
78
  };
136
79
 
@@ -1,6 +1,7 @@
1
- import { AspectRatio, ImageBlock, Slideshow, SlideshowItem, SlideshowProps } from '@lumx/react';
2
- import { boolean, number } from '@storybook/addon-knobs';
3
1
  import React from 'react';
2
+ import range from 'lodash/range';
3
+ import { AspectRatio, Button, FlexBox, ImageBlock, Slideshow, SlideshowItem } from '@lumx/react';
4
+ import { boolean, number } from '@storybook/addon-knobs';
4
5
  import { thumbnailsKnob } from '@lumx/react/stories/knobs/thumbnailsKnob';
5
6
 
6
7
  export default { title: 'LumX components/slideshow/Slideshow' };
@@ -12,16 +13,15 @@ export const Simple = ({ theme }: any) => {
12
13
  const autoPlay = boolean('Autoplay', false);
13
14
  const interval = number('Autoplay interval (in milliseconds)', 1000);
14
15
 
15
- const slideshowControlsProps: SlideshowProps['slideshowControlsProps'] = {
16
- nextButtonProps: { label: 'Next' },
17
- previousButtonProps: { label: 'Previous' },
18
- };
19
16
  return (
20
17
  <Slideshow
21
18
  activeIndex={activeIndex}
22
19
  autoPlay={autoPlay}
23
20
  interval={interval}
24
- slideshowControlsProps={slideshowControlsProps}
21
+ slideshowControlsProps={{
22
+ nextButtonProps: { label: 'Next' },
23
+ previousButtonProps: { label: 'Previous' },
24
+ }}
25
25
  theme={theme}
26
26
  groupBy={groupBy}
27
27
  style={{ width: '50%' }}
@@ -39,3 +39,43 @@ export const Simple = ({ theme }: any) => {
39
39
  </Slideshow>
40
40
  );
41
41
  };
42
+
43
+ export const ResponsiveSlideShowSwipe = () => {
44
+ const slides = range(3);
45
+ return (
46
+ <>
47
+ In responsive mode
48
+ <ul>
49
+ <li>The slideshow is swipe-able (horizontal left/right)</li>
50
+ <li>The vertical scroll should still be possible</li>
51
+ <li>Clicking on elements inside a slide should work correctly</li>
52
+ </ul>
53
+ <FlexBox vAlign="center">
54
+ <Slideshow
55
+ slideshowControlsProps={{
56
+ nextButtonProps: { label: 'Next' },
57
+ previousButtonProps: { label: 'Previous' },
58
+ }}
59
+ >
60
+ {slides.map((slide) => (
61
+ <SlideshowItem key={`${slide}`}>
62
+ <FlexBox
63
+ style={{ border: '1px solid grey', maxWidth: 300, height: 300 }}
64
+ hAlign="center"
65
+ vAlign="center"
66
+ >
67
+ <Button onClick={() => alert(`Clicked button ${slide}`)}>Button {slide}</Button>
68
+ </FlexBox>
69
+ </SlideshowItem>
70
+ ))}
71
+ </Slideshow>
72
+ </FlexBox>
73
+ {
74
+ /* Line padding to test the vertical scroll.*/
75
+ range(100).map((i) => (
76
+ <br key={i} />
77
+ ))
78
+ }
79
+ </>
80
+ );
81
+ };
@@ -1,4 +1,5 @@
1
1
  import React, { ReactElement } from 'react';
2
+ import pick from 'lodash/pick';
2
3
 
3
4
  import { mount, shallow } from 'enzyme';
4
5
  import 'jest-enzyme';
@@ -28,7 +29,9 @@ const setup = ({ ...propsOverride }: Partial<SlideshowProps> = {}, shallowRender
28
29
  describe(`<${Slideshow.displayName}>`, () => {
29
30
  // 1. Test render via snapshot.
30
31
  describe('Snapshots and structure', () => {
31
- itShouldRenderStories(stories, { or: [Slideshow, { path: [Slideshow, SlideshowControls] }] });
32
+ itShouldRenderStories(pick(stories, ['default', 'Simple']), {
33
+ or: [Slideshow, { path: [Slideshow, SlideshowControls] }],
34
+ });
32
35
  });
33
36
 
34
37
  // Common tests suite.
@@ -1,4 +1,4 @@
1
- import React, { CSSProperties, forwardRef, useCallback, useEffect, useRef, useState } from 'react';
1
+ import React, { CSSProperties, forwardRef, useCallback, useEffect, useState } from 'react';
2
2
 
3
3
  import classNames from 'classnames';
4
4
 
@@ -83,7 +83,8 @@ export const Slideshow: Comp<SlideshowProps, HTMLDivElement> = forwardRef((props
83
83
  ...forwardedProps
84
84
  } = props;
85
85
  const [currentIndex, setCurrentIndex] = useState(activeIndex as number);
86
- const rootRef: React.MutableRefObject<null> = useRef(null);
86
+ // Use state instead of a ref to make the slideshow controls update directly when the element is set.
87
+ const [element, setElement] = useState<HTMLDivElement>();
87
88
 
88
89
  // Number of slideshow items.
89
90
  const itemsCount = React.Children.count(children);
@@ -93,22 +94,40 @@ export const Slideshow: Comp<SlideshowProps, HTMLDivElement> = forwardRef((props
93
94
  const wrapperStyle: CSSProperties = { transform: `translateX(-${FULL_WIDTH_PERCENT * currentIndex}%)` };
94
95
 
95
96
  // Change current index to display next slide.
96
- const goToNextSlide = useCallback(() => {
97
- if (currentIndex === slidesCount - 1) {
98
- setCurrentIndex(0);
99
- } else if (currentIndex < slidesCount - 1) {
100
- setCurrentIndex((index) => index + 1);
101
- }
102
- }, [currentIndex, slidesCount, setCurrentIndex]);
97
+ const goToNextSlide = useCallback(
98
+ (loopback = true) => {
99
+ setCurrentIndex((index) => {
100
+ if (loopback && index === slidesCount - 1) {
101
+ // Loopback to the start.
102
+ return 0;
103
+ }
104
+ if (index < slidesCount - 1) {
105
+ // Next slide.
106
+ return index + 1;
107
+ }
108
+ return index;
109
+ });
110
+ },
111
+ [slidesCount, setCurrentIndex],
112
+ );
103
113
 
104
114
  // Change current index to display previous slide.
105
- const goToPreviousSlide = useCallback(() => {
106
- if (currentIndex === 0) {
107
- setCurrentIndex(slidesCount - 1);
108
- } else if (currentIndex > 0) {
109
- setCurrentIndex((index) => index - 1);
110
- }
111
- }, [currentIndex, slidesCount, setCurrentIndex]);
115
+ const goToPreviousSlide = useCallback(
116
+ (loopback = true) => {
117
+ setCurrentIndex((index) => {
118
+ if (loopback && index === 0) {
119
+ // Loopback to the end.
120
+ return slidesCount - 1;
121
+ }
122
+ if (index > 0) {
123
+ // Previous slide.
124
+ return index - 1;
125
+ }
126
+ return index;
127
+ });
128
+ },
129
+ [slidesCount, setCurrentIndex],
130
+ );
112
131
 
113
132
  // Auto play
114
133
  const [isAutoPlaying, setIsAutoPlaying] = useState(Boolean(autoPlay));
@@ -135,16 +154,22 @@ export const Slideshow: Comp<SlideshowProps, HTMLDivElement> = forwardRef((props
135
154
  );
136
155
 
137
156
  // Handle click or keyboard event to go to next slide.
138
- const handleControlNextSlide = useCallback(() => {
139
- setIsAutoPlaying(false);
140
- goToNextSlide();
141
- }, [goToNextSlide]);
157
+ const handleControlNextSlide = useCallback(
158
+ (loopback = true) => {
159
+ setIsAutoPlaying(false);
160
+ goToNextSlide(loopback);
161
+ },
162
+ [goToNextSlide],
163
+ );
142
164
 
143
165
  // Handle click or keyboard event to go to previous slide.
144
- const handleControlPreviousSlide = useCallback(() => {
145
- setIsAutoPlaying(false);
146
- goToPreviousSlide();
147
- }, [goToPreviousSlide]);
166
+ const handleControlPreviousSlide = useCallback(
167
+ (loopback = true) => {
168
+ setIsAutoPlaying(false);
169
+ goToPreviousSlide(loopback);
170
+ },
171
+ [goToPreviousSlide],
172
+ );
148
173
 
149
174
  // If the activeIndex props changes, update the current slide
150
175
  useEffect(() => {
@@ -160,7 +185,7 @@ export const Slideshow: Comp<SlideshowProps, HTMLDivElement> = forwardRef((props
160
185
  /* eslint-disable jsx-a11y/no-noninteractive-tabindex */
161
186
  return (
162
187
  <div
163
- ref={mergeRefs(ref, rootRef)}
188
+ ref={mergeRefs(ref, setElement)}
164
189
  {...forwardedProps}
165
190
  className={classNames(className, handleBasicClasses({ prefix: CLASSNAME, theme }), {
166
191
  [`${CLASSNAME}--fill-height`]: fillHeight,
@@ -183,7 +208,7 @@ export const Slideshow: Comp<SlideshowProps, HTMLDivElement> = forwardRef((props
183
208
  onNextClick={handleControlNextSlide}
184
209
  onPreviousClick={handleControlPreviousSlide}
185
210
  slidesCount={slidesCount}
186
- parentRef={rootRef}
211
+ parentRef={element}
187
212
  theme={theme}
188
213
  />
189
214
  </div>
@@ -6,7 +6,6 @@ import { thumbnailsKnob } from '@lumx/react/stories/knobs/thumbnailsKnob';
6
6
  export default { title: 'LumX components/slideshow/Slideshow controls' };
7
7
 
8
8
  export const Simple = () => {
9
- const parentRef = React.useRef(null);
10
9
  const slidesCount = 9;
11
10
  const [activeIndex, setActiveIndex] = React.useState(0);
12
11
  const maxIndex = slidesCount - 1;
@@ -19,7 +18,6 @@ export const Simple = () => {
19
18
  <SlideshowControls
20
19
  activeIndex={activeIndex}
21
20
  slidesCount={slidesCount}
22
- parentRef={parentRef}
23
21
  onNextClick={onNextClick}
24
22
  onPreviousClick={onPreviousClick}
25
23
  onPaginationClick={onPaginationClick}
@@ -30,7 +28,6 @@ export const Simple = () => {
30
28
  };
31
29
 
32
30
  export const ControllingSlideshow = ({ theme }: any) => {
33
- const parentRef = React.useRef(null);
34
31
  const items = thumbnailsKnob(6);
35
32
  const [activeIndex, setActiveIndex] = React.useState(0);
36
33
  const maxIndex = items.length - 1;
@@ -67,7 +64,6 @@ export const ControllingSlideshow = ({ theme }: any) => {
67
64
  <SlideshowControls
68
65
  activeIndex={activeIndex}
69
66
  slidesCount={items.length}
70
- parentRef={parentRef}
71
67
  onNextClick={onNextClick}
72
68
  onPreviousClick={onPreviousClick}
73
69
  onPaginationClick={onPaginationClick}
@@ -1,4 +1,4 @@
1
- import React, { forwardRef, RefObject, useMemo } from 'react';
1
+ import React, { forwardRef, RefObject, useCallback, useMemo } from 'react';
2
2
 
3
3
  import classNames from 'classnames';
4
4
  import range from 'lodash/range';
@@ -6,8 +6,10 @@ import range from 'lodash/range';
6
6
  import { mdiChevronLeft, mdiChevronRight } from '@lumx/icons';
7
7
  import { Emphasis, IconButton, IconButtonProps, Theme } from '@lumx/react';
8
8
  import { Comp, GenericProps, getRootClassName, handleBasicClasses } from '@lumx/react/utils';
9
- import { useKeyOrSwipeNavigate } from '@lumx/react/components/slideshow/useKeyOrSwipeNavigate';
9
+ import { WINDOW } from '@lumx/react/constants';
10
10
 
11
+ import { useSwipeNavigate } from './useSwipeNavigate';
12
+ import { useKeyNavigate } from './useKeyNavigate';
11
13
  import { PAGINATION_ITEM_SIZE, PAGINATION_ITEMS_MAX } from './constants';
12
14
  import { usePaginationVisibleRange } from './usePaginationVisibleRange';
13
15
 
@@ -20,8 +22,8 @@ export interface SlideshowControlsProps extends GenericProps {
20
22
  /** Props to pass to the next button (minus those already set by the SlideshowControls props). */
21
23
  nextButtonProps: Pick<IconButtonProps, 'label'> &
22
24
  Omit<IconButtonProps, 'label' | 'onClick' | 'icon' | 'emphasis' | 'color'>;
23
- /** Reference to the parent element. */
24
- parentRef: RefObject<HTMLDivElement>;
25
+ /** Reference to the parent element on which we want to listen touch swipe. */
26
+ parentRef?: RefObject<HTMLDivElement> | HTMLDivElement;
25
27
  /** Props to pass to the previous button (minus those already set by the SlideshowControls props). */
26
28
  previousButtonProps: Pick<IconButtonProps, 'label'> &
27
29
  Omit<IconButtonProps, 'label' | 'onClick' | 'icon' | 'emphasis' | 'color'>;
@@ -30,11 +32,11 @@ export interface SlideshowControlsProps extends GenericProps {
30
32
  /** Theme adapting the component to light or dark background. */
31
33
  theme?: Theme;
32
34
  /** On next button click callback. */
33
- onNextClick?(): void;
35
+ onNextClick?(loopback?: boolean): void;
34
36
  /** On pagination change callback. */
35
37
  onPaginationClick?(index: number): void;
36
38
  /** On previous button click callback. */
37
- onPreviousClick?(): void;
39
+ onPreviousClick?(loopback?: boolean): void;
38
40
  }
39
41
 
40
42
  /**
@@ -77,8 +79,21 @@ export const SlideshowControls: Comp<SlideshowControlsProps, HTMLDivElement> = f
77
79
  ...forwardedProps
78
80
  } = props;
79
81
 
80
- // Listen to keyboard & touch swipe to navigate left & right.
81
- useKeyOrSwipeNavigate(parentRef.current, onNextClick, onPreviousClick);
82
+ let parent;
83
+ if (WINDOW) {
84
+ // Checking window object to avoid errors in SSR.
85
+ parent = parentRef instanceof HTMLElement ? parentRef : parentRef?.current;
86
+ }
87
+ // Listen to keyboard navigate left & right.
88
+ useKeyNavigate(parent, onNextClick, onPreviousClick);
89
+ // Listen to touch swipe navigate left & right.
90
+ useSwipeNavigate(
91
+ parent,
92
+ // Go next without loopback.
93
+ useCallback(() => onNextClick?.(false), [onNextClick]),
94
+ // Go previous without loopback.
95
+ useCallback(() => onPreviousClick?.(false), [onPreviousClick]),
96
+ );
82
97
 
83
98
  // Pagination "bullet" range.
84
99
  const visibleRange = usePaginationVisibleRange(activeIndex as number, slidesCount);
@@ -133,11 +133,6 @@ Array [
133
133
  onNextClick={[Function]}
134
134
  onPaginationClick={[Function]}
135
135
  onPreviousClick={[Function]}
136
- parentRef={
137
- Object {
138
- "current": null,
139
- }
140
- }
141
136
  previousButtonProps={
142
137
  Object {
143
138
  "label": "Previous",
@@ -0,0 +1,28 @@
1
+ import { useEffect } from 'react';
2
+
3
+ /**
4
+ * Listen keyboard to navigate left and right.
5
+ */
6
+ export function useKeyNavigate(element?: HTMLElement | null, onNext?: () => void, onPrevious?: () => void): void {
7
+ useEffect(() => {
8
+ if (!element) return undefined;
9
+ const onKeyNavigate = (evt: KeyboardEvent) => {
10
+ let callback;
11
+ if (evt?.key === 'ArrowRight') {
12
+ callback = onPrevious;
13
+ } else if (evt?.key === 'ArrowLeft') {
14
+ callback = onNext;
15
+ }
16
+ if (!callback) return;
17
+
18
+ callback();
19
+ evt.preventDefault();
20
+ evt.stopPropagation();
21
+ };
22
+
23
+ element.addEventListener('keydown', onKeyNavigate);
24
+ return () => {
25
+ element.removeEventListener('keydown', onKeyNavigate);
26
+ };
27
+ }, [onPrevious, onNext, element]);
28
+ }