refactor(ui): replace modal dialogs with toast/sheet components
Remove DialogVant/NotifyVant and add Toast and Sheet components with useToast, useReveal, and usePageTransition composables. Rework Navigation, RegisterView, and styles across both apps; rebuild dist assets. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 5
parent
148b99c4a6
commit
c05352d6fd
@@ -0,0 +1,72 @@
|
||||
import { reactive } from 'vue'
|
||||
|
||||
/**
|
||||
* App-wide toasts. One shared queue, rendered by <ToastHost> (mounted once in
|
||||
* App.vue), so any view or store can call `toast.success(...)` without placing
|
||||
* a component in its own template.
|
||||
*/
|
||||
|
||||
export type ToastType = 'success' | 'error' | 'info' | 'warning'
|
||||
|
||||
export interface ToastOptions {
|
||||
title?: string
|
||||
/** ms before it closes by itself; 0 keeps it until the user dismisses it. */
|
||||
duration?: number
|
||||
/** Custom icon name; defaults to one per type. */
|
||||
icon?: string
|
||||
}
|
||||
|
||||
export interface ToastItem {
|
||||
id: number
|
||||
type: ToastType
|
||||
message: string
|
||||
title?: string
|
||||
icon: string
|
||||
duration: number
|
||||
}
|
||||
|
||||
const DEFAULT_DURATION = 3200
|
||||
// Older toasts beyond this are dropped so the stack never covers the screen.
|
||||
const MAX_VISIBLE = 3
|
||||
|
||||
const ICONS: Record<ToastType, string> = {
|
||||
success: 'vuesax-linear:tick-circle',
|
||||
error: 'vuesax-linear:close-circle',
|
||||
info: 'vuesax-linear:info-circle',
|
||||
warning: 'vuesax-linear:warning-2',
|
||||
}
|
||||
|
||||
const state = reactive<{ items: ToastItem[] }>({ items: [] })
|
||||
let nextId = 1
|
||||
|
||||
function show(type: ToastType, message: string, options: ToastOptions = {}): number {
|
||||
const id = nextId++
|
||||
state.items.push({
|
||||
id,
|
||||
type,
|
||||
message,
|
||||
title: options.title,
|
||||
icon: options.icon ?? ICONS[type],
|
||||
duration: options.duration ?? DEFAULT_DURATION,
|
||||
})
|
||||
if (state.items.length > MAX_VISIBLE) state.items.splice(0, state.items.length - MAX_VISIBLE)
|
||||
return id
|
||||
}
|
||||
|
||||
function dismiss(id: number) {
|
||||
const i = state.items.findIndex((t) => t.id === id)
|
||||
if (i !== -1) state.items.splice(i, 1)
|
||||
}
|
||||
|
||||
export const toast = {
|
||||
show,
|
||||
dismiss,
|
||||
success: (message: string, options?: ToastOptions) => show('success', message, options),
|
||||
error: (message: string, options?: ToastOptions) => show('error', message, options),
|
||||
info: (message: string, options?: ToastOptions) => show('info', message, options),
|
||||
warning: (message: string, options?: ToastOptions) => show('warning', message, options),
|
||||
}
|
||||
|
||||
export function useToast() {
|
||||
return { toasts: state.items, ...toast }
|
||||
}
|
||||
Reference in New Issue
Block a user