-
Notifications
You must be signed in to change notification settings - Fork 6
fix: ui/ux imrovements #573
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 1 commit
9e403dc
637ba08
fc4fa4d
bcb3903
db0948c
a0be3ca
905cc84
0d5313a
f9ddf9d
8c96e23
47c2919
691347c
571daa3
fb5c8e6
625921b
feb7828
5485dc6
cc975a5
3c9482a
a977ab7
09aa717
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,86 @@ | ||
| <script lang="ts"> | ||
| import { toastStore } from "./toast"; | ||
| import { cn } from "$lib/utils"; | ||
| import { XIcon } from "@hugeicons/core-free-icons"; | ||
| import { HugeiconsIcon } from "@hugeicons/svelte"; | ||
|
|
||
| let toasts = $state<Array<{ id: string; message: string; type?: "success" | "error" | "info"; duration?: number }>>([]); | ||
|
|
||
| $effect(() => { | ||
| const unsubscribe = toastStore.subscribe((value) => { | ||
| toasts = value; | ||
| }); | ||
| return unsubscribe; | ||
| }); | ||
|
|
||
| function removeToast(id: string) { | ||
| toastStore.remove(id); | ||
| } | ||
|
|
||
| const getToastClasses = (type?: string) => { | ||
| switch (type) { | ||
| case "success": | ||
| return "bg-green-50 border-green-200 text-green-800"; | ||
| case "error": | ||
| return "bg-red-50 border-red-200 text-red-800"; | ||
| case "info": | ||
| default: | ||
| return "bg-blue-50 border-blue-200 text-blue-800"; | ||
| } | ||
| }; | ||
| </script> | ||
|
|
||
| <div | ||
| class="fixed top-4 left-1/2 -translate-x-1/2 z-[9999] flex flex-col gap-2 pointer-events-none w-full max-w-md px-4" | ||
| > | ||
| {#each toasts as toast (toast.id)} | ||
| <div | ||
| class={cn( | ||
| "pointer-events-auto flex items-center justify-between gap-3 rounded-lg border p-4 shadow-lg animate-in slide-in-from-top-2 fade-in", | ||
| getToastClasses(toast.type), | ||
| )} | ||
| role="alert" | ||
| > | ||
| <p class="text-sm font-medium flex-1">{toast.message}</p> | ||
| <button | ||
| onclick={() => removeToast(toast.id)} | ||
| class="flex-shrink-0 hover:opacity-70 transition-opacity" | ||
| aria-label="Close toast" | ||
| > | ||
| <HugeiconsIcon | ||
| icon={XIcon} | ||
| size={20} | ||
| strokeWidth={2} | ||
| className="text-current" | ||
| /> | ||
| </button> | ||
| </div> | ||
| {/each} | ||
| </div> | ||
|
|
||
| <style> | ||
| @keyframes slide-in-from-top-2 { | ||
| from { | ||
| transform: translateY(-100%); | ||
| opacity: 0; | ||
| } | ||
| to { | ||
| transform: translateY(0); | ||
| opacity: 1; | ||
| } | ||
| } | ||
|
|
||
| @keyframes fade-in { | ||
| from { | ||
| opacity: 0; | ||
| } | ||
| to { | ||
| opacity: 1; | ||
| } | ||
| } | ||
|
|
||
| .animate-in { | ||
| animation: slide-in-from-top-2 0.3s ease-out, fade-in 0.3s ease-out; | ||
| } | ||
| </style> | ||
|
Comment on lines
+69
to
+95
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion | 🟠 Major Add exit animations for smoother UX. The component defines entrance animations ( Consider using Svelte's transition directives for smoother enter/exit animations: +import { fly, fade } from 'svelte/transition';
+
{#each toasts as toast (toast.id)}
<div
+ transition:fly={{ y: -20, duration: 300 }}
class={cn(
- "pointer-events-auto flex items-center justify-between gap-3 rounded-lg border p-4 shadow-lg animate-in slide-in-from-top-2 fade-in",
+ "pointer-events-auto flex items-center justify-between gap-3 rounded-lg border p-4 shadow-lg",
getToastClasses(toast.type),
)}
role="alert"
>Then you can remove the custom CSS animations and use Svelte's built-in transition system.
🤖 Prompt for AI Agents |
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,53 @@ | ||
| import { writable } from "svelte/store"; | ||
|
|
||
| export interface Toast { | ||
| id: string; | ||
| message: string; | ||
| type?: "success" | "error" | "info"; | ||
| duration?: number; | ||
| } | ||
|
|
||
| const createToastStore = () => { | ||
| const { subscribe, update } = writable<Toast[]>([]); | ||
|
|
||
| return { | ||
| subscribe, | ||
| add: (toast: Omit<Toast, "id">) => { | ||
| const id = crypto.randomUUID(); | ||
| const newToast: Toast = { | ||
| id, | ||
| duration: 3000, | ||
| ...toast, | ||
| }; | ||
|
|
||
| update((toasts) => [...toasts, newToast]); | ||
|
|
||
| // Auto remove after duration | ||
| if (newToast.duration && newToast.duration > 0) { | ||
| setTimeout(() => { | ||
| remove(id); | ||
| }, newToast.duration); | ||
| } | ||
|
|
||
| return id; | ||
| }, | ||
| remove: (id: string) => { | ||
| update((toasts) => toasts.filter((t) => t.id !== id)); | ||
| }, | ||
| clear: () => { | ||
| update(() => []); | ||
| }, | ||
| }; | ||
| }; | ||
|
|
||
| export const toastStore = createToastStore(); | ||
|
|
||
| export const toast = { | ||
| success: (message: string, duration?: number) => | ||
| toastStore.add({ message, type: "success", duration }), | ||
| error: (message: string, duration?: number) => | ||
| toastStore.add({ message, type: "error", duration }), | ||
| info: (message: string, duration?: number) => | ||
| toastStore.add({ message, type: "info", duration }), | ||
| }; | ||
|
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Add an accessible label or tooltip for the copy button.
The copy-to-clipboard functionality works correctly with proper error handling and toast notifications. However, the button lacks an accessible label or tooltip, which may leave users uncertain about its purpose.
Consider adding an
aria-labelortitleattribute to improve accessibility:<Button.Icon icon={Copy01Icon} iconColor={"white"} strokeWidth={2} + title="Copy eName to clipboard" onclick={async () => {Optional: Consider adding brief visual feedback on successful copy.
For enhanced UX, you could temporarily change the icon or add a visual indicator when the copy succeeds, in addition to the toast notification.
📝 Committable suggestion
🤖 Prompt for AI Agents