All files / composables useToast.ts

100% Statements 74/74
100% Branches 18/18
100% Functions 9/9
100% Lines 74/74

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136                                1x   1x       90x 36x 36x         36x 36x 36x   36x 36x 36x 36x 36x 36x 36x 36x   36x     36x 3x 3x   36x 36x         90x 7x 7x         90x 4x 4x         90x 3x 3x         90x 3x 3x         90x 7x 7x 6x 6x 6x   6x 5x 5x 5x 5x 5x 6x 6x 6x 7x         90x 55x 35x 55x 55x 54x 54x 55x 55x   90x 90x 90x 90x 90x 90x 90x 90x 90x 90x 90x     1x         1x 3x 3x  
/**
 * Toast Notification Composable
 * @description Manages toast notifications state and methods
 * Provides a simple API to show/hide toast messages
 */
 
export interface Toast {
  id: string
  variant: 'info' | 'success' | 'warning' | 'error'
  message: string
  actionLabel?: string
  duration?: number
  visible: boolean
}
 
// Shared state across all components
const toasts = ref<Toast[]>([])
 
export const useToast = () => {
  /**
   * Show a toast notification
   */
  const show = (
    message: string,
    options: {
      variant?: Toast['variant']
      actionLabel?: string
      duration?: number
      onAction?: () => void
    } = {}
  ): string => {
    const id = `toast-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`
 
    const toast: Toast = {
      id,
      variant: options.variant || 'info',
      message,
      actionLabel: options.actionLabel,
      duration: options.duration ?? 5000,
      visible: true
    }
 
    toasts.value.push(toast)
 
    // Store action callback if provided
    if (options.onAction) {
      toastActions.set(id, options.onAction)
    }
 
    return id
  }
 
  /**
   * Show info toast
   */
  const info = (message: string, options?: Omit<Parameters<typeof show>[1], 'variant'>) => {
    return show(message, { ...options, variant: 'info' })
  }
 
  /**
   * Show success toast
   */
  const success = (message: string, options?: Omit<Parameters<typeof show>[1], 'variant'>) => {
    return show(message, { ...options, variant: 'success' })
  }
 
  /**
   * Show warning toast
   */
  const warning = (message: string, options?: Omit<Parameters<typeof show>[1], 'variant'>) => {
    return show(message, { ...options, variant: 'warning' })
  }
 
  /**
   * Show error toast
   */
  const error = (message: string, options?: Omit<Parameters<typeof show>[1], 'variant'>) => {
    return show(message, { ...options, variant: 'error' })
  }
 
  /**
   * Dismiss a specific toast
   */
  const dismiss = (id: string) => {
    const index = toasts.value.findIndex(t => t.id === id)
    if (index !== -1) {
      const toast = toasts.value[index]
      if (toast) {
        toast.visible = false
        // Remove from array after transition completes
        setTimeout(() => {
          const currentIndex = toasts.value.findIndex(t => t.id === id)
          if (currentIndex !== -1) {
            toasts.value.splice(currentIndex, 1)
            toastActions.delete(id)
          }
        }, 300) // Match transition duration
      }
    }
  }
 
  /**
   * Dismiss all toasts
   */
  const dismissAll = () => {
    toasts.value.forEach(toast => {
      toast.visible = false
    })
    setTimeout(() => {
      toasts.value = []
      toastActions.clear()
    }, 300)
  }
 
  return {
    toasts: readonly(toasts),
    show,
    info,
    success,
    warning,
    error,
    dismiss,
    dismissAll
  }
}
 
// Store action callbacks
const toastActions = new Map<string, () => void>()
 
/**
 * Get action callback for a toast
 */
export const getToastAction = (id: string): (() => void) | undefined => {
  return toastActions.get(id)
}