From fdeeac88e2e168a7d9b3ffa08960045b83783dd2 Mon Sep 17 00:00:00 2001 From: Sebastian Frank Date: Wed, 25 Feb 2026 16:30:45 +0000 Subject: [PATCH] =?UTF-8?q?=E2=9C=A8=20feat:=20add=20loading=20bar=20and?= =?UTF-8?q?=20toast=20notification=20system=20with=20responsive=20design?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/src/App.svelte | 5 + frontend/src/lib/actions/portal.ts | 18 +++ frontend/src/lib/toast.ts | 85 ++++++++++++ frontend/src/widgets/LoadingBar.svelte | 63 +++++++++ frontend/src/widgets/ToastContainer.svelte | 145 +++++++++++++++++++++ 5 files changed, 316 insertions(+) create mode 100644 frontend/src/lib/actions/portal.ts create mode 100644 frontend/src/lib/toast.ts create mode 100644 frontend/src/widgets/LoadingBar.svelte create mode 100644 frontend/src/widgets/ToastContainer.svelte diff --git a/frontend/src/App.svelte b/frontend/src/App.svelte index d7ed9e7..5eb43e1 100644 --- a/frontend/src/App.svelte +++ b/frontend/src/App.svelte @@ -2,6 +2,8 @@ import { metricCall } from "./config" import { location } from "./lib/store" import { _, locale } from "./lib/i18n/index" + import LoadingBar from "./widgets/LoadingBar.svelte" + import ToastContainer from "./widgets/ToastContainer.svelte" import { SUPPORTED_LANGUAGES, LANGUAGE_LABELS, @@ -60,6 +62,9 @@ }) + + +
Tibi Svelte Starter diff --git a/frontend/src/lib/actions/portal.ts b/frontend/src/lib/actions/portal.ts new file mode 100644 index 0000000..67ebd0d --- /dev/null +++ b/frontend/src/lib/actions/portal.ts @@ -0,0 +1,18 @@ +/** + * Portal action — teleports an element to document.body. + * Usage:
+ * 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) + } + }, + } +} diff --git a/frontend/src/lib/toast.ts b/frontend/src/lib/toast.ts new file mode 100644 index 0000000..35ad367 --- /dev/null +++ b/frontend/src/lib/toast.ts @@ -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({ 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, +} diff --git a/frontend/src/widgets/LoadingBar.svelte b/frontend/src/widgets/LoadingBar.svelte new file mode 100644 index 0000000..98eb845 --- /dev/null +++ b/frontend/src/widgets/LoadingBar.svelte @@ -0,0 +1,63 @@ + + +
+
+
+ + diff --git a/frontend/src/widgets/ToastContainer.svelte b/frontend/src/widgets/ToastContainer.svelte new file mode 100644 index 0000000..5b512ea --- /dev/null +++ b/frontend/src/widgets/ToastContainer.svelte @@ -0,0 +1,145 @@ + + +{#if $toasts.length > 0} + + + + +
+ {#each $toasts as t (t.id)} + + {/each} +
+{/if} + +