feat: Рефакторинг модальных окон с добавлением обработчиков клика по фону для закрытия. Обновление компонентов ConfirmDialog, ConfirmDiffModal, ConfirmModal, FormModal, HistoryModal, ImportModal, SettingsModal и WsUpdateModal для улучшения взаимодействия с пользователем и унификации интерфейса.
Publish Fast Tabler Docker image / build-and-push-fast (push) Failing after 1m6s

This commit is contained in:
2025-10-03 13:09:14 +07:00
parent e85c8cf84e
commit f2b1db2092
9 changed files with 444 additions and 341 deletions
+75 -63
View File
@@ -18,76 +18,88 @@ export default function ConfirmDialog({
}) { }) {
if (!open) return null; if (!open) return null;
return ( const handleBackdropClick = (e) => {
<div if (e.target === e.currentTarget) {
className="modal show d-block" onCancel?.();
role="dialog" }
aria-modal="true" };
style={{ backgroundColor: 'rgba(0,0,0,0.5)' }}
onKeyDown={(e) => { if (e.key === 'Escape') onCancel?.(); }}
>
<div className={`modal-dialog ${size === 'sm' ? 'modal-sm' : size === 'lg' ? 'modal-lg' : ''} modal-dialog-centered`} role="document">
<div
className="modal-content"
tabIndex={-1}
onKeyDown={(e) => {
if (e.key === 'Tab') {
const c = e.currentTarget;
const focusable = c.querySelectorAll('button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])');
if (!focusable || focusable.length === 0) return;
const first = focusable[0];
const last = focusable[focusable.length - 1];
if (e.shiftKey && document.activeElement === first) {
e.preventDefault();
last.focus();
} else if (!e.shiftKey && document.activeElement === last) {
e.preventDefault();
first.focus();
}
}
}}
>
<div className="modal-header">
<h5 className="modal-title">{title}</h5>
<button type="button" className="btn-close" onClick={onCancel} />
</div>
<div className="modal-body"> return (
<div className="d-flex align-items-start"> <>
{destructive && ( {/* Modal Backdrop */}
<div className="flex-shrink-0 me-3"> <div className="modal-backdrop show" onClick={handleBackdropClick} />
<IconAlertTriangle className="text-danger" size={32} />
{/* Modal */}
<div
className="modal show d-block"
role="dialog"
aria-modal="true"
tabIndex={-1}
onKeyDown={(e) => { if (e.key === 'Escape') onCancel?.(); }}
>
<div className={`modal-dialog ${size === 'sm' ? 'modal-sm' : size === 'lg' ? 'modal-lg' : ''} modal-dialog-centered`} role="document">
<div
className="modal-content"
tabIndex={-1}
onKeyDown={(e) => {
if (e.key === 'Tab') {
const c = e.currentTarget;
const focusable = c.querySelectorAll('button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])');
if (!focusable || focusable.length === 0) return;
const first = focusable[0];
const last = focusable[focusable.length - 1];
if (e.shiftKey && document.activeElement === first) {
e.preventDefault();
last.focus();
} else if (!e.shiftKey && document.activeElement === last) {
e.preventDefault();
first.focus();
}
}
}}
>
<div className="modal-header">
<h5 className="modal-title">{title}</h5>
<button type="button" className="btn-close" onClick={onCancel} aria-label="Закрыть" />
</div>
<div className="modal-body">
<div className="d-flex align-items-start">
{destructive && (
<div className="flex-shrink-0 me-3">
<IconAlertTriangle className="text-danger" size={32} />
</div>
)}
<div className="flex-grow-1">
<p className="mb-0">{message}</p>
</div> </div>
)}
<div className="flex-grow-1">
<p className="mb-0">{message}</p>
</div> </div>
</div> </div>
</div>
<div className="modal-footer"> <div className="modal-footer">
<button <button
type="button" type="button"
className="btn btn-secondary" className="btn btn-secondary"
onClick={onCancel} onClick={onCancel}
disabled={loading} disabled={loading}
> >
{cancelText} {cancelText}
</button> </button>
<button <button
type="button" type="button"
className={`btn ${destructive ? 'btn-danger' : 'btn-primary'}`} className={`btn ${destructive ? 'btn-danger' : 'btn-primary'}`}
onClick={onConfirm} onClick={onConfirm}
disabled={loading} disabled={loading}
> >
{loading && ( {loading && (
<span className="spinner-border spinner-border-sm me-2" role="status" aria-hidden="true" /> <span className="spinner-border spinner-border-sm me-2" role="status" aria-hidden="true" />
)} )}
{confirmText} {confirmText}
</button> </button>
</div>
</div> </div>
</div> </div>
</div> </div>
</div> </>
); );
} }
+86 -74
View File
@@ -11,93 +11,105 @@ function ConfirmDiffModal({ show, diff, onConfirm, onClose, loading = false }) {
if (!show) return null; if (!show) return null;
return ( const handleBackdropClick = (e) => {
<div if (e.target === e.currentTarget) {
className="modal show d-block" onClose?.();
role="dialog" }
aria-modal="true" };
style={{ backgroundColor: 'rgba(0,0,0,0.5)' }}
onKeyDown={(e) => { if (e.key === 'Escape') onClose?.(); }}
>
<div className="modal-dialog modal-sm modal-dialog-centered" role="document">
<div
className="modal-content"
tabIndex={-1}
onKeyDown={(e) => {
if (e.key === 'Tab') {
const c = e.currentTarget;
const focusable = c.querySelectorAll('button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])');
if (!focusable || focusable.length === 0) return;
const first = focusable[0];
const last = focusable[focusable.length - 1];
if (e.shiftKey && document.activeElement === first) {
e.preventDefault();
last.focus();
} else if (!e.shiftKey && document.activeElement === last) {
e.preventDefault();
first.focus();
}
}
}}
>
<div className="modal-header">
<h5 className="modal-title">Подтвердить сохранение</h5>
<button type="button" className="btn-close" onClick={onClose} />
</div>
<div className="modal-body"> return (
<div className="row g-2 text-center"> <>
<div className="col"> {/* Modal Backdrop */}
<div className="card"> <div className="modal-backdrop show" onClick={handleBackdropClick} />
<div className="card-body p-2">
<strong>Добавлено</strong> {/* Modal */}
<div className="text-muted">{added}</div> <div
className="modal show d-block"
role="dialog"
aria-modal="true"
tabIndex={-1}
onKeyDown={(e) => { if (e.key === 'Escape') onClose?.(); }}
>
<div className="modal-dialog modal-sm modal-dialog-centered" role="document">
<div
className="modal-content"
tabIndex={-1}
onKeyDown={(e) => {
if (e.key === 'Tab') {
const c = e.currentTarget;
const focusable = c.querySelectorAll('button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])');
if (!focusable || focusable.length === 0) return;
const first = focusable[0];
const last = focusable[focusable.length - 1];
if (e.shiftKey && document.activeElement === first) {
e.preventDefault();
last.focus();
} else if (!e.shiftKey && document.activeElement === last) {
e.preventDefault();
first.focus();
}
}
}}
>
<div className="modal-header">
<h5 className="modal-title">Подтвердить сохранение</h5>
<button type="button" className="btn-close" onClick={onClose} aria-label="Закрыть" />
</div>
<div className="modal-body">
<div className="row g-2 text-center">
<div className="col">
<div className="card">
<div className="card-body p-2">
<strong>Добавлено</strong>
<div className="text-muted">{added}</div>
</div>
</div> </div>
</div> </div>
</div> <div className="col">
<div className="col"> <div className="card">
<div className="card"> <div className="card-body p-2">
<div className="card-body p-2"> <strong>Удалено</strong>
<strong>Удалено</strong> <div className="text-muted">{removed}</div>
<div className="text-muted">{removed}</div> </div>
</div> </div>
</div> </div>
</div> <div className="col">
<div className="col"> <div className="card">
<div className="card"> <div className="card-body p-2">
<div className="card-body p-2"> <strong>Изменено</strong>
<strong>Изменено</strong> <div className="text-muted">{changed}</div>
<div className="text-muted">{changed}</div> </div>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
</div>
<div className="modal-footer"> <div className="modal-footer">
<button <button
type="button" type="button"
className="btn btn-secondary" className="btn btn-secondary"
onClick={onClose} onClick={onClose}
disabled={loading} disabled={loading}
> >
Отмена Отмена
</button> </button>
<button <button
type="button" type="button"
className="btn btn-primary" className="btn btn-primary"
onClick={onConfirm} onClick={onConfirm}
disabled={loading} disabled={loading}
> >
{loading && ( {loading && (
<span className="spinner-border spinner-border-sm me-2" role="status" aria-hidden="true" /> <span className="spinner-border spinner-border-sm me-2" role="status" aria-hidden="true" />
)} )}
Сохранить Сохранить
</button> </button>
</div>
</div> </div>
</div> </div>
</div> </div>
</div> </>
); );
} }
+73 -61
View File
@@ -23,75 +23,87 @@ function ConfirmModal({
onConfirm?.(); onConfirm?.();
}; };
return ( const handleBackdropClick = (e) => {
<div if (e.target === e.currentTarget) {
className="modal show d-block" onClose?.();
role="dialog" }
aria-modal="true" };
style={{ backgroundColor: 'rgba(0,0,0,0.5)' }}
onKeyDown={(e) => { if (e.key === 'Escape') onClose?.(); }}
>
<div className="modal-dialog modal-sm modal-dialog-centered" role="document">
<div
className="modal-content"
tabIndex={-1}
onKeyDown={(e) => {
if (e.key === 'Tab') {
const c = e.currentTarget;
const focusable = c.querySelectorAll('button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])');
if (!focusable || focusable.length === 0) return;
const first = focusable[0];
const last = focusable[focusable.length - 1];
if (e.shiftKey && document.activeElement === first) {
e.preventDefault();
last.focus();
} else if (!e.shiftKey && document.activeElement === last) {
e.preventDefault();
first.focus();
}
}
}}
>
<div className="modal-header">
<h5 className="modal-title">{title}</h5>
<button type="button" className="btn-close" onClick={onClose} />
</div>
<div className="modal-body"> return (
<div className="d-flex align-items-start"> <>
{showIcon && Icon && ( {/* Modal Backdrop */}
<div className="flex-shrink-0 me-3"> <div className="modal-backdrop show" onClick={handleBackdropClick} />
<Icon className={`text-${variant}`} size={32} />
{/* Modal */}
<div
className="modal show d-block"
role="dialog"
aria-modal="true"
tabIndex={-1}
onKeyDown={(e) => { if (e.key === 'Escape') onClose?.(); }}
>
<div className="modal-dialog modal-sm modal-dialog-centered" role="document">
<div
className="modal-content"
tabIndex={-1}
onKeyDown={(e) => {
if (e.key === 'Tab') {
const c = e.currentTarget;
const focusable = c.querySelectorAll('button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])');
if (!focusable || focusable.length === 0) return;
const first = focusable[0];
const last = focusable[focusable.length - 1];
if (e.shiftKey && document.activeElement === first) {
e.preventDefault();
last.focus();
} else if (!e.shiftKey && document.activeElement === last) {
e.preventDefault();
first.focus();
}
}
}}
>
<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">
<div className="d-flex align-items-start">
{showIcon && Icon && (
<div className="flex-shrink-0 me-3">
<Icon className={`text-${variant}`} size={32} />
</div>
)}
<div className="flex-grow-1">
{typeof message === 'string' ? <p className="mb-0">{message}</p> : message}
</div> </div>
)}
<div className="flex-grow-1">
{typeof message === 'string' ? <p className="mb-0">{message}</p> : message}
</div> </div>
</div> </div>
</div>
<div className="modal-footer"> <div className="modal-footer">
<button <button
className="btn btn-secondary" className="btn btn-secondary"
onClick={onClose} onClick={onClose}
disabled={loading} disabled={loading}
> >
{cancelLabel} {cancelLabel}
</button> </button>
<button <button
className={`btn btn-${variant}`} className={`btn btn-${variant}`}
onClick={handleConfirm} onClick={handleConfirm}
disabled={loading} disabled={loading}
> >
{loading && ( {loading && (
<span className="spinner-border spinner-border-sm me-2" role="status" aria-hidden="true" /> <span className="spinner-border spinner-border-sm me-2" role="status" aria-hidden="true" />
)} )}
{confirmLabel} {confirmLabel}
</button> </button>
</div>
</div> </div>
</div> </div>
</div> </div>
</div> </>
); );
} }
+54 -42
View File
@@ -36,55 +36,67 @@ function FormModal({
} }
}; };
const handleBackdropClick = (e) => {
if (e.target === e.currentTarget) {
onClose?.();
}
};
return ( return (
<div <>
className="modal show d-block" {/* Modal Backdrop */}
role="dialog" <div className="modal-backdrop show" onClick={handleBackdropClick} />
aria-modal="true"
style={{ backgroundColor: 'rgba(0,0,0,0.5)' }} {/* Modal */}
onKeyDown={handleKeyDown}
>
<div <div
className={`modal-dialog${size !== 'md' ? ` modal-${size}` : ''}${centered ? ' modal-dialog-centered' : ''}`} className="modal show d-block"
role="document" role="dialog"
aria-modal="true"
tabIndex={-1}
onKeyDown={handleKeyDown}
> >
<div className="modal-content" tabIndex={-1}> <div
<form onSubmit={handleSubmit}> className={`modal-dialog${size !== 'md' ? ` modal-${size}` : ''}${centered ? ' modal-dialog-centered' : ''}`}
<div className="modal-header"> role="document"
<h5 className="modal-title">{title}</h5> >
<button type="button" className="btn-close" onClick={onClose} /> <div className="modal-content" tabIndex={-1}>
</div> <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"> <div className="modal-body">
{children} {children}
</div> </div>
<div className="modal-footer"> <div className="modal-footer">
<button <button
type="button" type="button"
className="btn btn-secondary" className="btn btn-secondary"
onClick={onClose} onClick={onClose}
disabled={loading} disabled={loading}
> >
{CancelIcon && <CancelIcon className="me-2" size={16} />} {CancelIcon && <CancelIcon className="me-2" size={16} />}
{cancelLabel} {cancelLabel}
</button> </button>
<button <button
type="submit" type="submit"
className={`btn btn-${submitVariant}`} className={`btn btn-${submitVariant}`}
disabled={loading || disabled} disabled={loading || disabled}
> >
{loading && ( {loading && (
<span className="spinner-border spinner-border-sm me-2" role="status" aria-hidden="true" /> <span className="spinner-border spinner-border-sm me-2" role="status" aria-hidden="true" />
)} )}
{!loading && SubmitIcon && <SubmitIcon className="me-2" size={16} />} {!loading && SubmitIcon && <SubmitIcon className="me-2" size={16} />}
{submitLabel} {submitLabel}
</button> </button>
</div> </div>
</form> </form>
</div>
</div> </div>
</div> </div>
</div> </>
); );
} }
+12 -2
View File
@@ -55,13 +55,23 @@ export default function HistoryModal({ resource, show, onClose, onRolledBack })
refetch().catch(() => notify.error('Не удалось загрузить историю версий')); refetch().catch(() => notify.error('Не удалось загрузить историю версий'));
} }
const handleBackdropClick = (e) => {
if (e.target === e.currentTarget) {
onClose?.();
}
};
return ( return (
<> <>
{/* Modal Backdrop */}
<div className="modal-backdrop show" onClick={handleBackdropClick} />
{/* Modal */}
<div <div
className="modal show d-block" className="modal show d-block"
role="dialog" role="dialog"
aria-modal="true" aria-modal="true"
style={{ backgroundColor: 'rgba(0,0,0,0.5)' }} tabIndex={-1}
onKeyDown={(e) => { if (e.key === 'Escape') onClose?.(); }} onKeyDown={(e) => { if (e.key === 'Escape') onClose?.(); }}
> >
<div className="modal-dialog modal-lg modal-dialog-centered" role="document"> <div className="modal-dialog modal-lg modal-dialog-centered" role="document">
@@ -90,7 +100,7 @@ export default function HistoryModal({ resource, show, onClose, onRolledBack })
<IconHistory className="me-2" size={24} /> <IconHistory className="me-2" size={24} />
История версий История версий
</h5> </h5>
<button type="button" className="btn-close" onClick={onClose} /> <button type="button" className="btn-close" onClick={onClose} aria-label="Закрыть" />
</div> </div>
<div className="modal-body"> <div className="modal-body">
+27 -16
View File
@@ -86,23 +86,34 @@ function ImportModal({
URL.revokeObjectURL(url); URL.revokeObjectURL(url);
}; };
const handleBackdropClick = (e) => {
if (e.target === e.currentTarget) {
onClose?.();
}
};
return ( return (
<div <>
className="modal show d-block" {/* Modal Backdrop */}
role="dialog" <div className="modal-backdrop show" onClick={handleBackdropClick} />
aria-modal="true"
style={{ backgroundColor: 'rgba(0,0,0,0.5)' }} {/* Modal */}
onKeyDown={(e) => { if (e.key === 'Escape') onClose?.(); }} <div
> className="modal show d-block"
<div className="modal-dialog modal-lg modal-dialog-centered" role="document"> role="dialog"
<div className="modal-content" tabIndex={-1}> aria-modal="true"
<div className="modal-header"> tabIndex={-1}
<h5 className="modal-title d-flex align-items-center"> onKeyDown={(e) => { if (e.key === 'Escape') onClose?.(); }}
<IconUpload className="me-2" size={24} /> >
{title} <div className="modal-dialog modal-lg modal-dialog-centered" role="document">
</h5> <div className="modal-content" tabIndex={-1}>
<button type="button" className="btn-close" onClick={onClose} /> <div className="modal-header">
</div> <h5 className="modal-title d-flex align-items-center">
<IconUpload className="me-2" size={24} />
{title}
</h5>
<button type="button" className="btn-close" onClick={onClose} aria-label="Закрыть" />
</div>
<div className="modal-body"> <div className="modal-body">
{/* Description */} {/* Description */}
+60 -48
View File
@@ -50,63 +50,75 @@ function Modal({
if (!show) return null; if (!show) return null;
const handleBackdropClick = (e) => {
if (backdrop && e.target === e.currentTarget) {
onClose?.();
}
};
return ( return (
<div <>
className={`modal show d-block ${className}`} {/* Modal Backdrop */}
role="dialog" <div className="modal-backdrop show" onClick={handleBackdropClick} />
aria-modal="true"
style={{ backgroundColor: 'rgba(0,0,0,0.5)' }} {/* Modal */}
onKeyDown={(e) => { if (e.key === 'Escape') onClose?.(); }}
>
<div <div
className={`modal-dialog${size !== 'md' ? ` modal-${size}` : ''}${scrollable ? ' modal-dialog-scrollable' : ''}${centered ? ' modal-dialog-centered' : ''}`} className={`modal show d-block ${className}`}
role="document" role="dialog"
aria-modal="true"
tabIndex={-1}
onKeyDown={(e) => { if (e.key === 'Escape') onClose?.(); }}
> >
<div <div
className="modal-content" className={`modal-dialog${size !== 'md' ? ` modal-${size}` : ''}${scrollable ? ' modal-dialog-scrollable' : ''}${centered ? ' modal-dialog-centered' : ''}`}
ref={modalRef} role="document"
tabIndex={-1}
onKeyDown={(e) => {
if (e.key === 'Tab') {
const c = e.currentTarget;
const focusable = c.querySelectorAll('button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])');
if (!focusable || focusable.length === 0) return;
const first = focusable[0];
const last = focusable[focusable.length - 1];
if (e.shiftKey && document.activeElement === first) {
e.preventDefault();
last.focus();
} else if (!e.shiftKey && document.activeElement === last) {
e.preventDefault();
first.focus();
}
}
}}
> >
{title && ( <div
<div className="modal-header"> className="modal-content"
<h5 className="modal-title">{title}</h5> ref={modalRef}
<button tabIndex={-1}
type="button" onKeyDown={(e) => {
className="btn-close" if (e.key === 'Tab') {
onClick={onClose} const c = e.currentTarget;
aria-label="Закрыть" const focusable = c.querySelectorAll('button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])');
/> if (!focusable || focusable.length === 0) return;
</div> const first = focusable[0];
)} const last = focusable[focusable.length - 1];
if (e.shiftKey && document.activeElement === first) {
e.preventDefault();
last.focus();
} else if (!e.shiftKey && document.activeElement === last) {
e.preventDefault();
first.focus();
}
}
}}
>
{title && (
<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"> <div className="modal-body">
{children} {children}
</div>
{footer && (
<div className="modal-footer">
{footer}
</div>
)}
</div> </div>
{footer && (
<div className="modal-footer">
{footer}
</div>
)}
</div> </div>
</div> </div>
</div> </>
); );
} }
+27 -16
View File
@@ -103,23 +103,34 @@ export default function SettingsModal({ open, onClose }) {
} }
}; };
const handleBackdropClick = (e) => {
if (e.target === e.currentTarget) {
onClose?.();
}
};
return ( return (
<div <>
className="modal show d-block" {/* Modal Backdrop */}
role="dialog" <div className="modal-backdrop show" onClick={handleBackdropClick} />
aria-modal="true"
style={{ backgroundColor: 'rgba(0,0,0,0.5)' }} {/* Modal */}
onKeyDown={(e) => { if (e.key === 'Escape') onClose?.(); }} <div
> className="modal show d-block"
<div className="modal-dialog modal-dialog-centered" role="document"> role="dialog"
<div className="modal-content" tabIndex={-1}> aria-modal="true"
<div className="modal-header"> tabIndex={-1}
<h5 className="modal-title d-flex align-items-center"> onKeyDown={(e) => { if (e.key === 'Escape') onClose?.(); }}
<IconSettings className="me-2" size={24} /> >
Настройки интерфейса <div className="modal-dialog modal-dialog-centered" role="document">
</h5> <div className="modal-content" tabIndex={-1}>
<button type="button" className="btn-close" onClick={onClose} /> <div className="modal-header">
</div> <h5 className="modal-title d-flex align-items-center">
<IconSettings className="me-2" size={24} />
Настройки интерфейса
</h5>
<button type="button" className="btn-close" onClick={onClose} aria-label="Закрыть" />
</div>
<div className="modal-body"> <div className="modal-body">
{error && <ErrorAlert message={error} onClose={() => setError('')} />} {error && <ErrorAlert message={error} onClose={() => setError('')} />}
+28 -17
View File
@@ -117,24 +117,35 @@ function WsUpdateModal({ show, url, onClose }) {
); );
}; };
const handleBackdropClick = (e) => {
if (e.target === e.currentTarget) {
onClose?.();
}
};
return ( return (
<div <>
className="modal show d-block" {/* Modal Backdrop */}
role="dialog" <div className="modal-backdrop show" onClick={handleBackdropClick} />
aria-modal="true"
style={{ backgroundColor: 'rgba(0,0,0,0.5)' }} {/* Modal */}
onKeyDown={(e) => { if (e.key === 'Escape') onClose?.(); }} <div
> className="modal show d-block"
<div className="modal-dialog modal-lg modal-dialog-centered" role="document"> role="dialog"
<div className="modal-content" tabIndex={-1}> aria-modal="true"
<div className="modal-header"> tabIndex={-1}
<h5 className="modal-title d-flex align-items-center"> onKeyDown={(e) => { if (e.key === 'Escape') onClose?.(); }}
<IconPlugConnected className="me-2" size={24} /> >
Логи запуска <div className="modal-dialog modal-lg modal-dialog-centered" role="document">
{getStatusBadge()} <div className="modal-content" tabIndex={-1}>
</h5> <div className="modal-header">
<button type="button" className="btn-close" onClick={onClose} /> <h5 className="modal-title d-flex align-items-center">
</div> <IconPlugConnected className="me-2" size={24} />
Логи запуска
{getStatusBadge()}
</h5>
<button type="button" className="btn-close" onClick={onClose} aria-label="Закрыть" />
</div>
<div className="modal-body"> <div className="modal-body">
{/* Controls */} {/* Controls */}