Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 | 4x 34x 34x 34x 1x 1x 34x 19x 19x 19x 19x 34x 33x 33x 34x 3x 1x 34x 15x 19x 19x 1x 18x 1x 19x | import {
Box,
ButtonIcon,
ButtonIconSize,
IconName,
Text as TextComponent,
TextVariant,
} from '@metamask/design-system-react';
import { useEffect, useRef } from 'react';
export type ModalProps = {
isOpen: boolean;
onClose: () => void;
title: string;
children: React.ReactNode;
size?: 'sm' | 'md' | 'lg';
};
/**
* A modal component that displays content in an overlay.
*
* @param props - The modal props.
* @param props.isOpen - Whether the modal is open.
* @param props.onClose - Function to call when the modal should be closed.
* @param props.title - The title to display in the modal header.
* @param props.children - The content to display in the modal body.
* @param props.size - The size of the modal.
* @returns A modal component.
*/
export const Modal: React.FC<ModalProps> = ({
isOpen,
onClose,
title,
children,
size = 'md',
}) => {
const modalRef = useRef<HTMLDivElement>(null);
// Handle ESC key to close modal
useEffect(() => {
const handleKeyDown = (event: KeyboardEvent): void => {
Eif (event.key === 'Escape') {
onClose();
}
};
if (isOpen) {
document.addEventListener('keydown', handleKeyDown);
// Prevent body scroll when modal is open
document.body.style.overflow = 'hidden';
// Focus the modal for accessibility
Eif (modalRef.current) {
modalRef.current.focus();
}
}
return () => {
document.removeEventListener('keydown', handleKeyDown);
document.body.style.overflow = 'unset';
};
}, [isOpen, onClose]);
// Handle click outside modal to close
const handleBackdropClick = (
event: React.MouseEvent<HTMLDivElement>,
): void => {
if (event.target === event.currentTarget) {
onClose();
}
};
if (!isOpen) {
return null;
}
let widthClass = 'w-2/3';
if (size === 'sm') {
widthClass = 'w-96';
} else if (size === 'lg') {
widthClass = 'w-4/5';
}
return (
<Box
className="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50"
onClick={handleBackdropClick}
role="dialog"
aria-modal="true"
aria-labelledby="modal-title"
>
<div
className={`bg-background-default rounded-lg shadow-lg max-h-[90vh] overflow-hidden ${widthClass}`}
ref={modalRef}
tabIndex={-1}
>
<Box className="flex justify-between items-center bg-alternative p-4 border-b border-muted">
<TextComponent
data-testid="modal-title"
className="!m-0"
variant={TextVariant.HeadingSm}
>
{title}
</TextComponent>
<ButtonIcon
iconName={IconName.Close}
size={ButtonIconSize.Sm}
onClick={onClose}
ariaLabel="Close modal"
data-testid="modal-close-button"
/>
</Box>
<Box className="p-4 overflow-y-auto">{children}</Box>
</div>
</Box>
);
};
|