@marv3l/canopy-ui 1.2.4 → 1.2.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +0 -4
- package/package.json +1 -1
- package/templates/toast/toast.tsx +350 -54
package/dist/index.js
CHANGED
package/package.json
CHANGED
|
@@ -1,14 +1,82 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
|
|
3
3
|
import * as React from "react";
|
|
4
|
-
import {
|
|
5
|
-
|
|
4
|
+
import {
|
|
5
|
+
CheckCircle2,
|
|
6
|
+
AlertCircle,
|
|
7
|
+
AlertTriangle,
|
|
8
|
+
Info,
|
|
9
|
+
Loader2,
|
|
10
|
+
X,
|
|
11
|
+
type LucideIcon,
|
|
12
|
+
} from "lucide-react";
|
|
13
|
+
|
|
14
|
+
// =============================================================================
|
|
15
|
+
// TYPES & INTERFACES
|
|
16
|
+
// =============================================================================
|
|
17
|
+
|
|
18
|
+
export type ToastVariant =
|
|
19
|
+
| "default"
|
|
20
|
+
| "success"
|
|
21
|
+
| "error"
|
|
22
|
+
| "warning"
|
|
23
|
+
| "info"
|
|
24
|
+
| "custom"
|
|
25
|
+
| "loading";
|
|
26
|
+
|
|
27
|
+
export interface ToastOptions {
|
|
28
|
+
/** Optional custom identifier. If omitted, a collision-resistant UUID is generated. */
|
|
29
|
+
id?: string;
|
|
30
|
+
/** Primary title text or JSX element displayed in the toast header. */
|
|
31
|
+
title?: React.ReactNode;
|
|
32
|
+
/** Secondary explanatory text or JSX node rendered below the title. */
|
|
33
|
+
description?: React.ReactNode;
|
|
34
|
+
/** Interactive action button or control rendered at the bottom of the toast. */
|
|
35
|
+
action?: React.ReactNode;
|
|
36
|
+
/** Visual variant styling preset. Defaults to "default". */
|
|
37
|
+
variant?: ToastVariant;
|
|
38
|
+
/** Display duration in milliseconds before auto-dismissing. Set to 0 or Infinity to prevent auto-dismiss. */
|
|
39
|
+
duration?: number;
|
|
40
|
+
/** Maximum number of duplicate message stacks allowed for this specific toast (defaults to 5). */
|
|
41
|
+
maxCount?: number;
|
|
42
|
+
/** Explicitly toggle the animated bottom progress bar. Defaults to true when auto-dismissible. */
|
|
43
|
+
showProgress?: boolean;
|
|
44
|
+
/** Optional custom icon node to override the variant preset icon. */
|
|
45
|
+
icon?: React.ReactNode;
|
|
46
|
+
/** Custom inline token styling overrides for background, border, text, progress bar, and icon. */
|
|
47
|
+
customColor?: {
|
|
48
|
+
bg?: string;
|
|
49
|
+
text?: string;
|
|
50
|
+
border?: string;
|
|
51
|
+
progress?: string;
|
|
52
|
+
icon?: string;
|
|
53
|
+
};
|
|
54
|
+
/** Additional Tailwind or CSS class names to apply to the root toast card container. */
|
|
55
|
+
className?: string;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export interface ToastItem extends ToastOptions {
|
|
59
|
+
id: string;
|
|
60
|
+
open: boolean;
|
|
61
|
+
count: number;
|
|
62
|
+
maxReached?: boolean;
|
|
63
|
+
}
|
|
6
64
|
|
|
7
65
|
export interface ToasterProps {
|
|
8
66
|
defaultDuration?: number;
|
|
9
|
-
position?:
|
|
67
|
+
position?:
|
|
68
|
+
| "top-right"
|
|
69
|
+
| "bottom-right"
|
|
70
|
+
| "top-center"
|
|
71
|
+
| "bottom-center"
|
|
72
|
+
| "top-left"
|
|
73
|
+
| "bottom-left";
|
|
10
74
|
}
|
|
11
75
|
|
|
76
|
+
// =============================================================================
|
|
77
|
+
// VARIANT STYLING CONFIGURATION
|
|
78
|
+
// =============================================================================
|
|
79
|
+
|
|
12
80
|
const variantStyles: Record<
|
|
13
81
|
string,
|
|
14
82
|
{
|
|
@@ -29,7 +97,8 @@ const variantStyles: Record<
|
|
|
29
97
|
description: "text-neutral-600 dark:text-neutral-200",
|
|
30
98
|
progress: "bg-neutral-900/20 dark:bg-neutral-400",
|
|
31
99
|
iconColor: "text-neutral-700 dark:text-neutral-300",
|
|
32
|
-
badge:
|
|
100
|
+
badge:
|
|
101
|
+
"bg-neutral-200 text-neutral-800 dark:bg-neutral-800 dark:text-neutral-100 border-neutral-300 dark:border-neutral-700",
|
|
33
102
|
icon: null,
|
|
34
103
|
},
|
|
35
104
|
success: {
|
|
@@ -39,7 +108,8 @@ const variantStyles: Record<
|
|
|
39
108
|
description: "text-emerald-800 dark:text-emerald-200/90",
|
|
40
109
|
progress: "bg-emerald-600 dark:bg-emerald-400",
|
|
41
110
|
iconColor: "text-emerald-600 dark:text-emerald-400",
|
|
42
|
-
badge:
|
|
111
|
+
badge:
|
|
112
|
+
"bg-emerald-100 text-emerald-900 dark:bg-emerald-900/80 dark:text-emerald-100 border-emerald-300 dark:border-emerald-700",
|
|
43
113
|
icon: CheckCircle2,
|
|
44
114
|
},
|
|
45
115
|
error: {
|
|
@@ -49,7 +119,8 @@ const variantStyles: Record<
|
|
|
49
119
|
description: "text-red-800 dark:text-red-200/90",
|
|
50
120
|
progress: "bg-destructive dark:bg-red-400",
|
|
51
121
|
iconColor: "text-red-600 dark:text-red-400",
|
|
52
|
-
badge:
|
|
122
|
+
badge:
|
|
123
|
+
"bg-red-100 text-red-900 dark:bg-red-900/80 dark:text-red-100 border-red-300 dark:border-red-700",
|
|
53
124
|
icon: AlertCircle,
|
|
54
125
|
},
|
|
55
126
|
warning: {
|
|
@@ -59,7 +130,8 @@ const variantStyles: Record<
|
|
|
59
130
|
description: "text-amber-800 dark:text-amber-200/90",
|
|
60
131
|
progress: "bg-amber-600 dark:bg-amber-400",
|
|
61
132
|
iconColor: "text-amber-600 dark:text-amber-400",
|
|
62
|
-
badge:
|
|
133
|
+
badge:
|
|
134
|
+
"bg-amber-100 text-amber-900 dark:bg-amber-900/80 dark:text-amber-100 border-amber-300 dark:border-amber-700",
|
|
63
135
|
icon: AlertTriangle,
|
|
64
136
|
},
|
|
65
137
|
info: {
|
|
@@ -69,7 +141,8 @@ const variantStyles: Record<
|
|
|
69
141
|
description: "text-sky-800 dark:text-sky-200/90",
|
|
70
142
|
progress: "bg-primary dark:bg-sky-400",
|
|
71
143
|
iconColor: "text-sky-600 dark:text-sky-400",
|
|
72
|
-
badge:
|
|
144
|
+
badge:
|
|
145
|
+
"bg-sky-100 text-sky-900 dark:bg-sky-900/80 dark:text-sky-100 border-sky-300 dark:border-sky-700",
|
|
73
146
|
icon: Info,
|
|
74
147
|
},
|
|
75
148
|
loading: {
|
|
@@ -79,11 +152,255 @@ const variantStyles: Record<
|
|
|
79
152
|
description: "text-neutral-600 dark:text-neutral-200",
|
|
80
153
|
progress: "bg-primary dark:bg-sky-400",
|
|
81
154
|
iconColor: "text-primary dark:text-sky-400 animate-spin",
|
|
82
|
-
badge:
|
|
155
|
+
badge:
|
|
156
|
+
"bg-neutral-200 text-neutral-800 dark:bg-neutral-800 dark:text-neutral-100 border-neutral-300 dark:border-neutral-700",
|
|
83
157
|
icon: Loader2,
|
|
84
158
|
},
|
|
85
159
|
};
|
|
86
160
|
|
|
161
|
+
// =============================================================================
|
|
162
|
+
// STATE STORE & ENGINE
|
|
163
|
+
// =============================================================================
|
|
164
|
+
|
|
165
|
+
const TOAST_LIMIT = 5;
|
|
166
|
+
const DEFAULT_MAX_COUNT = 5;
|
|
167
|
+
const TOAST_REMOVE_DELAY = 0;
|
|
168
|
+
|
|
169
|
+
type Action =
|
|
170
|
+
| { type: "ADD_TOAST"; toast: ToastItem }
|
|
171
|
+
| { type: "UPDATE_TOAST"; toast: Partial<ToastItem> }
|
|
172
|
+
| { type: "DISMISS_TOAST"; toastId?: string }
|
|
173
|
+
| { type: "REMOVE_TOAST"; toastId?: string };
|
|
174
|
+
|
|
175
|
+
interface State {
|
|
176
|
+
toasts: ToastItem[];
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
function genId(): string {
|
|
180
|
+
if (typeof crypto !== "undefined" && crypto.randomUUID) {
|
|
181
|
+
return crypto.randomUUID();
|
|
182
|
+
}
|
|
183
|
+
return `${Date.now()}-${Math.random().toString(36).slice(2, 9)}`;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
const toastTimeouts = new Map<string, ReturnType<typeof setTimeout>>();
|
|
187
|
+
|
|
188
|
+
const addToRemoveQueue = (toastId: string) => {
|
|
189
|
+
if (toastTimeouts.has(toastId)) return;
|
|
190
|
+
|
|
191
|
+
const timeout = setTimeout(() => {
|
|
192
|
+
toastTimeouts.delete(toastId);
|
|
193
|
+
dispatch({ type: "REMOVE_TOAST", toastId });
|
|
194
|
+
}, TOAST_REMOVE_DELAY);
|
|
195
|
+
|
|
196
|
+
toastTimeouts.set(toastId, timeout);
|
|
197
|
+
};
|
|
198
|
+
|
|
199
|
+
export const reducer = (state: State, action: Action): State => {
|
|
200
|
+
switch (action.type) {
|
|
201
|
+
case "ADD_TOAST": {
|
|
202
|
+
const existingByIdIndex = state.toasts.findIndex((t) => t.id === action.toast.id);
|
|
203
|
+
|
|
204
|
+
if (existingByIdIndex !== -1) {
|
|
205
|
+
return {
|
|
206
|
+
...state,
|
|
207
|
+
toasts: state.toasts.map((t) =>
|
|
208
|
+
t.id === action.toast.id ? { ...t, ...action.toast, open: true } : t
|
|
209
|
+
),
|
|
210
|
+
};
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
const existingIndex = state.toasts.findIndex(
|
|
214
|
+
(t) =>
|
|
215
|
+
t.open &&
|
|
216
|
+
t.title === action.toast.title &&
|
|
217
|
+
t.description === action.toast.description &&
|
|
218
|
+
(t.variant || "default") === (action.toast.variant || "default")
|
|
219
|
+
);
|
|
220
|
+
|
|
221
|
+
if (existingIndex !== -1) {
|
|
222
|
+
const existing = state.toasts[existingIndex];
|
|
223
|
+
const maxLimit = existing.maxCount ?? action.toast.maxCount ?? DEFAULT_MAX_COUNT;
|
|
224
|
+
const newCount = Math.min(existing.count + 1, maxLimit);
|
|
225
|
+
const maxReached = existing.count + 1 >= maxLimit;
|
|
226
|
+
|
|
227
|
+
const updatedToast: ToastItem = {
|
|
228
|
+
...existing,
|
|
229
|
+
...action.toast,
|
|
230
|
+
id: existing.id,
|
|
231
|
+
count: newCount,
|
|
232
|
+
maxReached,
|
|
233
|
+
open: true,
|
|
234
|
+
};
|
|
235
|
+
|
|
236
|
+
const rest = state.toasts.filter((t) => t.id !== existing.id);
|
|
237
|
+
return {
|
|
238
|
+
...state,
|
|
239
|
+
toasts: [updatedToast, ...rest].slice(0, TOAST_LIMIT),
|
|
240
|
+
};
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
return {
|
|
244
|
+
...state,
|
|
245
|
+
toasts: [action.toast, ...state.toasts].slice(0, TOAST_LIMIT),
|
|
246
|
+
};
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
case "UPDATE_TOAST":
|
|
250
|
+
return {
|
|
251
|
+
...state,
|
|
252
|
+
toasts: state.toasts.map((t) =>
|
|
253
|
+
t.id === action.toast.id ? { ...t, ...action.toast } : t
|
|
254
|
+
),
|
|
255
|
+
};
|
|
256
|
+
|
|
257
|
+
case "DISMISS_TOAST": {
|
|
258
|
+
const { toastId } = action;
|
|
259
|
+
|
|
260
|
+
if (toastId) {
|
|
261
|
+
addToRemoveQueue(toastId);
|
|
262
|
+
} else {
|
|
263
|
+
state.toasts.forEach((t) => addToRemoveQueue(t.id));
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
return {
|
|
267
|
+
...state,
|
|
268
|
+
toasts: state.toasts.map((t) =>
|
|
269
|
+
t.id === toastId || toastId === undefined ? { ...t, open: false } : t
|
|
270
|
+
),
|
|
271
|
+
};
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
case "REMOVE_TOAST":
|
|
275
|
+
if (action.toastId === undefined) return { ...state, toasts: [] };
|
|
276
|
+
return {
|
|
277
|
+
...state,
|
|
278
|
+
toasts: state.toasts.filter((t) => t.id !== action.toastId),
|
|
279
|
+
};
|
|
280
|
+
|
|
281
|
+
default:
|
|
282
|
+
return state;
|
|
283
|
+
}
|
|
284
|
+
};
|
|
285
|
+
|
|
286
|
+
const listeners: Array<(state: State) => void> = [];
|
|
287
|
+
let memoryState: State = { toasts: [] };
|
|
288
|
+
|
|
289
|
+
function dispatch(action: Action) {
|
|
290
|
+
memoryState = reducer(memoryState, action);
|
|
291
|
+
listeners.forEach((listener) => listener(memoryState));
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
// =============================================================================
|
|
295
|
+
// DISPATCHER API & CONVENIENCE METHODS
|
|
296
|
+
// =============================================================================
|
|
297
|
+
|
|
298
|
+
export function toast(props: ToastOptions) {
|
|
299
|
+
const id = props.id || genId();
|
|
300
|
+
|
|
301
|
+
const update = (updatedProps: ToastOptions) =>
|
|
302
|
+
dispatch({ type: "UPDATE_TOAST", toast: { ...updatedProps, id } });
|
|
303
|
+
|
|
304
|
+
const dismiss = () => dispatch({ type: "DISMISS_TOAST", toastId: id });
|
|
305
|
+
|
|
306
|
+
dispatch({
|
|
307
|
+
type: "ADD_TOAST",
|
|
308
|
+
toast: {
|
|
309
|
+
...props,
|
|
310
|
+
id,
|
|
311
|
+
open: true,
|
|
312
|
+
count: 1,
|
|
313
|
+
maxReached: false,
|
|
314
|
+
},
|
|
315
|
+
});
|
|
316
|
+
|
|
317
|
+
return { id, dismiss, update };
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
toast.success = (title: React.ReactNode, options?: Omit<ToastOptions, "title" | "variant">) =>
|
|
321
|
+
toast({ ...options, title, variant: "success" });
|
|
322
|
+
|
|
323
|
+
toast.error = (title: React.ReactNode, options?: Omit<ToastOptions, "title" | "variant">) =>
|
|
324
|
+
toast({ ...options, title, variant: "error" });
|
|
325
|
+
|
|
326
|
+
toast.warning = (title: React.ReactNode, options?: Omit<ToastOptions, "title" | "variant">) =>
|
|
327
|
+
toast({ ...options, title, variant: "warning" });
|
|
328
|
+
|
|
329
|
+
toast.info = (title: React.ReactNode, options?: Omit<ToastOptions, "title" | "variant">) =>
|
|
330
|
+
toast({ ...options, title, variant: "info" });
|
|
331
|
+
|
|
332
|
+
toast.loading = (title: React.ReactNode, options?: Omit<ToastOptions, "title" | "variant">) =>
|
|
333
|
+
toast({ ...options, title, variant: "loading", duration: 0 });
|
|
334
|
+
|
|
335
|
+
toast.dismiss = (toastId?: string) => dispatch({ type: "DISMISS_TOAST", toastId });
|
|
336
|
+
|
|
337
|
+
toast.promise = <T,>(
|
|
338
|
+
promise: Promise<T> | (() => Promise<T>),
|
|
339
|
+
msgs: {
|
|
340
|
+
loading: React.ReactNode;
|
|
341
|
+
success: React.ReactNode | ((data: T) => React.ReactNode);
|
|
342
|
+
error: React.ReactNode | ((err: unknown) => React.ReactNode);
|
|
343
|
+
},
|
|
344
|
+
options?: ToastOptions
|
|
345
|
+
) => {
|
|
346
|
+
const instance = toast({
|
|
347
|
+
...options,
|
|
348
|
+
variant: "loading",
|
|
349
|
+
title: msgs.loading,
|
|
350
|
+
duration: 0,
|
|
351
|
+
});
|
|
352
|
+
|
|
353
|
+
const promiseFn = typeof promise === "function" ? promise() : promise;
|
|
354
|
+
|
|
355
|
+
promiseFn
|
|
356
|
+
.then((data) => {
|
|
357
|
+
const successTitle = typeof msgs.success === "function" ? msgs.success(data) : msgs.success;
|
|
358
|
+
toast.success(successTitle, {
|
|
359
|
+
...options,
|
|
360
|
+
id: instance.id,
|
|
361
|
+
duration: options?.duration ?? 4000,
|
|
362
|
+
});
|
|
363
|
+
})
|
|
364
|
+
.catch((err: unknown) => {
|
|
365
|
+
const errorTitle = typeof msgs.error === "function" ? msgs.error(err) : msgs.error;
|
|
366
|
+
toast.error(errorTitle, {
|
|
367
|
+
...options,
|
|
368
|
+
id: instance.id,
|
|
369
|
+
duration: options?.duration ?? 5000,
|
|
370
|
+
});
|
|
371
|
+
});
|
|
372
|
+
|
|
373
|
+
return promiseFn;
|
|
374
|
+
};
|
|
375
|
+
|
|
376
|
+
// =============================================================================
|
|
377
|
+
// REACT CONSUMER HOOK
|
|
378
|
+
// =============================================================================
|
|
379
|
+
|
|
380
|
+
export function useToast() {
|
|
381
|
+
const [state, setState] = React.useState<State>(memoryState);
|
|
382
|
+
|
|
383
|
+
React.useEffect(() => {
|
|
384
|
+
listeners.push(setState);
|
|
385
|
+
return () => {
|
|
386
|
+
const index = listeners.indexOf(setState);
|
|
387
|
+
if (index > -1) {
|
|
388
|
+
listeners.splice(index, 1);
|
|
389
|
+
}
|
|
390
|
+
};
|
|
391
|
+
}, []);
|
|
392
|
+
|
|
393
|
+
return {
|
|
394
|
+
...state,
|
|
395
|
+
toast,
|
|
396
|
+
dismiss: (toastId?: string) => dispatch({ type: "DISMISS_TOAST", toastId }),
|
|
397
|
+
};
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
// =============================================================================
|
|
401
|
+
// UI COMPONENTS (TOASTER & TOAST ELEMENT)
|
|
402
|
+
// =============================================================================
|
|
403
|
+
|
|
87
404
|
export function Toaster({ defaultDuration = 4000, position = "top-center" }: ToasterProps) {
|
|
88
405
|
const { toasts, dismiss } = useToast();
|
|
89
406
|
|
|
@@ -100,52 +417,26 @@ export function Toaster({ defaultDuration = 4000, position = "top-center" }: Toa
|
|
|
100
417
|
<>
|
|
101
418
|
<style>{`
|
|
102
419
|
@keyframes toast-progress {
|
|
103
|
-
from {
|
|
104
|
-
|
|
105
|
-
}
|
|
106
|
-
to {
|
|
107
|
-
transform: scaleX(0);
|
|
108
|
-
}
|
|
420
|
+
from { transform: scaleX(1); }
|
|
421
|
+
to { transform: scaleX(0); }
|
|
109
422
|
}
|
|
110
423
|
@keyframes toast-expand {
|
|
111
|
-
from {
|
|
112
|
-
|
|
113
|
-
}
|
|
114
|
-
to {
|
|
115
|
-
grid-template-rows: 1fr;
|
|
116
|
-
}
|
|
424
|
+
from { grid-template-rows: 0fr; }
|
|
425
|
+
to { grid-template-rows: 1fr; }
|
|
117
426
|
}
|
|
118
427
|
@keyframes toast-fade-in {
|
|
119
|
-
from {
|
|
120
|
-
|
|
121
|
-
transform: translateY(-4px);
|
|
122
|
-
}
|
|
123
|
-
to {
|
|
124
|
-
opacity: 1;
|
|
125
|
-
transform: translateY(0);
|
|
126
|
-
}
|
|
428
|
+
from { opacity: 0; transform: translateY(-4px); }
|
|
429
|
+
to { opacity: 1; transform: translateY(0); }
|
|
127
430
|
}
|
|
128
431
|
@keyframes toast-pop {
|
|
129
|
-
0% {
|
|
130
|
-
|
|
131
|
-
}
|
|
132
|
-
50% {
|
|
133
|
-
transform: scale(1.2);
|
|
134
|
-
}
|
|
135
|
-
100% {
|
|
136
|
-
transform: scale(1);
|
|
137
|
-
}
|
|
432
|
+
0% { transform: scale(0.6); }
|
|
433
|
+
50% { transform: scale(1.2); }
|
|
434
|
+
100% { transform: scale(1); }
|
|
138
435
|
}
|
|
139
436
|
@keyframes toast-shake {
|
|
140
|
-
0%, 100% {
|
|
141
|
-
|
|
142
|
-
}
|
|
143
|
-
25% {
|
|
144
|
-
transform: translateX(-2px);
|
|
145
|
-
}
|
|
146
|
-
75% {
|
|
147
|
-
transform: translateX(2px);
|
|
148
|
-
}
|
|
437
|
+
0%, 100% { transform: translateX(0); }
|
|
438
|
+
25% { transform: translateX(-2px); }
|
|
439
|
+
75% { transform: translateX(2px); }
|
|
149
440
|
}
|
|
150
441
|
`}</style>
|
|
151
442
|
|
|
@@ -181,12 +472,10 @@ function ToastElement({
|
|
|
181
472
|
const startTimeRef = React.useRef<number>(0);
|
|
182
473
|
const timerRef = React.useRef<ReturnType<typeof setTimeout> | null>(null);
|
|
183
474
|
|
|
184
|
-
// Update remaining ref on prop changes without triggering synchronous setState re-renders
|
|
185
475
|
React.useEffect(() => {
|
|
186
476
|
remainingTimeRef.current = activeDuration;
|
|
187
477
|
}, [toast.variant, toast.title, toast.count, activeDuration]);
|
|
188
478
|
|
|
189
|
-
// Handle countdown timeout
|
|
190
479
|
React.useEffect(() => {
|
|
191
480
|
if (!isAutoDismissible || isPaused) {
|
|
192
481
|
if (timerRef.current) clearTimeout(timerRef.current);
|
|
@@ -227,6 +516,7 @@ function ToastElement({
|
|
|
227
516
|
|
|
228
517
|
const userHasBg = Boolean(toast.customColor?.bg || toast.className?.match(/(?:^|\s)bg-/));
|
|
229
518
|
const userHasBorder = Boolean(toast.customColor?.border || toast.className?.match(/(?:^|\s)border-/));
|
|
519
|
+
const userHasText = Boolean(toast.customColor?.text || toast.className?.match(/(?:^|\s)text-/));
|
|
230
520
|
|
|
231
521
|
const isError = variant === "error";
|
|
232
522
|
|
|
@@ -246,7 +536,7 @@ function ToastElement({
|
|
|
246
536
|
) : (
|
|
247
537
|
IconComponent && (
|
|
248
538
|
<IconComponent
|
|
249
|
-
className={`w-5 h-5 mt-0.5 shrink-0 ${defaultStyle.iconColor}`}
|
|
539
|
+
className={`w-5 h-5 mt-0.5 shrink-0 ${!toast.customColor?.icon ? defaultStyle.iconColor : ""}`}
|
|
250
540
|
style={{ color: toast.customColor?.icon }}
|
|
251
541
|
/>
|
|
252
542
|
)
|
|
@@ -256,7 +546,11 @@ function ToastElement({
|
|
|
256
546
|
<div className="flex-1 text-sm">
|
|
257
547
|
<div className="flex items-center gap-2">
|
|
258
548
|
{toast.title && (
|
|
259
|
-
<div
|
|
549
|
+
<div
|
|
550
|
+
className={`font-semibold leading-tight tracking-tight ${
|
|
551
|
+
!userHasText ? defaultStyle.title : ""
|
|
552
|
+
}`}
|
|
553
|
+
>
|
|
260
554
|
{toast.title}
|
|
261
555
|
</div>
|
|
262
556
|
)}
|
|
@@ -289,7 +583,9 @@ function ToastElement({
|
|
|
289
583
|
>
|
|
290
584
|
<div className="overflow-hidden">
|
|
291
585
|
<div
|
|
292
|
-
className={`leading-relaxed text-xs pt-1 opacity-0 font-normal ${
|
|
586
|
+
className={`leading-relaxed text-xs pt-1 opacity-0 font-normal ${
|
|
587
|
+
!userHasText ? defaultStyle.description : "opacity-90"
|
|
588
|
+
}`}
|
|
293
589
|
style={{
|
|
294
590
|
animation: "toast-fade-in 200ms ease-out 250ms forwards",
|
|
295
591
|
}}
|
|
@@ -306,12 +602,12 @@ function ToastElement({
|
|
|
306
602
|
<button
|
|
307
603
|
onClick={onDismiss}
|
|
308
604
|
aria-label="Close toast"
|
|
309
|
-
className="p-1 rounded-md opacity-70 hover:opacity-100 hover:bg-neutral-500/15 dark:hover:bg-white/10 transition-colors text-
|
|
605
|
+
className="p-1 rounded-md opacity-70 hover:opacity-100 hover:bg-neutral-500/15 dark:hover:bg-white/10 transition-colors text-inherit"
|
|
310
606
|
>
|
|
311
607
|
<X className="w-4 h-4" />
|
|
312
608
|
</button>
|
|
313
609
|
|
|
314
|
-
{/* Progress Bar
|
|
610
|
+
{/* Progress Bar */}
|
|
315
611
|
{showProgress && isAutoDismissible && (
|
|
316
612
|
<div
|
|
317
613
|
key={`${toast.id}-${toast.variant}-${toast.count}`}
|