Publish Docker image / build-and-push (push) Successful in 2m12s
Restore project files to match the requested baseline before subsequent updates. Made-with: Cursor
104 lines
2.8 KiB
React
104 lines
2.8 KiB
React
/**
|
|
* FormModal - модальное окно с формой
|
|
* Простой подход со встроенным backdrop (как в HistoryModal)
|
|
*/
|
|
function FormModal({
|
|
show,
|
|
onClose,
|
|
onSubmit,
|
|
title,
|
|
children,
|
|
submitLabel = 'Сохранить',
|
|
cancelLabel = 'Отмена',
|
|
submitIcon: SubmitIcon,
|
|
cancelIcon: CancelIcon,
|
|
loading = false,
|
|
submitVariant = 'primary',
|
|
disabled = false,
|
|
size = 'md',
|
|
centered = false
|
|
}) {
|
|
if (!show) return null;
|
|
|
|
const handleSubmit = (e) => {
|
|
e.preventDefault();
|
|
onSubmit?.(e);
|
|
};
|
|
|
|
const handleKeyDown = (e) => {
|
|
// Ctrl+Enter или Cmd+Enter для быстрого submit
|
|
if ((e.ctrlKey || e.metaKey) && e.key === 'Enter') {
|
|
handleSubmit(e);
|
|
}
|
|
// ESC для закрытия
|
|
if (e.key === 'Escape') {
|
|
onClose?.();
|
|
}
|
|
};
|
|
|
|
const handleBackdropClick = (e) => {
|
|
if (e.target === e.currentTarget) {
|
|
onClose?.();
|
|
}
|
|
};
|
|
|
|
return (
|
|
<>
|
|
{/* Modal Backdrop */}
|
|
<div className="modal-backdrop show" onClick={handleBackdropClick} />
|
|
|
|
{/* Modal */}
|
|
<div
|
|
className="modal show d-block"
|
|
role="dialog"
|
|
aria-modal="true"
|
|
tabIndex={-1}
|
|
onKeyDown={handleKeyDown}
|
|
>
|
|
<div
|
|
className={`modal-dialog${size !== 'md' ? ` modal-${size}` : ''}${centered ? ' modal-dialog-centered' : ''}`}
|
|
role="document"
|
|
>
|
|
<div className="modal-content" tabIndex={-1}>
|
|
<form onSubmit={handleSubmit}>
|
|
<div className="modal-header">
|
|
<h5 className="modal-title">{title}</h5>
|
|
<button type="button" className="btn-close" onClick={onClose} aria-label="Закрыть" />
|
|
</div>
|
|
|
|
<div className="modal-body">
|
|
{children}
|
|
</div>
|
|
|
|
<div className="modal-footer">
|
|
<button
|
|
type="button"
|
|
className="btn btn-secondary"
|
|
onClick={onClose}
|
|
disabled={loading}
|
|
>
|
|
{CancelIcon && <CancelIcon className="me-2" size={16} />}
|
|
{cancelLabel}
|
|
</button>
|
|
<button
|
|
type="submit"
|
|
className={`btn btn-${submitVariant}`}
|
|
disabled={loading || disabled}
|
|
>
|
|
{loading && (
|
|
<span className="spinner-border spinner-border-sm me-2" role="status" aria-hidden="true" />
|
|
)}
|
|
{!loading && SubmitIcon && <SubmitIcon className="me-2" size={16} />}
|
|
{submitLabel}
|
|
</button>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</>
|
|
);
|
|
}
|
|
|
|
export default FormModal;
|