@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
|
|
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
|
-
|
|
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
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
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
|
-
|
|
21
|
-
|
|
22
|
-
|
|
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
|
-
|
|
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
|
-
|
|
32
|
-
|
|
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
|
+
}
|