Files
HaBit/components/ui/toast-provider.tsx
Harkamal Randhawa 91cced4c75 Fix app shell layout with stable top bar and dynamic nav offset.
Unified chrome keeps the home logo top-left, measures bottom nav height
for scroll clearance, and adds responsive content widths for mobile/desktop.
2026-05-24 04:26:09 -06:00

81 lines
2.1 KiB
TypeScript

"use client";
import {
createContext,
useCallback,
useContext,
useMemo,
useState,
type ReactNode,
} from "react";
import { cn } from "@/lib/utils";
type ToastVariant = "default" | "success" | "error";
type ToastMessage = {
id: number;
title: string;
variant: ToastVariant;
};
type ToastContextValue = {
toast: (title: string, variant?: ToastVariant) => void;
};
const ToastContext = createContext<ToastContextValue | null>(null);
export function ToastProvider({ children }: { children: ReactNode }) {
const [messages, setMessages] = useState<ToastMessage[]>([]);
const toast = useCallback((title: string, variant: ToastVariant = "default") => {
const id = Date.now() + Math.random();
setMessages((current) => [...current, { id, title, variant }]);
window.setTimeout(() => {
setMessages((current) => current.filter((message) => message.id !== id));
}, 3200);
}, []);
const value = useMemo(() => ({ toast }), [toast]);
return (
<ToastContext.Provider value={value}>
{children}
<div
aria-live="polite"
className="pointer-events-none fixed right-4 z-[60] flex w-[min(100vw-2rem,20rem)] flex-col gap-2"
style={{ bottom: "calc(var(--app-nav-offset, var(--bottom-nav-height)) + 0.75rem)" }}
>
{messages.map((message) => (
<div
key={message.id}
className={cn(
"rounded-lg border px-4 py-3 text-xs shadow-lg backdrop-blur-sm",
message.variant === "success" &&
"border-emerald-500/30 bg-emerald-500/15 text-emerald-100",
message.variant === "error" &&
"border-red-500/30 bg-red-500/15 text-red-100",
message.variant === "default" &&
"border-border/60 bg-background/95 text-foreground",
)}
>
{message.title}
</div>
))}
</div>
</ToastContext.Provider>
);
}
export function useToast() {
const context = useContext(ToastContext);
if (!context) {
throw new Error("useToast must be used within ToastProvider.");
}
return context;
}