SPB Git

spb/findata

Public
1.9 KB · 41 lines tsx
1"use client";2import { AlertTriangle, CheckCircle2, Info, X, XCircle } from "lucide-react";3import clsx from "clsx";4import { useToast, type ToastKind } from "@/lib/toast";56const ICON: Record<ToastKind, typeof Info> = { info: Info, success: CheckCircle2, warning: AlertTriangle, error: XCircle };7const STYLE: Record<ToastKind, string> = {8  info: "border-accent/40 bg-surface-1",9  success: "border-ok/50 bg-surface-1",10  warning: "border-warn/60 bg-surface-1",11  error: "border-crit/60 bg-surface-1",12};13const ICON_STYLE: Record<ToastKind, string> = { info: "text-accent", success: "text-ok", warning: "text-warn-ink", error: "text-crit" };1415export function Toaster() {16  const { toasts, dismiss } = useToast();17  if (!toasts.length) return null;18  return (19    <div className="pointer-events-none fixed inset-x-0 bottom-16 z-50 flex flex-col items-center gap-2 px-3 sm:bottom-4 sm:items-end sm:pr-4" aria-live="polite">20      {toasts.map((t) => {21        const Icon = ICON[t.kind];22        return (23          <div key={t.id} role="status" className={clsx("pointer-events-auto flex w-full max-w-sm items-start gap-2 rounded-lg border p-3 shadow-lg", STYLE[t.kind])}>24            <Icon className={clsx("mt-0.5 h-4 w-4 shrink-0", ICON_STYLE[t.kind])} aria-hidden />25            <div className="min-w-0 flex-1">26              <div className="text-sm font-medium break-words">{t.title}</div>27              {t.detail && <div className="text-xs text-ink-2 break-words">{t.detail}</div>}28              {t.requestId && (29                <div className="mt-0.5 font-mono text-[11px] text-ink-3">request_id {t.requestId}</div>30              )}31            </div>32            <button className="btn btn-ghost btn-sm -mr-1 -mt-1 px-1" onClick={() => dismiss(t.id)} aria-label="Dismiss">33              <X className="h-3.5 w-3.5" />34            </button>35          </div>36        );37      })}38    </div>39  );40}41
Files · f85ef07ccaee074d0f21ee15a0887b71bc6ad458
Menu

Theme