PageSourceSearch

https://adessoadvantage.com/assets/services/toast-1fb6c8fcad42ce96…cea2a4fd4a15ba6f8de289b9782c103885d9fef0.js

js adessoadvantage.com collected 2026-10-10 02:37:06 UTC 6,079 bytes, 126 lines download raw bytes

1// Toast service - unified toast notifications
2// Usage: showToast("Message", "success") or import { showToast } from "services/toast"
3
4const icons = {
5  success: '<svg class="w-5 h-5 text-green-500" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z"></path></svg>',
6  error: '<svg class="w-5 h-5 text-red-500" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 8v4m0 4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"></path></svg>',
7  warning: '<svg class="w-5 h-5 text-gray-500" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z"></path></svg>',
8  info: '<svg class="w-5 h-5 text-blue-500" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"></path></svg>'
9}
10
11const styles = {
12  success: 'bg-white dark:bg-gray-800 border-green-200 dark:border-green-800',
13  error: 'bg-white dark:bg-gray-800 border-red-200 dark:border-red-800',
14  warning: 'bg-white dark:bg-gray-800 border-gray-300 dark:border-gray-600',
15  info: 'bg-white dark:bg-gray-800 border-blue-200 dark:border-blue-800'
16}
17
18export function showToast(message, type = 'success', duration = 4000, { icon = null } = {}) {
19  // Check if a dialog is open (dialogs use browser's top layer, ignoring all z-index)
20  // If so, render toast inside the dialog so it's visible above the modal
21  const openDialog = document.querySelector('dialog[open]')
22  let container
23
24  if (openDialog) {
25    // Get or create toast container inside the dialog
26    container = openDialog.querySelector('.dialog-toast-container')
27    if (!container) {
28      container = document.createElement('div')
29      container.className = 'dialog-toast-container fixed top-4 right-4 flex flex-col gap-2 pointer-events-none'
30      openDialog.appendChild(container)
31    }
32  } else {
33    container = document.getElementById('toast-container')
34  }
35
36  if (!container) return
37
38  const toast = document.createElement('div')
39  toast.className = `flex items-center gap-3 px-4 py-3 rounded-lg border shadow-lg pointer-events-auto transform transition-all duration-300 translate-x-full opacity-0 ${styles[type] || styles.info}`
40  toast.innerHTML = `
41    ${icon || icons[type] || icons.info}
42    <span class="text-sm font-medium text-gray-900 dark:text-gray-100">${message}</span>
43    <button onclick="this.parentElement.remove()" class="ml-2 text-gray-400 hover:text-gray-600 dark:hover:text-gray-300">
44      <svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12"></path></svg>
45    </button>
46  `
47
48  container.appendChild(toast)
49  requestAnimationFrame(() => {
50    toast.classList.remove('translate-x-full', 'opacity-0')
51  })
52
53  setTimeout(() => {
54    toast.classList.add('translate-x-full', 'opacity-0')
55    setTimeout(() => toast.remove(), 300)
56  }, duration)
57}
58
59// Linked toast - clickable, navigates to URL on click
60export function showLinkedToast(message, type = 'info', url = null, duration = 8000, { image = null } = {}) {
61  const openDialog = document.querySelector('dialog[open]')
62  let container
63
64  if (openDialog) {
65    container = openDialog.querySelector('.dialog-toast-container')
66    if (!container) {
67      container = document.createElement('div')
68      container.className = 'dialog-toast-container fixed top-4 right-4 flex flex-col gap-2 pointer-events-none'
69      openDialog.appendChild(container)
70    }
71  } else {
72    container = document.getElementById('toast-container')
73  }
74
75  if (!container) return
76
77  const toast = document.createElement('div')
78  toast.className = `flex items-center gap-3 px-4 py-3 rounded-lg border shadow-lg pointer-events-auto transform transition-all duration-300 translate-x-full opacity-0 ${url ? 'cursor-pointer hover:shadow-xl' : ''} ${styles[type] || styles.info}`
79  const leading = image
80    ? `<img src="${image}" alt="" class="w-9 h-9 rounded object-cover shrink-0">`
81    : (icons[type] || icons.info)
82  toast.innerHTML = `
83    ${leading}
84    <span class="text-sm font-medium text-gray-900 dark:text-gray-100 flex-1">${message}</span>
85    ${url ? '<svg class="w-4 h-4 text-gray-400 shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24">
85<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7"></path></svg>' : ''}
86    <button class="ml-1 text-gray-400 hover:text-gray-600 dark:hover:text-gray-300 shrink-0" data-toast-dismiss>
87      <svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12"></path></svg>
88    </button>
89  `
90
91  // Dismiss button
92  toast.querySelector('[data-toast-dismiss]').addEventListener('click', (e) => {
93    e.stopPropagation()
94    toast.classList.add('translate-x-full', 'opacity-0')
95    setTimeout(() => toast.remove(), 300)
96  })
97
98  // Click navigates to URL
99  if (url) {
100    toast.addEventListener('click', (e) => {
101      if (e.target.closest('[data-toast-dismiss]')) return
102      window.Turbo ? window.Turbo.visit(url) : (window.location.href = url)
103      toast.remove()
104    })
105  }
106
107  container.appendChild(toast)
108  requestAnimationFrame(() => {
109    toast.classList.remove('translate-x-full', 'opacity-0')
110  })
111
112  setTimeout(() => {
113    if (toast.parentElement) {
114      toast.classList.add('translate-x-full', 'opacity-0')
115      setTimeout(() => toast.remove(), 300)
116    }
117  }, duration)
118
119  return toast
120}
121
122// Attach to window for inline script usage (flash messages)
123window.showToast = showToast
124window.showLinkedToast = showLinkedToast
125
126export default { showToast, showLinkedToast };

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.