Files
qiming/qiming-vite-plugin-design-mode/packages/client-react/src/ui/Toast.ts
2026-06-01 13:43:09 +08:00

99 lines
3.1 KiB
TypeScript

/**
* Simple Toast Notification Utility
*/
export class Toast {
private static container: HTMLElement | null = null;
private static styleId = 'appdev-design-mode-toast-styles';
private static ensureContainer() {
if (this.container && document.body.contains(this.container)) return;
// Inject styles
if (!document.getElementById(this.styleId)) {
const style = document.createElement('style');
style.id = this.styleId;
style.textContent = `
.design-mode-toast-container {
position: fixed;
top: 20px;
left: 50%;
transform: translateX(-50%);
z-index: 10000;
display: flex;
flex-direction: column;
gap: 10px;
pointer-events: none;
}
.design-mode-toast {
background: white;
color: #333;
padding: 10px 20px;
border-radius: 6px;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
font-family: system-ui, -apple-system, sans-serif;
font-size: 14px;
display: flex;
align-items: center;
gap: 8px;
animation: design-mode-toast-in 0.3s ease-out forwards;
border: 1px solid #eee;
min-width: 200px;
max-width: 400px;
}
.design-mode-toast.error {
border-left: 4px solid #ef4444;
}
.design-mode-toast.success {
border-left: 4px solid #22c55e;
}
.design-mode-toast.info {
border-left: 4px solid #3b82f6;
}
@keyframes design-mode-toast-in {
from { opacity: 0; transform: translateY(-20px); }
to { opacity: 1; transform: translateY(0); }
}
@keyframes design-mode-toast-out {
from { opacity: 1; transform: translateY(0); }
to { opacity: 0; transform: translateY(-20px); }
}
`;
document.head.appendChild(style);
}
// Create container
this.container = document.createElement('div');
this.container.className = 'design-mode-toast-container';
document.body.appendChild(this.container);
}
public static show(message: string, type: 'success' | 'error' | 'info' = 'info', duration = 3000) {
this.ensureContainer();
const toast = document.createElement('div');
toast.className = `design-mode-toast ${type}`;
toast.textContent = message;
this.container!.appendChild(toast);
setTimeout(() => {
toast.style.animation = 'design-mode-toast-out 0.3s ease-in forwards';
toast.addEventListener('animationend', () => {
toast.remove();
});
}, duration);
}
public static error(message: string, duration = 4000) {
this.show(message, 'error', duration);
}
public static success(message: string, duration = 3000) {
this.show(message, 'success', duration);
}
public static info(message: string, duration = 3000) {
this.show(message, 'info', duration);
}
}