presenton/electron/servers/nextjs/components/ui/sonner.tsx
2026-03-29 12:31:57 +05:45

241 lines
9.6 KiB
TypeScript

"use client"
import type React from "react"
import { BadgeCheck, Loader2, ShieldAlert } from "lucide-react"
import { Toaster as Sonner, toast as sonnerToast } from "sonner"
/** Toasts with both title and description (matches styled [data-title] / [data-description]). */
export const notify = {
error: (title: string, description: string) =>
sonnerToast.error(title, { description }),
success: (title: string, description: string) =>
sonnerToast.success(title, { description }),
info: (title: string, description: string) =>
sonnerToast.info(title, { description }),
} as const
type ToasterProps = React.ComponentProps<typeof Sonner>
const Toaster = ({ icons, ...props }: ToasterProps) => {
const defaultIcons: NonNullable<ToasterProps["icons"]> = {
success: <BadgeCheck aria-hidden="true" />,
error: <ShieldAlert aria-hidden="true" />,
info: <ShieldAlert aria-hidden="true" />,
warning: <ShieldAlert aria-hidden="true" />,
loading: <Loader2 aria-hidden="true" className="animate-spin" />,
close: <span aria-hidden="true">Got it!</span>,
}
return (
<>
<style jsx global>{`
/* Near Sonner default width on desktop; nearly full width on narrow screens */
[data-sonner-toaster] {
--width: min(100dvw - 1.5rem, 22.5rem) !important;
box-sizing: border-box !important;
}
@media (min-width: 640px) {
[data-sonner-toaster] {
--width: min(100dvw - 2rem, 24rem) !important;
}
}
/* Neutral "card" toast container — design tokens */
[data-sonner-toast][data-styled="true"] {
border-radius: 10px !important;
border: 1px solid var(--Base-Gray-700, #e1e1e5) !important;
background: rgba(255, 255, 255, 0.6) !important;
box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.06) !important;
padding: clamp(9px, 0.5rem + 0.35vw, 12px) clamp(11px, 0.65rem + 0.5vw, 14px) !important;
gap: clamp(8px, 0.5rem + 0.35vw, 11px) !important;
backdrop-filter: blur(6px) !important;
-webkit-backdrop-filter: blur(6px) !important;
width: 100% !important;
max-width: 100% !important;
}
/* Typography — slight scale-up from original 12px, capped modestly */
[data-sonner-toast][data-styled="true"] [data-title] {
font-family: var(--font-syne), ui-sans-serif, system-ui, -apple-system,
BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial,
"Noto Sans", sans-serif !important;
font-size: clamp(0.8125rem, 0.8rem + 0.12vw, 0.9375rem) !important;
font-weight: 500 !important;
line-height: 1.35 !important;
letter-spacing: 0.03em !important;
color: rgb(15 23 42) !important; /* slate-900 */
text-transform: none !important;
}
[data-sonner-toast][data-styled="true"] [data-description] {
font-family: var(--font-syne), ui-sans-serif, system-ui, -apple-system,
BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial,
"Noto Sans", sans-serif !important;
font-size: clamp(0.6875rem, 0.67rem + 0.1vw, 0.8125rem) !important;
font-weight: 400 !important;
line-height: 1.4 !important;
letter-spacing: 0.03em !important;
color: rgb(100 116 139) !important; /* slate-500 */
}
[data-sonner-toast][data-styled="true"] [data-content] {
gap: clamp(2px, 0.15vw, 5px) !important;
flex: 1 1 auto !important;
min-width: 0 !important;
}
/* Left icon badge */
[data-sonner-toast][data-styled="true"] [data-icon] {
width: clamp(20px, 1.15rem + 0.35vw, 22px) !important;
height: clamp(20px, 1.15rem + 0.35vw, 22px) !important;
flex-shrink: 0 !important;
display: flex !important;
align-items: center !important;
justify-content: center !important;
margin: 0 !important;
color: rgb(51 65 85) !important; /* slate-700 */
}
[data-sonner-toast][data-styled="true"] [data-icon] svg {
width: clamp(20px, 1.15rem + 0.35vw, 22px) !important;
height: clamp(20px, 1.15rem + 0.35vw, 22px) !important;
}
/* Per-type icon colors */
[data-sonner-toast][data-type="success"] [data-icon] {
color: rgb(22, 163, 74) !important;
}
[data-sonner-toast][data-type="error"] [data-icon] {
color: rgb(220, 38, 38) !important;
}
[data-sonner-toast][data-type="info"] [data-icon] {
color: rgb(37, 99, 235) !important;
}
[data-sonner-toast][data-type="warning"] [data-icon] {
color: rgb(217, 119, 6) !important;
}
[data-sonner-toast][data-type="loading"] [data-icon] {
color: rgb(124, 58, 237) !important;
}
/* Outline buttons like the mock ("Got it!") */
[data-sonner-toast][data-styled="true"] [data-button] {
height: auto !important;
padding: clamp(4px, 0.3rem + 0.2vw, 7px)
clamp(7px, 0.5rem + 0.25vw, 10px) !important;
border-radius: 6px !important;
font-family: var(--font-syne), ui-sans-serif, system-ui, -apple-system,
BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial,
"Noto Sans", sans-serif !important;
font-size: clamp(0.625rem, 0.62rem + 0.08vw, 0.75rem) !important;
font-weight: 400 !important;
background: rgb(255 255 255) !important;
color: #3F3F3F !important;
border: 1px solid #EDEEEF !important;
box-shadow: none !important;
}
/* Always-present "Got it!" button (styled close button) */
[data-sonner-toast][data-styled="true"] [data-close-button] {
position: static !important;
inset: auto !important;
transform: none !important;
order: 9999 !important;
flex: 0 0 auto !important;
flex-shrink: 0 !important;
white-space: nowrap !important;
width: auto !important;
height: auto !important;
padding: clamp(4px, 0.3rem + 0.2vw, 7px)
clamp(7px, 0.5rem + 0.25vw, 10px) !important;
border-radius: 6px !important;
margin-left: auto !important;
margin-right: 0 !important;
align-self: center !important;
background: rgb(255 255 255) !important;
color: #3f3f3f !important;
border: 1px solid #edeeef !important;
box-shadow: none !important;
font-family: var(--font-syne), ui-sans-serif, system-ui, -apple-system,
BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial,
"Noto Sans", sans-serif !important;
font-size: clamp(0.625rem, 0.62rem + 0.08vw, 0.75rem) !important;
font-weight: 400 !important;
line-height: 1.3 !important;
letter-spacing: 0.02em !important;
}
[data-sonner-toast][data-styled="true"] [data-close-button]:hover {
background: rgb(248 250 252) !important; /* slate-50 */
}
[data-sonner-toast][data-styled="true"] [data-button]:hover {
background: rgb(248 250 252) !important; /* slate-50 */
}
/* Dark mode — same radius, border weight, shadow; frosted dark surface */
.dark [data-sonner-toast][data-styled="true"] {
border-radius: 10px !important;
border: 1px solid rgba(148, 163, 184, 0.22) !important;
background: rgba(2, 6, 23, 0.6) !important;
box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.06) !important;
backdrop-filter: blur(6px) !important;
-webkit-backdrop-filter: blur(6px) !important;
}
.dark [data-sonner-toast][data-styled="true"] [data-title] {
color: rgb(248 250 252) !important; /* slate-50 */
}
.dark [data-sonner-toast][data-styled="true"] [data-description] {
color: rgb(148 163 184) !important; /* slate-400 */
}
.dark [data-sonner-toast][data-styled="true"] [data-button] {
background: rgb(2 6 23) !important;
color: rgb(248 250 252) !important;
border: 1px solid rgba(148, 163, 184, 0.26) !important;
}
.dark [data-sonner-toast][data-styled="true"] [data-close-button] {
background: rgb(2 6 23) !important;
color: rgb(248 250 252) !important;
border: 1px solid rgba(148, 163, 184, 0.26) !important;
}
.dark [data-sonner-toast][data-styled="true"] [data-button]:hover {
background: rgb(15 23 42) !important; /* slate-900 */
}
.dark [data-sonner-toast][data-styled="true"] [data-close-button]:hover {
background: rgb(15 23 42) !important; /* slate-900 */
}
`}</style>
<Sonner
style={{ zIndex: 999999999 }}
className="toaster group z-50 bg-transparent"
icons={{ ...defaultIcons, ...(icons ?? {}) }}
toastOptions={{
closeButtonAriaLabel: "Dismiss notification",
classNames: {
toast: "group toast",
description: "group-[.toast]:text-muted-foreground",
actionButton:
"group-[.toast]:rounded-2xl group-[.toast]:border group-[.toast]:border-slate-200 group-[.toast]:bg-white group-[.toast]:text-slate-900 hover:group-[.toast]:bg-slate-50",
cancelButton:
"group-[.toast]:rounded-2xl group-[.toast]:border group-[.toast]:border-slate-200 group-[.toast]:bg-white group-[.toast]:text-slate-700 hover:group-[.toast]:bg-slate-50",
},
}}
{...props}
/>
</>
)
}
export { Toaster }