eventx-design-system
Version:
Design System do EventX
132 lines (128 loc) • 4.22 kB
JavaScript
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
import { useEffect } from 'react';
import { createPortal } from 'react-dom';
import styled, { css } from 'styled-components';
import { colors, spacing, borderRadius, shadows, transitions, zIndex } from '../tokens/design-tokens';
// Overlay do modal
const Overlay = styled.div `
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: rgba(0, 0, 0, 0.5);
display: flex;
align-items: center;
justify-content: center;
z-index: ${zIndex.modal};
opacity: ${({ isOpen }) => (isOpen ? 1 : 0)};
visibility: ${({ isOpen }) => (isOpen ? 'visible' : 'hidden')};
transition: all ${transitions.duration[300]} ${transitions.easing.inOut};
padding: ${spacing[4]};
`;
// Container do modal
const ModalContainer = styled.div `
background-color: ${colors.neutral[50]};
border-radius: ${borderRadius.xl};
box-shadow: ${shadows['2xl']};
max-width: 90vw;
max-height: 90vh;
overflow: hidden;
transform: ${({ isOpen }) => (isOpen ? 'scale(1)' : 'scale(0.95)')};
transition: all ${transitions.duration[300]} ${transitions.easing.inOut};
display: flex;
flex-direction: column;
${({ size }) => {
switch (size) {
case 'sm':
return css `width: 400px;`;
case 'lg':
return css `width: 600px;`;
case 'xl':
return css `width: 800px;`;
default:
return css `width: 500px;`;
}
}}
`;
// Header do modal
const ModalHeader = styled.div `
display: flex;
align-items: center;
justify-content: space-between;
padding: ${spacing[6]} ${spacing[6]} ${spacing[4]};
border-bottom: 1px solid ${colors.neutral[200]};
`;
const ModalTitle = styled.h2 `
margin: 0;
font-family: inherit;
font-size: 1.25rem;
font-weight: 600;
color: ${colors.neutral[950]};
`;
const CloseButton = styled.button `
background: none;
border: none;
font-size: 1.5rem;
cursor: pointer;
color: ${colors.neutral[500]};
padding: ${spacing[1]};
border-radius: ${borderRadius.base};
transition: all ${transitions.duration[200]} ${transitions.easing.inOut};
display: flex;
align-items: center;
justify-content: center;
width: 32px;
height: 32px;
&:hover {
background-color: ${colors.neutral[100]};
color: ${colors.neutral[700]};
}
&:focus {
outline: 2px solid ${colors.brand.primary[500]};
outline-offset: 2px;
}
`;
// Body do modal
const ModalBody = styled.div `
padding: ${spacing[6]};
overflow-y: auto;
flex: 1;
`;
// Footer do modal
const ModalFooter = styled.div `
display: flex;
align-items: center;
justify-content: flex-end;
gap: ${spacing[3]};
padding: ${spacing[4]} ${spacing[6]};
border-top: 1px solid ${colors.neutral[200]};
background-color: ${colors.neutral[50]};
`;
export const Modal = ({ isOpen, onClose, title, children, size = 'md', closeOnOverlayClick = true, closeOnEscape = true, className, ...props }) => {
useEffect(() => {
const handleEscape = (event) => {
if (event.key === 'Escape' && closeOnEscape) {
onClose();
}
};
if (isOpen) {
document.addEventListener('keydown', handleEscape);
document.body.style.overflow = 'hidden';
}
return () => {
document.removeEventListener('keydown', handleEscape);
document.body.style.overflow = 'unset';
};
}, [isOpen, onClose, closeOnEscape]);
const handleOverlayClick = (event) => {
if (event.target === event.currentTarget && closeOnOverlayClick) {
onClose();
}
};
if (!isOpen)
return null;
const modalContent = (_jsx(Overlay, { isOpen: isOpen, onClick: handleOverlayClick, children: _jsxs(ModalContainer, { isOpen: isOpen, size: size, className: className, ...props, children: [(title || closeOnOverlayClick) && (_jsxs(ModalHeader, { children: [title && _jsx(ModalTitle, { children: title }), _jsx(CloseButton, { onClick: onClose, "aria-label": "Fechar modal", children: "\u00D7" })] })), _jsx(ModalBody, { children: children })] }) }));
return createPortal(modalContent, document.body);
};
Modal.displayName = 'Modal';