This commit is contained in:
2026-04-18 23:11:35 +02:00
commit f7cfd2a86e
196 changed files with 35538 additions and 0 deletions
+62
View File
@@ -0,0 +1,62 @@
import { useEffect } from 'react';
import { XIcon } from './icons';
interface Props {
open: boolean;
title: string;
onClose: () => void;
children: React.ReactNode;
size?: 'md' | 'lg';
}
export function Modal({ open, title, onClose, children, size = 'md' }: Props) {
useEffect(() => {
if (!open) return;
function onKey(e: KeyboardEvent) {
if (e.key === 'Escape') onClose();
}
document.addEventListener('keydown', onKey);
const prevOverflow = document.body.style.overflow;
document.body.style.overflow = 'hidden';
return () => {
document.removeEventListener('keydown', onKey);
document.body.style.overflow = prevOverflow;
};
}, [open, onClose]);
if (!open) return null;
const width = size === 'lg' ? 'max-w-xl' : 'max-w-md';
return (
<div
role="dialog"
aria-modal="true"
aria-label={title}
onClick={onClose}
className="fixed inset-0 z-50 flex items-center justify-center bg-ink-950/80 p-6 backdrop-blur-sm animate-fade-in"
>
<div
onClick={(e) => e.stopPropagation()}
className={
'relative w-full animate-slide-up rounded-2xl border border-white/10 bg-ink-900/95 shadow-xl backdrop-blur-xl ' +
width
}
>
<header className="flex items-center justify-between border-b border-white/5 px-6 py-4">
<h2 className="font-display text-lg font-semibold text-white">{title}</h2>
<button
type="button"
onClick={onClose}
aria-label="Close"
className="flex h-8 w-8 cursor-pointer items-center justify-center rounded-lg text-neutral-400 transition hover:bg-white/10 hover:text-neutral-100 focus:outline-none focus-visible:ring-2 focus-visible:ring-brand-400/40"
>
<XIcon className="h-4 w-4" />
</button>
</header>
<div className="max-h-[75vh] overflow-y-auto p-6">{children}</div>
</div>
</div>
);
}