Уведомленияmodalglass
Glass Modal
Модальное окно с глассморфизмом, размытым задним фоном и доступным фокусом.
Живое превью
Использование
Скопируйте и вставьте этот код в ваш React-компонент:
tsx
import { GlassModal } from 'react-component-kit-ui/feedback';
const [open, setOpen] = useState(false);
<GlassButton onClick={() => setOpen(true)}>Delete project</GlassButton>
<GlassModal
open={open}
title="Delete project?"
onClose={() => setOpen(false)}
onConfirm={() => { remove(); setOpen(false); }}
>
This action cannot be undone.
</GlassModal>Ручная установка
Если вы не используете пакет npm, создайте файл и вставьте следующий код: src/components/GlassModal.tsx + GlassModal.css:
GlassModal.tsx
'use client';
import { useEffect, useRef } from 'react';
import type { DialogHTMLAttributes, ReactNode } from 'react';
import './GlassModal.css';
/**
* GlassModal — A premium overlay dialog for critical confirmations or focused interactions.
* Built on the native <dialog> element (focus trap, Esc to close, top layer).
*
* @param open - Required. Controls visibility.
* @param onClose - Called on cancel, Esc, or backdrop click.
* @param onConfirm - Called when the confirm button is pressed.
* @param title - Optional. Default 'Confirm Action'.
* @param confirmLabel - Optional. Default 'Confirm'.
* @param cancelLabel - Optional. Default 'Cancel'.
*/
export interface GlassModalProps extends Omit<DialogHTMLAttributes<HTMLDialogElement>, 'open' | 'title'> {
open: boolean;
onClose?: () => void;
onConfirm?: () => void;
title?: ReactNode;
confirmLabel?: string;
cancelLabel?: string;
children?: ReactNode;
}
export function GlassModal({
open,
onClose,
onConfirm,
title = 'Confirm Action',
confirmLabel = 'Confirm',
cancelLabel = 'Cancel',
className,
children,
...rest
}: GlassModalProps) {
const ref = useRef<HTMLDialogElement>(null);
useEffect(() => {
const dialog = ref.current;
if (!dialog) return;
if (open && !dialog.open) dialog.showModal();
if (!open && dialog.open) dialog.close();
}, [open]);
return (
<dialog
{...rest}
ref={ref}
className={['modal-dialog', className].filter(Boolean).join(' ')}
onClose={() => onClose?.()}
onClick={(e) => {
// Clicking the backdrop targets the <dialog> itself.
if (e.target === e.currentTarget) onClose?.();
}}
>
<div className="modal-box">
<div className="modal-header"><h3>{title}</h3></div>
<div className="modal-body">{children}</div>
<div className="modal-footer">
<button className="modal-btn modal-btn--sec" type="button" onClick={() => onClose?.()}>
{cancelLabel}
</button>
<button className="modal-btn modal-btn--pri" type="button" onClick={() => onConfirm?.()}>
{confirmLabel}
</button>
</div>
</div>
</dialog>
);
}
export default GlassModal;GlassModal.css
/* GlassModal — ported from astro-component-kit (feedback/GlassModal.astro) */
.modal-dialog {
background: transparent;
border: none;
padding: 0;
max-width: 400px;
width: calc(100% - 2rem);
margin: auto; /* the global reset zeroes margins; re-center the native dialog */
overflow: visible;
}
.modal-dialog[open] .modal-box { animation: glass-modal-scale-up 0.3s cubic-bezier(0.175, 0.885, 0.32, 1.275) both; }
.modal-dialog::backdrop {
background: rgba(0, 0, 0, 0.4);
backdrop-filter: blur(8px);
animation: glass-modal-fade-in 0.3s forwards;
}
.modal-box {
background: var(--c-bg-elev, rgba(255,255,255,0.05));
border: 1px solid var(--c-border, rgba(255,255,255,0.1));
border-radius: var(--r-xl, 24px);
padding: var(--sp-8, 2rem);
box-shadow: 0 30px 100px rgba(0,0,0,0.6);
color: var(--c-text-1, #fff);
}
.animate-open { animation: glass-modal-scale-up 0.3s cubic-bezier(0.175, 0.885, 0.32, 1.275) forwards; }
.modal-header h3 { margin: 0 0 var(--sp-4, 1rem); color: var(--c-text-1, #fff); font-size: 1.25rem; }
.modal-body { color: var(--c-text-2, #94a3b8); font-size: 0.95rem; line-height: 1.6; }
.modal-footer { display: flex; justify-content: flex-end; gap: var(--sp-4, 1rem); margin-top: var(--sp-8, 2rem); }
.modal-btn {
padding: 0.7rem 1.4rem;
border-radius: var(--r-md, 12px);
border: none;
font-weight: 700;
cursor: pointer;
font-family: inherit;
transition: 0.2s;
}
.modal-btn--sec { background: transparent; color: var(--c-text-1, #fff); border: 1px solid var(--c-border, rgba(255,255,255,0.1)); }
.modal-btn--sec:hover { background: rgba(255,255,255,0.05); }
.modal-btn--pri {
background: var(--c-primary, #6366f1);
color: #fff;
box-shadow: 0 4px 15px rgba(99, 102, 241, 0.3);
}
.modal-btn--pri:hover { transform: translateY(-2px); filter: brightness(1.1); }
@keyframes glass-modal-fade-in { from { opacity: 0; } to { opacity: 1; } }
@keyframes glass-modal-scale-up { 0% { transform: scale(0.8); opacity: 0; } 100% { transform: scale(1); opacity: 1; } }Краткая информация
- Категория
- Уведомления
- Имя файла
GlassModal.tsx- Зависимости
- Нет — чистый React + CSS
- Теги
- modalglass