@saasmakers/ui 1.4.15 → 1.4.17

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -31,7 +31,7 @@ function onClose(event: KeyboardEvent | MouseEvent) {
31
31
  <template>
32
32
  <div
33
33
  class="group flex select-none items-center border border-gray-200 rounded-full bg-white px-3 py-2 text-base text-gray-800 font-normal shadow-sm transition-colors dark:border-gray-700 dark:bg-gray-800 dark:text-gray-100"
34
- :class="{ 'cursor-pointer hover:bg-gray-50 dark:hover:bg-gray-700/50': hasClose }"
34
+ :class="{ 'cursor-pointer': hasClose }"
35
35
  role="button"
36
36
  tabindex="0"
37
37
  @click="onClose"
@@ -39,7 +39,7 @@ function onClose(event: KeyboardEvent | MouseEvent) {
39
39
  @keydown.space.prevent="onClose"
40
40
  >
41
41
  <BaseIcon
42
- class="pointer-events-none flex-initial"
42
+ class="pointer-events-none text-lg flex-initial"
43
43
  :status="status"
44
44
  :text="text"
45
45
  />
@@ -70,7 +70,7 @@ function onClose(event: KeyboardEvent | MouseEvent) {
70
70
  <div class="ml-2 mr-1.5 h-5 border-l border-gray-200 flex-initial dark:border-gray-700" />
71
71
 
72
72
  <BaseIcon
73
- class="text-gray-400 transition-colors flex-initial dark:text-gray-500"
73
+ class="text-lg text-gray-400 transition-colors flex-initial dark:text-gray-500"
74
74
  :class="{
75
75
  'group-hover:text-red-600 dark:group-hover:text-red-400': status === 'error',
76
76
  'group-hover:text-indigo-600 dark:group-hover:text-indigo-400': status === 'info',
@@ -1,36 +1,55 @@
1
1
  const toasts = ref<BaseToast[]>([])
2
+ const dismissTimers = new Map<string, ReturnType<typeof setTimeout>>()
2
3
 
3
4
  export default function useToast() {
4
5
  const nuxtApp = useNuxtApp()
5
6
 
6
7
  const clearToasts = () => {
7
- if (import.meta.client) {
8
- toasts.value = []
8
+ if (!import.meta.client)
9
+ return
10
+
11
+ for (const timer of dismissTimers.values()) {
12
+ clearTimeout(timer)
9
13
  }
14
+
15
+ dismissTimers.clear()
16
+ toasts.value = []
10
17
  }
11
18
 
12
- const closeToast = (toastId: string) => {
13
- if (import.meta.client) {
14
- toasts.value = toasts.value.filter((item) => {
15
- return item.id !== toastId
16
- })
19
+ const createToast = (
20
+ message: string,
21
+ status: BaseStatus = 'success',
22
+ i18nParams?: Record<string, number | string>,
23
+ action?: BaseToastAction,
24
+ ) => {
25
+ if (!import.meta.client) {
26
+ return
17
27
  }
18
- }
19
28
 
20
- const createToast = (message: string, status: BaseStatus = 'success', i18nParams?: Record<string, number | string>, action?: BaseToastAction) => {
21
- if (import.meta.client) {
22
- const toast: BaseToast = {
23
- action,
24
- id: crypto.randomUUID(),
25
- status: status || 'success',
26
- text: message.includes(' ') ? message : nuxtApp.$i18n.t(`toasts.${message}`, i18nParams || {}),
27
- }
29
+ const text = message.includes(' ')
30
+ ? message
31
+ : nuxtApp.$i18n.t(`toasts.${message}`, i18nParams ?? {})
28
32
 
29
- toasts.value.push(toast)
33
+ const fingerprint = getFingerprint(status, text, action?.label)
34
+ const existingToast = toasts.value.find(item => getFingerprint(item.status, item.text, item.action?.label) === fingerprint)
30
35
 
31
- // Give more time to interact when the toast offers an action
32
- setTimeout(() => closeToast(toast.id), (action ? 8 : 5) * 1000)
36
+ if (existingToast) {
37
+ if (action) {
38
+ existingToast.action = action
39
+ }
40
+ scheduleDismiss(existingToast.id, !!existingToast.action)
41
+ return
33
42
  }
43
+
44
+ const toast: BaseToast = {
45
+ action,
46
+ id: crypto.randomUUID(),
47
+ status,
48
+ text,
49
+ }
50
+
51
+ toasts.value.push(toast)
52
+ scheduleDismiss(toast.id, !!action)
34
53
  }
35
54
 
36
55
  return {
@@ -40,3 +59,34 @@ export default function useToast() {
40
59
  toasts,
41
60
  }
42
61
  }
62
+
63
+ function clearDismissTimer(toastId: string) {
64
+ const timer = dismissTimers.get(toastId)
65
+ if (timer !== undefined) {
66
+ clearTimeout(timer)
67
+ dismissTimers.delete(toastId)
68
+ }
69
+ }
70
+
71
+ function closeToast(toastId: string) {
72
+ if (!import.meta.client)
73
+ return
74
+
75
+ clearDismissTimer(toastId)
76
+ toasts.value = toasts.value.filter(item => item.id !== toastId)
77
+ }
78
+
79
+ function getFingerprint(status: BaseStatus | undefined, text: BaseTextText, actionLabel?: BaseTextText): string {
80
+ const normalize = (t: BaseTextText) => typeof t === 'string' ? t : JSON.stringify(t)
81
+
82
+ return `${status ?? 'success'}:${normalize(text)}:${actionLabel ? normalize(actionLabel) : ''}`
83
+ }
84
+
85
+ function scheduleDismiss(toastId: string, hasAction: boolean) {
86
+ clearDismissTimer(toastId)
87
+
88
+ const duration = (hasAction ? 8 : 5) * 1000
89
+ const timer = setTimeout(() => closeToast(toastId), duration)
90
+
91
+ dismissTimers.set(toastId, timer)
92
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@saasmakers/ui",
3
- "version": "1.4.15",
3
+ "version": "1.4.17",
4
4
  "private": false,
5
5
  "description": "Reusable Nuxt UI components for SaaS Makers projects",
6
6
  "license": "MIT",