feat: add loading bar and toast notification system with responsive design

This commit is contained in:
2026-02-25 16:30:45 +00:00
parent e13e696253
commit fdeeac88e2
5 changed files with 316 additions and 0 deletions
+18
View File
@@ -0,0 +1,18 @@
/**
* Portal action — teleports an element to document.body.
* Usage: <div use:portal>…</div>
* SSR-safe: only runs when document is available.
*/
export function portal(node: HTMLElement) {
if (typeof document === "undefined") return
document.body.appendChild(node)
return {
destroy() {
if (node.parentNode) {
node.parentNode.removeChild(node)
}
},
}
}
+85
View File
@@ -0,0 +1,85 @@
/**
* Toast Notification System
* Provides a simple toast notification API with auto-dismiss and responsive positioning.
*/
import { writable, derived } from "svelte/store"
export type ToastType = "success" | "error" | "warning" | "info"
export interface Toast {
id: string
message: string
type: ToastType
duration: number
createdAt: number
}
interface ToastStore {
toasts: Toast[]
}
const DEFAULT_DURATION = 3000 // 3 seconds
// Create the toast store
const toastStore = writable<ToastStore>({ toasts: [] })
/**
* Generate unique toast ID
*/
function generateToastId(): string {
return `toast-${Date.now()}-${Math.random().toString(36).substring(2, 9)}`
}
/**
* Add a new toast notification
*/
export function addToast(message: string, type: ToastType = "info", duration: number = DEFAULT_DURATION): string {
const id = generateToastId()
const toast: Toast = {
id,
message,
type,
duration,
createdAt: Date.now(),
}
toastStore.update((store) => ({
toasts: [...store.toasts, toast],
}))
// Auto-remove after duration
if (duration > 0) {
setTimeout(() => {
removeToast(id)
}, duration)
}
return id
}
/**
* Remove a toast by ID
*/
export function removeToast(id: string): void {
toastStore.update((store) => ({
toasts: store.toasts.filter((t) => t.id !== id),
}))
}
/**
* Clear all toasts
*/
export function clearToasts(): void {
toastStore.set({ toasts: [] })
}
// Export derived readable store for reactive access
export const toasts = derived(toastStore, ($store) => $store.toasts)
// Export convenience object
export const toast = {
add: addToast,
remove: removeToast,
clear: clearToasts,
}