- Run record
- loading
Loading this Run…
Loading the Revisions of this Run…
A published source file of Revision 0 of Boardly — Website Relaunch 1.0.0, shown as text in the browser.
import { useEffect, useRef, type ReactNode, type RefObject } from "react";
import { createPortal } from "react-dom";
const FOCUSABLE =
'a[href], button:not([disabled]), input:not([disabled]), select:not([disabled]), textarea:not([disabled]), [tabindex]:not([tabindex="-1"])';
interface ModalProps {
labelledBy: string;
describedBy?: string;
role?: "dialog" | "alertdialog";
className?: string;
initialFocus?: RefObject<HTMLElement | null>;
/** Called for Escape; defaults to onClose. */
onEscape?: () => void;
onClose: () => void;
children: ReactNode;
}
/**
* Modal dialog rendered into <body>. The app shell is made inert by the caller;
* this component also wraps Tab focus so it can never leave the dialog.
*/
export function Modal({
labelledBy,
describedBy,
role = "dialog",
className = "",
initialFocus,
onEscape,
onClose,
children,
}: ModalProps) {
const panelRef = useRef<HTMLDivElement>(null);
useEffect(() => {
const target = initialFocus?.current ?? panelRef.current?.querySelector<HTMLElement>(FOCUSABLE);
(target ?? panelRef.current)?.focus();
// Only on mount.
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
useEffect(() => {
const prev = document.body.style.overflow;
document.body.style.overflow = "hidden";
return () => {
document.body.style.overflow = prev;
};
}, []);
function onKeyDown(e: React.KeyboardEvent) {
if (e.key === "Escape") {
e.preventDefault();
e.stopPropagation();
(onEscape ?? onClose)();
return;
}
if (e.key !== "Tab" || !panelRef.current) return;
const items = Array.from(panelRef.current.querySelectorAll<HTMLElement>(FOCUSABLE)).filter(
(el) => el.offsetParent !== null || el === document.activeElement,
);
if (items.length === 0) {
e.preventDefault();
return;
}
const first = items[0];
const last = items[items.length - 1];
const active = document.activeElement;
if (e.shiftKey && (active === first || !panelRef.current.contains(active))) {
e.preventDefault();
last.focus();
} else if (!e.shiftKey && (active === last || !panelRef.current.contains(active))) {
e.preventDefault();
first.focus();
}
}
return createPortal(
<div
className="modal-backdrop"
onMouseDown={(e) => {
if (e.target === e.currentTarget) onClose();
}}
>
<div
ref={panelRef}
role={role}
aria-modal="true"
aria-labelledby={labelledBy}
aria-describedby={describedBy}
tabIndex={-1}
className={`modal ${className}`}
onKeyDown={onKeyDown}
>
{children}
</div>
</div>,
document.body,
);
}Source is browser-viewable only. There is no download or archive endpoint, and no additional license is granted beyond inspection and research.