1- "use client" ;
2- import React , { createContext , useCallback , useContext , useMemo , useRef , useState } from "react" ;
1+ 'use client' ;
2+ import React , {
3+ createContext ,
4+ useCallback ,
5+ useContext ,
6+ useMemo ,
7+ useRef ,
8+ useState ,
9+ } from 'react' ;
310
411// toast types (style variants)
5- type ToastVariant = " success" | " error" | " info" | " warning" ;
12+ type ToastVariant = ' success' | ' error' | ' info' | ' warning' ;
613
714// structure for each toast item
815export type Toast = {
@@ -15,18 +22,31 @@ export type Toast = {
1522
1623// methods exposed by the toast context
1724type ToastContextValue = {
18- show : ( t : Omit < Toast , "id" > ) => string ;
25+ show : ( t : Omit < Toast , 'id' > ) => string ;
1926 dismiss : ( id : string ) => void ;
20- success : ( message : string , opts ?: Omit < Toast , "id" | "message" | "variant" > ) => string ;
21- error : ( message : string , opts ?: Omit < Toast , "id" | "message" | "variant" > ) => string ;
22- info : ( message : string , opts ?: Omit < Toast , "id" | "message" | "variant" > ) => string ;
23- warning : ( message : string , opts ?: Omit < Toast , "id" | "message" | "variant" > ) => string ;
27+ success : (
28+ message : string ,
29+ opts ?: Omit < Toast , 'id' | 'message' | 'variant' > ,
30+ ) => string ;
31+ error : (
32+ message : string ,
33+ opts ?: Omit < Toast , 'id' | 'message' | 'variant' > ,
34+ ) => string ;
35+ info : (
36+ message : string ,
37+ opts ?: Omit < Toast , 'id' | 'message' | 'variant' > ,
38+ ) => string ;
39+ warning : (
40+ message : string ,
41+ opts ?: Omit < Toast , 'id' | 'message' | 'variant' > ,
42+ ) => string ;
2443} ;
2544
2645const ToastContext = createContext < ToastContextValue | null > ( null ) ;
2746
2847// generate simple random id
29- const makeId = ( ) => Math . random ( ) . toString ( 36 ) . slice ( 2 ) + Date . now ( ) . toString ( 36 ) ;
48+ const makeId = ( ) =>
49+ Math . random ( ) . toString ( 36 ) . slice ( 2 ) + Date . now ( ) . toString ( 36 ) ;
3050
3151// provider wraps app and manages toast state
3252export function ToastProvider ( { children } : { children : React . ReactNode } ) {
@@ -45,33 +65,38 @@ export function ToastProvider({ children }: { children: React.ReactNode }) {
4565
4666 // create and display a new toast
4767 const show = useCallback (
48- ( { title, message, variant = "info" , duration = 4000 } : Omit < Toast , "id" > ) => {
68+ ( {
69+ title,
70+ message,
71+ variant = 'info' ,
72+ duration = 4000 ,
73+ } : Omit < Toast , 'id' > ) => {
4974 const id = makeId ( ) ;
5075 const toast : Toast = { id, title, message, variant, duration } ;
5176 setToasts ( ( prev ) => [ toast , ...prev ] ) ;
5277 timers . current [ id ] = window . setTimeout ( ( ) => dismiss ( id ) , duration ) ;
5378 return id ;
5479 } ,
55- [ dismiss ]
80+ [ dismiss ] ,
5681 ) ;
5782
5883 // quick helpers for each variant (success, error, etc.)
5984 const factory =
6085 ( variant : ToastVariant ) =>
61- ( message : string , opts ?: Omit < Toast , "id" | " message" | " variant" > ) =>
86+ ( message : string , opts ?: Omit < Toast , 'id' | ' message' | ' variant' > ) =>
6287 show ( { message, variant, ...opts } ) ;
6388
6489 // memoized context value
6590 const value = useMemo < ToastContextValue > (
6691 ( ) => ( {
6792 show,
6893 dismiss,
69- success : factory ( " success" ) ,
70- error : factory ( " error" ) ,
71- info : factory ( " info" ) ,
72- warning : factory ( " warning" ) ,
94+ success : factory ( ' success' ) ,
95+ error : factory ( ' error' ) ,
96+ info : factory ( ' info' ) ,
97+ warning : factory ( ' warning' ) ,
7398 } ) ,
74- [ show , dismiss ]
99+ [ show , dismiss ] ,
75100 ) ;
76101
77102 return (
@@ -86,7 +111,7 @@ export function ToastProvider({ children }: { children: React.ReactNode }) {
86111export function useToast ( ) {
87112 const ctx = useContext ( ToastContext ) ;
88113 if ( ! ctx ) {
89- throw new Error ( " useToast must be used within <ToastProvider>" ) ;
114+ throw new Error ( ' useToast must be used within <ToastProvider>' ) ;
90115 }
91116 return ctx ;
92117}
@@ -114,33 +139,38 @@ function ToastViewport({
114139}
115140
116141// single toast card UI
117- function ToastCard ( { toast, onDismiss } : { toast : Toast ; onDismiss : ( ) => void } ) {
118- const { title, message, variant = "info" } = toast ;
142+ function ToastCard ( {
143+ toast,
144+ onDismiss,
145+ } : {
146+ toast : Toast ;
147+ onDismiss : ( ) => void ;
148+ } ) {
149+ const { title, message, variant = 'info' } = toast ;
119150
120151 // variant-specific styling
121152 const styles : Record < ToastVariant , string > = {
122153 success :
123- " border-green-200 bg-green-50 text-green-900 dark:border-green-900/30 dark:bg-green-950 dark:text-green-100" ,
154+ ' border-green-200 bg-green-50 text-green-900 dark:border-green-900/30 dark:bg-green-950 dark:text-green-100' ,
124155 error :
125- "border-red-200 bg-red-50 text-red-900 dark:border-red-900/30 dark:bg-red-950 dark:text-red-100" ,
126- info :
127- "border-blue-200 bg-blue-50 text-blue-900 dark:border-blue-900/30 dark:bg-blue-950 dark:text-blue-100" ,
156+ 'border-red-200 bg-red-50 text-red-900 dark:border-red-900/30 dark:bg-red-950 dark:text-red-100' ,
157+ info : 'border-blue-200 bg-blue-50 text-blue-900 dark:border-blue-900/30 dark:bg-blue-950 dark:text-blue-100' ,
128158 warning :
129- " border-amber-200 bg-amber-50 text-amber-900 dark:border-amber-900/30 dark:bg-amber-950 dark:text-amber-100" ,
159+ ' border-amber-200 bg-amber-50 text-amber-900 dark:border-amber-900/30 dark:bg-amber-950 dark:text-amber-100' ,
130160 } ;
131161
132162 const badge : Record < ToastVariant , string > = {
133- success : "✓" ,
134- error : "✕" ,
135- info : "ℹ" ,
136- warning : "⚠" ,
163+ success : '✓' ,
164+ error : '✕' ,
165+ info : 'ℹ' ,
166+ warning : '⚠' ,
137167 } ;
138168
139169 return (
140170 < div
141171 className = { `pointer-events-auto w-full rounded-2xl border p-4 shadow-lg backdrop-blur-sm ${ styles [ variant ] } ` }
142- role = { variant === " error" ? " alert" : " status" }
143- aria-live = { variant === " error" ? " assertive" : " polite" }
172+ role = { variant === ' error' ? ' alert' : ' status' }
173+ aria-live = { variant === ' error' ? ' assertive' : ' polite' }
144174 >
145175 < div className = "flex items-start gap-3" >
146176 < div className = "mt-0.5 text-xl leading-none" > { badge [ variant ] } </ div >
@@ -151,7 +181,7 @@ function ToastCard({ toast, onDismiss }: { toast: Toast; onDismiss: () => void }
151181 < button
152182 type = "button"
153183 onClick = { onDismiss }
154- className = "ml-2 inline-flex h-7 w-7 items-center justify-center rounded-full text-sm hover:opacity-80 focus:outline-none focus: ring-2 focus:ring-offset-2"
184+ className = "ml-2 inline-flex h-7 w-7 items-center justify-center rounded-full text-sm hover:opacity-80 focus:ring-2 focus:ring-offset-2 focus:outline-none "
155185 aria-label = "Dismiss notification"
156186 >
157187 ✖
0 commit comments