Files
core/src/shared/components/Modal.js
T

123 lines
3.9 KiB
JavaScript

import React from 'react';
import { Dialog } from '@headlessui/react';
import { Cancel01Icon } from '../icons/index.js';
import Button from './Button.js';
const FORM_ID = 'modal-form-inner';
const Modal = ({
isOpen = true,
onClose,
title,
children,
footer,
size = 'lg',
closable = true,
// Form props — when provided, wraps children in a <form> and shows a standard footer
onSubmit,
submitLabel = 'Enregistrer',
cancelLabel = 'Annuler',
submitVariant = 'primary',
loading = false,
disabled = false,
}) => {
const sizeClasses = {
sm: 'max-w-md',
md: 'max-w-lg',
lg: 'max-w-2xl',
xl: 'max-w-4xl',
full: 'max-w-7xl',
};
const isForm = typeof onSubmit === 'function';
const handleFormSubmit = (e) => {
e.preventDefault();
onSubmit(e);
};
const resolvedFooter = footer ?? (isForm ? (
<div className="flex items-center justify-end gap-2">
<Button
variant="ghost"
size="sm"
onClick={onClose}
disabled={loading}
>
{cancelLabel}
</Button>
<Button
type="submit"
form={FORM_ID}
variant={submitVariant}
size="sm"
loading={loading}
disabled={disabled || loading}
>
{submitLabel}
</Button>
</div>
) : null);
return (
<Dialog
open={isOpen}
onClose={closable ? onClose : () => {}}
className="relative z-50"
>
{/* Backdrop */}
<div className="fixed inset-0 bg-black/35 backdrop-blur-[2px]" aria-hidden="true" />
{/* Container */}
<div className="fixed inset-0 flex items-center justify-center p-4">
<Dialog.Panel
className={`
w-full ${sizeClasses[size]}
bg-white dark:bg-[#0B0B0B]
border border-neutral-200 dark:border-neutral-800
rounded-lg
shadow-xl
max-h-[90vh]
overflow-hidden
flex flex-col
`}
>
{/* Header */}
<div className="flex items-center justify-between px-4 py-3 border-b border-neutral-200 dark:border-neutral-800">
<Dialog.Title className="text-sm font-medium text-neutral-900 dark:text-white">
{title}
</Dialog.Title>
{closable && (
<Button
variant="ghost"
size="sm"
onClick={onClose}
icon={<Cancel01Icon className="w-4 h-4" />}
className="!p-1 -mr-1"
/>
)}
</div>
{/* Content */}
<div className="flex-1 overflow-y-auto p-4">
{isForm ? (
<form id={FORM_ID} onSubmit={handleFormSubmit} noValidate>
{children}
</form>
) : children}
</div>
{/* Footer */}
{resolvedFooter && (
<div className="px-4 py-3 border-t border-neutral-200 dark:border-neutral-800 bg-neutral-50 dark:bg-[#0B0B0B]">
{resolvedFooter}
</div>
)}
</Dialog.Panel>
</div>
</Dialog>
);
};
export default Modal;