- Refactored PaymentsPage, RatingsPage, RepresentationDetailPage, RepresentationsPage, SecretariesPage, SettlementsPage, SmsPage, UserDetailPage, and UsersPage to use consistent class names for styling. - Updated button styles to use new utility classes for primary, secondary, and danger buttons. - Enhanced dark mode support across various components by adjusting text and background colors. - Introduced new utility classes for form inputs, labels, and info rows to standardize styling. - Implemented Zustand for persistent UI state management, including dark mode toggle functionality. - Updated CSS to include new styles for skeleton loading and animations. - Added optional dependencies for improved compatibility with different platforms.
74 lines
2.3 KiB
TypeScript
74 lines
2.3 KiB
TypeScript
import React, { useEffect } from 'react';
|
|
import { XMarkIcon } from '@heroicons/react/24/outline';
|
|
|
|
type ModalSize = 'sm' | 'md' | 'lg' | 'xl';
|
|
|
|
const sizeMap: Record<ModalSize, string> = {
|
|
sm: 'max-w-sm',
|
|
md: 'max-w-lg',
|
|
lg: 'max-w-2xl',
|
|
xl: 'max-w-4xl',
|
|
};
|
|
|
|
interface Props {
|
|
open: boolean;
|
|
title: string;
|
|
size?: ModalSize;
|
|
onClose: () => void;
|
|
children: React.ReactNode;
|
|
footer?: React.ReactNode;
|
|
}
|
|
|
|
export default function Modal({ open, title, size = 'md', onClose, children, footer }: Props) {
|
|
useEffect(() => {
|
|
if (!open) return;
|
|
const onKey = (e: KeyboardEvent) => { if (e.key === 'Escape') onClose(); };
|
|
document.addEventListener('keydown', onKey);
|
|
return () => document.removeEventListener('keydown', onKey);
|
|
}, [open, onClose]);
|
|
|
|
if (!open) return null;
|
|
|
|
return (
|
|
<div className="fixed inset-0 z-50 flex items-center justify-center p-4 animate-fade-in">
|
|
{/* Backdrop */}
|
|
<div
|
|
className="absolute inset-0 bg-black/50 dark:bg-black/70 backdrop-blur-sm"
|
|
onClick={onClose}
|
|
/>
|
|
|
|
{/* Panel */}
|
|
<div
|
|
className={`
|
|
relative bg-white dark:bg-gray-900
|
|
rounded-2xl shadow-2xl dark:shadow-black/40
|
|
border border-slate-200/60 dark:border-gray-700/50
|
|
w-full ${sizeMap[size]} flex flex-col max-h-[90vh]
|
|
animate-scale-in
|
|
`}
|
|
>
|
|
{/* Header */}
|
|
<div className="flex items-center justify-between px-6 py-4 border-b border-slate-100 dark:border-gray-700/50 shrink-0">
|
|
<h2 className="font-semibold text-slate-900 dark:text-slate-100 text-base">{title}</h2>
|
|
<button
|
|
onClick={onClose}
|
|
className="w-8 h-8 flex items-center justify-center rounded-lg text-slate-400 hover:text-slate-600 dark:hover:text-slate-200 hover:bg-slate-100 dark:hover:bg-gray-700 transition-colors"
|
|
>
|
|
<XMarkIcon className="w-5 h-5" />
|
|
</button>
|
|
</div>
|
|
|
|
{/* Body */}
|
|
<div className="flex-1 overflow-y-auto px-6 py-5">{children}</div>
|
|
|
|
{/* Footer */}
|
|
{footer && (
|
|
<div className="flex items-center justify-end gap-3 px-6 py-4 border-t border-slate-100 dark:border-gray-700/50 shrink-0 bg-slate-50/50 dark:bg-gray-800/30 rounded-b-2xl">
|
|
{footer}
|
|
</div>
|
|
)}
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|