Files
recipe_tool/components/ui/toast.tsx
T
hanhan a249fb038c feat: add recipe translation tool with AI translation and settings management
- Add Word document parsing and translation export
- Add database configuration management
- Add AI/LLM translation configuration
- Add translation vocabulary management
- Add export settings (colors, page orientation, headers)
- Separate database and AI configurations
- Add debounced search to prevent database overload
2026-04-12 23:50:54 +08:00

72 lines
2.1 KiB
TypeScript

"use client";
import { createContext, useContext, useState, useCallback, ReactNode } from "react";
import { X } from "lucide-react";
import { Alert, AlertTitle, AlertDescription } from "@/components/ui/alert";
interface Toast {
id: string;
type: "success" | "error" | "info";
title: string;
description?: string;
}
interface ToastContextType {
toasts: Toast[];
addToast: (toast: Omit<Toast, "id">) => void;
removeToast: (id: string) => void;
}
const ToastContext = createContext<ToastContextType | null>(null);
export function ToastProvider({ children }: { children: ReactNode }) {
const [toasts, setToasts] = useState<Toast[]>([]);
const addToast = useCallback((toast: Omit<Toast, "id">) => {
const id = Math.random().toString(36).substring(2, 9);
setToasts((prev) => [...prev, { ...toast, id }]);
// Auto remove after 4 seconds
setTimeout(() => {
setToasts((prev) => prev.filter((t) => t.id !== id));
}, 4000);
}, []);
const removeToast = useCallback((id: string) => {
setToasts((prev) => prev.filter((t) => t.id !== id));
}, []);
return (
<ToastContext.Provider value={{ toasts, addToast, removeToast }}>
{children}
{/* Toast Container */}
<div className="fixed bottom-4 right-4 z-[100] flex flex-col gap-2 max-w-sm">
{toasts.map((toast) => (
<Alert key={toast.id} variant={toast.type === "error" ? "destructive" : "default"}>
<AlertTitle>{toast.title}</AlertTitle>
{toast.description && (
<AlertDescription>{toast.description}</AlertDescription>
)}
<div className="absolute top-2.5 right-3 flex items-center gap-1">
<button
onClick={() => removeToast(toast.id)}
className="rounded-sm p-1 hover:bg-muted"
>
<X className="h-4 w-4" />
</button>
</div>
</Alert>
))}
</div>
</ToastContext.Provider>
);
}
export function useToast() {
const context = useContext(ToastContext);
if (!context) {
throw new Error("useToast must be used within a ToastProvider");
}
return context;
}