feat(ui): composant Modal réutilisable (overlay + Escape)

This commit is contained in:
2026-05-24 15:40:57 +02:00
parent 1ca2d986ce
commit ffe28cc4e3
+72
View File
@@ -0,0 +1,72 @@
import { useEffect } from 'react'
interface ModalProps {
title: string
onClose: () => void
children: React.ReactNode
width?: number
}
export default function Modal({ title, onClose, children, width = 480 }: ModalProps) {
useEffect(() => {
function onKey(e: KeyboardEvent) {
if (e.key === 'Escape') onClose()
}
window.addEventListener('keydown', onKey)
return () => window.removeEventListener('keydown', onKey)
}, [onClose])
return (
<div
onClick={onClose}
style={{
position: 'fixed',
inset: 0,
background: 'rgba(0,0,0,0.6)',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
zIndex: 200,
padding: 16,
}}
>
<div
onClick={e => e.stopPropagation()}
className="glass"
style={{
width: '100%',
maxWidth: width,
borderRadius: 12,
padding: 20,
display: 'flex',
flexDirection: 'column',
gap: 16,
maxHeight: '90dvh',
overflowY: 'auto',
}}
>
<div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between' }}>
<h2 style={{ margin: 0, color: 'var(--accent)', fontFamily: 'var(--font-mono)', fontSize: 16 }}>
{title}
</h2>
<button
onClick={onClose}
style={{
background: 'transparent',
border: 'none',
color: 'var(--ink-3)',
fontSize: 20,
cursor: 'pointer',
lineHeight: 1,
padding: 4,
}}
aria-label="Fermer"
>
</button>
</div>
{children}
</div>
</div>
)
}