react-responsive-modal 7.0.0 → 7.2.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.
- package/LICENSE +21 -0
- package/dist/index.cjs +87 -93
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +4 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.js +76 -86
- package/dist/index.js.map +1 -1
- package/package.json +67 -65
- package/src/CloseIcon.tsx +1 -1
- package/src/FocusTrap.tsx +2 -1
- package/src/index.tsx +10 -3
- package/src/useScrollLock.ts +1 -1
- package/CHANGELOG.md +0 -153
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
The MIT License (MIT)
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2020 Léo Pradel
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/dist/index.cjs
CHANGED
|
@@ -1,5 +1,8 @@
|
|
|
1
|
-
Object.
|
|
2
|
-
|
|
1
|
+
Object.defineProperties(exports, {
|
|
2
|
+
__esModule: { value: true },
|
|
3
|
+
[Symbol.toStringTag]: { value: "Module" }
|
|
4
|
+
});
|
|
5
|
+
//#region \0rolldown/runtime.js
|
|
3
6
|
var __create = Object.create;
|
|
4
7
|
var __defProp = Object.defineProperty;
|
|
5
8
|
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
@@ -20,38 +23,29 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
20
23
|
value: mod,
|
|
21
24
|
enumerable: true
|
|
22
25
|
}) : target, mod));
|
|
23
|
-
|
|
24
26
|
//#endregion
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
27
|
+
let _bedrock_layout_use_forwarded_ref = require("@bedrock-layout/use-forwarded-ref");
|
|
28
|
+
let classnames = require("classnames");
|
|
29
|
+
classnames = __toESM(classnames);
|
|
30
|
+
let react = require("react");
|
|
31
|
+
react = __toESM(react, 1);
|
|
32
|
+
let react_dom = require("react-dom");
|
|
33
|
+
let body_scroll_lock = require("body-scroll-lock");
|
|
32
34
|
//#region src/CloseIcon.tsx
|
|
33
|
-
const CloseIcon = ({ classes
|
|
35
|
+
const CloseIcon = ({ classes, classNames, styles, id, closeIcon, onClick }) => /* @__PURE__ */ react.default.createElement("button", {
|
|
34
36
|
id,
|
|
35
|
-
className: (0, classnames.default)(classes
|
|
37
|
+
className: (0, classnames.default)(classes.closeButton, classNames?.closeButton),
|
|
36
38
|
style: styles?.closeButton,
|
|
37
39
|
onClick,
|
|
38
|
-
"data-testid": "close-button"
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
})
|
|
48
|
-
});
|
|
49
|
-
var CloseIcon_default = CloseIcon;
|
|
50
|
-
|
|
51
|
-
//#endregion
|
|
52
|
-
//#region src/utils.ts
|
|
53
|
-
const isBrowser = typeof window !== "undefined";
|
|
54
|
-
|
|
40
|
+
"data-testid": "close-button"
|
|
41
|
+
}, closeIcon ? closeIcon : /* @__PURE__ */ react.default.createElement("svg", {
|
|
42
|
+
className: classNames?.closeIcon,
|
|
43
|
+
style: styles?.closeIcon,
|
|
44
|
+
width: 28,
|
|
45
|
+
height: 28,
|
|
46
|
+
viewBox: "0 0 36 36",
|
|
47
|
+
"data-testid": "close-icon"
|
|
48
|
+
}, /* @__PURE__ */ react.default.createElement("path", { d: "M28.5 9.62L26.38 7.5 18 15.88 9.62 7.5 7.5 9.62 15.88 18 7.5 26.38l2.12 2.12L18 20.12l8.38 8.38 2.12-2.12L20.12 18z" })));
|
|
55
49
|
//#endregion
|
|
56
50
|
//#region src/lib/focusTrapJs.ts
|
|
57
51
|
const candidateSelectors = [
|
|
@@ -73,8 +67,7 @@ function getCheckedRadio(nodes, form) {
|
|
|
73
67
|
}
|
|
74
68
|
function isNotRadioOrTabbableRadio(node) {
|
|
75
69
|
if (node.tagName !== "INPUT" || node.type !== "radio" || !node.name) return true;
|
|
76
|
-
var
|
|
77
|
-
var radioSet = radioScope.querySelectorAll("input[type=\"radio\"][name=\"" + node.name + "\"]");
|
|
70
|
+
var radioSet = (node.form || node.ownerDocument).querySelectorAll("input[type=\"radio\"][name=\"" + node.name + "\"]");
|
|
78
71
|
var checked = getCheckedRadio(radioSet, node.form);
|
|
79
72
|
return checked === node || checked === void 0 && radioSet[0] === node;
|
|
80
73
|
}
|
|
@@ -118,7 +111,9 @@ function getTabindex(node) {
|
|
|
118
111
|
function isContentEditable(node) {
|
|
119
112
|
return node.getAttribute("contentEditable");
|
|
120
113
|
}
|
|
121
|
-
|
|
114
|
+
//#endregion
|
|
115
|
+
//#region src/utils.ts
|
|
116
|
+
const isBrowser = typeof window !== "undefined";
|
|
122
117
|
//#endregion
|
|
123
118
|
//#region src/FocusTrap.tsx
|
|
124
119
|
const FocusTrap = ({ container, initialFocusRef }) => {
|
|
@@ -157,7 +152,6 @@ const FocusTrap = ({ container, initialFocusRef }) => {
|
|
|
157
152
|
}, [container, initialFocusRef]);
|
|
158
153
|
return null;
|
|
159
154
|
};
|
|
160
|
-
|
|
161
155
|
//#endregion
|
|
162
156
|
//#region src/modalManager.ts
|
|
163
157
|
let modals = [];
|
|
@@ -166,12 +160,21 @@ let modals = [];
|
|
|
166
160
|
* Inspired by the material-ui implementation.
|
|
167
161
|
*/
|
|
168
162
|
const modalManager = {
|
|
163
|
+
/**
|
|
164
|
+
* Register a new modal
|
|
165
|
+
*/
|
|
169
166
|
add: (newModal) => {
|
|
170
167
|
modals.push(newModal);
|
|
171
168
|
},
|
|
169
|
+
/**
|
|
170
|
+
* Remove a modal
|
|
171
|
+
*/
|
|
172
172
|
remove: (oldModal) => {
|
|
173
173
|
modals = modals.filter((modal) => modal !== oldModal);
|
|
174
174
|
},
|
|
175
|
+
/**
|
|
176
|
+
* When multiple modals are rendered will return true if current modal is the last one
|
|
177
|
+
*/
|
|
175
178
|
isTopModal: (modal) => !!modals.length && modals[modals.length - 1] === modal
|
|
176
179
|
};
|
|
177
180
|
function useModalManager(ref, open) {
|
|
@@ -182,7 +185,6 @@ function useModalManager(ref, open) {
|
|
|
182
185
|
};
|
|
183
186
|
}, [open, ref]);
|
|
184
187
|
}
|
|
185
|
-
|
|
186
188
|
//#endregion
|
|
187
189
|
//#region src/useScrollLock.ts
|
|
188
190
|
const useScrollLock = (refModal, open, showPortal, blockScroll, reserveScrollBarGap) => {
|
|
@@ -206,7 +208,6 @@ const useScrollLock = (refModal, open, showPortal, blockScroll, reserveScrollBar
|
|
|
206
208
|
reserveScrollBarGap
|
|
207
209
|
]);
|
|
208
210
|
};
|
|
209
|
-
|
|
210
211
|
//#endregion
|
|
211
212
|
//#region src/index.tsx
|
|
212
213
|
const classes = {
|
|
@@ -221,8 +222,8 @@ const classes = {
|
|
|
221
222
|
modalAnimationOut: "react-responsive-modal-modal-out",
|
|
222
223
|
closeButton: "react-responsive-modal-closeButton"
|
|
223
224
|
};
|
|
224
|
-
const Modal = react.default.forwardRef(({ open, center, blockScroll = true, closeOnEsc = true, closeOnOverlayClick = true, container, showCloseIcon = true, closeIconId, closeIcon, focusTrapped = true, initialFocusRef
|
|
225
|
-
const refDialog = (0,
|
|
225
|
+
const Modal = react.default.forwardRef(({ open, center, blockScroll = true, closeOnEsc = true, closeOnOverlayClick = true, container, showCloseIcon = true, closeIconId, closeIcon, focusTrapped = true, initialFocusRef, animationDuration = 300, classNames, styles, role = "dialog", ariaLabel, ariaDescribedby, ariaLabelledby, containerId, modalId, onClose, onEscKeyDown, onOverlayClick, onAnimationEnd, children, reserveScrollBarGap }, ref) => {
|
|
226
|
+
const refDialog = (0, _bedrock_layout_use_forwarded_ref.useForwardedRef)(ref);
|
|
226
227
|
const refModal = (0, react.useRef)(null);
|
|
227
228
|
const refShouldClose = (0, react.useRef)(null);
|
|
228
229
|
const refContainer = (0, react.useRef)(null);
|
|
@@ -274,65 +275,58 @@ const Modal = react.default.forwardRef(({ open, center, blockScroll = true, clos
|
|
|
274
275
|
const containerModal = container || refContainer.current;
|
|
275
276
|
const overlayAnimation = open ? classNames?.overlayAnimationIn ?? classes.overlayAnimationIn : classNames?.overlayAnimationOut ?? classes.overlayAnimationOut;
|
|
276
277
|
const modalAnimation = open ? classNames?.modalAnimationIn ?? classes.modalAnimationIn : classNames?.modalAnimationOut ?? classes.modalAnimationOut;
|
|
277
|
-
return showPortal && containerModal ? (0, react_dom.createPortal)(/* @__PURE__ */
|
|
278
|
+
return showPortal && containerModal ? (0, react_dom.createPortal)(/* @__PURE__ */ react.default.createElement("div", {
|
|
278
279
|
className: (0, classnames.default)(classes.root, classNames?.root),
|
|
279
280
|
style: styles?.root,
|
|
280
|
-
"data-testid": "root"
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
id: closeIconId
|
|
327
|
-
})
|
|
328
|
-
]
|
|
329
|
-
})
|
|
330
|
-
})]
|
|
331
|
-
}), containerModal) : null;
|
|
281
|
+
"data-testid": "root"
|
|
282
|
+
}, /* @__PURE__ */ react.default.createElement("div", {
|
|
283
|
+
className: (0, classnames.default)(classes.overlay, classNames?.overlay),
|
|
284
|
+
"data-testid": "overlay",
|
|
285
|
+
"aria-hidden": true,
|
|
286
|
+
style: {
|
|
287
|
+
animation: `${overlayAnimation} ${animationDuration}ms`,
|
|
288
|
+
...styles?.overlay
|
|
289
|
+
}
|
|
290
|
+
}), /* @__PURE__ */ react.default.createElement("div", {
|
|
291
|
+
ref: refModal,
|
|
292
|
+
id: containerId,
|
|
293
|
+
className: (0, classnames.default)(classes.modalContainer, center && classes.modalContainerCenter, classNames?.modalContainer),
|
|
294
|
+
style: styles?.modalContainer,
|
|
295
|
+
"data-testid": "modal-container",
|
|
296
|
+
onClick: handleClickOverlay
|
|
297
|
+
}, /* @__PURE__ */ react.default.createElement("div", {
|
|
298
|
+
ref: refDialog,
|
|
299
|
+
className: (0, classnames.default)(classes.modal, classNames?.modal),
|
|
300
|
+
style: {
|
|
301
|
+
animation: `${modalAnimation} ${animationDuration}ms`,
|
|
302
|
+
...styles?.modal
|
|
303
|
+
},
|
|
304
|
+
onMouseDown: handleModalEvent,
|
|
305
|
+
onMouseUp: handleModalEvent,
|
|
306
|
+
onClick: handleModalEvent,
|
|
307
|
+
onAnimationEnd: handleAnimationEnd,
|
|
308
|
+
id: modalId,
|
|
309
|
+
role,
|
|
310
|
+
"aria-modal": "true",
|
|
311
|
+
"aria-label": ariaLabel,
|
|
312
|
+
"aria-labelledby": ariaLabelledby,
|
|
313
|
+
"aria-describedby": ariaDescribedby,
|
|
314
|
+
"data-testid": "modal",
|
|
315
|
+
tabIndex: -1
|
|
316
|
+
}, focusTrapped && /* @__PURE__ */ react.default.createElement(FocusTrap, {
|
|
317
|
+
container: refDialog,
|
|
318
|
+
initialFocusRef
|
|
319
|
+
}), children, showCloseIcon && /* @__PURE__ */ react.default.createElement(CloseIcon, {
|
|
320
|
+
classes,
|
|
321
|
+
classNames,
|
|
322
|
+
styles,
|
|
323
|
+
closeIcon,
|
|
324
|
+
onClick: onClose,
|
|
325
|
+
id: closeIconId
|
|
326
|
+
})))), containerModal) : null;
|
|
332
327
|
});
|
|
333
|
-
var src_default = Modal;
|
|
334
|
-
|
|
335
328
|
//#endregion
|
|
336
329
|
exports.Modal = Modal;
|
|
337
|
-
exports.default =
|
|
330
|
+
exports.default = Modal;
|
|
331
|
+
|
|
338
332
|
//# sourceMappingURL=index.cjs.map
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.cjs","names":["classes","node: any","nodes: any","form: any","parentElem: any","event: any","event: KeyboardEvent","modals: Ref<Element>[]","newModal: Ref<Element>","oldModal: Ref<Element>","modal: Ref<Element>","ref: Ref<Element>","open: boolean","refModal: React.RefObject<Element | null>","open: boolean","showPortal: boolean","blockScroll: boolean","reserveScrollBarGap?: boolean","ref: React.ForwardedRef<HTMLDivElement>","event: KeyboardEvent","event: React.MouseEvent<HTMLDivElement, MouseEvent>","CloseIcon"],"sources":["../src/CloseIcon.tsx","../src/utils.ts","../src/lib/focusTrapJs.ts","../src/FocusTrap.tsx","../src/modalManager.ts","../src/useScrollLock.ts","../src/index.tsx"],"sourcesContent":["import React from 'react';\nimport cx from 'classnames';\n\ninterface CloseIconProps {\n id?: string;\n closeIcon?: React.ReactNode;\n styles?: {\n closeButton?: React.CSSProperties;\n closeIcon?: React.CSSProperties;\n };\n classNames?: {\n closeButton?: string;\n closeIcon?: string;\n };\n classes: {\n closeButton?: string;\n };\n onClick: () => void;\n}\n\nconst CloseIcon = ({\n classes,\n classNames,\n styles,\n id,\n closeIcon,\n onClick,\n}: CloseIconProps) => (\n <button\n id={id}\n className={cx(classes.closeButton, classNames?.closeButton)}\n style={styles?.closeButton}\n onClick={onClick}\n data-testid=\"close-button\"\n >\n {closeIcon ? (\n closeIcon\n ) : (\n <svg\n className={classNames?.closeIcon}\n style={styles?.closeIcon}\n width={28}\n height={28}\n viewBox=\"0 0 36 36\"\n data-testid=\"close-icon\"\n >\n <path d=\"M28.5 9.62L26.38 7.5 18 15.88 9.62 7.5 7.5 9.62 15.88 18 7.5 26.38l2.12 2.12L18 20.12l8.38 8.38 2.12-2.12L20.12 18z\" />\n </svg>\n )}\n </button>\n);\n\nexport default CloseIcon;\n","export const isBrowser = typeof window !== 'undefined';\n","// https://github.com/alexandrzavalii/focus-trap-js/blob/master/src/index.js v1.1.0\n\nexport const candidateSelectors = [\n 'input',\n 'select',\n 'textarea',\n 'a[href]',\n 'button',\n '[tabindex]',\n 'audio[controls]',\n 'video[controls]',\n '[contenteditable]:not([contenteditable=\"false\"])',\n];\n\nfunction isHidden(node: any) {\n // offsetParent being null will allow detecting cases where an element is invisible or inside an invisible element,\n // as long as the element does not use position: fixed. For them, their visibility has to be checked directly as well.\n return (\n node.offsetParent === null || getComputedStyle(node).visibility === 'hidden'\n );\n}\n\nfunction getCheckedRadio(nodes: any, form: any) {\n for (var i = 0; i < nodes.length; i++) {\n if (nodes[i].checked && nodes[i].form === form) {\n return nodes[i];\n }\n }\n}\n\nfunction isNotRadioOrTabbableRadio(node: any) {\n if (node.tagName !== 'INPUT' || node.type !== 'radio' || !node.name) {\n return true;\n }\n var radioScope = node.form || node.ownerDocument;\n var radioSet = radioScope.querySelectorAll(\n 'input[type=\"radio\"][name=\"' + node.name + '\"]',\n );\n var checked = getCheckedRadio(radioSet, node.form);\n return checked === node || (checked === undefined && radioSet[0] === node);\n}\n\nexport function getAllTabbingElements(parentElem: any) {\n var currentActiveElement = document.activeElement;\n var tabbableNodes = parentElem.querySelectorAll(candidateSelectors.join(','));\n var onlyTabbable = [];\n for (var i = 0; i < tabbableNodes.length; i++) {\n var node = tabbableNodes[i];\n if (\n currentActiveElement === node ||\n (!node.disabled &&\n getTabindex(node) > -1 &&\n !isHidden(node) &&\n isNotRadioOrTabbableRadio(node))\n ) {\n onlyTabbable.push(node);\n }\n }\n return onlyTabbable;\n}\n\nexport function tabTrappingKey(event: any, parentElem: any) {\n // check if current event keyCode is tab\n if (!event || event.key !== 'Tab') return;\n\n if (!parentElem || !parentElem.contains) {\n if (process && process.env.NODE_ENV === 'development') {\n console.warn('focus-trap-js: parent element is not defined');\n }\n return false;\n }\n\n if (!parentElem.contains(event.target)) {\n return false;\n }\n\n var allTabbingElements = getAllTabbingElements(parentElem);\n var firstFocusableElement = allTabbingElements[0];\n var lastFocusableElement = allTabbingElements[allTabbingElements.length - 1];\n\n if (event.shiftKey && event.target === firstFocusableElement) {\n lastFocusableElement.focus();\n event.preventDefault();\n return true;\n } else if (!event.shiftKey && event.target === lastFocusableElement) {\n firstFocusableElement.focus();\n event.preventDefault();\n return true;\n }\n return false;\n}\n\nfunction getTabindex(node: any) {\n var tabindexAttr = parseInt(node.getAttribute('tabindex'), 10);\n\n if (!isNaN(tabindexAttr)) return tabindexAttr;\n // Browsers do not return tabIndex correctly for contentEditable nodes;\n // so if they don't have a tabindex attribute specifically set, assume it's 0.\n\n if (isContentEditable(node)) return 0;\n return node.tabIndex;\n}\n\nfunction isContentEditable(node: any) {\n return node.getAttribute('contentEditable');\n}\n","import { useEffect, useRef } from 'react';\nimport { isBrowser } from './utils';\nimport {\n tabTrappingKey,\n candidateSelectors,\n getAllTabbingElements,\n} from './lib/focusTrapJs';\n\ninterface FocusTrapProps {\n container?: React.RefObject<HTMLElement> | null;\n initialFocusRef?: React.RefObject<HTMLElement | null>;\n}\n\nexport const FocusTrap = ({ container, initialFocusRef }: FocusTrapProps) => {\n const refLastFocus = useRef<HTMLElement | null>(null);\n /**\n * Handle focus lock on the modal\n */\n useEffect(() => {\n const handleKeyEvent = (event: KeyboardEvent) => {\n if (container?.current) {\n tabTrappingKey(event, container.current);\n }\n };\n\n if (isBrowser) {\n document.addEventListener('keydown', handleKeyEvent);\n }\n // On mount we focus on the first focusable element in the modal if there is one\n if (isBrowser && container?.current) {\n const savePreviousFocus = () => {\n // First we save the last focused element\n // only if it's a focusable element\n if (\n candidateSelectors.findIndex((selector) =>\n document.activeElement?.matches(selector),\n ) !== -1\n ) {\n refLastFocus.current = document.activeElement as HTMLElement;\n }\n };\n\n if (initialFocusRef) {\n savePreviousFocus();\n // We need to schedule focusing on a next frame - this allows to focus on the modal root\n requestAnimationFrame(() => {\n initialFocusRef.current?.focus();\n });\n } else {\n const allTabbingElements = getAllTabbingElements(container.current);\n if (allTabbingElements[0]) {\n savePreviousFocus();\n allTabbingElements[0].focus();\n }\n }\n }\n return () => {\n if (isBrowser) {\n document.removeEventListener('keydown', handleKeyEvent);\n // On unmount we restore the focus to the last focused element\n refLastFocus.current?.focus();\n }\n };\n }, [container, initialFocusRef]);\n\n return null;\n};\n","import { Ref, useEffect } from 'react';\n\nlet modals: Ref<Element>[] = [];\n\n/**\n * Handle the order of the modals.\n * Inspired by the material-ui implementation.\n */\nexport const modalManager = {\n /**\n * Register a new modal\n */\n add: (newModal: Ref<Element>) => {\n modals.push(newModal);\n },\n\n /**\n * Remove a modal\n */\n remove: (oldModal: Ref<Element>) => {\n modals = modals.filter((modal) => modal !== oldModal);\n },\n\n /**\n * When multiple modals are rendered will return true if current modal is the last one\n */\n isTopModal: (modal: Ref<Element>) =>\n !!modals.length && modals[modals.length - 1] === modal,\n};\n\nexport function useModalManager(ref: Ref<Element>, open: boolean) {\n useEffect(() => {\n if (open) {\n modalManager.add(ref);\n }\n return () => {\n modalManager.remove(ref);\n };\n }, [open, ref]);\n}\n","import { useEffect, useRef } from 'react';\nimport { disableBodyScroll, enableBodyScroll } from 'body-scroll-lock';\n\nexport const useScrollLock = (\n refModal: React.RefObject<Element | null>,\n open: boolean,\n showPortal: boolean,\n blockScroll: boolean,\n reserveScrollBarGap?: boolean,\n) => {\n const oldRef = useRef<Element | null>(null);\n\n useEffect(() => {\n if (open && refModal.current && blockScroll) {\n oldRef.current = refModal.current;\n disableBodyScroll(refModal.current, { reserveScrollBarGap });\n }\n return () => {\n if (oldRef.current) {\n enableBodyScroll(oldRef.current);\n oldRef.current = null;\n }\n };\n }, [open, showPortal, refModal, blockScroll, reserveScrollBarGap]);\n};\n","import React, { useEffect, useRef, useState } from 'react';\nimport { createPortal } from 'react-dom';\nimport cx from 'classnames';\nimport { useForwardedRef } from '@bedrock-layout/use-forwarded-ref';\nimport CloseIcon from './CloseIcon';\nimport { FocusTrap } from './FocusTrap';\nimport { modalManager, useModalManager } from './modalManager';\nimport { useScrollLock } from './useScrollLock';\nimport { isBrowser } from './utils';\n\nconst classes = {\n root: 'react-responsive-modal-root',\n overlay: 'react-responsive-modal-overlay',\n overlayAnimationIn: 'react-responsive-modal-overlay-in',\n overlayAnimationOut: 'react-responsive-modal-overlay-out',\n modalContainer: 'react-responsive-modal-container',\n modalContainerCenter: 'react-responsive-modal-containerCenter',\n modal: 'react-responsive-modal-modal',\n modalAnimationIn: 'react-responsive-modal-modal-in',\n modalAnimationOut: 'react-responsive-modal-modal-out',\n closeButton: 'react-responsive-modal-closeButton',\n};\n\nexport interface ModalProps {\n /**\n * Control if the modal is open or not.\n */\n open: boolean;\n /**\n * Should the dialog be centered.\n *\n * Default to false.\n */\n center?: boolean;\n /**\n * Is the modal closable when user press esc key.\n *\n * Default to true.\n */\n closeOnEsc?: boolean;\n /**\n * Is the modal closable when user click on overlay.\n *\n * Default to true.\n */\n closeOnOverlayClick?: boolean;\n /**\n * Whether to block scrolling when dialog is open.\n *\n * Default to true.\n */\n blockScroll?: boolean;\n /**\n * Show the close icon.\n *\n * Default to true.\n */\n showCloseIcon?: boolean;\n /**\n * id attribute for the close icon button.\n */\n closeIconId?: string;\n /**\n * Custom icon to render (svg, img, etc...).\n */\n closeIcon?: React.ReactNode;\n /**\n * When the modal is open, trap focus within it.\n *\n * Default to true.\n */\n focusTrapped?: boolean;\n /**\n * Element to focus when focus trap is used.\n *\n * Default to undefined.\n */\n initialFocusRef?: React.RefObject<HTMLElement | null>;\n /**\n * You can specify a container prop which should be of type `Element`.\n * The portal will be rendered inside that element.\n * The default behavior will create a div node and render it at the at the end of document.body.\n */\n container?: Element | null;\n /**\n * An object containing classNames to style the modal.\n */\n classNames?: {\n root?: string;\n overlay?: string;\n overlayAnimationIn?: string;\n overlayAnimationOut?: string;\n modalContainer?: string;\n modal?: string;\n modalAnimationIn?: string;\n modalAnimationOut?: string;\n closeButton?: string;\n closeIcon?: string;\n };\n /**\n * An object containing the styles objects to style the modal.\n */\n styles?: {\n root?: React.CSSProperties;\n overlay?: React.CSSProperties;\n modalContainer?: React.CSSProperties;\n modal?: React.CSSProperties;\n closeButton?: React.CSSProperties;\n closeIcon?: React.CSSProperties;\n };\n /**\n * Animation duration in milliseconds.\n *\n * Default to 300.\n */\n animationDuration?: number;\n /**\n * ARIA role for modal\n *\n * Default to 'dialog'.\n */\n role?: string;\n /**\n * ARIA label for modal\n */\n ariaLabelledby?: string;\n /**\n * ARIA description for modal\n */\n ariaDescribedby?: string;\n /**\n * Avoid unpleasant flickering effect when body overflow is hidden. For more information see https://www.npmjs.com/package/body-scroll-lock\n */\n reserveScrollBarGap?: boolean;\n /**\n * id attribute for modal container\n */\n containerId?: string;\n /**\n * id attribute for modal\n */\n modalId?: string;\n /**\n * Callback fired when the Modal is requested to be closed by a click on the overlay or when user press esc key.\n */\n onClose: () => void;\n /**\n * Callback fired when the escape key is pressed.\n */\n onEscKeyDown?: (event: KeyboardEvent) => void;\n /**\n * Callback fired when the overlay is clicked.\n */\n onOverlayClick?: (\n event: React.MouseEvent<HTMLDivElement, MouseEvent>,\n ) => void;\n /**\n * Callback fired when the Modal has exited and the animation is finished.\n */\n onAnimationEnd?: () => void;\n children?: React.ReactNode;\n}\n\nexport const Modal = React.forwardRef(\n (\n {\n open,\n center,\n blockScroll = true,\n closeOnEsc = true,\n closeOnOverlayClick = true,\n container,\n showCloseIcon = true,\n closeIconId,\n closeIcon,\n focusTrapped = true,\n initialFocusRef = undefined,\n animationDuration = 300,\n classNames,\n styles,\n role = 'dialog',\n ariaDescribedby,\n ariaLabelledby,\n containerId,\n modalId,\n onClose,\n onEscKeyDown,\n onOverlayClick,\n onAnimationEnd,\n children,\n reserveScrollBarGap,\n }: ModalProps,\n ref: React.ForwardedRef<HTMLDivElement>,\n ) => {\n const refDialog = useForwardedRef(ref);\n const refModal = useRef<HTMLDivElement>(null);\n const refShouldClose = useRef<boolean | null>(null);\n const refContainer = useRef<HTMLDivElement | null>(null);\n // Lazily create the ref instance\n // https://reactjs.org/docs/hooks-faq.html#how-to-create-expensive-objects-lazily\n if (refContainer.current === null && isBrowser) {\n refContainer.current = document.createElement('div');\n }\n\n // The value should be false for srr, that way when the component is hydrated client side,\n // it will match the server rendered content\n const [showPortal, setShowPortal] = useState(false);\n\n // Hook used to manage multiple modals opened at the same time\n useModalManager(refModal, open);\n\n // Hook used to manage the scroll\n useScrollLock(refModal, open, showPortal, blockScroll, reserveScrollBarGap);\n\n const handleOpen = () => {\n if (\n refContainer.current &&\n !container &&\n !document.body.contains(refContainer.current)\n ) {\n document.body.appendChild(refContainer.current);\n }\n\n document.addEventListener('keydown', handleKeydown);\n };\n\n const handleClose = () => {\n if (\n refContainer.current &&\n !container &&\n document.body.contains(refContainer.current)\n ) {\n document.body.removeChild(refContainer.current);\n }\n document.removeEventListener('keydown', handleKeydown);\n };\n\n const handleKeydown = (event: KeyboardEvent) => {\n // Only the last modal need to be escaped when pressing the esc key\n if (event.keyCode !== 27 || !modalManager.isTopModal(refModal)) {\n return;\n }\n\n onEscKeyDown?.(event);\n\n if (closeOnEsc) {\n onClose();\n }\n };\n\n useEffect(() => {\n return () => {\n if (showPortal) {\n // When the modal is closed or removed directly, cleanup the listeners\n handleClose();\n }\n };\n }, [showPortal]);\n\n useEffect(() => {\n // If the open prop is changing, we need to open the modal\n // This is also called on the first render if the open prop is true when the modal is created\n if (open && !showPortal) {\n setShowPortal(true);\n handleOpen();\n }\n }, [open]);\n\n const handleClickOverlay = (\n event: React.MouseEvent<HTMLDivElement, MouseEvent>,\n ) => {\n if (refShouldClose.current === null) {\n refShouldClose.current = true;\n }\n\n if (!refShouldClose.current) {\n refShouldClose.current = null;\n return;\n }\n\n onOverlayClick?.(event);\n\n if (closeOnOverlayClick) {\n onClose();\n }\n\n refShouldClose.current = null;\n };\n\n const handleModalEvent = () => {\n refShouldClose.current = false;\n };\n\n const handleAnimationEnd = () => {\n if (!open) {\n setShowPortal(false);\n }\n\n onAnimationEnd?.();\n };\n\n const containerModal = container || refContainer.current;\n\n const overlayAnimation = open\n ? (classNames?.overlayAnimationIn ?? classes.overlayAnimationIn)\n : (classNames?.overlayAnimationOut ?? classes.overlayAnimationOut);\n\n const modalAnimation = open\n ? (classNames?.modalAnimationIn ?? classes.modalAnimationIn)\n : (classNames?.modalAnimationOut ?? classes.modalAnimationOut);\n\n return showPortal && containerModal\n ? createPortal(\n <div\n className={cx(classes.root, classNames?.root)}\n style={styles?.root}\n data-testid=\"root\"\n >\n <div\n className={cx(classes.overlay, classNames?.overlay)}\n data-testid=\"overlay\"\n aria-hidden={true}\n style={{\n animation: `${overlayAnimation} ${animationDuration}ms`,\n ...styles?.overlay,\n }}\n />\n <div\n ref={refModal}\n id={containerId}\n className={cx(\n classes.modalContainer,\n center && classes.modalContainerCenter,\n classNames?.modalContainer,\n )}\n style={styles?.modalContainer}\n data-testid=\"modal-container\"\n onClick={handleClickOverlay}\n >\n <div\n ref={refDialog}\n className={cx(classes.modal, classNames?.modal)}\n style={{\n animation: `${modalAnimation} ${animationDuration}ms`,\n ...styles?.modal,\n }}\n onMouseDown={handleModalEvent}\n onMouseUp={handleModalEvent}\n onClick={handleModalEvent}\n onAnimationEnd={handleAnimationEnd}\n id={modalId}\n role={role}\n aria-modal=\"true\"\n aria-labelledby={ariaLabelledby}\n aria-describedby={ariaDescribedby}\n data-testid=\"modal\"\n tabIndex={-1}\n >\n {focusTrapped && (\n <FocusTrap\n container={refDialog}\n initialFocusRef={initialFocusRef}\n />\n )}\n {children}\n {showCloseIcon && (\n <CloseIcon\n classes={classes}\n classNames={classNames}\n styles={styles}\n closeIcon={closeIcon}\n onClick={onClose}\n id={closeIconId}\n />\n )}\n </div>\n </div>\n </div>,\n containerModal,\n )\n : null;\n },\n);\n\nexport default Modal;\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.cjs","names":["React"],"sources":["../src/CloseIcon.tsx","../src/lib/focusTrapJs.ts","../src/utils.ts","../src/FocusTrap.tsx","../src/modalManager.ts","../src/useScrollLock.ts","../src/index.tsx"],"sourcesContent":["import cx from 'classnames';\nimport React from 'react';\n\ninterface CloseIconProps {\n id?: string;\n closeIcon?: React.ReactNode;\n styles?: {\n closeButton?: React.CSSProperties;\n closeIcon?: React.CSSProperties;\n };\n classNames?: {\n closeButton?: string;\n closeIcon?: string;\n };\n classes: {\n closeButton?: string;\n };\n onClick: () => void;\n}\n\nconst CloseIcon = ({\n classes,\n classNames,\n styles,\n id,\n closeIcon,\n onClick,\n}: CloseIconProps) => (\n <button\n id={id}\n className={cx(classes.closeButton, classNames?.closeButton)}\n style={styles?.closeButton}\n onClick={onClick}\n data-testid=\"close-button\"\n >\n {closeIcon ? (\n closeIcon\n ) : (\n <svg\n className={classNames?.closeIcon}\n style={styles?.closeIcon}\n width={28}\n height={28}\n viewBox=\"0 0 36 36\"\n data-testid=\"close-icon\"\n >\n <path d=\"M28.5 9.62L26.38 7.5 18 15.88 9.62 7.5 7.5 9.62 15.88 18 7.5 26.38l2.12 2.12L18 20.12l8.38 8.38 2.12-2.12L20.12 18z\" />\n </svg>\n )}\n </button>\n);\n\nexport default CloseIcon;\n","// https://github.com/alexandrzavalii/focus-trap-js/blob/master/src/index.js v1.1.0\n\nexport const candidateSelectors = [\n 'input',\n 'select',\n 'textarea',\n 'a[href]',\n 'button',\n '[tabindex]',\n 'audio[controls]',\n 'video[controls]',\n '[contenteditable]:not([contenteditable=\"false\"])',\n];\n\nfunction isHidden(node: any) {\n // offsetParent being null will allow detecting cases where an element is invisible or inside an invisible element,\n // as long as the element does not use position: fixed. For them, their visibility has to be checked directly as well.\n return (\n node.offsetParent === null || getComputedStyle(node).visibility === 'hidden'\n );\n}\n\nfunction getCheckedRadio(nodes: any, form: any) {\n for (var i = 0; i < nodes.length; i++) {\n if (nodes[i].checked && nodes[i].form === form) {\n return nodes[i];\n }\n }\n}\n\nfunction isNotRadioOrTabbableRadio(node: any) {\n if (node.tagName !== 'INPUT' || node.type !== 'radio' || !node.name) {\n return true;\n }\n var radioScope = node.form || node.ownerDocument;\n var radioSet = radioScope.querySelectorAll(\n 'input[type=\"radio\"][name=\"' + node.name + '\"]',\n );\n var checked = getCheckedRadio(radioSet, node.form);\n return checked === node || (checked === undefined && radioSet[0] === node);\n}\n\nexport function getAllTabbingElements(parentElem: any) {\n var currentActiveElement = document.activeElement;\n var tabbableNodes = parentElem.querySelectorAll(candidateSelectors.join(','));\n var onlyTabbable = [];\n for (var i = 0; i < tabbableNodes.length; i++) {\n var node = tabbableNodes[i];\n if (\n currentActiveElement === node ||\n (!node.disabled &&\n getTabindex(node) > -1 &&\n !isHidden(node) &&\n isNotRadioOrTabbableRadio(node))\n ) {\n onlyTabbable.push(node);\n }\n }\n return onlyTabbable;\n}\n\nexport function tabTrappingKey(event: any, parentElem: any) {\n // check if current event keyCode is tab\n if (!event || event.key !== 'Tab') return;\n\n if (!parentElem || !parentElem.contains) {\n if (process && process.env.NODE_ENV === 'development') {\n console.warn('focus-trap-js: parent element is not defined');\n }\n return false;\n }\n\n if (!parentElem.contains(event.target)) {\n return false;\n }\n\n var allTabbingElements = getAllTabbingElements(parentElem);\n var firstFocusableElement = allTabbingElements[0];\n var lastFocusableElement = allTabbingElements[allTabbingElements.length - 1];\n\n if (event.shiftKey && event.target === firstFocusableElement) {\n lastFocusableElement.focus();\n event.preventDefault();\n return true;\n } else if (!event.shiftKey && event.target === lastFocusableElement) {\n firstFocusableElement.focus();\n event.preventDefault();\n return true;\n }\n return false;\n}\n\nfunction getTabindex(node: any) {\n var tabindexAttr = parseInt(node.getAttribute('tabindex'), 10);\n\n if (!isNaN(tabindexAttr)) return tabindexAttr;\n // Browsers do not return tabIndex correctly for contentEditable nodes;\n // so if they don't have a tabindex attribute specifically set, assume it's 0.\n\n if (isContentEditable(node)) return 0;\n return node.tabIndex;\n}\n\nfunction isContentEditable(node: any) {\n return node.getAttribute('contentEditable');\n}\n","export const isBrowser = typeof window !== 'undefined';\n","import { useEffect, useRef } from 'react';\n\nimport {\n tabTrappingKey,\n candidateSelectors,\n getAllTabbingElements,\n} from './lib/focusTrapJs';\nimport { isBrowser } from './utils';\n\ninterface FocusTrapProps {\n container?: React.RefObject<HTMLElement> | null;\n initialFocusRef?: React.RefObject<HTMLElement | null>;\n}\n\nexport const FocusTrap = ({ container, initialFocusRef }: FocusTrapProps) => {\n const refLastFocus = useRef<HTMLElement | null>(null);\n /**\n * Handle focus lock on the modal\n */\n useEffect(() => {\n const handleKeyEvent = (event: KeyboardEvent) => {\n if (container?.current) {\n tabTrappingKey(event, container.current);\n }\n };\n\n if (isBrowser) {\n document.addEventListener('keydown', handleKeyEvent);\n }\n // On mount we focus on the first focusable element in the modal if there is one\n if (isBrowser && container?.current) {\n const savePreviousFocus = () => {\n // First we save the last focused element\n // only if it's a focusable element\n if (\n candidateSelectors.findIndex((selector) =>\n document.activeElement?.matches(selector),\n ) !== -1\n ) {\n refLastFocus.current = document.activeElement as HTMLElement;\n }\n };\n\n if (initialFocusRef) {\n savePreviousFocus();\n // We need to schedule focusing on a next frame - this allows to focus on the modal root\n requestAnimationFrame(() => {\n initialFocusRef.current?.focus();\n });\n } else {\n const allTabbingElements = getAllTabbingElements(container.current);\n if (allTabbingElements[0]) {\n savePreviousFocus();\n allTabbingElements[0].focus();\n }\n }\n }\n return () => {\n if (isBrowser) {\n document.removeEventListener('keydown', handleKeyEvent);\n // On unmount we restore the focus to the last focused element\n refLastFocus.current?.focus();\n }\n };\n }, [container, initialFocusRef]);\n\n return null;\n};\n","import { Ref, useEffect } from 'react';\n\nlet modals: Ref<Element>[] = [];\n\n/**\n * Handle the order of the modals.\n * Inspired by the material-ui implementation.\n */\nexport const modalManager = {\n /**\n * Register a new modal\n */\n add: (newModal: Ref<Element>) => {\n modals.push(newModal);\n },\n\n /**\n * Remove a modal\n */\n remove: (oldModal: Ref<Element>) => {\n modals = modals.filter((modal) => modal !== oldModal);\n },\n\n /**\n * When multiple modals are rendered will return true if current modal is the last one\n */\n isTopModal: (modal: Ref<Element>) =>\n !!modals.length && modals[modals.length - 1] === modal,\n};\n\nexport function useModalManager(ref: Ref<Element>, open: boolean) {\n useEffect(() => {\n if (open) {\n modalManager.add(ref);\n }\n return () => {\n modalManager.remove(ref);\n };\n }, [open, ref]);\n}\n","import { disableBodyScroll, enableBodyScroll } from 'body-scroll-lock';\nimport { useEffect, useRef } from 'react';\n\nexport const useScrollLock = (\n refModal: React.RefObject<Element | null>,\n open: boolean,\n showPortal: boolean,\n blockScroll: boolean,\n reserveScrollBarGap?: boolean,\n) => {\n const oldRef = useRef<Element | null>(null);\n\n useEffect(() => {\n if (open && refModal.current && blockScroll) {\n oldRef.current = refModal.current;\n disableBodyScroll(refModal.current, { reserveScrollBarGap });\n }\n return () => {\n if (oldRef.current) {\n enableBodyScroll(oldRef.current);\n oldRef.current = null;\n }\n };\n }, [open, showPortal, refModal, blockScroll, reserveScrollBarGap]);\n};\n","import { useForwardedRef } from '@bedrock-layout/use-forwarded-ref';\nimport cx from 'classnames';\nimport React, { useEffect, useRef, useState } from 'react';\nimport { createPortal } from 'react-dom';\n\nimport CloseIcon from './CloseIcon';\nimport { FocusTrap } from './FocusTrap';\nimport { modalManager, useModalManager } from './modalManager';\nimport { useScrollLock } from './useScrollLock';\nimport { isBrowser } from './utils';\n\nconst classes = {\n root: 'react-responsive-modal-root',\n overlay: 'react-responsive-modal-overlay',\n overlayAnimationIn: 'react-responsive-modal-overlay-in',\n overlayAnimationOut: 'react-responsive-modal-overlay-out',\n modalContainer: 'react-responsive-modal-container',\n modalContainerCenter: 'react-responsive-modal-containerCenter',\n modal: 'react-responsive-modal-modal',\n modalAnimationIn: 'react-responsive-modal-modal-in',\n modalAnimationOut: 'react-responsive-modal-modal-out',\n closeButton: 'react-responsive-modal-closeButton',\n};\n\nexport interface ModalProps {\n /**\n * Control if the modal is open or not.\n */\n open: boolean;\n /**\n * Should the dialog be centered.\n *\n * Default to false.\n */\n center?: boolean;\n /**\n * Is the modal closable when user press esc key.\n *\n * Default to true.\n */\n closeOnEsc?: boolean;\n /**\n * Is the modal closable when user click on overlay.\n *\n * Default to true.\n */\n closeOnOverlayClick?: boolean;\n /**\n * Whether to block scrolling when dialog is open.\n *\n * Default to true.\n */\n blockScroll?: boolean;\n /**\n * Show the close icon.\n *\n * Default to true.\n */\n showCloseIcon?: boolean;\n /**\n * id attribute for the close icon button.\n */\n closeIconId?: string;\n /**\n * Custom icon to render (svg, img, etc...).\n */\n closeIcon?: React.ReactNode;\n /**\n * When the modal is open, trap focus within it.\n *\n * Default to true.\n */\n focusTrapped?: boolean;\n /**\n * Element to focus when focus trap is used.\n *\n * Default to undefined.\n */\n initialFocusRef?: React.RefObject<HTMLElement | null>;\n /**\n * You can specify a container prop which should be of type `Element`.\n * The portal will be rendered inside that element.\n * The default behavior will create a div node and render it at the at the end of document.body.\n */\n container?: Element | null;\n /**\n * An object containing classNames to style the modal.\n */\n classNames?: {\n root?: string;\n overlay?: string;\n overlayAnimationIn?: string;\n overlayAnimationOut?: string;\n modalContainer?: string;\n modal?: string;\n modalAnimationIn?: string;\n modalAnimationOut?: string;\n closeButton?: string;\n closeIcon?: string;\n };\n /**\n * An object containing the styles objects to style the modal.\n */\n styles?: {\n root?: React.CSSProperties;\n overlay?: React.CSSProperties;\n modalContainer?: React.CSSProperties;\n modal?: React.CSSProperties;\n closeButton?: React.CSSProperties;\n closeIcon?: React.CSSProperties;\n };\n /**\n * Animation duration in milliseconds.\n *\n * Default to 300.\n */\n animationDuration?: number;\n /**\n * ARIA role for modal\n *\n * Default to 'dialog'.\n */\n role?: string;\n /**\n * ARIA label for modal\n */\n ariaLabel?: string;\n /**\n * ARIA label for modal\n */\n ariaLabelledby?: string;\n /**\n * ARIA description for modal\n */\n ariaDescribedby?: string;\n /**\n * Avoid unpleasant flickering effect when body overflow is hidden. For more information see https://www.npmjs.com/package/body-scroll-lock\n */\n reserveScrollBarGap?: boolean;\n /**\n * id attribute for modal container\n */\n containerId?: string;\n /**\n * id attribute for modal\n */\n modalId?: string;\n /**\n * Callback fired when the Modal is requested to be closed by a click on the overlay or when user press esc key.\n */\n onClose: () => void;\n /**\n * Callback fired when the escape key is pressed.\n */\n onEscKeyDown?: (event: KeyboardEvent) => void;\n /**\n * Callback fired when the overlay is clicked.\n */\n onOverlayClick?: (\n event: React.MouseEvent<HTMLDivElement, MouseEvent>,\n ) => void;\n /**\n * Callback fired when the Modal has exited and the animation is finished.\n */\n onAnimationEnd?: () => void;\n children?: React.ReactNode;\n}\n\nexport const Modal = React.forwardRef(\n (\n {\n open,\n center,\n blockScroll = true,\n closeOnEsc = true,\n closeOnOverlayClick = true,\n container,\n showCloseIcon = true,\n closeIconId,\n closeIcon,\n focusTrapped = true,\n initialFocusRef,\n animationDuration = 300,\n classNames,\n styles,\n role = 'dialog',\n ariaLabel,\n ariaDescribedby,\n ariaLabelledby,\n containerId,\n modalId,\n onClose,\n onEscKeyDown,\n onOverlayClick,\n onAnimationEnd,\n children,\n reserveScrollBarGap,\n }: ModalProps,\n ref: React.ForwardedRef<HTMLDivElement>,\n ) => {\n const refDialog = useForwardedRef(ref);\n const refModal = useRef<HTMLDivElement>(null);\n const refShouldClose = useRef<boolean | null>(null);\n const refContainer = useRef<HTMLDivElement | null>(null);\n // Lazily create the ref instance\n // https://reactjs.org/docs/hooks-faq.html#how-to-create-expensive-objects-lazily\n if (refContainer.current === null && isBrowser) {\n refContainer.current = document.createElement('div');\n }\n\n // The value should be false for srr, that way when the component is hydrated client side,\n // it will match the server rendered content\n const [showPortal, setShowPortal] = useState(false);\n\n // Hook used to manage multiple modals opened at the same time\n useModalManager(refModal, open);\n\n // Hook used to manage the scroll\n useScrollLock(refModal, open, showPortal, blockScroll, reserveScrollBarGap);\n\n const handleOpen = () => {\n if (\n refContainer.current &&\n !container &&\n !document.body.contains(refContainer.current)\n ) {\n document.body.appendChild(refContainer.current);\n }\n\n document.addEventListener('keydown', handleKeydown);\n };\n\n const handleClose = () => {\n if (\n refContainer.current &&\n !container &&\n document.body.contains(refContainer.current)\n ) {\n document.body.removeChild(refContainer.current);\n }\n document.removeEventListener('keydown', handleKeydown);\n };\n\n const handleKeydown = (event: KeyboardEvent) => {\n // Only the last modal need to be escaped when pressing the esc key\n if (event.keyCode !== 27 || !modalManager.isTopModal(refModal)) {\n return;\n }\n\n onEscKeyDown?.(event);\n\n if (closeOnEsc) {\n onClose();\n }\n };\n\n useEffect(() => {\n return () => {\n if (showPortal) {\n // When the modal is closed or removed directly, cleanup the listeners\n handleClose();\n }\n };\n }, [showPortal]);\n\n useEffect(() => {\n // If the open prop is changing, we need to open the modal\n // This is also called on the first render if the open prop is true when the modal is created\n if (open && !showPortal) {\n setShowPortal(true);\n handleOpen();\n }\n }, [open]);\n\n const handleClickOverlay = (\n event: React.MouseEvent<HTMLDivElement, MouseEvent>,\n ) => {\n if (refShouldClose.current === null) {\n refShouldClose.current = true;\n }\n\n if (!refShouldClose.current) {\n refShouldClose.current = null;\n return;\n }\n\n onOverlayClick?.(event);\n\n if (closeOnOverlayClick) {\n onClose();\n }\n\n refShouldClose.current = null;\n };\n\n const handleModalEvent = () => {\n refShouldClose.current = false;\n };\n\n const handleAnimationEnd = () => {\n if (!open) {\n setShowPortal(false);\n }\n\n onAnimationEnd?.();\n };\n\n const containerModal = container || refContainer.current;\n\n const overlayAnimation = open\n ? (classNames?.overlayAnimationIn ?? classes.overlayAnimationIn)\n : (classNames?.overlayAnimationOut ?? classes.overlayAnimationOut);\n\n const modalAnimation = open\n ? (classNames?.modalAnimationIn ?? classes.modalAnimationIn)\n : (classNames?.modalAnimationOut ?? classes.modalAnimationOut);\n\n return showPortal && containerModal\n ? createPortal(\n <div\n className={cx(classes.root, classNames?.root)}\n style={styles?.root}\n data-testid=\"root\"\n >\n <div\n className={cx(classes.overlay, classNames?.overlay)}\n data-testid=\"overlay\"\n aria-hidden={true}\n style={{\n animation: `${overlayAnimation} ${animationDuration}ms`,\n ...styles?.overlay,\n }}\n />\n <div\n ref={refModal}\n id={containerId}\n className={cx(\n classes.modalContainer,\n center && classes.modalContainerCenter,\n classNames?.modalContainer,\n )}\n style={styles?.modalContainer}\n data-testid=\"modal-container\"\n onClick={handleClickOverlay}\n >\n <div\n ref={refDialog}\n className={cx(classes.modal, classNames?.modal)}\n style={{\n animation: `${modalAnimation} ${animationDuration}ms`,\n ...styles?.modal,\n }}\n onMouseDown={handleModalEvent}\n onMouseUp={handleModalEvent}\n onClick={handleModalEvent}\n onAnimationEnd={handleAnimationEnd}\n id={modalId}\n role={role}\n aria-modal=\"true\"\n aria-label={ariaLabel}\n aria-labelledby={ariaLabelledby}\n aria-describedby={ariaDescribedby}\n data-testid=\"modal\"\n tabIndex={-1}\n >\n {focusTrapped && (\n <FocusTrap\n container={refDialog}\n initialFocusRef={initialFocusRef}\n />\n )}\n {children}\n {showCloseIcon && (\n <CloseIcon\n classes={classes}\n classNames={classNames}\n styles={styles}\n closeIcon={closeIcon}\n onClick={onClose}\n id={closeIconId}\n />\n )}\n </div>\n </div>\n </div>,\n containerModal,\n )\n : null;\n },\n);\n\nexport default Modal;\n"],"mappings":"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"}
|
package/dist/index.d.cts
CHANGED
package/dist/index.d.ts
CHANGED
package/dist/index.js
CHANGED
|
@@ -1,33 +1,23 @@
|
|
|
1
|
+
import { useForwardedRef } from "@bedrock-layout/use-forwarded-ref";
|
|
2
|
+
import cx from "classnames";
|
|
1
3
|
import React, { useEffect, useRef, useState } from "react";
|
|
2
4
|
import { createPortal } from "react-dom";
|
|
3
|
-
import cx from "classnames";
|
|
4
|
-
import { useForwardedRef } from "@bedrock-layout/use-forwarded-ref";
|
|
5
|
-
import { jsx, jsxs } from "react/jsx-runtime";
|
|
6
5
|
import { disableBodyScroll, enableBodyScroll } from "body-scroll-lock";
|
|
7
|
-
|
|
8
6
|
//#region src/CloseIcon.tsx
|
|
9
|
-
const CloseIcon = ({ classes
|
|
7
|
+
const CloseIcon = ({ classes, classNames, styles, id, closeIcon, onClick }) => /* @__PURE__ */ React.createElement("button", {
|
|
10
8
|
id,
|
|
11
|
-
className: cx(classes
|
|
9
|
+
className: cx(classes.closeButton, classNames?.closeButton),
|
|
12
10
|
style: styles?.closeButton,
|
|
13
11
|
onClick,
|
|
14
|
-
"data-testid": "close-button"
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
})
|
|
24
|
-
});
|
|
25
|
-
var CloseIcon_default = CloseIcon;
|
|
26
|
-
|
|
27
|
-
//#endregion
|
|
28
|
-
//#region src/utils.ts
|
|
29
|
-
const isBrowser = typeof window !== "undefined";
|
|
30
|
-
|
|
12
|
+
"data-testid": "close-button"
|
|
13
|
+
}, closeIcon ? closeIcon : /* @__PURE__ */ React.createElement("svg", {
|
|
14
|
+
className: classNames?.closeIcon,
|
|
15
|
+
style: styles?.closeIcon,
|
|
16
|
+
width: 28,
|
|
17
|
+
height: 28,
|
|
18
|
+
viewBox: "0 0 36 36",
|
|
19
|
+
"data-testid": "close-icon"
|
|
20
|
+
}, /* @__PURE__ */ React.createElement("path", { d: "M28.5 9.62L26.38 7.5 18 15.88 9.62 7.5 7.5 9.62 15.88 18 7.5 26.38l2.12 2.12L18 20.12l8.38 8.38 2.12-2.12L20.12 18z" })));
|
|
31
21
|
//#endregion
|
|
32
22
|
//#region src/lib/focusTrapJs.ts
|
|
33
23
|
const candidateSelectors = [
|
|
@@ -49,8 +39,7 @@ function getCheckedRadio(nodes, form) {
|
|
|
49
39
|
}
|
|
50
40
|
function isNotRadioOrTabbableRadio(node) {
|
|
51
41
|
if (node.tagName !== "INPUT" || node.type !== "radio" || !node.name) return true;
|
|
52
|
-
var
|
|
53
|
-
var radioSet = radioScope.querySelectorAll("input[type=\"radio\"][name=\"" + node.name + "\"]");
|
|
42
|
+
var radioSet = (node.form || node.ownerDocument).querySelectorAll("input[type=\"radio\"][name=\"" + node.name + "\"]");
|
|
54
43
|
var checked = getCheckedRadio(radioSet, node.form);
|
|
55
44
|
return checked === node || checked === void 0 && radioSet[0] === node;
|
|
56
45
|
}
|
|
@@ -94,7 +83,9 @@ function getTabindex(node) {
|
|
|
94
83
|
function isContentEditable(node) {
|
|
95
84
|
return node.getAttribute("contentEditable");
|
|
96
85
|
}
|
|
97
|
-
|
|
86
|
+
//#endregion
|
|
87
|
+
//#region src/utils.ts
|
|
88
|
+
const isBrowser = typeof window !== "undefined";
|
|
98
89
|
//#endregion
|
|
99
90
|
//#region src/FocusTrap.tsx
|
|
100
91
|
const FocusTrap = ({ container, initialFocusRef }) => {
|
|
@@ -133,7 +124,6 @@ const FocusTrap = ({ container, initialFocusRef }) => {
|
|
|
133
124
|
}, [container, initialFocusRef]);
|
|
134
125
|
return null;
|
|
135
126
|
};
|
|
136
|
-
|
|
137
127
|
//#endregion
|
|
138
128
|
//#region src/modalManager.ts
|
|
139
129
|
let modals = [];
|
|
@@ -142,12 +132,21 @@ let modals = [];
|
|
|
142
132
|
* Inspired by the material-ui implementation.
|
|
143
133
|
*/
|
|
144
134
|
const modalManager = {
|
|
135
|
+
/**
|
|
136
|
+
* Register a new modal
|
|
137
|
+
*/
|
|
145
138
|
add: (newModal) => {
|
|
146
139
|
modals.push(newModal);
|
|
147
140
|
},
|
|
141
|
+
/**
|
|
142
|
+
* Remove a modal
|
|
143
|
+
*/
|
|
148
144
|
remove: (oldModal) => {
|
|
149
145
|
modals = modals.filter((modal) => modal !== oldModal);
|
|
150
146
|
},
|
|
147
|
+
/**
|
|
148
|
+
* When multiple modals are rendered will return true if current modal is the last one
|
|
149
|
+
*/
|
|
151
150
|
isTopModal: (modal) => !!modals.length && modals[modals.length - 1] === modal
|
|
152
151
|
};
|
|
153
152
|
function useModalManager(ref, open) {
|
|
@@ -158,7 +157,6 @@ function useModalManager(ref, open) {
|
|
|
158
157
|
};
|
|
159
158
|
}, [open, ref]);
|
|
160
159
|
}
|
|
161
|
-
|
|
162
160
|
//#endregion
|
|
163
161
|
//#region src/useScrollLock.ts
|
|
164
162
|
const useScrollLock = (refModal, open, showPortal, blockScroll, reserveScrollBarGap) => {
|
|
@@ -182,7 +180,6 @@ const useScrollLock = (refModal, open, showPortal, blockScroll, reserveScrollBar
|
|
|
182
180
|
reserveScrollBarGap
|
|
183
181
|
]);
|
|
184
182
|
};
|
|
185
|
-
|
|
186
183
|
//#endregion
|
|
187
184
|
//#region src/index.tsx
|
|
188
185
|
const classes = {
|
|
@@ -197,7 +194,7 @@ const classes = {
|
|
|
197
194
|
modalAnimationOut: "react-responsive-modal-modal-out",
|
|
198
195
|
closeButton: "react-responsive-modal-closeButton"
|
|
199
196
|
};
|
|
200
|
-
const Modal = React.forwardRef(({ open, center, blockScroll = true, closeOnEsc = true, closeOnOverlayClick = true, container, showCloseIcon = true, closeIconId, closeIcon, focusTrapped = true, initialFocusRef
|
|
197
|
+
const Modal = React.forwardRef(({ open, center, blockScroll = true, closeOnEsc = true, closeOnOverlayClick = true, container, showCloseIcon = true, closeIconId, closeIcon, focusTrapped = true, initialFocusRef, animationDuration = 300, classNames, styles, role = "dialog", ariaLabel, ariaDescribedby, ariaLabelledby, containerId, modalId, onClose, onEscKeyDown, onOverlayClick, onAnimationEnd, children, reserveScrollBarGap }, ref) => {
|
|
201
198
|
const refDialog = useForwardedRef(ref);
|
|
202
199
|
const refModal = useRef(null);
|
|
203
200
|
const refShouldClose = useRef(null);
|
|
@@ -250,64 +247,57 @@ const Modal = React.forwardRef(({ open, center, blockScroll = true, closeOnEsc =
|
|
|
250
247
|
const containerModal = container || refContainer.current;
|
|
251
248
|
const overlayAnimation = open ? classNames?.overlayAnimationIn ?? classes.overlayAnimationIn : classNames?.overlayAnimationOut ?? classes.overlayAnimationOut;
|
|
252
249
|
const modalAnimation = open ? classNames?.modalAnimationIn ?? classes.modalAnimationIn : classNames?.modalAnimationOut ?? classes.modalAnimationOut;
|
|
253
|
-
return showPortal && containerModal ? createPortal(/* @__PURE__ */
|
|
250
|
+
return showPortal && containerModal ? createPortal(/* @__PURE__ */ React.createElement("div", {
|
|
254
251
|
className: cx(classes.root, classNames?.root),
|
|
255
252
|
style: styles?.root,
|
|
256
|
-
"data-testid": "root"
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
id: closeIconId
|
|
303
|
-
})
|
|
304
|
-
]
|
|
305
|
-
})
|
|
306
|
-
})]
|
|
307
|
-
}), containerModal) : null;
|
|
253
|
+
"data-testid": "root"
|
|
254
|
+
}, /* @__PURE__ */ React.createElement("div", {
|
|
255
|
+
className: cx(classes.overlay, classNames?.overlay),
|
|
256
|
+
"data-testid": "overlay",
|
|
257
|
+
"aria-hidden": true,
|
|
258
|
+
style: {
|
|
259
|
+
animation: `${overlayAnimation} ${animationDuration}ms`,
|
|
260
|
+
...styles?.overlay
|
|
261
|
+
}
|
|
262
|
+
}), /* @__PURE__ */ React.createElement("div", {
|
|
263
|
+
ref: refModal,
|
|
264
|
+
id: containerId,
|
|
265
|
+
className: cx(classes.modalContainer, center && classes.modalContainerCenter, classNames?.modalContainer),
|
|
266
|
+
style: styles?.modalContainer,
|
|
267
|
+
"data-testid": "modal-container",
|
|
268
|
+
onClick: handleClickOverlay
|
|
269
|
+
}, /* @__PURE__ */ React.createElement("div", {
|
|
270
|
+
ref: refDialog,
|
|
271
|
+
className: cx(classes.modal, classNames?.modal),
|
|
272
|
+
style: {
|
|
273
|
+
animation: `${modalAnimation} ${animationDuration}ms`,
|
|
274
|
+
...styles?.modal
|
|
275
|
+
},
|
|
276
|
+
onMouseDown: handleModalEvent,
|
|
277
|
+
onMouseUp: handleModalEvent,
|
|
278
|
+
onClick: handleModalEvent,
|
|
279
|
+
onAnimationEnd: handleAnimationEnd,
|
|
280
|
+
id: modalId,
|
|
281
|
+
role,
|
|
282
|
+
"aria-modal": "true",
|
|
283
|
+
"aria-label": ariaLabel,
|
|
284
|
+
"aria-labelledby": ariaLabelledby,
|
|
285
|
+
"aria-describedby": ariaDescribedby,
|
|
286
|
+
"data-testid": "modal",
|
|
287
|
+
tabIndex: -1
|
|
288
|
+
}, focusTrapped && /* @__PURE__ */ React.createElement(FocusTrap, {
|
|
289
|
+
container: refDialog,
|
|
290
|
+
initialFocusRef
|
|
291
|
+
}), children, showCloseIcon && /* @__PURE__ */ React.createElement(CloseIcon, {
|
|
292
|
+
classes,
|
|
293
|
+
classNames,
|
|
294
|
+
styles,
|
|
295
|
+
closeIcon,
|
|
296
|
+
onClick: onClose,
|
|
297
|
+
id: closeIconId
|
|
298
|
+
})))), containerModal) : null;
|
|
308
299
|
});
|
|
309
|
-
var src_default = Modal;
|
|
310
|
-
|
|
311
300
|
//#endregion
|
|
312
|
-
export { Modal,
|
|
301
|
+
export { Modal, Modal as default };
|
|
302
|
+
|
|
313
303
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":["classes","node: any","nodes: any","form: any","parentElem: any","event: any","event: KeyboardEvent","modals: Ref<Element>[]","newModal: Ref<Element>","oldModal: Ref<Element>","modal: Ref<Element>","ref: Ref<Element>","open: boolean","refModal: React.RefObject<Element | null>","open: boolean","showPortal: boolean","blockScroll: boolean","reserveScrollBarGap?: boolean","ref: React.ForwardedRef<HTMLDivElement>","event: KeyboardEvent","event: React.MouseEvent<HTMLDivElement, MouseEvent>","CloseIcon"],"sources":["../src/CloseIcon.tsx","../src/utils.ts","../src/lib/focusTrapJs.ts","../src/FocusTrap.tsx","../src/modalManager.ts","../src/useScrollLock.ts","../src/index.tsx"],"sourcesContent":["import React from 'react';\nimport cx from 'classnames';\n\ninterface CloseIconProps {\n id?: string;\n closeIcon?: React.ReactNode;\n styles?: {\n closeButton?: React.CSSProperties;\n closeIcon?: React.CSSProperties;\n };\n classNames?: {\n closeButton?: string;\n closeIcon?: string;\n };\n classes: {\n closeButton?: string;\n };\n onClick: () => void;\n}\n\nconst CloseIcon = ({\n classes,\n classNames,\n styles,\n id,\n closeIcon,\n onClick,\n}: CloseIconProps) => (\n <button\n id={id}\n className={cx(classes.closeButton, classNames?.closeButton)}\n style={styles?.closeButton}\n onClick={onClick}\n data-testid=\"close-button\"\n >\n {closeIcon ? (\n closeIcon\n ) : (\n <svg\n className={classNames?.closeIcon}\n style={styles?.closeIcon}\n width={28}\n height={28}\n viewBox=\"0 0 36 36\"\n data-testid=\"close-icon\"\n >\n <path d=\"M28.5 9.62L26.38 7.5 18 15.88 9.62 7.5 7.5 9.62 15.88 18 7.5 26.38l2.12 2.12L18 20.12l8.38 8.38 2.12-2.12L20.12 18z\" />\n </svg>\n )}\n </button>\n);\n\nexport default CloseIcon;\n","export const isBrowser = typeof window !== 'undefined';\n","// https://github.com/alexandrzavalii/focus-trap-js/blob/master/src/index.js v1.1.0\n\nexport const candidateSelectors = [\n 'input',\n 'select',\n 'textarea',\n 'a[href]',\n 'button',\n '[tabindex]',\n 'audio[controls]',\n 'video[controls]',\n '[contenteditable]:not([contenteditable=\"false\"])',\n];\n\nfunction isHidden(node: any) {\n // offsetParent being null will allow detecting cases where an element is invisible or inside an invisible element,\n // as long as the element does not use position: fixed. For them, their visibility has to be checked directly as well.\n return (\n node.offsetParent === null || getComputedStyle(node).visibility === 'hidden'\n );\n}\n\nfunction getCheckedRadio(nodes: any, form: any) {\n for (var i = 0; i < nodes.length; i++) {\n if (nodes[i].checked && nodes[i].form === form) {\n return nodes[i];\n }\n }\n}\n\nfunction isNotRadioOrTabbableRadio(node: any) {\n if (node.tagName !== 'INPUT' || node.type !== 'radio' || !node.name) {\n return true;\n }\n var radioScope = node.form || node.ownerDocument;\n var radioSet = radioScope.querySelectorAll(\n 'input[type=\"radio\"][name=\"' + node.name + '\"]',\n );\n var checked = getCheckedRadio(radioSet, node.form);\n return checked === node || (checked === undefined && radioSet[0] === node);\n}\n\nexport function getAllTabbingElements(parentElem: any) {\n var currentActiveElement = document.activeElement;\n var tabbableNodes = parentElem.querySelectorAll(candidateSelectors.join(','));\n var onlyTabbable = [];\n for (var i = 0; i < tabbableNodes.length; i++) {\n var node = tabbableNodes[i];\n if (\n currentActiveElement === node ||\n (!node.disabled &&\n getTabindex(node) > -1 &&\n !isHidden(node) &&\n isNotRadioOrTabbableRadio(node))\n ) {\n onlyTabbable.push(node);\n }\n }\n return onlyTabbable;\n}\n\nexport function tabTrappingKey(event: any, parentElem: any) {\n // check if current event keyCode is tab\n if (!event || event.key !== 'Tab') return;\n\n if (!parentElem || !parentElem.contains) {\n if (process && process.env.NODE_ENV === 'development') {\n console.warn('focus-trap-js: parent element is not defined');\n }\n return false;\n }\n\n if (!parentElem.contains(event.target)) {\n return false;\n }\n\n var allTabbingElements = getAllTabbingElements(parentElem);\n var firstFocusableElement = allTabbingElements[0];\n var lastFocusableElement = allTabbingElements[allTabbingElements.length - 1];\n\n if (event.shiftKey && event.target === firstFocusableElement) {\n lastFocusableElement.focus();\n event.preventDefault();\n return true;\n } else if (!event.shiftKey && event.target === lastFocusableElement) {\n firstFocusableElement.focus();\n event.preventDefault();\n return true;\n }\n return false;\n}\n\nfunction getTabindex(node: any) {\n var tabindexAttr = parseInt(node.getAttribute('tabindex'), 10);\n\n if (!isNaN(tabindexAttr)) return tabindexAttr;\n // Browsers do not return tabIndex correctly for contentEditable nodes;\n // so if they don't have a tabindex attribute specifically set, assume it's 0.\n\n if (isContentEditable(node)) return 0;\n return node.tabIndex;\n}\n\nfunction isContentEditable(node: any) {\n return node.getAttribute('contentEditable');\n}\n","import { useEffect, useRef } from 'react';\nimport { isBrowser } from './utils';\nimport {\n tabTrappingKey,\n candidateSelectors,\n getAllTabbingElements,\n} from './lib/focusTrapJs';\n\ninterface FocusTrapProps {\n container?: React.RefObject<HTMLElement> | null;\n initialFocusRef?: React.RefObject<HTMLElement | null>;\n}\n\nexport const FocusTrap = ({ container, initialFocusRef }: FocusTrapProps) => {\n const refLastFocus = useRef<HTMLElement | null>(null);\n /**\n * Handle focus lock on the modal\n */\n useEffect(() => {\n const handleKeyEvent = (event: KeyboardEvent) => {\n if (container?.current) {\n tabTrappingKey(event, container.current);\n }\n };\n\n if (isBrowser) {\n document.addEventListener('keydown', handleKeyEvent);\n }\n // On mount we focus on the first focusable element in the modal if there is one\n if (isBrowser && container?.current) {\n const savePreviousFocus = () => {\n // First we save the last focused element\n // only if it's a focusable element\n if (\n candidateSelectors.findIndex((selector) =>\n document.activeElement?.matches(selector),\n ) !== -1\n ) {\n refLastFocus.current = document.activeElement as HTMLElement;\n }\n };\n\n if (initialFocusRef) {\n savePreviousFocus();\n // We need to schedule focusing on a next frame - this allows to focus on the modal root\n requestAnimationFrame(() => {\n initialFocusRef.current?.focus();\n });\n } else {\n const allTabbingElements = getAllTabbingElements(container.current);\n if (allTabbingElements[0]) {\n savePreviousFocus();\n allTabbingElements[0].focus();\n }\n }\n }\n return () => {\n if (isBrowser) {\n document.removeEventListener('keydown', handleKeyEvent);\n // On unmount we restore the focus to the last focused element\n refLastFocus.current?.focus();\n }\n };\n }, [container, initialFocusRef]);\n\n return null;\n};\n","import { Ref, useEffect } from 'react';\n\nlet modals: Ref<Element>[] = [];\n\n/**\n * Handle the order of the modals.\n * Inspired by the material-ui implementation.\n */\nexport const modalManager = {\n /**\n * Register a new modal\n */\n add: (newModal: Ref<Element>) => {\n modals.push(newModal);\n },\n\n /**\n * Remove a modal\n */\n remove: (oldModal: Ref<Element>) => {\n modals = modals.filter((modal) => modal !== oldModal);\n },\n\n /**\n * When multiple modals are rendered will return true if current modal is the last one\n */\n isTopModal: (modal: Ref<Element>) =>\n !!modals.length && modals[modals.length - 1] === modal,\n};\n\nexport function useModalManager(ref: Ref<Element>, open: boolean) {\n useEffect(() => {\n if (open) {\n modalManager.add(ref);\n }\n return () => {\n modalManager.remove(ref);\n };\n }, [open, ref]);\n}\n","import { useEffect, useRef } from 'react';\nimport { disableBodyScroll, enableBodyScroll } from 'body-scroll-lock';\n\nexport const useScrollLock = (\n refModal: React.RefObject<Element | null>,\n open: boolean,\n showPortal: boolean,\n blockScroll: boolean,\n reserveScrollBarGap?: boolean,\n) => {\n const oldRef = useRef<Element | null>(null);\n\n useEffect(() => {\n if (open && refModal.current && blockScroll) {\n oldRef.current = refModal.current;\n disableBodyScroll(refModal.current, { reserveScrollBarGap });\n }\n return () => {\n if (oldRef.current) {\n enableBodyScroll(oldRef.current);\n oldRef.current = null;\n }\n };\n }, [open, showPortal, refModal, blockScroll, reserveScrollBarGap]);\n};\n","import React, { useEffect, useRef, useState } from 'react';\nimport { createPortal } from 'react-dom';\nimport cx from 'classnames';\nimport { useForwardedRef } from '@bedrock-layout/use-forwarded-ref';\nimport CloseIcon from './CloseIcon';\nimport { FocusTrap } from './FocusTrap';\nimport { modalManager, useModalManager } from './modalManager';\nimport { useScrollLock } from './useScrollLock';\nimport { isBrowser } from './utils';\n\nconst classes = {\n root: 'react-responsive-modal-root',\n overlay: 'react-responsive-modal-overlay',\n overlayAnimationIn: 'react-responsive-modal-overlay-in',\n overlayAnimationOut: 'react-responsive-modal-overlay-out',\n modalContainer: 'react-responsive-modal-container',\n modalContainerCenter: 'react-responsive-modal-containerCenter',\n modal: 'react-responsive-modal-modal',\n modalAnimationIn: 'react-responsive-modal-modal-in',\n modalAnimationOut: 'react-responsive-modal-modal-out',\n closeButton: 'react-responsive-modal-closeButton',\n};\n\nexport interface ModalProps {\n /**\n * Control if the modal is open or not.\n */\n open: boolean;\n /**\n * Should the dialog be centered.\n *\n * Default to false.\n */\n center?: boolean;\n /**\n * Is the modal closable when user press esc key.\n *\n * Default to true.\n */\n closeOnEsc?: boolean;\n /**\n * Is the modal closable when user click on overlay.\n *\n * Default to true.\n */\n closeOnOverlayClick?: boolean;\n /**\n * Whether to block scrolling when dialog is open.\n *\n * Default to true.\n */\n blockScroll?: boolean;\n /**\n * Show the close icon.\n *\n * Default to true.\n */\n showCloseIcon?: boolean;\n /**\n * id attribute for the close icon button.\n */\n closeIconId?: string;\n /**\n * Custom icon to render (svg, img, etc...).\n */\n closeIcon?: React.ReactNode;\n /**\n * When the modal is open, trap focus within it.\n *\n * Default to true.\n */\n focusTrapped?: boolean;\n /**\n * Element to focus when focus trap is used.\n *\n * Default to undefined.\n */\n initialFocusRef?: React.RefObject<HTMLElement | null>;\n /**\n * You can specify a container prop which should be of type `Element`.\n * The portal will be rendered inside that element.\n * The default behavior will create a div node and render it at the at the end of document.body.\n */\n container?: Element | null;\n /**\n * An object containing classNames to style the modal.\n */\n classNames?: {\n root?: string;\n overlay?: string;\n overlayAnimationIn?: string;\n overlayAnimationOut?: string;\n modalContainer?: string;\n modal?: string;\n modalAnimationIn?: string;\n modalAnimationOut?: string;\n closeButton?: string;\n closeIcon?: string;\n };\n /**\n * An object containing the styles objects to style the modal.\n */\n styles?: {\n root?: React.CSSProperties;\n overlay?: React.CSSProperties;\n modalContainer?: React.CSSProperties;\n modal?: React.CSSProperties;\n closeButton?: React.CSSProperties;\n closeIcon?: React.CSSProperties;\n };\n /**\n * Animation duration in milliseconds.\n *\n * Default to 300.\n */\n animationDuration?: number;\n /**\n * ARIA role for modal\n *\n * Default to 'dialog'.\n */\n role?: string;\n /**\n * ARIA label for modal\n */\n ariaLabelledby?: string;\n /**\n * ARIA description for modal\n */\n ariaDescribedby?: string;\n /**\n * Avoid unpleasant flickering effect when body overflow is hidden. For more information see https://www.npmjs.com/package/body-scroll-lock\n */\n reserveScrollBarGap?: boolean;\n /**\n * id attribute for modal container\n */\n containerId?: string;\n /**\n * id attribute for modal\n */\n modalId?: string;\n /**\n * Callback fired when the Modal is requested to be closed by a click on the overlay or when user press esc key.\n */\n onClose: () => void;\n /**\n * Callback fired when the escape key is pressed.\n */\n onEscKeyDown?: (event: KeyboardEvent) => void;\n /**\n * Callback fired when the overlay is clicked.\n */\n onOverlayClick?: (\n event: React.MouseEvent<HTMLDivElement, MouseEvent>,\n ) => void;\n /**\n * Callback fired when the Modal has exited and the animation is finished.\n */\n onAnimationEnd?: () => void;\n children?: React.ReactNode;\n}\n\nexport const Modal = React.forwardRef(\n (\n {\n open,\n center,\n blockScroll = true,\n closeOnEsc = true,\n closeOnOverlayClick = true,\n container,\n showCloseIcon = true,\n closeIconId,\n closeIcon,\n focusTrapped = true,\n initialFocusRef = undefined,\n animationDuration = 300,\n classNames,\n styles,\n role = 'dialog',\n ariaDescribedby,\n ariaLabelledby,\n containerId,\n modalId,\n onClose,\n onEscKeyDown,\n onOverlayClick,\n onAnimationEnd,\n children,\n reserveScrollBarGap,\n }: ModalProps,\n ref: React.ForwardedRef<HTMLDivElement>,\n ) => {\n const refDialog = useForwardedRef(ref);\n const refModal = useRef<HTMLDivElement>(null);\n const refShouldClose = useRef<boolean | null>(null);\n const refContainer = useRef<HTMLDivElement | null>(null);\n // Lazily create the ref instance\n // https://reactjs.org/docs/hooks-faq.html#how-to-create-expensive-objects-lazily\n if (refContainer.current === null && isBrowser) {\n refContainer.current = document.createElement('div');\n }\n\n // The value should be false for srr, that way when the component is hydrated client side,\n // it will match the server rendered content\n const [showPortal, setShowPortal] = useState(false);\n\n // Hook used to manage multiple modals opened at the same time\n useModalManager(refModal, open);\n\n // Hook used to manage the scroll\n useScrollLock(refModal, open, showPortal, blockScroll, reserveScrollBarGap);\n\n const handleOpen = () => {\n if (\n refContainer.current &&\n !container &&\n !document.body.contains(refContainer.current)\n ) {\n document.body.appendChild(refContainer.current);\n }\n\n document.addEventListener('keydown', handleKeydown);\n };\n\n const handleClose = () => {\n if (\n refContainer.current &&\n !container &&\n document.body.contains(refContainer.current)\n ) {\n document.body.removeChild(refContainer.current);\n }\n document.removeEventListener('keydown', handleKeydown);\n };\n\n const handleKeydown = (event: KeyboardEvent) => {\n // Only the last modal need to be escaped when pressing the esc key\n if (event.keyCode !== 27 || !modalManager.isTopModal(refModal)) {\n return;\n }\n\n onEscKeyDown?.(event);\n\n if (closeOnEsc) {\n onClose();\n }\n };\n\n useEffect(() => {\n return () => {\n if (showPortal) {\n // When the modal is closed or removed directly, cleanup the listeners\n handleClose();\n }\n };\n }, [showPortal]);\n\n useEffect(() => {\n // If the open prop is changing, we need to open the modal\n // This is also called on the first render if the open prop is true when the modal is created\n if (open && !showPortal) {\n setShowPortal(true);\n handleOpen();\n }\n }, [open]);\n\n const handleClickOverlay = (\n event: React.MouseEvent<HTMLDivElement, MouseEvent>,\n ) => {\n if (refShouldClose.current === null) {\n refShouldClose.current = true;\n }\n\n if (!refShouldClose.current) {\n refShouldClose.current = null;\n return;\n }\n\n onOverlayClick?.(event);\n\n if (closeOnOverlayClick) {\n onClose();\n }\n\n refShouldClose.current = null;\n };\n\n const handleModalEvent = () => {\n refShouldClose.current = false;\n };\n\n const handleAnimationEnd = () => {\n if (!open) {\n setShowPortal(false);\n }\n\n onAnimationEnd?.();\n };\n\n const containerModal = container || refContainer.current;\n\n const overlayAnimation = open\n ? (classNames?.overlayAnimationIn ?? classes.overlayAnimationIn)\n : (classNames?.overlayAnimationOut ?? classes.overlayAnimationOut);\n\n const modalAnimation = open\n ? (classNames?.modalAnimationIn ?? classes.modalAnimationIn)\n : (classNames?.modalAnimationOut ?? classes.modalAnimationOut);\n\n return showPortal && containerModal\n ? createPortal(\n <div\n className={cx(classes.root, classNames?.root)}\n style={styles?.root}\n data-testid=\"root\"\n >\n <div\n className={cx(classes.overlay, classNames?.overlay)}\n data-testid=\"overlay\"\n aria-hidden={true}\n style={{\n animation: `${overlayAnimation} ${animationDuration}ms`,\n ...styles?.overlay,\n }}\n />\n <div\n ref={refModal}\n id={containerId}\n className={cx(\n classes.modalContainer,\n center && classes.modalContainerCenter,\n classNames?.modalContainer,\n )}\n style={styles?.modalContainer}\n data-testid=\"modal-container\"\n onClick={handleClickOverlay}\n >\n <div\n ref={refDialog}\n className={cx(classes.modal, classNames?.modal)}\n style={{\n animation: `${modalAnimation} ${animationDuration}ms`,\n ...styles?.modal,\n }}\n onMouseDown={handleModalEvent}\n onMouseUp={handleModalEvent}\n onClick={handleModalEvent}\n onAnimationEnd={handleAnimationEnd}\n id={modalId}\n role={role}\n aria-modal=\"true\"\n aria-labelledby={ariaLabelledby}\n aria-describedby={ariaDescribedby}\n data-testid=\"modal\"\n tabIndex={-1}\n >\n {focusTrapped && (\n <FocusTrap\n container={refDialog}\n initialFocusRef={initialFocusRef}\n />\n )}\n {children}\n {showCloseIcon && (\n <CloseIcon\n classes={classes}\n classNames={classNames}\n styles={styles}\n closeIcon={closeIcon}\n onClick={onClose}\n id={closeIconId}\n />\n )}\n </div>\n </div>\n </div>,\n containerModal,\n )\n : null;\n },\n);\n\nexport default Modal;\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../src/CloseIcon.tsx","../src/lib/focusTrapJs.ts","../src/utils.ts","../src/FocusTrap.tsx","../src/modalManager.ts","../src/useScrollLock.ts","../src/index.tsx"],"sourcesContent":["import cx from 'classnames';\nimport React from 'react';\n\ninterface CloseIconProps {\n id?: string;\n closeIcon?: React.ReactNode;\n styles?: {\n closeButton?: React.CSSProperties;\n closeIcon?: React.CSSProperties;\n };\n classNames?: {\n closeButton?: string;\n closeIcon?: string;\n };\n classes: {\n closeButton?: string;\n };\n onClick: () => void;\n}\n\nconst CloseIcon = ({\n classes,\n classNames,\n styles,\n id,\n closeIcon,\n onClick,\n}: CloseIconProps) => (\n <button\n id={id}\n className={cx(classes.closeButton, classNames?.closeButton)}\n style={styles?.closeButton}\n onClick={onClick}\n data-testid=\"close-button\"\n >\n {closeIcon ? (\n closeIcon\n ) : (\n <svg\n className={classNames?.closeIcon}\n style={styles?.closeIcon}\n width={28}\n height={28}\n viewBox=\"0 0 36 36\"\n data-testid=\"close-icon\"\n >\n <path d=\"M28.5 9.62L26.38 7.5 18 15.88 9.62 7.5 7.5 9.62 15.88 18 7.5 26.38l2.12 2.12L18 20.12l8.38 8.38 2.12-2.12L20.12 18z\" />\n </svg>\n )}\n </button>\n);\n\nexport default CloseIcon;\n","// https://github.com/alexandrzavalii/focus-trap-js/blob/master/src/index.js v1.1.0\n\nexport const candidateSelectors = [\n 'input',\n 'select',\n 'textarea',\n 'a[href]',\n 'button',\n '[tabindex]',\n 'audio[controls]',\n 'video[controls]',\n '[contenteditable]:not([contenteditable=\"false\"])',\n];\n\nfunction isHidden(node: any) {\n // offsetParent being null will allow detecting cases where an element is invisible or inside an invisible element,\n // as long as the element does not use position: fixed. For them, their visibility has to be checked directly as well.\n return (\n node.offsetParent === null || getComputedStyle(node).visibility === 'hidden'\n );\n}\n\nfunction getCheckedRadio(nodes: any, form: any) {\n for (var i = 0; i < nodes.length; i++) {\n if (nodes[i].checked && nodes[i].form === form) {\n return nodes[i];\n }\n }\n}\n\nfunction isNotRadioOrTabbableRadio(node: any) {\n if (node.tagName !== 'INPUT' || node.type !== 'radio' || !node.name) {\n return true;\n }\n var radioScope = node.form || node.ownerDocument;\n var radioSet = radioScope.querySelectorAll(\n 'input[type=\"radio\"][name=\"' + node.name + '\"]',\n );\n var checked = getCheckedRadio(radioSet, node.form);\n return checked === node || (checked === undefined && radioSet[0] === node);\n}\n\nexport function getAllTabbingElements(parentElem: any) {\n var currentActiveElement = document.activeElement;\n var tabbableNodes = parentElem.querySelectorAll(candidateSelectors.join(','));\n var onlyTabbable = [];\n for (var i = 0; i < tabbableNodes.length; i++) {\n var node = tabbableNodes[i];\n if (\n currentActiveElement === node ||\n (!node.disabled &&\n getTabindex(node) > -1 &&\n !isHidden(node) &&\n isNotRadioOrTabbableRadio(node))\n ) {\n onlyTabbable.push(node);\n }\n }\n return onlyTabbable;\n}\n\nexport function tabTrappingKey(event: any, parentElem: any) {\n // check if current event keyCode is tab\n if (!event || event.key !== 'Tab') return;\n\n if (!parentElem || !parentElem.contains) {\n if (process && process.env.NODE_ENV === 'development') {\n console.warn('focus-trap-js: parent element is not defined');\n }\n return false;\n }\n\n if (!parentElem.contains(event.target)) {\n return false;\n }\n\n var allTabbingElements = getAllTabbingElements(parentElem);\n var firstFocusableElement = allTabbingElements[0];\n var lastFocusableElement = allTabbingElements[allTabbingElements.length - 1];\n\n if (event.shiftKey && event.target === firstFocusableElement) {\n lastFocusableElement.focus();\n event.preventDefault();\n return true;\n } else if (!event.shiftKey && event.target === lastFocusableElement) {\n firstFocusableElement.focus();\n event.preventDefault();\n return true;\n }\n return false;\n}\n\nfunction getTabindex(node: any) {\n var tabindexAttr = parseInt(node.getAttribute('tabindex'), 10);\n\n if (!isNaN(tabindexAttr)) return tabindexAttr;\n // Browsers do not return tabIndex correctly for contentEditable nodes;\n // so if they don't have a tabindex attribute specifically set, assume it's 0.\n\n if (isContentEditable(node)) return 0;\n return node.tabIndex;\n}\n\nfunction isContentEditable(node: any) {\n return node.getAttribute('contentEditable');\n}\n","export const isBrowser = typeof window !== 'undefined';\n","import { useEffect, useRef } from 'react';\n\nimport {\n tabTrappingKey,\n candidateSelectors,\n getAllTabbingElements,\n} from './lib/focusTrapJs';\nimport { isBrowser } from './utils';\n\ninterface FocusTrapProps {\n container?: React.RefObject<HTMLElement> | null;\n initialFocusRef?: React.RefObject<HTMLElement | null>;\n}\n\nexport const FocusTrap = ({ container, initialFocusRef }: FocusTrapProps) => {\n const refLastFocus = useRef<HTMLElement | null>(null);\n /**\n * Handle focus lock on the modal\n */\n useEffect(() => {\n const handleKeyEvent = (event: KeyboardEvent) => {\n if (container?.current) {\n tabTrappingKey(event, container.current);\n }\n };\n\n if (isBrowser) {\n document.addEventListener('keydown', handleKeyEvent);\n }\n // On mount we focus on the first focusable element in the modal if there is one\n if (isBrowser && container?.current) {\n const savePreviousFocus = () => {\n // First we save the last focused element\n // only if it's a focusable element\n if (\n candidateSelectors.findIndex((selector) =>\n document.activeElement?.matches(selector),\n ) !== -1\n ) {\n refLastFocus.current = document.activeElement as HTMLElement;\n }\n };\n\n if (initialFocusRef) {\n savePreviousFocus();\n // We need to schedule focusing on a next frame - this allows to focus on the modal root\n requestAnimationFrame(() => {\n initialFocusRef.current?.focus();\n });\n } else {\n const allTabbingElements = getAllTabbingElements(container.current);\n if (allTabbingElements[0]) {\n savePreviousFocus();\n allTabbingElements[0].focus();\n }\n }\n }\n return () => {\n if (isBrowser) {\n document.removeEventListener('keydown', handleKeyEvent);\n // On unmount we restore the focus to the last focused element\n refLastFocus.current?.focus();\n }\n };\n }, [container, initialFocusRef]);\n\n return null;\n};\n","import { Ref, useEffect } from 'react';\n\nlet modals: Ref<Element>[] = [];\n\n/**\n * Handle the order of the modals.\n * Inspired by the material-ui implementation.\n */\nexport const modalManager = {\n /**\n * Register a new modal\n */\n add: (newModal: Ref<Element>) => {\n modals.push(newModal);\n },\n\n /**\n * Remove a modal\n */\n remove: (oldModal: Ref<Element>) => {\n modals = modals.filter((modal) => modal !== oldModal);\n },\n\n /**\n * When multiple modals are rendered will return true if current modal is the last one\n */\n isTopModal: (modal: Ref<Element>) =>\n !!modals.length && modals[modals.length - 1] === modal,\n};\n\nexport function useModalManager(ref: Ref<Element>, open: boolean) {\n useEffect(() => {\n if (open) {\n modalManager.add(ref);\n }\n return () => {\n modalManager.remove(ref);\n };\n }, [open, ref]);\n}\n","import { disableBodyScroll, enableBodyScroll } from 'body-scroll-lock';\nimport { useEffect, useRef } from 'react';\n\nexport const useScrollLock = (\n refModal: React.RefObject<Element | null>,\n open: boolean,\n showPortal: boolean,\n blockScroll: boolean,\n reserveScrollBarGap?: boolean,\n) => {\n const oldRef = useRef<Element | null>(null);\n\n useEffect(() => {\n if (open && refModal.current && blockScroll) {\n oldRef.current = refModal.current;\n disableBodyScroll(refModal.current, { reserveScrollBarGap });\n }\n return () => {\n if (oldRef.current) {\n enableBodyScroll(oldRef.current);\n oldRef.current = null;\n }\n };\n }, [open, showPortal, refModal, blockScroll, reserveScrollBarGap]);\n};\n","import { useForwardedRef } from '@bedrock-layout/use-forwarded-ref';\nimport cx from 'classnames';\nimport React, { useEffect, useRef, useState } from 'react';\nimport { createPortal } from 'react-dom';\n\nimport CloseIcon from './CloseIcon';\nimport { FocusTrap } from './FocusTrap';\nimport { modalManager, useModalManager } from './modalManager';\nimport { useScrollLock } from './useScrollLock';\nimport { isBrowser } from './utils';\n\nconst classes = {\n root: 'react-responsive-modal-root',\n overlay: 'react-responsive-modal-overlay',\n overlayAnimationIn: 'react-responsive-modal-overlay-in',\n overlayAnimationOut: 'react-responsive-modal-overlay-out',\n modalContainer: 'react-responsive-modal-container',\n modalContainerCenter: 'react-responsive-modal-containerCenter',\n modal: 'react-responsive-modal-modal',\n modalAnimationIn: 'react-responsive-modal-modal-in',\n modalAnimationOut: 'react-responsive-modal-modal-out',\n closeButton: 'react-responsive-modal-closeButton',\n};\n\nexport interface ModalProps {\n /**\n * Control if the modal is open or not.\n */\n open: boolean;\n /**\n * Should the dialog be centered.\n *\n * Default to false.\n */\n center?: boolean;\n /**\n * Is the modal closable when user press esc key.\n *\n * Default to true.\n */\n closeOnEsc?: boolean;\n /**\n * Is the modal closable when user click on overlay.\n *\n * Default to true.\n */\n closeOnOverlayClick?: boolean;\n /**\n * Whether to block scrolling when dialog is open.\n *\n * Default to true.\n */\n blockScroll?: boolean;\n /**\n * Show the close icon.\n *\n * Default to true.\n */\n showCloseIcon?: boolean;\n /**\n * id attribute for the close icon button.\n */\n closeIconId?: string;\n /**\n * Custom icon to render (svg, img, etc...).\n */\n closeIcon?: React.ReactNode;\n /**\n * When the modal is open, trap focus within it.\n *\n * Default to true.\n */\n focusTrapped?: boolean;\n /**\n * Element to focus when focus trap is used.\n *\n * Default to undefined.\n */\n initialFocusRef?: React.RefObject<HTMLElement | null>;\n /**\n * You can specify a container prop which should be of type `Element`.\n * The portal will be rendered inside that element.\n * The default behavior will create a div node and render it at the at the end of document.body.\n */\n container?: Element | null;\n /**\n * An object containing classNames to style the modal.\n */\n classNames?: {\n root?: string;\n overlay?: string;\n overlayAnimationIn?: string;\n overlayAnimationOut?: string;\n modalContainer?: string;\n modal?: string;\n modalAnimationIn?: string;\n modalAnimationOut?: string;\n closeButton?: string;\n closeIcon?: string;\n };\n /**\n * An object containing the styles objects to style the modal.\n */\n styles?: {\n root?: React.CSSProperties;\n overlay?: React.CSSProperties;\n modalContainer?: React.CSSProperties;\n modal?: React.CSSProperties;\n closeButton?: React.CSSProperties;\n closeIcon?: React.CSSProperties;\n };\n /**\n * Animation duration in milliseconds.\n *\n * Default to 300.\n */\n animationDuration?: number;\n /**\n * ARIA role for modal\n *\n * Default to 'dialog'.\n */\n role?: string;\n /**\n * ARIA label for modal\n */\n ariaLabel?: string;\n /**\n * ARIA label for modal\n */\n ariaLabelledby?: string;\n /**\n * ARIA description for modal\n */\n ariaDescribedby?: string;\n /**\n * Avoid unpleasant flickering effect when body overflow is hidden. For more information see https://www.npmjs.com/package/body-scroll-lock\n */\n reserveScrollBarGap?: boolean;\n /**\n * id attribute for modal container\n */\n containerId?: string;\n /**\n * id attribute for modal\n */\n modalId?: string;\n /**\n * Callback fired when the Modal is requested to be closed by a click on the overlay or when user press esc key.\n */\n onClose: () => void;\n /**\n * Callback fired when the escape key is pressed.\n */\n onEscKeyDown?: (event: KeyboardEvent) => void;\n /**\n * Callback fired when the overlay is clicked.\n */\n onOverlayClick?: (\n event: React.MouseEvent<HTMLDivElement, MouseEvent>,\n ) => void;\n /**\n * Callback fired when the Modal has exited and the animation is finished.\n */\n onAnimationEnd?: () => void;\n children?: React.ReactNode;\n}\n\nexport const Modal = React.forwardRef(\n (\n {\n open,\n center,\n blockScroll = true,\n closeOnEsc = true,\n closeOnOverlayClick = true,\n container,\n showCloseIcon = true,\n closeIconId,\n closeIcon,\n focusTrapped = true,\n initialFocusRef,\n animationDuration = 300,\n classNames,\n styles,\n role = 'dialog',\n ariaLabel,\n ariaDescribedby,\n ariaLabelledby,\n containerId,\n modalId,\n onClose,\n onEscKeyDown,\n onOverlayClick,\n onAnimationEnd,\n children,\n reserveScrollBarGap,\n }: ModalProps,\n ref: React.ForwardedRef<HTMLDivElement>,\n ) => {\n const refDialog = useForwardedRef(ref);\n const refModal = useRef<HTMLDivElement>(null);\n const refShouldClose = useRef<boolean | null>(null);\n const refContainer = useRef<HTMLDivElement | null>(null);\n // Lazily create the ref instance\n // https://reactjs.org/docs/hooks-faq.html#how-to-create-expensive-objects-lazily\n if (refContainer.current === null && isBrowser) {\n refContainer.current = document.createElement('div');\n }\n\n // The value should be false for srr, that way when the component is hydrated client side,\n // it will match the server rendered content\n const [showPortal, setShowPortal] = useState(false);\n\n // Hook used to manage multiple modals opened at the same time\n useModalManager(refModal, open);\n\n // Hook used to manage the scroll\n useScrollLock(refModal, open, showPortal, blockScroll, reserveScrollBarGap);\n\n const handleOpen = () => {\n if (\n refContainer.current &&\n !container &&\n !document.body.contains(refContainer.current)\n ) {\n document.body.appendChild(refContainer.current);\n }\n\n document.addEventListener('keydown', handleKeydown);\n };\n\n const handleClose = () => {\n if (\n refContainer.current &&\n !container &&\n document.body.contains(refContainer.current)\n ) {\n document.body.removeChild(refContainer.current);\n }\n document.removeEventListener('keydown', handleKeydown);\n };\n\n const handleKeydown = (event: KeyboardEvent) => {\n // Only the last modal need to be escaped when pressing the esc key\n if (event.keyCode !== 27 || !modalManager.isTopModal(refModal)) {\n return;\n }\n\n onEscKeyDown?.(event);\n\n if (closeOnEsc) {\n onClose();\n }\n };\n\n useEffect(() => {\n return () => {\n if (showPortal) {\n // When the modal is closed or removed directly, cleanup the listeners\n handleClose();\n }\n };\n }, [showPortal]);\n\n useEffect(() => {\n // If the open prop is changing, we need to open the modal\n // This is also called on the first render if the open prop is true when the modal is created\n if (open && !showPortal) {\n setShowPortal(true);\n handleOpen();\n }\n }, [open]);\n\n const handleClickOverlay = (\n event: React.MouseEvent<HTMLDivElement, MouseEvent>,\n ) => {\n if (refShouldClose.current === null) {\n refShouldClose.current = true;\n }\n\n if (!refShouldClose.current) {\n refShouldClose.current = null;\n return;\n }\n\n onOverlayClick?.(event);\n\n if (closeOnOverlayClick) {\n onClose();\n }\n\n refShouldClose.current = null;\n };\n\n const handleModalEvent = () => {\n refShouldClose.current = false;\n };\n\n const handleAnimationEnd = () => {\n if (!open) {\n setShowPortal(false);\n }\n\n onAnimationEnd?.();\n };\n\n const containerModal = container || refContainer.current;\n\n const overlayAnimation = open\n ? (classNames?.overlayAnimationIn ?? classes.overlayAnimationIn)\n : (classNames?.overlayAnimationOut ?? classes.overlayAnimationOut);\n\n const modalAnimation = open\n ? (classNames?.modalAnimationIn ?? classes.modalAnimationIn)\n : (classNames?.modalAnimationOut ?? classes.modalAnimationOut);\n\n return showPortal && containerModal\n ? createPortal(\n <div\n className={cx(classes.root, classNames?.root)}\n style={styles?.root}\n data-testid=\"root\"\n >\n <div\n className={cx(classes.overlay, classNames?.overlay)}\n data-testid=\"overlay\"\n aria-hidden={true}\n style={{\n animation: `${overlayAnimation} ${animationDuration}ms`,\n ...styles?.overlay,\n }}\n />\n <div\n ref={refModal}\n id={containerId}\n className={cx(\n classes.modalContainer,\n center && classes.modalContainerCenter,\n classNames?.modalContainer,\n )}\n style={styles?.modalContainer}\n data-testid=\"modal-container\"\n onClick={handleClickOverlay}\n >\n <div\n ref={refDialog}\n className={cx(classes.modal, classNames?.modal)}\n style={{\n animation: `${modalAnimation} ${animationDuration}ms`,\n ...styles?.modal,\n }}\n onMouseDown={handleModalEvent}\n onMouseUp={handleModalEvent}\n onClick={handleModalEvent}\n onAnimationEnd={handleAnimationEnd}\n id={modalId}\n role={role}\n aria-modal=\"true\"\n aria-label={ariaLabel}\n aria-labelledby={ariaLabelledby}\n aria-describedby={ariaDescribedby}\n data-testid=\"modal\"\n tabIndex={-1}\n >\n {focusTrapped && (\n <FocusTrap\n container={refDialog}\n initialFocusRef={initialFocusRef}\n />\n )}\n {children}\n {showCloseIcon && (\n <CloseIcon\n classes={classes}\n classNames={classNames}\n styles={styles}\n closeIcon={closeIcon}\n onClick={onClose}\n id={closeIconId}\n />\n )}\n </div>\n </div>\n </div>,\n containerModal,\n )\n : null;\n },\n);\n\nexport default Modal;\n"],"mappings":";;;;;;AAoBA,MAAM,aAAa,EACjB,SACA,YACA,QACA,IACA,WACA,cAEA,sBAAA,cAAC,UAAD;CACM;CACJ,WAAW,GAAG,QAAQ,aAAa,YAAY,WAAW;CAC1D,OAAO,QAAQ;CACN;CACT,eAAY;AAgBN,GAdL,YACC,YAEA,sBAAA,cAAC,OAAD;CACE,WAAW,YAAY;CACvB,OAAO,QAAQ;CACf,OAAO;CACP,QAAQ;CACR,SAAQ;CACR,eAAY;AAGT,GADH,sBAAA,cAAC,QAAD,EAAM,GAAE,sHAAuH,CAAA,CAC5H,CAED;;;AC/CV,MAAa,qBAAqB;CAChC;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;AACF;AAEA,SAAS,SAAS,MAAW;CAG3B,OACE,KAAK,iBAAiB,QAAQ,iBAAiB,IAAI,EAAE,eAAe;AAExE;AAEA,SAAS,gBAAgB,OAAY,MAAW;CAC9C,KAAK,IAAI,IAAI,GAAG,IAAI,MAAM,QAAQ,KAChC,IAAI,MAAM,GAAG,WAAW,MAAM,GAAG,SAAS,MACxC,OAAO,MAAM;AAGnB;AAEA,SAAS,0BAA0B,MAAW;CAC5C,IAAI,KAAK,YAAY,WAAW,KAAK,SAAS,WAAW,CAAC,KAAK,MAC7D,OAAO;CAGT,IAAI,YADa,KAAK,QAAQ,KAAK,eACT,iBACxB,kCAA+B,KAAK,OAAO,KAC7C;CACA,IAAI,UAAU,gBAAgB,UAAU,KAAK,IAAI;CACjD,OAAO,YAAY,QAAS,YAAY,KAAA,KAAa,SAAS,OAAO;AACvE;AAEA,SAAgB,sBAAsB,YAAiB;CACrD,IAAI,uBAAuB,SAAS;CACpC,IAAI,gBAAgB,WAAW,iBAAiB,mBAAmB,KAAK,GAAG,CAAC;CAC5E,IAAI,eAAe,CAAC;CACpB,KAAK,IAAI,IAAI,GAAG,IAAI,cAAc,QAAQ,KAAK;EAC7C,IAAI,OAAO,cAAc;EACzB,IACE,yBAAyB,QACxB,CAAC,KAAK,YACL,YAAY,IAAI,IAAI,MACpB,CAAC,SAAS,IAAI,KACd,0BAA0B,IAAI,GAEhC,aAAa,KAAK,IAAI;CAE1B;CACA,OAAO;AACT;AAEA,SAAgB,eAAe,OAAY,YAAiB;CAE1D,IAAI,CAAC,SAAS,MAAM,QAAQ,OAAO;CAEnC,IAAI,CAAC,cAAc,CAAC,WAAW,UAAU;EACvC,IAAI,WAAW,MACb,QAAQ,KAAK,8CAA8C;EAE7D,OAAO;CACT;CAEA,IAAI,CAAC,WAAW,SAAS,MAAM,MAAM,GACnC,OAAO;CAGT,IAAI,qBAAqB,sBAAsB,UAAU;CACzD,IAAI,wBAAwB,mBAAmB;CAC/C,IAAI,uBAAuB,mBAAmB,mBAAmB,SAAS;CAE1E,IAAI,MAAM,YAAY,MAAM,WAAW,uBAAuB;EAC5D,qBAAqB,MAAM;EAC3B,MAAM,eAAe;EACrB,OAAO;CACT,OAAO,IAAI,CAAC,MAAM,YAAY,MAAM,WAAW,sBAAsB;EACnE,sBAAsB,MAAM;EAC5B,MAAM,eAAe;EACrB,OAAO;CACT;CACA,OAAO;AACT;AAEA,SAAS,YAAY,MAAW;CAC9B,IAAI,eAAe,SAAS,KAAK,aAAa,UAAU,GAAG,EAAE;CAE7D,IAAI,CAAC,MAAM,YAAY,GAAG,OAAO;CAIjC,IAAI,kBAAkB,IAAI,GAAG,OAAO;CACpC,OAAO,KAAK;AACd;AAEA,SAAS,kBAAkB,MAAW;CACpC,OAAO,KAAK,aAAa,iBAAiB;AAC5C;;;ACzGA,MAAa,YAAY,OAAO,WAAW;;;ACc3C,MAAa,aAAa,EAAE,WAAW,sBAAsC;CAC3E,MAAM,eAAe,OAA2B,IAAI;;;;CAIpD,gBAAgB;EACd,MAAM,kBAAkB,UAAyB;GAC/C,IAAI,WAAW,SACb,eAAe,OAAO,UAAU,OAAO;EAE3C;EAEA,IAAI,WACF,SAAS,iBAAiB,WAAW,cAAc;EAGrD,IAAI,aAAa,WAAW,SAAS;GACnC,MAAM,0BAA0B;IAG9B,IACE,mBAAmB,WAAW,aAC5B,SAAS,eAAe,QAAQ,QAAQ,CAC1C,MAAM,IAEN,aAAa,UAAU,SAAS;GAEpC;GAEA,IAAI,iBAAiB;IACnB,kBAAkB;IAElB,4BAA4B;KAC1B,gBAAgB,SAAS,MAAM;IACjC,CAAC;GACH,OAAO;IACL,MAAM,qBAAqB,sBAAsB,UAAU,OAAO;IAClE,IAAI,mBAAmB,IAAI;KACzB,kBAAkB;KAClB,mBAAmB,GAAG,MAAM;IAC9B;GACF;EACF;EACA,aAAa;GACX,IAAI,WAAW;IACb,SAAS,oBAAoB,WAAW,cAAc;IAEtD,aAAa,SAAS,MAAM;GAC9B;EACF;CACF,GAAG,CAAC,WAAW,eAAe,CAAC;CAE/B,OAAO;AACT;;;ACjEA,IAAI,SAAyB,CAAC;;;;;AAM9B,MAAa,eAAe;;;;CAI1B,MAAM,aAA2B;EAC/B,OAAO,KAAK,QAAQ;CACtB;;;;CAKA,SAAS,aAA2B;EAClC,SAAS,OAAO,QAAQ,UAAU,UAAU,QAAQ;CACtD;;;;CAKA,aAAa,UACX,CAAC,CAAC,OAAO,UAAU,OAAO,OAAO,SAAS,OAAO;AACrD;AAEA,SAAgB,gBAAgB,KAAmB,MAAe;CAChE,gBAAgB;EACd,IAAI,MACF,aAAa,IAAI,GAAG;EAEtB,aAAa;GACX,aAAa,OAAO,GAAG;EACzB;CACF,GAAG,CAAC,MAAM,GAAG,CAAC;AAChB;;;ACpCA,MAAa,iBACX,UACA,MACA,YACA,aACA,wBACG;CACH,MAAM,SAAS,OAAuB,IAAI;CAE1C,gBAAgB;EACd,IAAI,QAAQ,SAAS,WAAW,aAAa;GAC3C,OAAO,UAAU,SAAS;GAC1B,kBAAkB,SAAS,SAAS,EAAE,oBAAoB,CAAC;EAC7D;EACA,aAAa;GACX,IAAI,OAAO,SAAS;IAClB,iBAAiB,OAAO,OAAO;IAC/B,OAAO,UAAU;GACnB;EACF;CACF,GAAG;EAAC;EAAM;EAAY;EAAU;EAAa;CAAmB,CAAC;AACnE;;;ACbA,MAAM,UAAU;CACd,MAAM;CACN,SAAS;CACT,oBAAoB;CACpB,qBAAqB;CACrB,gBAAgB;CAChB,sBAAsB;CACtB,OAAO;CACP,kBAAkB;CAClB,mBAAmB;CACnB,aAAa;AACf;AAkJA,MAAa,QAAQ,MAAM,YAEvB,EACE,MACA,QACA,cAAc,MACd,aAAa,MACb,sBAAsB,MACtB,WACA,gBAAgB,MAChB,aACA,WACA,eAAe,MACf,iBACA,oBAAoB,KACpB,YACA,QACA,OAAO,UACP,WACA,iBACA,gBACA,aACA,SACA,SACA,cACA,gBACA,gBACA,UACA,uBAEF,QACG;CACH,MAAM,YAAY,gBAAgB,GAAG;CACrC,MAAM,WAAW,OAAuB,IAAI;CAC5C,MAAM,iBAAiB,OAAuB,IAAI;CAClD,MAAM,eAAe,OAA8B,IAAI;CAGvD,IAAI,aAAa,YAAY,QAAQ,WACnC,aAAa,UAAU,SAAS,cAAc,KAAK;CAKrD,MAAM,CAAC,YAAY,iBAAiB,SAAS,KAAK;CAGlD,gBAAgB,UAAU,IAAI;CAG9B,cAAc,UAAU,MAAM,YAAY,aAAa,mBAAmB;CAE1E,MAAM,mBAAmB;EACvB,IACE,aAAa,WACb,CAAC,aACD,CAAC,SAAS,KAAK,SAAS,aAAa,OAAO,GAE5C,SAAS,KAAK,YAAY,aAAa,OAAO;EAGhD,SAAS,iBAAiB,WAAW,aAAa;CACpD;CAEA,MAAM,oBAAoB;EACxB,IACE,aAAa,WACb,CAAC,aACD,SAAS,KAAK,SAAS,aAAa,OAAO,GAE3C,SAAS,KAAK,YAAY,aAAa,OAAO;EAEhD,SAAS,oBAAoB,WAAW,aAAa;CACvD;CAEA,MAAM,iBAAiB,UAAyB;EAE9C,IAAI,MAAM,YAAY,MAAM,CAAC,aAAa,WAAW,QAAQ,GAC3D;EAGF,eAAe,KAAK;EAEpB,IAAI,YACF,QAAQ;CAEZ;CAEA,gBAAgB;EACd,aAAa;GACX,IAAI,YAEF,YAAY;EAEhB;CACF,GAAG,CAAC,UAAU,CAAC;CAEf,gBAAgB;EAGd,IAAI,QAAQ,CAAC,YAAY;GACvB,cAAc,IAAI;GAClB,WAAW;EACb;CACF,GAAG,CAAC,IAAI,CAAC;CAET,MAAM,sBACJ,UACG;EACH,IAAI,eAAe,YAAY,MAC7B,eAAe,UAAU;EAG3B,IAAI,CAAC,eAAe,SAAS;GAC3B,eAAe,UAAU;GACzB;EACF;EAEA,iBAAiB,KAAK;EAEtB,IAAI,qBACF,QAAQ;EAGV,eAAe,UAAU;CAC3B;CAEA,MAAM,yBAAyB;EAC7B,eAAe,UAAU;CAC3B;CAEA,MAAM,2BAA2B;EAC/B,IAAI,CAAC,MACH,cAAc,KAAK;EAGrB,iBAAiB;CACnB;CAEA,MAAM,iBAAiB,aAAa,aAAa;CAEjD,MAAM,mBAAmB,OACpB,YAAY,sBAAsB,QAAQ,qBAC1C,YAAY,uBAAuB,QAAQ;CAEhD,MAAM,iBAAiB,OAClB,YAAY,oBAAoB,QAAQ,mBACxC,YAAY,qBAAqB,QAAQ;CAE9C,OAAO,cAAc,iBACjB,aACE,sBAAA,cAAC,OAAD;EACE,WAAW,GAAG,QAAQ,MAAM,YAAY,IAAI;EAC5C,OAAO,QAAQ;EACf,eAAY;CA8DT,GA5DH,sBAAA,cAAC,OAAD;EACE,WAAW,GAAG,QAAQ,SAAS,YAAY,OAAO;EAClD,eAAY;EACZ,eAAa;EACb,OAAO;GACL,WAAW,GAAG,iBAAiB,GAAG,kBAAkB;GACpD,GAAG,QAAQ;EACb;CACD,CAAA,GACD,sBAAA,cAAC,OAAD;EACE,KAAK;EACL,IAAI;EACJ,WAAW,GACT,QAAQ,gBACR,UAAU,QAAQ,sBAClB,YAAY,cACd;EACA,OAAO,QAAQ;EACf,eAAY;EACZ,SAAS;CAwCN,GAtCH,sBAAA,cAAC,OAAD;EACE,KAAK;EACL,WAAW,GAAG,QAAQ,OAAO,YAAY,KAAK;EAC9C,OAAO;GACL,WAAW,GAAG,eAAe,GAAG,kBAAkB;GAClD,GAAG,QAAQ;EACb;EACA,aAAa;EACb,WAAW;EACX,SAAS;EACT,gBAAgB;EAChB,IAAI;EACE;EACN,cAAW;EACX,cAAY;EACZ,mBAAiB;EACjB,oBAAkB;EAClB,eAAY;EACZ,UAAU;CAmBP,GAjBF,gBACC,sBAAA,cAAC,WAAD;EACE,WAAW;EACM;CAClB,CAAA,GAEF,UACA,iBACC,sBAAA,cAAC,WAAD;EACW;EACG;EACJ;EACG;EACX,SAAS;EACT,IAAI;CACL,CAAA,CAEA,CACF,CACF,GACL,cACF,IACA;AACN,CACF"}
|
package/package.json
CHANGED
|
@@ -1,93 +1,95 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "react-responsive-modal",
|
|
3
|
-
"version": "7.
|
|
3
|
+
"version": "7.2.0",
|
|
4
4
|
"description": "A simple responsive and accessible react modal",
|
|
5
|
-
"license": "MIT",
|
|
6
|
-
"type": "module",
|
|
7
|
-
"main": "./dist/index.cjs",
|
|
8
|
-
"module": "./dist/index.js",
|
|
9
|
-
"typings": "dist/index.d.ts",
|
|
10
|
-
"scripts": {
|
|
11
|
-
"start": "tsdown --watch",
|
|
12
|
-
"build": "tsdown",
|
|
13
|
-
"typecheck": "tsc --noEmit",
|
|
14
|
-
"test": "vitest",
|
|
15
|
-
"prepare": "tsdown",
|
|
16
|
-
"size": "size-limit"
|
|
17
|
-
},
|
|
18
|
-
"files": [
|
|
19
|
-
"dist",
|
|
20
|
-
"src",
|
|
21
|
-
"styles.css"
|
|
22
|
-
],
|
|
23
5
|
"keywords": [
|
|
24
|
-
"
|
|
25
|
-
"responsive",
|
|
26
|
-
"modal",
|
|
6
|
+
"flex",
|
|
27
7
|
"mobile",
|
|
28
|
-
"
|
|
8
|
+
"modal",
|
|
9
|
+
"react",
|
|
10
|
+
"responsive"
|
|
29
11
|
],
|
|
12
|
+
"homepage": "https://react-responsive-modal.leopradel.com/",
|
|
13
|
+
"bugs": {
|
|
14
|
+
"url": "https://github.com/pradel/react-responsive-modal/issues"
|
|
15
|
+
},
|
|
16
|
+
"license": "MIT",
|
|
17
|
+
"author": "Léo Pradel",
|
|
30
18
|
"repository": {
|
|
31
19
|
"type": "git",
|
|
32
20
|
"url": "git+https://github.com/pradel/react-responsive-modal.git",
|
|
33
21
|
"directory": "react-responsive-modal"
|
|
34
22
|
},
|
|
35
|
-
"author": "Léo Pradel",
|
|
36
|
-
"bugs": {
|
|
37
|
-
"url": "https://github.com/pradel/react-responsive-modal/issues"
|
|
38
|
-
},
|
|
39
|
-
"homepage": "https://react-responsive-modal.leopradel.com/",
|
|
40
23
|
"funding": "https://github.com/sponsors/pradel",
|
|
41
|
-
"
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
},
|
|
46
|
-
{
|
|
47
|
-
"path": "dist/index.cjs",
|
|
48
|
-
"limit": "4.0 KB"
|
|
49
|
-
}
|
|
24
|
+
"files": [
|
|
25
|
+
"dist",
|
|
26
|
+
"src",
|
|
27
|
+
"styles.css"
|
|
50
28
|
],
|
|
29
|
+
"type": "module",
|
|
30
|
+
"main": "./dist/index.cjs",
|
|
31
|
+
"module": "./dist/index.js",
|
|
32
|
+
"types": "./dist/index.d.cts",
|
|
33
|
+
"typings": "dist/index.d.ts",
|
|
34
|
+
"exports": {
|
|
35
|
+
".": {
|
|
36
|
+
"import": "./dist/index.js",
|
|
37
|
+
"require": "./dist/index.cjs"
|
|
38
|
+
},
|
|
39
|
+
"./package.json": "./package.json",
|
|
40
|
+
"./styles.css": "./styles.css"
|
|
41
|
+
},
|
|
42
|
+
"publishConfig": {
|
|
43
|
+
"access": "public",
|
|
44
|
+
"provenance": true
|
|
45
|
+
},
|
|
51
46
|
"dependencies": {
|
|
52
47
|
"@bedrock-layout/use-forwarded-ref": "^2.0.17",
|
|
53
48
|
"body-scroll-lock": "^3.1.5",
|
|
54
49
|
"classnames": "^2.3.1"
|
|
55
50
|
},
|
|
56
|
-
"peerDependencies": {
|
|
57
|
-
"react": "^16.8.0 || ^17 || ^18 || ^19",
|
|
58
|
-
"react-dom": "^16.8.0 || ^17 || ^18 || ^19"
|
|
59
|
-
},
|
|
60
51
|
"devDependencies": {
|
|
61
52
|
"@codecov/vite-plugin": "1.9.1",
|
|
62
|
-
"@size-limit/preset-small-lib": "
|
|
63
|
-
"@testing-library/dom": "10.4.
|
|
64
|
-
"@testing-library/jest-dom": "6.
|
|
65
|
-
"@testing-library/react": "16.3.
|
|
53
|
+
"@size-limit/preset-small-lib": "12.0.1",
|
|
54
|
+
"@testing-library/dom": "10.4.1",
|
|
55
|
+
"@testing-library/jest-dom": "6.9.1",
|
|
56
|
+
"@testing-library/react": "16.3.2",
|
|
66
57
|
"@types/body-scroll-lock": "2.6.2",
|
|
67
58
|
"@types/classnames": "2.3.4",
|
|
68
|
-
"@types/node": "
|
|
69
|
-
"@types/react": "19.
|
|
70
|
-
"@types/react-dom": "19.
|
|
71
|
-
"@vitejs/plugin-react": "
|
|
72
|
-
"@vitest/coverage-istanbul": "
|
|
73
|
-
"cypress": "
|
|
59
|
+
"@types/node": "25.5.0",
|
|
60
|
+
"@types/react": "19.2.14",
|
|
61
|
+
"@types/react-dom": "19.2.3",
|
|
62
|
+
"@vitejs/plugin-react": "6.0.1",
|
|
63
|
+
"@vitest/coverage-istanbul": "4.1.1",
|
|
64
|
+
"cypress": "15.12.0",
|
|
74
65
|
"jsdom": "26.1.0",
|
|
75
|
-
"publint": "0.3.
|
|
76
|
-
"react": "19.
|
|
77
|
-
"react-dom": "19.
|
|
78
|
-
"size-limit": "
|
|
79
|
-
"tsdown": "0.12.9",
|
|
66
|
+
"publint": "0.3.18",
|
|
67
|
+
"react": "19.2.4",
|
|
68
|
+
"react-dom": "19.2.4",
|
|
69
|
+
"size-limit": "12.0.1",
|
|
80
70
|
"tslib": "2.8.1",
|
|
81
|
-
"typescript": "5.
|
|
82
|
-
"
|
|
71
|
+
"typescript": "5.9.3",
|
|
72
|
+
"vite-plus": "0.1.24",
|
|
73
|
+
"vitest": "4.1.8"
|
|
83
74
|
},
|
|
84
|
-
"
|
|
85
|
-
|
|
86
|
-
"
|
|
87
|
-
|
|
88
|
-
|
|
75
|
+
"peerDependencies": {
|
|
76
|
+
"react": "^16.8.0 || ^17 || ^18 || ^19",
|
|
77
|
+
"react-dom": "^16.8.0 || ^17 || ^18 || ^19"
|
|
78
|
+
},
|
|
79
|
+
"size-limit": [
|
|
80
|
+
{
|
|
81
|
+
"path": "dist/index.js",
|
|
82
|
+
"limit": "3.6 KB"
|
|
89
83
|
},
|
|
90
|
-
|
|
91
|
-
|
|
84
|
+
{
|
|
85
|
+
"path": "dist/index.cjs",
|
|
86
|
+
"limit": "4.0 KB"
|
|
87
|
+
}
|
|
88
|
+
],
|
|
89
|
+
"scripts": {
|
|
90
|
+
"build": "vp pack",
|
|
91
|
+
"dev": "vp pack --watch",
|
|
92
|
+
"test": "vp test",
|
|
93
|
+
"size": "size-limit"
|
|
92
94
|
}
|
|
93
95
|
}
|
package/src/CloseIcon.tsx
CHANGED
package/src/FocusTrap.tsx
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import { useEffect, useRef } from 'react';
|
|
2
|
-
|
|
2
|
+
|
|
3
3
|
import {
|
|
4
4
|
tabTrappingKey,
|
|
5
5
|
candidateSelectors,
|
|
6
6
|
getAllTabbingElements,
|
|
7
7
|
} from './lib/focusTrapJs';
|
|
8
|
+
import { isBrowser } from './utils';
|
|
8
9
|
|
|
9
10
|
interface FocusTrapProps {
|
|
10
11
|
container?: React.RefObject<HTMLElement> | null;
|
package/src/index.tsx
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
|
+
import { useForwardedRef } from '@bedrock-layout/use-forwarded-ref';
|
|
2
|
+
import cx from 'classnames';
|
|
1
3
|
import React, { useEffect, useRef, useState } from 'react';
|
|
2
4
|
import { createPortal } from 'react-dom';
|
|
3
|
-
|
|
4
|
-
import { useForwardedRef } from '@bedrock-layout/use-forwarded-ref';
|
|
5
|
+
|
|
5
6
|
import CloseIcon from './CloseIcon';
|
|
6
7
|
import { FocusTrap } from './FocusTrap';
|
|
7
8
|
import { modalManager, useModalManager } from './modalManager';
|
|
@@ -120,6 +121,10 @@ export interface ModalProps {
|
|
|
120
121
|
* Default to 'dialog'.
|
|
121
122
|
*/
|
|
122
123
|
role?: string;
|
|
124
|
+
/**
|
|
125
|
+
* ARIA label for modal
|
|
126
|
+
*/
|
|
127
|
+
ariaLabel?: string;
|
|
123
128
|
/**
|
|
124
129
|
* ARIA label for modal
|
|
125
130
|
*/
|
|
@@ -174,11 +179,12 @@ export const Modal = React.forwardRef(
|
|
|
174
179
|
closeIconId,
|
|
175
180
|
closeIcon,
|
|
176
181
|
focusTrapped = true,
|
|
177
|
-
initialFocusRef
|
|
182
|
+
initialFocusRef,
|
|
178
183
|
animationDuration = 300,
|
|
179
184
|
classNames,
|
|
180
185
|
styles,
|
|
181
186
|
role = 'dialog',
|
|
187
|
+
ariaLabel,
|
|
182
188
|
ariaDescribedby,
|
|
183
189
|
ariaLabelledby,
|
|
184
190
|
containerId,
|
|
@@ -351,6 +357,7 @@ export const Modal = React.forwardRef(
|
|
|
351
357
|
id={modalId}
|
|
352
358
|
role={role}
|
|
353
359
|
aria-modal="true"
|
|
360
|
+
aria-label={ariaLabel}
|
|
354
361
|
aria-labelledby={ariaLabelledby}
|
|
355
362
|
aria-describedby={ariaDescribedby}
|
|
356
363
|
data-testid="modal"
|
package/src/useScrollLock.ts
CHANGED
package/CHANGELOG.md
DELETED
|
@@ -1,153 +0,0 @@
|
|
|
1
|
-
# Changelog
|
|
2
|
-
|
|
3
|
-
## [7.0.0](https://github.com/pradel/react-responsive-modal/compare/react-responsive-modal-v6.4.2...react-responsive-modal-v7.0.0) (2025-07-13)
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
### ⚠ BREAKING CHANGES
|
|
7
|
-
|
|
8
|
-
* add react 19 support ([#526](https://github.com/pradel/react-responsive-modal/issues/526))
|
|
9
|
-
* modernise package with rolldown ([#527](https://github.com/pradel/react-responsive-modal/issues/527))
|
|
10
|
-
|
|
11
|
-
### Features
|
|
12
|
-
|
|
13
|
-
* add react 19 support ([#526](https://github.com/pradel/react-responsive-modal/issues/526)) ([d379354](https://github.com/pradel/react-responsive-modal/commit/d379354111e35659dc398e30f69fac65f1766a35))
|
|
14
|
-
* modernise package with rolldown ([#527](https://github.com/pradel/react-responsive-modal/issues/527)) ([dc124f3](https://github.com/pradel/react-responsive-modal/commit/dc124f35d18991c39edafed260d1f52cfe1d391f))
|
|
15
|
-
|
|
16
|
-
## [6.3.2](https://github.com/pradel/react-responsive-modal/compare/v6.3.1...v6.3.2) (2022-07-21)
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
### Miscellaneous
|
|
20
|
-
|
|
21
|
-
* fix publishing process ([2cec9a2](https://github.com/pradel/react-responsive-modal/commit/2cec9a28a4a2eff8de7de2d1550d0e9550824bbe))
|
|
22
|
-
|
|
23
|
-
### [6.3.1](https://www.github.com/pradel/react-responsive-modal/compare/v6.3.0...v6.3.1) (2022-04-25)
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
### Bug Fixes
|
|
27
|
-
|
|
28
|
-
* fix publishing process ([#492](https://www.github.com/pradel/react-responsive-modal/issues/492)) ([2ecd108](https://www.github.com/pradel/react-responsive-modal/commit/2ecd1084a0398a0ae0d7baa40b9b2b4e03f3d91c))
|
|
29
|
-
|
|
30
|
-
## [6.3.0](https://www.github.com/pradel/react-responsive-modal/compare/v6.2.0...v6.3.0) (2022-04-25)
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
### Features
|
|
34
|
-
|
|
35
|
-
* ability to specify id for container ([#489](https://www.github.com/pradel/react-responsive-modal/issues/489)) ([3e09b5c](https://www.github.com/pradel/react-responsive-modal/commit/3e09b5c668e5cbe6127c5b439d57a80a3d24bd33))
|
|
36
|
-
|
|
37
|
-
## [6.2.0](https://www.github.com/pradel/react-responsive-modal/compare/v6.1.0...v6.2.0) (2021-12-14)
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
### Features
|
|
41
|
-
|
|
42
|
-
* add optional reserveScrollBarGap ([#484](https://www.github.com/pradel/react-responsive-modal/issues/484)) ([69249f8](https://www.github.com/pradel/react-responsive-modal/commit/69249f8f97d02e4eaf07bedb52cb4ff1b1d4f636))
|
|
43
|
-
|
|
44
|
-
## [6.1.0](https://www.github.com/pradel/react-responsive-modal/compare/v6.0.1...v6.1.0) (2021-06-01)
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
### Features
|
|
48
|
-
|
|
49
|
-
* add options to set initial focus within modal ([#476](https://www.github.com/pradel/react-responsive-modal/issues/476)) ([5bdc362](https://www.github.com/pradel/react-responsive-modal/commit/5bdc362521a6db00030d723015c8abd2e76f19c7))
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
### Documentation
|
|
53
|
-
|
|
54
|
-
* fix typo in menu ([#473](https://www.github.com/pradel/react-responsive-modal/issues/473)) ([23cccc6](https://www.github.com/pradel/react-responsive-modal/commit/23cccc60a71342e3c122f968888118b911387056))
|
|
55
|
-
|
|
56
|
-
### [6.0.1](https://www.github.com/pradel/react-responsive-modal/compare/v6.0.0...v6.0.1) (2021-01-08)
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
### Bug Fixes
|
|
60
|
-
|
|
61
|
-
* **docs:** fix website dependencies ([222c6ed](https://www.github.com/pradel/react-responsive-modal/commit/222c6edef3851d2939a4fafa4d6c96e19ef35b1a))
|
|
62
|
-
* fix iOS problem with scroll not working in Safari ([#464](https://www.github.com/pradel/react-responsive-modal/issues/464)) ([0e38605](https://www.github.com/pradel/react-responsive-modal/commit/0e38605e37fa0e9d67ff5104e79fadfde5941bb0))
|
|
63
|
-
* fix release-script publishing script ([9347ad5](https://www.github.com/pradel/react-responsive-modal/commit/9347ad57d781aeca637b0527e89e34acd1cf6b3a))
|
|
64
|
-
* update changelogs path ([f6f3a65](https://www.github.com/pradel/react-responsive-modal/commit/f6f3a655b4d4a5ffc7f208684f439af9b20ef897))
|
|
65
|
-
* update README path ([fce1829](https://www.github.com/pradel/react-responsive-modal/commit/fce1829fe051ab5bef85811ad6d1d34d68bbfc5a))
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
### Miscellaneous
|
|
69
|
-
|
|
70
|
-
* remove unused dev dependencies ([38d2f1b](https://www.github.com/pradel/react-responsive-modal/commit/38d2f1bbda80641e857ce80ba71e995d8c44c438))
|
|
71
|
-
* upgrade yarn ([523536e](https://www.github.com/pradel/react-responsive-modal/commit/523536e783f82d69b8af0af9ec7dd2062af15349))
|
|
72
|
-
* uppdate release-please-action ([2172030](https://www.github.com/pradel/react-responsive-modal/commit/2172030427023c068644c71d8cbbe88c389ccf18))
|
|
73
|
-
|
|
74
|
-
## [6.0.0](https://www.github.com/pradel/react-responsive-modal/compare/v5.2.6...v6.0.0) (2020-11-15)
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
### ⚠ BREAKING CHANGES
|
|
78
|
-
|
|
79
|
-
* fix rendering issues in Safari, Firefox by changing the structure
|
|
80
|
-
|
|
81
|
-
### Features
|
|
82
|
-
|
|
83
|
-
* create E2E suite to test on real browser ([#449](https://www.github.com/pradel/react-responsive-modal/issues/449)) ([847ab1c](https://www.github.com/pradel/react-responsive-modal/commit/847ab1cac2044a6e11e3474f5fc34d7af69250bc))
|
|
84
|
-
* new documentation website ✨ ([#450](https://www.github.com/pradel/react-responsive-modal/issues/450)) ([3f620aa](https://www.github.com/pradel/react-responsive-modal/commit/3f620aa058c57ee251c968816a790a390edeba6e))
|
|
85
|
-
* switch project to monorepo ([#451](https://www.github.com/pradel/react-responsive-modal/issues/451)) ([ce59bad](https://www.github.com/pradel/react-responsive-modal/commit/ce59bad87178986bd1a87f80fd6a4489e066e614))
|
|
86
|
-
* upgrade focus-trap-js to support focus on radio elements ([#447](https://www.github.com/pradel/react-responsive-modal/issues/447)) ([d51f8e0](https://www.github.com/pradel/react-responsive-modal/commit/d51f8e06a81694b753d4e7777f5388bb05b69423))
|
|
87
|
-
* use body-scroll-lock instead of no-scroll ([#455](https://www.github.com/pradel/react-responsive-modal/issues/455)) ([033f901](https://www.github.com/pradel/react-responsive-modal/commit/033f9014b9951112da610435e0360f5ce463232b))
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
### Bug Fixes
|
|
91
|
-
|
|
92
|
-
* fix rendering issues in Safari, Firefox by changing the structure ([5727913](https://www.github.com/pradel/react-responsive-modal/commit/572791340fcc7b0f66e519fcbb7d4be9b998e088))
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
### Tests
|
|
96
|
-
|
|
97
|
-
* test that scroll is not blocked when blockScroll is false ([3d909b6](https://www.github.com/pradel/react-responsive-modal/commit/3d909b6c90261e6bd1a40de0a522ac4f85a487a8))
|
|
98
|
-
* test that scroll is unblocked when multiple modals are closed ([fba3593](https://www.github.com/pradel/react-responsive-modal/commit/fba35933ec6f270bbeb1fd779a6feef97b65bb82))
|
|
99
|
-
* test that scroll is unblocked when second modal has blockScroll set to false ([cf4b6b3](https://www.github.com/pradel/react-responsive-modal/commit/cf4b6b37ec55c24003d085cd5ad4d3bccc031bec))
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
### Miscellaneous
|
|
103
|
-
|
|
104
|
-
* delete `xmlns` prop from svg close icon ([#429](https://www.github.com/pradel/react-responsive-modal/issues/429)) ([8743327](https://www.github.com/pradel/react-responsive-modal/commit/87433278e10dc7077a7fddeaf6d2d088a3227bc9))
|
|
105
|
-
* use hook with modal manager ([#453](https://www.github.com/pradel/react-responsive-modal/issues/453)) ([b016ec4](https://www.github.com/pradel/react-responsive-modal/commit/b016ec41ff1208f0a56713c30734aae482abf3d6))
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
### Documentation
|
|
109
|
-
|
|
110
|
-
* **readme:** add integration tips ([4ad6d1d](https://www.github.com/pradel/react-responsive-modal/commit/4ad6d1d005fc441875cd680e4e42e1e0fb4b62cc))
|
|
111
|
-
* **readme:** remove dependencies badge ([5f7d0ad](https://www.github.com/pradel/react-responsive-modal/commit/5f7d0adc66783ed11b1bb0ed7610318c53dde17f))
|
|
112
|
-
* **readme:** remove example links ([9a06a62](https://www.github.com/pradel/react-responsive-modal/commit/9a06a62d7566380c74febf2b3d7a3e8b4268f71f))
|
|
113
|
-
* **readme:** update features ([ce05bd2](https://www.github.com/pradel/react-responsive-modal/commit/ce05bd2bab1605c14c4e63e8817bb81fd1aa35d4))
|
|
114
|
-
|
|
115
|
-
### [5.2.6](https://www.github.com/pradel/react-responsive-modal/compare/v5.2.5...v5.2.6) (2020-11-07)
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
### Miscellaneous
|
|
119
|
-
|
|
120
|
-
* fix publishing ([f024bd5](https://www.github.com/pradel/react-responsive-modal/commit/f024bd588ff315f440cc090eb90595d6f165fb98))
|
|
121
|
-
|
|
122
|
-
### [5.2.5](https://www.github.com/pradel/react-responsive-modal/compare/v5.2.4...v5.2.5) (2020-11-07)
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
### Miscellaneous
|
|
126
|
-
|
|
127
|
-
* fix build ([1a5f07c](https://www.github.com/pradel/react-responsive-modal/commit/1a5f07cb7a6f6682c01d487129309152e41b23c0))
|
|
128
|
-
|
|
129
|
-
### [5.2.4](https://www.github.com/pradel/react-responsive-modal/compare/v5.2.3...v5.2.4) (2020-11-07)
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
### Miscellaneous
|
|
133
|
-
|
|
134
|
-
* trigger release ([d14af23](https://www.github.com/pradel/react-responsive-modal/commit/d14af2334292d9aaf81385ccfdcd0b7ff506a7cb))
|
|
135
|
-
|
|
136
|
-
### [5.2.3](https://www.github.com/pradel/react-responsive-modal/compare/v5.2.2...v5.2.3) (2020-11-07)
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
### Miscellaneous
|
|
140
|
-
|
|
141
|
-
* upgrade dev dependencies ([6745a09](https://www.github.com/pradel/react-responsive-modal/commit/6745a09ddd26ac938f77615afc7ced8ff1703e62))
|
|
142
|
-
|
|
143
|
-
### [5.2.2](https://www.github.com/pradel/react-responsive-modal/compare/v5.2.1...v5.2.2) (2020-11-07)
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
### Bug Fixes
|
|
147
|
-
|
|
148
|
-
* fix closing modal via close icon ([#437](https://www.github.com/pradel/react-responsive-modal/issues/437)) ([f13ee4a](https://www.github.com/pradel/react-responsive-modal/commit/f13ee4abfce63b156f64a8cf5ea5ea50dfff4e19))
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
### Tests
|
|
152
|
-
|
|
153
|
-
* add tests for body scroll blocking ([#438](https://www.github.com/pradel/react-responsive-modal/issues/438)) ([f4077b8](https://www.github.com/pradel/react-responsive-modal/commit/f4077b8f0f24d9e4b12107d8ebe7382d5dafbfef))
|