@faststore/ui 2.0.53-alpha.0 → 2.0.56-alpha.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 (41) hide show
  1. package/dist/index.d.ts +0 -6
  2. package/dist/index.js +0 -3
  3. package/dist/index.js.map +1 -1
  4. package/package.json +2 -2
  5. package/src/components/atoms/Slider/styles.scss +137 -0
  6. package/src/components/molecules/Modal/styles.scss +97 -0
  7. package/src/components/organisms/PriceRange/styles.scss +36 -0
  8. package/src/index.ts +0 -9
  9. package/src/styles/components.scss +3 -0
  10. package/dist/components/atoms/Slider/Slider.d.ts +0 -67
  11. package/dist/components/atoms/Slider/Slider.js +0 -48
  12. package/dist/components/atoms/Slider/Slider.js.map +0 -1
  13. package/dist/components/atoms/Slider/index.d.ts +0 -2
  14. package/dist/components/atoms/Slider/index.js +0 -2
  15. package/dist/components/atoms/Slider/index.js.map +0 -1
  16. package/dist/components/molecules/Modal/Modal.d.ts +0 -25
  17. package/dist/components/molecules/Modal/Modal.js +0 -31
  18. package/dist/components/molecules/Modal/Modal.js.map +0 -1
  19. package/dist/components/molecules/Modal/ModalContent.d.ts +0 -10
  20. package/dist/components/molecules/Modal/ModalContent.js +0 -23
  21. package/dist/components/molecules/Modal/ModalContent.js.map +0 -1
  22. package/dist/components/molecules/Modal/index.d.ts +0 -2
  23. package/dist/components/molecules/Modal/index.js +0 -2
  24. package/dist/components/molecules/Modal/index.js.map +0 -1
  25. package/dist/components/molecules/Modal/useTrapFocus.d.ts +0 -8
  26. package/dist/components/molecules/Modal/useTrapFocus.js +0 -76
  27. package/dist/components/molecules/Modal/useTrapFocus.js.map +0 -1
  28. package/dist/components/molecules/PriceRange/PriceRange.d.ts +0 -47
  29. package/dist/components/molecules/PriceRange/PriceRange.js +0 -28
  30. package/dist/components/molecules/PriceRange/PriceRange.js.map +0 -1
  31. package/dist/components/molecules/PriceRange/index.d.ts +0 -2
  32. package/dist/components/molecules/PriceRange/index.js +0 -2
  33. package/dist/components/molecules/PriceRange/index.js.map +0 -1
  34. package/src/components/atoms/Slider/Slider.tsx +0 -182
  35. package/src/components/atoms/Slider/index.ts +0 -2
  36. package/src/components/molecules/Modal/Modal.tsx +0 -82
  37. package/src/components/molecules/Modal/ModalContent.tsx +0 -90
  38. package/src/components/molecules/Modal/index.tsx +0 -2
  39. package/src/components/molecules/Modal/useTrapFocus.ts +0 -110
  40. package/src/components/molecules/PriceRange/PriceRange.tsx +0 -108
  41. package/src/components/molecules/PriceRange/index.ts +0 -2
@@ -1,8 +0,0 @@
1
- import type { RefObject } from 'react';
2
- interface TrapFocusParams {
3
- beforeElementRef: RefObject<HTMLElement>;
4
- trapFocusRef: RefObject<HTMLElement>;
5
- afterElementRef: RefObject<HTMLElement>;
6
- }
7
- declare const useTrapFocus: ({ trapFocusRef, beforeElementRef, afterElementRef, }: TrapFocusParams) => void;
8
- export default useTrapFocus;
@@ -1,76 +0,0 @@
1
- import { useEffect, useRef } from 'react';
2
- import { tabbable } from 'tabbable';
3
- /*
4
- * Element that will maintain the focus inside trapFocusRef, focus the first element,
5
- * and focus back on the element that was in focus when useTrapFocus was triggered.
6
- *
7
- * Inspired by Reakit useTrapFocus https://github.com/reakit/reakit/blob/a211d94da9f3b683182568a56479b91afb1b85ae/packages/reakit/src/Dialog/__utils/useFocusTrap.ts
8
- */
9
- const useTrapFocus = ({ trapFocusRef, beforeElementRef, afterElementRef, }) => {
10
- const tabbableNodesRef = useRef();
11
- const nodeToRestoreRef = useRef(document.hasFocus() ? document.activeElement : null);
12
- // Focus back on the element that was focused when useTrapFocus is triggered.
13
- useEffect(() => {
14
- const nodeToRestore = nodeToRestoreRef.current;
15
- return () => {
16
- nodeToRestore?.focus();
17
- };
18
- }, [nodeToRestoreRef]);
19
- // Set focus on first tabbable element
20
- useEffect(() => {
21
- if (!trapFocusRef.current) {
22
- return;
23
- }
24
- if (!tabbableNodesRef.current) {
25
- tabbableNodesRef.current = tabbable(trapFocusRef.current);
26
- }
27
- const [firstTabbable] = tabbableNodesRef.current;
28
- if (!firstTabbable) {
29
- trapFocusRef.current.focus();
30
- return;
31
- }
32
- firstTabbable.focus();
33
- }, [trapFocusRef]);
34
- // Handle loop focus. Set keydown and focusin event listeners
35
- useEffect(() => {
36
- if (!trapFocusRef.current ||
37
- !beforeElementRef.current ||
38
- !afterElementRef.current) {
39
- return;
40
- }
41
- const beforeElement = beforeElementRef.current;
42
- const afterElement = afterElementRef.current;
43
- const trapFocus = trapFocusRef.current;
44
- const handleLoopFocus = (nativeEvent) => {
45
- if (!document.hasFocus()) {
46
- return;
47
- }
48
- tabbableNodesRef.current = tabbable(trapFocusRef.current);
49
- if (!tabbableNodesRef.current.length) {
50
- trapFocus.focus();
51
- }
52
- /*
53
- * Handle loop focus from beforeElementRef. This node can only be focused if the user press shift tab.
54
- * It will focus the last element of the trapFocusRef.
55
- */
56
- if (nativeEvent.target === beforeElement) {
57
- tabbableNodesRef.current[tabbableNodesRef.current.length - 1]?.focus();
58
- }
59
- /*
60
- * Handle loop focus from afterElementRef. This node can only be focused if the user press tab.
61
- * It will focus the first element of the trapFocusRef.
62
- */
63
- if (nativeEvent.target === afterElement) {
64
- tabbableNodesRef.current[0]?.focus();
65
- }
66
- };
67
- beforeElement?.addEventListener('focusin', handleLoopFocus);
68
- afterElement?.addEventListener('focusin', handleLoopFocus);
69
- return () => {
70
- beforeElement?.removeEventListener('focusin', handleLoopFocus);
71
- afterElement?.removeEventListener('focusin', handleLoopFocus);
72
- };
73
- }, [tabbableNodesRef, afterElementRef, beforeElementRef, trapFocusRef]);
74
- };
75
- export default useTrapFocus;
76
- //# sourceMappingURL=useTrapFocus.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"useTrapFocus.js","sourceRoot":"","sources":["../../../../src/components/molecules/Modal/useTrapFocus.ts"],"names":[],"mappings":"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"}
@@ -1,47 +0,0 @@
1
- import React from 'react';
2
- import type { AriaAttributes } from 'react';
3
- import type { PriceProps } from '@faststore/components';
4
- import type { SliderProps } from '../../atoms/Slider';
5
- export type PriceRangeProps = SliderProps & {
6
- /**
7
- * The current use case variant for prices.
8
- */
9
- variant?: PriceProps['variant'];
10
- /**
11
- * Formatter function that transforms the raw price value and render the result.
12
- */
13
- formatter: PriceProps['formatter'];
14
- /**
15
- * Returns the value of element's class content attribute.
16
- */
17
- className?: string;
18
- /**
19
- * Defines a string value that labels the current element.
20
- */
21
- 'aria-label'?: AriaAttributes['aria-label'];
22
- };
23
- type PriceRangeRefType = {
24
- setPriceRangeValues: (values: {
25
- min: number;
26
- max: number;
27
- }) => void;
28
- };
29
- declare const PriceRange: React.ForwardRefExoticComponent<SliderProps & {
30
- /**
31
- * The current use case variant for prices.
32
- */
33
- variant?: PriceProps['variant'];
34
- /**
35
- * Formatter function that transforms the raw price value and render the result.
36
- */
37
- formatter: PriceProps['formatter'];
38
- /**
39
- * Returns the value of element's class content attribute.
40
- */
41
- className?: string | undefined;
42
- /**
43
- * Defines a string value that labels the current element.
44
- */
45
- 'aria-label'?: AriaAttributes['aria-label'];
46
- } & React.RefAttributes<PriceRangeRefType | undefined>>;
47
- export default PriceRange;
@@ -1,28 +0,0 @@
1
- import React, { useRef, useImperativeHandle, forwardRef } from 'react';
2
- import { Price } from '@faststore/components';
3
- import Slider from '../../atoms/Slider';
4
- const PriceRange = forwardRef(function PriceRange({ className, formatter, max, min, step, onChange, onEnd, testId = 'store-price-range', variant, 'aria-label': ariaLabel, }, ref) {
5
- const sliderRef = useRef();
6
- useImperativeHandle(ref, () => ({
7
- setPriceRangeValues: (values) => {
8
- onChange?.(values);
9
- sliderRef.current?.setSliderValues(values);
10
- },
11
- }));
12
- return (React.createElement("div", { "data-fs-price-range": true, "data-testid": testId, className: className },
13
- React.createElement(Slider, { ref: sliderRef, min: min, max: max, step: step, onEnd: onEnd, "aria-label": ariaLabel, onChange: (value) => onChange?.(value), minValueLabelComponent: (minValue) => {
14
- const minPercent = (minValue / max.absolute) * 100;
15
- return (React.createElement(Price, { value: minValue, variant: variant, formatter: formatter, "data-price-range-value-label": "min", style: {
16
- position: 'absolute',
17
- left: `calc(${minPercent}% + (${8 - minPercent * 0.2}px))`,
18
- } }));
19
- }, maxValueLabelComponent: (maxValue) => {
20
- const maxPercent = (maxValue / max.absolute) * 100;
21
- return (React.createElement(Price, { formatter: formatter, variant: variant, value: maxValue, "data-price-range-value-label": "max", style: {
22
- position: 'absolute',
23
- left: `calc(${maxPercent}% + (${8 - maxPercent * 0.2}px))`,
24
- } }));
25
- } })));
26
- });
27
- export default PriceRange;
28
- //# sourceMappingURL=PriceRange.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"PriceRange.js","sourceRoot":"","sources":["../../../../src/components/molecules/PriceRange/PriceRange.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,EAAE,MAAM,EAAE,mBAAmB,EAAE,UAAU,EAAE,MAAM,OAAO,CAAA;AAGtE,OAAO,EAAE,KAAK,EAAE,MAAM,uBAAuB,CAAA;AAG7C,OAAO,MAAM,MAAM,oBAAoB,CAAA;AA0BvC,MAAM,UAAU,GAAG,UAAU,CAC3B,SAAS,UAAU,CACjB,EACE,SAAS,EACT,SAAS,EACT,GAAG,EACH,GAAG,EACH,IAAI,EACJ,QAAQ,EACR,KAAK,EACL,MAAM,GAAG,mBAAmB,EAC5B,OAAO,EACP,YAAY,EAAE,SAAS,GACxB,EACD,GAAG;IAEH,MAAM,SAAS,GAAG,MAAM,EAEpB,CAAA;IAEJ,mBAAmB,CAAC,GAAG,EAAE,GAAG,EAAE,CAAC,CAAC;QAC9B,mBAAmB,EAAE,CAAC,MAAoC,EAAE,EAAE;YAC5D,QAAQ,EAAE,CAAC,MAAM,CAAC,CAAA;YAClB,SAAS,CAAC,OAAO,EAAE,eAAe,CAAC,MAAM,CAAC,CAAA;QAC5C,CAAC;KACF,CAAC,CAAC,CAAA;IAEH,OAAO,CACL,yEAAsC,MAAM,EAAE,SAAS,EAAE,SAAS;QAChE,oBAAC,MAAM,IACL,GAAG,EAAE,SAAS,EACd,GAAG,EAAE,GAAG,EACR,GAAG,EAAE,GAAG,EACR,IAAI,EAAE,IAAI,EACV,KAAK,EAAE,KAAK,gBACA,SAAS,EACrB,QAAQ,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,QAAQ,EAAE,CAAC,KAAK,CAAC,EACtC,sBAAsB,EAAE,CAAC,QAAQ,EAAE,EAAE;gBACnC,MAAM,UAAU,GAAG,CAAC,QAAQ,GAAG,GAAG,CAAC,QAAQ,CAAC,GAAG,GAAG,CAAA;gBAElD,OAAO,CACL,oBAAC,KAAK,IACJ,KAAK,EAAE,QAAQ,EACf,OAAO,EAAE,OAAO,EAChB,SAAS,EAAE,SAAS,kCACS,KAAK,EAClC,KAAK,EAAE;wBACL,QAAQ,EAAE,UAAU;wBACpB,IAAI,EAAE,QAAQ,UAAU,QAAQ,CAAC,GAAG,UAAU,GAAG,GAAG,MAAM;qBAC3D,GACD,CACH,CAAA;YACH,CAAC,EACD,sBAAsB,EAAE,CAAC,QAAQ,EAAE,EAAE;gBACnC,MAAM,UAAU,GAAG,CAAC,QAAQ,GAAG,GAAG,CAAC,QAAQ,CAAC,GAAG,GAAG,CAAA;gBAElD,OAAO,CACL,oBAAC,KAAK,IACJ,SAAS,EAAE,SAAS,EACpB,OAAO,EAAE,OAAO,EAChB,KAAK,EAAE,QAAQ,kCACc,KAAK,EAClC,KAAK,EAAE;wBACL,QAAQ,EAAE,UAAU;wBACpB,IAAI,EAAE,QAAQ,UAAU,QAAQ,CAAC,GAAG,UAAU,GAAG,GAAG,MAAM;qBAC3D,GACD,CACH,CAAA;YACH,CAAC,GACD,CACE,CACP,CAAA;AACH,CAAC,CACF,CAAA;AAED,eAAe,UAAU,CAAA"}
@@ -1,2 +0,0 @@
1
- export { default } from './PriceRange';
2
- export type { PriceRangeProps } from './PriceRange';
@@ -1,2 +0,0 @@
1
- export { default } from './PriceRange';
2
- //# sourceMappingURL=index.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../src/components/molecules/PriceRange/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAA"}
@@ -1,182 +0,0 @@
1
- /**
2
- * This code is inspired by the work of [sandra-lewis](https://codesandbox.io/u/sandra-lewis)
3
- */
4
- import React, {
5
- useState,
6
- useMemo,
7
- useImperativeHandle,
8
- forwardRef,
9
- } from 'react'
10
- import type { ReactNode } from 'react'
11
-
12
- interface Range {
13
- absolute: number
14
- selected: number
15
- }
16
-
17
- export type SliderProps = {
18
- /**
19
- * The minimum value of the slider.
20
- */
21
- min: Range
22
- /**
23
- * The maximum value of the slider.
24
- */
25
- max: Range
26
- /**
27
- * Specifies the number interval to be used in the inputs.
28
- */
29
- step?: number
30
- /**
31
- * ID to find this component in testing tools (e.g.: cypress, testing library, and jest).
32
- *
33
- * @default 'store-slider'
34
- */
35
- testId?: string
36
- /**
37
- * Callback that fires when the slider value changes.
38
- */
39
- onChange?: (value: { min: number; max: number }) => void
40
- /**
41
- * Callback that fires when the slider value ends changing.
42
- */
43
- onEnd?: (value: { min: number; max: number }) => void
44
- /**
45
- * A function used to set a human-readable value text based on the slider's current value.
46
- */
47
- getAriaValueText?(value: number, thumb?: 'min' | 'max'): string
48
- /**
49
- * Returns the value of element's class content attribute.
50
- */
51
- className?: string
52
- /**
53
- * Component that renders min value label above the left thumb.
54
- */
55
- minValueLabelComponent?: (minValue: number) => ReactNode
56
- /**
57
- * Component that renders max value label above the right thumb.
58
- */
59
- maxValueLabelComponent?: (maxValue: number) => ReactNode
60
- }
61
-
62
- type SliderRefType = {
63
- setSliderValues: (values: { min: number; max: number }) => void
64
- }
65
-
66
- const percent = (value: number, min: number, max: number) =>
67
- Math.round(((value - min) / (max - min)) * 100)
68
-
69
- const Slider = forwardRef<SliderRefType | undefined, SliderProps>(
70
- function Slider(
71
- {
72
- min,
73
- max,
74
- onChange,
75
- onEnd,
76
- testId = 'store-slider',
77
- getAriaValueText,
78
- className,
79
- step,
80
- minValueLabelComponent,
81
- maxValueLabelComponent,
82
- },
83
- ref
84
- ) {
85
- const widthPercent = useMemo(
86
- () => (max.absolute - min.absolute) / 100,
87
- [max.absolute, min.absolute]
88
- )
89
- const [minPercent, setMinPercent] = useState(() =>
90
- percent(min.selected, min.absolute, max.absolute)
91
- )
92
-
93
- const [maxPercent, setMaxPercent] = useState(() =>
94
- percent(max.selected, min.absolute, max.absolute)
95
- )
96
-
97
- const [minVal, setMinVal] = useState(() =>
98
- Math.round(min.absolute + minPercent * widthPercent)
99
- )
100
- const [maxVal, setMaxVal] = useState(() =>
101
- Math.round(min.absolute + maxPercent * widthPercent)
102
- )
103
-
104
- useImperativeHandle(ref, () => ({
105
- setSliderValues: (values: { min: number; max: number }) => {
106
- const sliderMinValue = Math.min(Number(values.min), maxVal)
107
- setMinVal(sliderMinValue)
108
- setMinPercent(percent(sliderMinValue, min.absolute, max.absolute))
109
-
110
- if (values.max > max.absolute) {
111
- setMaxVal(max.absolute)
112
- setMaxPercent(percent(max.absolute, min.absolute, max.absolute))
113
- return
114
- }
115
-
116
- const sliderMaxValue = Math.max(Number(values.max), minVal)
117
- setMaxVal(sliderMaxValue)
118
- setMaxPercent(percent(sliderMaxValue, min.absolute, max.absolute))
119
- },
120
- }))
121
-
122
- return (
123
- <div data-fs-slider data-testid={testId} className={className}>
124
- <div
125
- data-slider-range
126
- style={{
127
- left: `${minPercent}%`,
128
- width: `${maxPercent - minPercent}%`,
129
- }}
130
- />
131
- <input
132
- type="range"
133
- min={Math.round(min.absolute)}
134
- max={Math.round(max.absolute)}
135
- value={minVal}
136
- step={step}
137
- onMouseUp={() => onEnd?.({ min: minVal, max: maxVal })}
138
- onTouchEnd={() => onEnd?.({ min: minVal, max: maxVal })}
139
- onChange={(event) => {
140
- const minValue = Math.min(Number(event.target.value), maxVal)
141
-
142
- setMinVal(minValue)
143
- setMinPercent(percent(minValue, min.absolute, max.absolute))
144
- onChange?.({ min: minValue, max: maxVal })
145
- }}
146
- data-slider-thumb="left"
147
- aria-valuemin={min.absolute}
148
- aria-valuemax={max.absolute}
149
- aria-valuenow={minVal}
150
- aria-label={String(minVal)}
151
- aria-labelledby={getAriaValueText?.(minVal, 'min')}
152
- />
153
- {minValueLabelComponent && minValueLabelComponent(minVal)}
154
- <input
155
- type="range"
156
- min={Math.round(min.absolute)}
157
- max={Math.round(max.absolute)}
158
- value={maxVal}
159
- step={step}
160
- onMouseUp={() => onEnd?.({ min: minVal, max: maxVal })}
161
- onTouchEnd={() => onEnd?.({ min: minVal, max: maxVal })}
162
- onChange={(event) => {
163
- const maxValue = Math.max(Number(event.target.value), minVal)
164
-
165
- setMaxVal(maxValue)
166
- setMaxPercent(percent(maxValue, min.absolute, max.absolute))
167
- onChange?.({ min: minVal, max: maxValue })
168
- }}
169
- data-slider-thumb="right"
170
- aria-valuemin={min.absolute}
171
- aria-valuemax={max.absolute}
172
- aria-valuenow={maxVal}
173
- aria-label={String(maxVal)}
174
- aria-labelledby={getAriaValueText?.(maxVal, 'max')}
175
- />
176
- {maxValueLabelComponent && maxValueLabelComponent(maxVal)}
177
- </div>
178
- )
179
- }
180
- )
181
-
182
- export default Slider
@@ -1,2 +0,0 @@
1
- export { default } from './Slider'
2
- export type { SliderProps } from './Slider'
@@ -1,82 +0,0 @@
1
- import type {
2
- AriaAttributes,
3
- KeyboardEvent,
4
- MouseEvent,
5
- PropsWithChildren,
6
- } from 'react'
7
- import React from 'react'
8
- import { createPortal } from 'react-dom'
9
-
10
- import { Overlay } from '@faststore/components'
11
- import type { ModalContentProps } from './ModalContent'
12
- import ModalContent from './ModalContent'
13
-
14
- export interface ModalProps extends ModalContentProps {
15
- /**
16
- * ID to find this component in testing tools (e.g.: cypress, testing library, and jest).
17
- */
18
- testId?: string
19
- /**
20
- * Identifies the element (or elements) that labels the current element.
21
- * @see aria-labelledby https://www.w3.org/TR/wai-aria-1.1/#aria-labelledby
22
- */
23
- 'aria-labelledby'?: AriaAttributes['aria-label']
24
-
25
- /**
26
- * This function is called whenever the user hits "Escape" or clicks outside
27
- * the dialog.
28
- */
29
- onDismiss?: (event: MouseEvent | KeyboardEvent) => void
30
- /**
31
- * Controls whether or not the dialog is open.
32
- */
33
- isOpen: boolean
34
- }
35
-
36
- /*
37
- * This component is based on @reach/dialog.
38
- * https://github.com/reach/reach-ui/blob/main/packages/dialog/src/index.tsx
39
- * https://reach.tech/dialog
40
- */
41
-
42
- const Modal = ({
43
- isOpen,
44
- children,
45
- onDismiss,
46
- testId = 'store-modal',
47
- ...otherProps
48
- }: PropsWithChildren<ModalProps>) => {
49
- const handleBackdropClick = (event: MouseEvent) => {
50
- if (event.defaultPrevented) {
51
- return
52
- }
53
-
54
- event.stopPropagation()
55
- onDismiss?.(event)
56
- }
57
-
58
- const handleBackdropKeyDown = (event: KeyboardEvent) => {
59
- if (event.key !== 'Escape' || event.defaultPrevented) {
60
- return
61
- }
62
-
63
- event.stopPropagation()
64
- onDismiss?.(event)
65
- }
66
-
67
- return isOpen
68
- ? createPortal(
69
- <Overlay
70
- onClick={handleBackdropClick}
71
- onKeyDown={handleBackdropKeyDown}
72
- >
73
- <ModalContent {...otherProps} testId={testId}>
74
- {children}
75
- </ModalContent>
76
- </Overlay>,
77
- document.body
78
- )
79
- : null
80
- }
81
-
82
- export default Modal
@@ -1,90 +0,0 @@
1
- import type {
2
- DetailedHTMLProps,
3
- HTMLAttributes,
4
- MouseEvent,
5
- RefObject,
6
- } from 'react'
7
- import React, { useRef } from 'react'
8
-
9
- import useTrapFocus from './useTrapFocus'
10
-
11
- interface ModalContentPureProps
12
- extends Omit<
13
- DetailedHTMLProps<HTMLAttributes<HTMLDivElement>, HTMLDivElement>,
14
- 'ref'
15
- > {
16
- beforeElementRef: RefObject<HTMLDivElement>
17
- trapFocusRef: RefObject<HTMLDivElement>
18
- afterElementRef: RefObject<HTMLDivElement>
19
- testId?: string
20
- }
21
-
22
- const ModalContentPure = ({
23
- beforeElementRef,
24
- trapFocusRef,
25
- afterElementRef,
26
- testId = 'store-modal-content',
27
- children,
28
- ...otherProps
29
- }: ModalContentPureProps) => {
30
- return (
31
- <>
32
- <div
33
- ref={beforeElementRef}
34
- data-testid="beforeElement"
35
- tabIndex={0}
36
- aria-hidden="true"
37
- />
38
- <div
39
- data-fs-modal-content
40
- data-testid={testId}
41
- ref={trapFocusRef}
42
- aria-modal="true"
43
- role="dialog"
44
- tabIndex={-1}
45
- {...otherProps}
46
- >
47
- {children}
48
- </div>
49
- <div
50
- ref={afterElementRef}
51
- data-testid="afterElement"
52
- tabIndex={0}
53
- aria-hidden="true"
54
- />
55
- </>
56
- )
57
- }
58
-
59
- export type ModalContentProps = Omit<
60
- ModalContentPureProps,
61
- 'trapFocusRef' | 'onClick' | 'beforeElementRef' | 'afterElementRef'
62
- >
63
-
64
- const ModalContent = ({ children, ...otherProps }: ModalContentProps) => {
65
- const trapFocusRef = useRef<HTMLDivElement>(null)
66
- const beforeElementRef = useRef<HTMLDivElement>(null)
67
- const afterElementRef = useRef<HTMLDivElement>(null)
68
-
69
- useTrapFocus({
70
- beforeElementRef,
71
- trapFocusRef,
72
- afterElementRef,
73
- })
74
-
75
- return (
76
- <ModalContentPure
77
- {...otherProps}
78
- trapFocusRef={trapFocusRef}
79
- beforeElementRef={beforeElementRef}
80
- afterElementRef={afterElementRef}
81
- onClick={(event: MouseEvent) => {
82
- event.stopPropagation()
83
- }}
84
- >
85
- {children}
86
- </ModalContentPure>
87
- )
88
- }
89
-
90
- export default ModalContent
@@ -1,2 +0,0 @@
1
- export { default } from './Modal'
2
- export type { ModalProps } from './Modal'
@@ -1,110 +0,0 @@
1
- import { useEffect, useRef } from 'react'
2
- import type { RefObject } from 'react'
3
- import type { FocusableElement } from 'tabbable'
4
- import { tabbable } from 'tabbable'
5
-
6
- interface TrapFocusParams {
7
- beforeElementRef: RefObject<HTMLElement>
8
- trapFocusRef: RefObject<HTMLElement>
9
- afterElementRef: RefObject<HTMLElement>
10
- }
11
-
12
- /*
13
- * Element that will maintain the focus inside trapFocusRef, focus the first element,
14
- * and focus back on the element that was in focus when useTrapFocus was triggered.
15
- *
16
- * Inspired by Reakit useTrapFocus https://github.com/reakit/reakit/blob/a211d94da9f3b683182568a56479b91afb1b85ae/packages/reakit/src/Dialog/__utils/useFocusTrap.ts
17
- */
18
- const useTrapFocus = ({
19
- trapFocusRef,
20
- beforeElementRef,
21
- afterElementRef,
22
- }: TrapFocusParams) => {
23
- const tabbableNodesRef = useRef<FocusableElement[]>()
24
- const nodeToRestoreRef = useRef<HTMLElement | null>(
25
- document.hasFocus() ? (document.activeElement as HTMLElement) : null
26
- )
27
-
28
- // Focus back on the element that was focused when useTrapFocus is triggered.
29
- useEffect(() => {
30
- const nodeToRestore = nodeToRestoreRef.current
31
-
32
- return () => {
33
- nodeToRestore?.focus()
34
- }
35
- }, [nodeToRestoreRef])
36
-
37
- // Set focus on first tabbable element
38
- useEffect(() => {
39
- if (!trapFocusRef.current) {
40
- return
41
- }
42
-
43
- if (!tabbableNodesRef.current) {
44
- tabbableNodesRef.current = tabbable(trapFocusRef.current)
45
- }
46
-
47
- const [firstTabbable] = tabbableNodesRef.current
48
-
49
- if (!firstTabbable) {
50
- trapFocusRef.current.focus()
51
-
52
- return
53
- }
54
-
55
- firstTabbable.focus()
56
- }, [trapFocusRef])
57
-
58
- // Handle loop focus. Set keydown and focusin event listeners
59
- useEffect(() => {
60
- if (
61
- !trapFocusRef.current ||
62
- !beforeElementRef.current ||
63
- !afterElementRef.current
64
- ) {
65
- return
66
- }
67
-
68
- const beforeElement = beforeElementRef.current
69
- const afterElement = afterElementRef.current
70
- const trapFocus = trapFocusRef.current
71
-
72
- const handleLoopFocus = (nativeEvent: FocusEvent) => {
73
- if (!document.hasFocus()) {
74
- return
75
- }
76
-
77
- tabbableNodesRef.current = tabbable(trapFocusRef.current!)
78
-
79
- if (!tabbableNodesRef.current.length) {
80
- trapFocus.focus()
81
- }
82
-
83
- /*
84
- * Handle loop focus from beforeElementRef. This node can only be focused if the user press shift tab.
85
- * It will focus the last element of the trapFocusRef.
86
- */
87
- if (nativeEvent.target === beforeElement) {
88
- tabbableNodesRef.current[tabbableNodesRef.current.length - 1]?.focus()
89
- }
90
-
91
- /*
92
- * Handle loop focus from afterElementRef. This node can only be focused if the user press tab.
93
- * It will focus the first element of the trapFocusRef.
94
- */
95
- if (nativeEvent.target === afterElement) {
96
- tabbableNodesRef.current[0]?.focus()
97
- }
98
- }
99
-
100
- beforeElement?.addEventListener('focusin', handleLoopFocus)
101
- afterElement?.addEventListener('focusin', handleLoopFocus)
102
-
103
- return () => {
104
- beforeElement?.removeEventListener('focusin', handleLoopFocus)
105
- afterElement?.removeEventListener('focusin', handleLoopFocus)
106
- }
107
- }, [tabbableNodesRef, afterElementRef, beforeElementRef, trapFocusRef])
108
- }
109
-
110
- export default useTrapFocus