Уведомленияfeedbacktoastnotificationalertstack
Toast Notification Group
Стек всплывающих уведомлений в углу экрана с анимацией появления и скрытия.
Живое превью
✓
Changes Saved
Your project settings were synchronized with production.
✕
Deploy failed
Build exited with code 1.
Использование
Скопируйте и вставьте этот код в ваш React-компонент:
tsx
import { ToastNotificationGroup } from 'react-component-kit-ui/feedback';
<ToastNotificationGroup
type="success"
title="Changes saved"
message="Your project settings were synchronized with production."
actionText="Undo"
onAction={undo}
onDismiss={() => setToast(null)}
/>Ручная установка
Если вы не используете пакет npm, создайте файл и вставьте следующий код: src/components/ToastNotificationGroup.tsx + ToastNotificationGroup.css:
ToastNotificationGroup.tsx
'use client';
import { useState } from 'react';
import type { CSSProperties, HTMLAttributes, ReactNode } from 'react';
import './ToastNotificationGroup.css';
/**
* ToastNotificationGroup — Stackable notification banner with auto-timed progress bar, icon, and dismiss action.
*
* @param type - Optional. 'success' | 'warning' | 'error' | 'info'. Default 'success'.
* @param title - Optional. Default 'Changes Saved'.
* @param message - Optional.
* @param actionText - Optional. Default 'Undo'. Pass '' to hide.
* @param onAction - Optional. Action button handler.
* @param onDismiss - Optional. Called after the dismiss animation ends.
*/
export interface ToastNotificationGroupProps extends Omit<HTMLAttributes<HTMLDivElement>, 'title'> {
type?: 'success' | 'warning' | 'error' | 'info';
title?: ReactNode;
message?: ReactNode;
actionText?: string;
onAction?: () => void;
onDismiss?: () => void;
}
const icons = { success: '✓', warning: '!', error: '✕', info: 'ℹ' };
export function ToastNotificationGroup({
type = 'success',
title = 'Changes Saved',
message = 'Your project settings were synchronized with production.',
actionText = 'Undo',
onAction,
onDismiss,
className,
style,
...rest
}: ToastNotificationGroupProps) {
const [state, setState] = useState<'shown' | 'leaving' | 'gone'>('shown');
if (state === 'gone') return null;
const dismiss = () => {
setState('leaving');
window.setTimeout(() => {
setState('gone');
onDismiss?.();
}, 250);
};
const leaving: CSSProperties = state === 'leaving' ? { opacity: 0, transform: 'translateY(10px) scale(0.95)' } : {};
return (
<div role="alert" {...rest} className={['toast-item', `toast-item--${type}`, className].filter(Boolean).join(' ')} style={{ ...style, ...leaving }}>
<div className="toast-item__body">
<span className="toast-item__icon" aria-hidden="true">{icons[type]}</span>
<div className="toast-item__content">
<h5 className="toast-item__title">{title}</h5>
<p className="toast-item__msg">{message}</p>
</div>
{actionText && (
<button type="button" className="toast-item__action" onClick={onAction}>
{actionText}
</button>
)}
<button type="button" className="toast-item__close" aria-label="Dismiss alert" onClick={dismiss}>
✕
</button>
</div>
<div className="toast-item__progress" aria-hidden="true" />
</div>
);
}
export default ToastNotificationGroup;ToastNotificationGroup.css
/* ToastNotificationGroup — ported from astro-component-kit (feedback/ToastNotificationGroup.astro) */
.toast-item {
position: relative;
display: flex;
flex-direction: column;
width: 100%;
max-width: 380px;
background: rgba(14, 14, 18, 0.95);
backdrop-filter: blur(20px);
border: 1px solid rgba(255, 255, 255, 0.1);
border-radius: var(--r-md, 14px);
overflow: hidden;
box-shadow: 0 16px 40px rgba(0, 0, 0, 0.5), inset 0 1px 0 rgba(255, 255, 255, 0.05);
font-family: var(--font-sans, system-ui, sans-serif);
transition: transform 0.25s ease, opacity 0.25s ease;
}
.toast-item--success { --toast-accent: #10b981; }
.toast-item--warning { --toast-accent: #f59e0b; }
.toast-item--error { --toast-accent: #ef4444; }
.toast-item--info { --toast-accent: #38bdf8; }
.toast-item__body {
display: flex;
align-items: flex-start;
gap: 12px;
padding: 1rem 1.15rem;
}
.toast-item__icon {
width: 24px;
height: 24px;
border-radius: 50%;
background: rgba(255, 255, 255, 0.06);
border: 1px solid var(--toast-accent);
color: var(--toast-accent);
display: grid;
place-items: center;
font-size: 0.75rem;
font-weight: 900;
flex-shrink: 0;
margin-top: 2px;
}
.toast-item__content {
flex: 1;
}
.toast-item__title {
font-size: 0.88rem;
font-weight: 700;
color: var(--c-text-1, #ffffff);
margin: 0 0 2px 0;
}
.toast-item__msg {
font-size: 0.78rem;
color: var(--c-text-2, #a3a3a3);
margin: 0;
line-height: 1.4;
}
.toast-item__action {
background: transparent;
border: none;
color: var(--toast-accent);
font-size: 0.78rem;
font-weight: 700;
cursor: pointer;
padding: 0.2rem 0.4rem;
border-radius: 4px;
transition: background 0.2s;
}
.toast-item__action:hover {
background: rgba(255, 255, 255, 0.08);
}
.toast-item__close {
background: transparent;
border: none;
color: var(--c-text-3, #737373);
font-size: 0.85rem;
cursor: pointer;
padding: 0.2rem;
line-height: 1;
transition: color 0.2s;
}
.toast-item__close:hover {
color: var(--c-text-1, #ffffff);
}
.toast-item__progress {
height: 3px;
background: var(--toast-accent);
width: 100%;
animation: toast-notification-group-toast-timer 5s linear forwards;
}
@keyframes toast-notification-group-toast-timer {
from { width: 100%; }
to { width: 0%; }
}Краткая информация
- Категория
- Уведомления
- Имя файла
ToastNotificationGroup.tsx- Зависимости
- Нет — чистый React + CSS
- Теги
- feedbacktoastnotificationalertstack