initial: standalone repo from monorepo split
This commit is contained in:
55
src/components/ui/form-modal.tsx
Normal file
55
src/components/ui/form-modal.tsx
Normal file
@@ -0,0 +1,55 @@
|
||||
'use client';
|
||||
|
||||
import { useEffect, useRef } from 'react';
|
||||
|
||||
interface FormModalProps {
|
||||
open: boolean;
|
||||
onClose: () => void;
|
||||
title: string;
|
||||
description?: string;
|
||||
children: React.ReactNode;
|
||||
wide?: boolean;
|
||||
}
|
||||
|
||||
export function FormModal({ open, onClose, title, description, children, wide }: FormModalProps) {
|
||||
const overlayRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
useEffect(() => {
|
||||
if (open) document.body.style.overflow = 'hidden';
|
||||
return () => { document.body.style.overflow = ''; };
|
||||
}, [open]);
|
||||
|
||||
useEffect(() => {
|
||||
function handleKey(e: KeyboardEvent) {
|
||||
if (e.key === 'Escape' && open) onClose();
|
||||
}
|
||||
window.addEventListener('keydown', handleKey);
|
||||
return () => window.removeEventListener('keydown', handleKey);
|
||||
}, [open, onClose]);
|
||||
|
||||
if (!open) return null;
|
||||
|
||||
return (
|
||||
<div
|
||||
ref={overlayRef}
|
||||
className="fixed inset-0 z-50 flex items-start justify-center pt-[10vh] px-4 bg-surface-900/40 backdrop-blur-sm overflow-y-auto"
|
||||
role="dialog"
|
||||
aria-modal="true"
|
||||
aria-labelledby="form-modal-title"
|
||||
onClick={(e) => { if (e.target === overlayRef.current) onClose(); }}
|
||||
>
|
||||
<div className={`bg-white dark:bg-surface-800 rounded-xl shadow-xl w-full p-6 mb-10 animate-in fade-in slide-in-from-top-4 duration-200 ${wide ? 'max-w-2xl' : 'max-w-lg'}`}>
|
||||
<div className="flex items-start justify-between mb-5">
|
||||
<div>
|
||||
<h2 id="form-modal-title" className="text-lg font-semibold text-surface-900 dark:text-surface-200">{title}</h2>
|
||||
{description && <p className="mt-1 text-sm text-surface-500 dark:text-surface-400">{description}</p>}
|
||||
</div>
|
||||
<button onClick={onClose} className="p-1 text-surface-400 hover:text-surface-600 dark:hover:text-surface-300 transition-colors cursor-pointer rounded-lg hover:bg-surface-50 dark:hover:bg-surface-700" aria-label="Close">
|
||||
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round"><path d="M6 6l8 8M14 6l-8 8" /></svg>
|
||||
</button>
|
||||
</div>
|
||||
{children}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user