mirror of
https://gitlab.nic.cz/turris/reforis/foris-js.git
synced 2024-11-14 17:35:35 +01:00
Merge branch 'modal-focus-trap' into 'dev'
Refactor Modal component to use useFocusTrap hook See merge request turris/reforis/foris-js!237
This commit is contained in:
commit
29a7c491a4
|
@ -9,7 +9,7 @@ import React, { useRef, useEffect } from "react";
|
||||||
|
|
||||||
import PropTypes from "prop-types";
|
import PropTypes from "prop-types";
|
||||||
|
|
||||||
import { useClickOutside } from "../utils/hooks";
|
import { useClickOutside, useFocusTrap } from "../utils/hooks";
|
||||||
import Portal from "../utils/Portal";
|
import Portal from "../utils/Portal";
|
||||||
import "./Modal.css";
|
import "./Modal.css";
|
||||||
|
|
||||||
|
@ -29,10 +29,11 @@ Modal.propTypes = {
|
||||||
};
|
};
|
||||||
|
|
||||||
export function Modal({ shown, setShown, scrollable, size, children }) {
|
export function Modal({ shown, setShown, scrollable, size, children }) {
|
||||||
const dialogRef = useRef();
|
const modalRef = useRef();
|
||||||
let modalSize = "modal-";
|
let modalSize = "modal-";
|
||||||
|
|
||||||
useClickOutside(dialogRef, () => setShown(false));
|
useClickOutside(modalRef, () => setShown(false));
|
||||||
|
useFocusTrap(modalRef);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const handleEsc = (event) => {
|
const handleEsc = (event) => {
|
||||||
|
@ -45,7 +46,7 @@ export function Modal({ shown, setShown, scrollable, size, children }) {
|
||||||
return () => {
|
return () => {
|
||||||
window.removeEventListener("keydown", handleEsc);
|
window.removeEventListener("keydown", handleEsc);
|
||||||
};
|
};
|
||||||
}, [setShown]);
|
}, [shown]);
|
||||||
|
|
||||||
switch (size) {
|
switch (size) {
|
||||||
case "sm":
|
case "sm":
|
||||||
|
@ -65,11 +66,13 @@ export function Modal({ shown, setShown, scrollable, size, children }) {
|
||||||
return (
|
return (
|
||||||
<Portal containerId="modal-container">
|
<Portal containerId="modal-container">
|
||||||
<div
|
<div
|
||||||
|
ref={modalRef}
|
||||||
className={`modal fade ${shown ? "show" : ""}`.trim()}
|
className={`modal fade ${shown ? "show" : ""}`.trim()}
|
||||||
role="dialog"
|
role="dialog"
|
||||||
|
aria-modal="true"
|
||||||
|
aria-labelledby="modal-title"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
ref={dialogRef}
|
|
||||||
className={`${modalSize.trim()} modal-dialog modal-dialog-centered ${
|
className={`${modalSize.trim()} modal-dialog modal-dialog-centered ${
|
||||||
scrollable ? "modal-dialog-scrollable" : ""
|
scrollable ? "modal-dialog-scrollable" : ""
|
||||||
}`.trim()}
|
}`.trim()}
|
||||||
|
@ -90,7 +93,7 @@ ModalHeader.propTypes = {
|
||||||
export function ModalHeader({ setShown, title }) {
|
export function ModalHeader({ setShown, title }) {
|
||||||
return (
|
return (
|
||||||
<div className="modal-header">
|
<div className="modal-header">
|
||||||
<h5 className="modal-title">{title}</h5>
|
<h1 className="modal-title fs-5">{title}</h1>
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
className="btn-close"
|
className="btn-close"
|
||||||
|
|
|
@ -40,3 +40,41 @@ export function useClickOutside(element, callback) {
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Trap focus inside modal. */
|
||||||
|
export function useFocusTrap(modalRef) {
|
||||||
|
useEffect(() => {
|
||||||
|
if (shown) {
|
||||||
|
const modal = modalRef.current;
|
||||||
|
const focusableElements = modal.querySelectorAll(
|
||||||
|
'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])'
|
||||||
|
);
|
||||||
|
const firstElement = focusableElements[0];
|
||||||
|
const lastElement = focusableElements[focusableElements.length - 1];
|
||||||
|
|
||||||
|
const handleTab = (event) => {
|
||||||
|
if (event.key === "Tab") {
|
||||||
|
if (event.shiftKey) {
|
||||||
|
if (document.activeElement === firstElement) {
|
||||||
|
lastElement.focus();
|
||||||
|
event.preventDefault();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (document.activeElement === lastElement) {
|
||||||
|
firstElement.focus();
|
||||||
|
event.preventDefault();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
modal.addEventListener("keydown", handleTab);
|
||||||
|
|
||||||
|
firstElement.focus();
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
modal.removeEventListener("keydown", handleTab);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}, [shown]);
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user