112 lines
2.8 KiB
TypeScript
112 lines
2.8 KiB
TypeScript
import React, { useEffect } from 'react';
|
||
import { Button } from './Button';
|
||
|
||
interface ModalProps {
|
||
isOpen: boolean;
|
||
onClose: () => void;
|
||
title: string;
|
||
children: React.ReactNode;
|
||
actions?: React.ReactNode;
|
||
danger?: boolean;
|
||
}
|
||
|
||
export function Modal({ isOpen, onClose, title, children, actions, danger = false }: ModalProps) {
|
||
useEffect(() => {
|
||
if (isOpen) {
|
||
document.body.style.overflow = 'hidden';
|
||
} else {
|
||
document.body.style.overflow = 'unset';
|
||
}
|
||
return () => {
|
||
document.body.style.overflow = 'unset';
|
||
};
|
||
}, [isOpen]);
|
||
|
||
if (!isOpen) return null;
|
||
|
||
return (
|
||
<div
|
||
style={{
|
||
position: 'fixed',
|
||
top: 0,
|
||
left: 0,
|
||
right: 0,
|
||
bottom: 0,
|
||
background: 'rgba(0, 0, 0, 0.5)',
|
||
display: 'flex',
|
||
alignItems: 'center',
|
||
justifyContent: 'center',
|
||
zIndex: 1000,
|
||
padding: 'var(--spacing-4)',
|
||
}}
|
||
onClick={onClose}
|
||
className="animate-fade-in"
|
||
>
|
||
<div
|
||
onClick={(e) => e.stopPropagation()}
|
||
style={{
|
||
background: 'var(--color-background-primary)',
|
||
borderRadius: 'var(--border-radius-xl)',
|
||
boxShadow: 'var(--shadow-xl)',
|
||
maxWidth: '500px',
|
||
width: '100%',
|
||
overflow: 'hidden',
|
||
}}
|
||
className="animate-slide-up"
|
||
>
|
||
<div
|
||
style={{
|
||
padding: 'var(--spacing-6)',
|
||
borderBottom: `1px solid var(--color-border-secondary)`,
|
||
display: 'flex',
|
||
justifyContent: 'space-between',
|
||
alignItems: 'center',
|
||
background: danger ? '#fee2e2' : 'var(--color-background-secondary)',
|
||
}}
|
||
>
|
||
<h3
|
||
style={{
|
||
fontSize: 'var(--font-size-xl)',
|
||
fontWeight: 600,
|
||
color: danger ? 'var(--color-error)' : 'var(--color-text-primary)',
|
||
margin: 0,
|
||
}}
|
||
>
|
||
{title}
|
||
</h3>
|
||
<button
|
||
onClick={onClose}
|
||
style={{
|
||
background: 'transparent',
|
||
border: 'none',
|
||
fontSize: 'var(--font-size-xl)',
|
||
color: 'var(--color-text-secondary)',
|
||
cursor: 'pointer',
|
||
padding: 'var(--spacing-2)',
|
||
lineHeight: 1,
|
||
}}
|
||
>
|
||
×
|
||
</button>
|
||
</div>
|
||
<div style={{ padding: 'var(--spacing-6)' }}>
|
||
{children}
|
||
</div>
|
||
{actions && (
|
||
<div
|
||
style={{
|
||
padding: 'var(--spacing-6)',
|
||
borderTop: `1px solid var(--color-border-secondary)`,
|
||
display: 'flex',
|
||
gap: 'var(--spacing-3)',
|
||
justifyContent: 'flex-end',
|
||
}}
|
||
>
|
||
{actions}
|
||
</div>
|
||
)}
|
||
</div>
|
||
</div>
|
||
);
|
||
}
|