first commit
This commit is contained in:
@@ -0,0 +1,115 @@
|
||||
"use client"
|
||||
|
||||
import * as React from "react"
|
||||
import { Toast as ToastPrimitive } from "@base-ui/react/toast"
|
||||
import { XIcon } from "lucide-react"
|
||||
|
||||
import { cn } from "@/lib/utils"
|
||||
|
||||
export const toastManager = ToastPrimitive.createToastManager()
|
||||
|
||||
type ToastType = "default" | "success" | "error" | "warning" | "info"
|
||||
|
||||
interface ToastData {
|
||||
type?: ToastType
|
||||
}
|
||||
|
||||
function ToastList() {
|
||||
const { toasts } = ToastPrimitive.useToastManager<ToastData>()
|
||||
|
||||
return (
|
||||
<ToastPrimitive.Viewport className="fixed top-4 right-4 z-9999 flex w-full max-w-sm flex-col gap-2 outline-none sm:top-6 sm:right-6">
|
||||
{toasts.map((toast) => (
|
||||
<ToastPrimitive.Root
|
||||
key={toast.id}
|
||||
toast={toast}
|
||||
className={cn(
|
||||
"group relative flex w-full flex-col gap-1 overflow-hidden rounded-xl px-4 py-3 text-sm shadow-lg ring-1 transition-all duration-200",
|
||||
"data-[transitionstatus=starting]:-translate-y-2 data-[transitionstatus=starting]:opacity-0",
|
||||
"data-[transitionstatus=ending]:translate-x-full data-[transitionstatus=ending]:opacity-0",
|
||||
toast.data?.type === "success" &&
|
||||
"bg-emerald-50 text-emerald-900 ring-emerald-200 dark:bg-emerald-950 dark:text-emerald-100 dark:ring-emerald-800",
|
||||
toast.data?.type === "error" &&
|
||||
"bg-red-50 text-red-900 ring-red-200 dark:bg-red-950 dark:text-red-100 dark:ring-red-800",
|
||||
toast.data?.type === "warning" &&
|
||||
"bg-amber-50 text-amber-900 ring-amber-200 dark:bg-amber-950 dark:text-amber-100 dark:ring-amber-800",
|
||||
toast.data?.type === "info" &&
|
||||
"bg-sky-50 text-sky-900 ring-sky-200 dark:bg-sky-950 dark:text-sky-100 dark:ring-sky-800",
|
||||
(!toast.data?.type || toast.data.type === "default") &&
|
||||
"bg-white text-foreground ring-foreground/10 dark:bg-neutral-900"
|
||||
)}
|
||||
>
|
||||
<ToastPrimitive.Content className="flex items-start justify-between gap-3">
|
||||
<div className="flex flex-col gap-0.5">
|
||||
{toast.title && (
|
||||
<ToastPrimitive.Title className="font-semibold leading-snug">
|
||||
{toast.title}
|
||||
</ToastPrimitive.Title>
|
||||
)}
|
||||
{toast.description && (
|
||||
<ToastPrimitive.Description className="text-xs opacity-80">
|
||||
{toast.description}
|
||||
</ToastPrimitive.Description>
|
||||
)}
|
||||
</div>
|
||||
<ToastPrimitive.Close className="mt-0.5 shrink-0 rounded-md p-1 opacity-60 transition-opacity hover:opacity-100 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring">
|
||||
<XIcon className="size-3.5" />
|
||||
<span className="sr-only">Dismiss</span>
|
||||
</ToastPrimitive.Close>
|
||||
</ToastPrimitive.Content>
|
||||
|
||||
{toast.actionProps && (
|
||||
<ToastPrimitive.Action
|
||||
{...toast.actionProps}
|
||||
className="mt-1 self-start rounded-md px-2 py-1 text-xs font-medium underline-offset-2 hover:underline focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring"
|
||||
/>
|
||||
)}
|
||||
</ToastPrimitive.Root>
|
||||
))}
|
||||
</ToastPrimitive.Viewport>
|
||||
)
|
||||
}
|
||||
|
||||
function Toaster() {
|
||||
return (
|
||||
<ToastPrimitive.Provider toastManager={toastManager}>
|
||||
<ToastList />
|
||||
</ToastPrimitive.Provider>
|
||||
)
|
||||
}
|
||||
|
||||
function toast(options: {
|
||||
title?: string
|
||||
description?: string
|
||||
type?: ToastType
|
||||
timeout?: number
|
||||
actionLabel?: string
|
||||
onAction?: () => void
|
||||
}) {
|
||||
toastManager.add({
|
||||
title: options.title,
|
||||
description: options.description,
|
||||
timeout: options.timeout ?? 4000,
|
||||
data: { type: options.type ?? "default" },
|
||||
...(options.actionLabel && {
|
||||
actionProps: {
|
||||
children: options.actionLabel,
|
||||
onClick: options.onAction,
|
||||
},
|
||||
}),
|
||||
})
|
||||
}
|
||||
|
||||
toast.success = (title: string, description?: string) =>
|
||||
toast({ title, description, type: "success" })
|
||||
|
||||
toast.error = (title: string, description?: string) =>
|
||||
toast({ title, description, type: "error" })
|
||||
|
||||
toast.warning = (title: string, description?: string) =>
|
||||
toast({ title, description, type: "warning" })
|
||||
|
||||
toast.info = (title: string, description?: string) =>
|
||||
toast({ title, description, type: "info" })
|
||||
|
||||
export { Toaster, toast }
|
||||
Reference in New Issue
Block a user