1import { createSignal, For, Show } from "solid-js";
2
3export type Notification = {
4 id: string;
5 message: string;
6 progress?: number;
7 total?: number;
8 type?: "info" | "success" | "error";
9};
10
11const [notifications, setNotifications] = createSignal<Notification[]>([]);
12
13export const addNotification = (notification: Omit<Notification, "id">) => {
14 const id = `notification-${Date.now()}-${Math.random()}`;
15 setNotifications([...notifications(), { ...notification, id }]);
16 return id;
17};
18
19export const updateNotification = (id: string, updates: Partial<Notification>) => {
20 setNotifications(notifications().map((n) => (n.id === id ? { ...n, ...updates } : n)));
21};
22
23export const removeNotification = (id: string) => {
24 setNotifications(notifications().filter((n) => n.id !== id));
25};
26
27export const NotificationContainer = () => {
28 return (
29 <div class="pointer-events-none fixed bottom-4 left-4 z-50 flex flex-col gap-2">
30 <For each={notifications()}>
31 {(notification) => (
32 <div
33 class="dark:bg-dark-300 dark:shadow-dark-700 pointer-events-auto flex min-w-64 flex-col gap-2 rounded-lg border-[0.5px] border-neutral-300 bg-neutral-50 p-3 shadow-md select-none dark:border-neutral-700"
34 classList={{
35 "border-blue-500 dark:border-blue-400": notification.type === "info",
36 "border-green-500 dark:border-green-400": notification.type === "success",
37 "border-red-500 dark:border-red-400": notification.type === "error",
38 }}
39 onClick={() => removeNotification(notification.id)}
40 >
41 <div class="flex items-center gap-2 text-sm">
42 <Show when={notification.progress !== undefined}>
43 <span class="iconify lucide--download" />
44 </Show>
45 <Show when={notification.type === "success"}>
46 <span class="iconify lucide--check-circle text-green-600 dark:text-green-400" />
47 </Show>
48 <Show when={notification.type === "error"}>
49 <span class="iconify lucide--x-circle text-red-500 dark:text-red-400" />
50 </Show>
51 <span>{notification.message}</span>
52 </div>
53 <Show when={notification.progress !== undefined}>
54 <div class="flex flex-col gap-1">
55 <Show
56 when={notification.total !== undefined && notification.total > 0}
57 fallback={
58 <div class="text-xs text-neutral-600 dark:text-neutral-400">
59 {notification.progress} MB
60 </div>
61 }
62 >
63 <div class="h-2 w-full overflow-hidden rounded-full bg-neutral-200 dark:bg-neutral-700">
64 <div
65 class="h-full rounded-full bg-blue-500 transition-all dark:bg-blue-400"
66 style={{ width: `${notification.progress}%` }}
67 />
68 </div>
69 <div class="text-xs text-neutral-600 dark:text-neutral-400">
70 {notification.progress}%
71 </div>
72 </Show>
73 </div>
74 </Show>
75 </div>
76 )}
77 </For>
78 </div>
79 );
80};