atproto explorer pdsls.dev
atproto tool
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};