mirror of
				https://gitlab.nic.cz/turris/reforis/foris-js.git
				synced 2025-11-03 23:00:31 +01:00 
			
		
		
		
	Refactor Modal component to use useFocusTrap hook
This commit is contained in:
		@@ -9,7 +9,7 @@ import React, { useRef, useEffect } from "react";
 | 
			
		||||
 | 
			
		||||
import PropTypes from "prop-types";
 | 
			
		||||
 | 
			
		||||
import { useClickOutside } from "../utils/hooks";
 | 
			
		||||
import { useClickOutside, useFocusTrap } from "../utils/hooks";
 | 
			
		||||
import Portal from "../utils/Portal";
 | 
			
		||||
import "./Modal.css";
 | 
			
		||||
 | 
			
		||||
@@ -29,10 +29,11 @@ Modal.propTypes = {
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export function Modal({ shown, setShown, scrollable, size, children }) {
 | 
			
		||||
    const dialogRef = useRef();
 | 
			
		||||
    const modalRef = useRef();
 | 
			
		||||
    let modalSize = "modal-";
 | 
			
		||||
 | 
			
		||||
    useClickOutside(dialogRef, () => setShown(false));
 | 
			
		||||
    useClickOutside(modalRef, () => setShown(false));
 | 
			
		||||
    useFocusTrap(modalRef, shown);
 | 
			
		||||
 | 
			
		||||
    useEffect(() => {
 | 
			
		||||
        const handleEsc = (event) => {
 | 
			
		||||
@@ -65,11 +66,13 @@ export function Modal({ shown, setShown, scrollable, size, children }) {
 | 
			
		||||
    return (
 | 
			
		||||
        <Portal containerId="modal-container">
 | 
			
		||||
            <div
 | 
			
		||||
                ref={modalRef}
 | 
			
		||||
                className={`modal fade ${shown ? "show" : ""}`.trim()}
 | 
			
		||||
                role="dialog"
 | 
			
		||||
                aria-modal="true"
 | 
			
		||||
                aria-labelledby="modal-title"
 | 
			
		||||
            >
 | 
			
		||||
                <div
 | 
			
		||||
                    ref={dialogRef}
 | 
			
		||||
                    className={`${modalSize.trim()} modal-dialog modal-dialog-centered ${
 | 
			
		||||
                        scrollable ? "modal-dialog-scrollable" : ""
 | 
			
		||||
                    }`.trim()}
 | 
			
		||||
@@ -90,7 +93,7 @@ ModalHeader.propTypes = {
 | 
			
		||||
export function ModalHeader({ setShown, title }) {
 | 
			
		||||
    return (
 | 
			
		||||
        <div className="modal-header">
 | 
			
		||||
            <h5 className="modal-title">{title}</h5>
 | 
			
		||||
            <h1 className="modal-title fs-5">{title}</h1>
 | 
			
		||||
            <button
 | 
			
		||||
                type="button"
 | 
			
		||||
                className="btn-close"
 | 
			
		||||
 
 | 
			
		||||
@@ -40,3 +40,40 @@ export function useClickOutside(element, callback) {
 | 
			
		||||
        };
 | 
			
		||||
    });
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Trap focus inside element. */
 | 
			
		||||
export function useFocusTrap(elementRef, condition = true) {
 | 
			
		||||
    useEffect(() => {
 | 
			
		||||
        if (!condition) {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
        const currentElement = elementRef.current;
 | 
			
		||||
        const focusableElements = currentElement.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();
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        currentElement.addEventListener("keydown", handleTab);
 | 
			
		||||
 | 
			
		||||
        firstElement.focus();
 | 
			
		||||
 | 
			
		||||
        return () => {
 | 
			
		||||
            currentElement.removeEventListener("keydown", handleTab);
 | 
			
		||||
        };
 | 
			
		||||
    }, [elementRef, condition]);
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user