@fy-/fws-vue 2.1.42 → 2.1.44

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.
@@ -388,7 +388,7 @@ onUnmounted(() => {
388
388
  </Dialog>
389
389
  </TransitionRoot>
390
390
 
391
- <div v-if="mode === 'grid' || mode === 'mason' || mode === 'custom'" class="min-h-[600px] lg:min-h-0">
391
+ <div v-if="mode === 'grid' || mode === 'mason' || mode === 'custom'" class="min-h-[600px]">
392
392
  <div
393
393
  :class="{
394
394
  ' grid grid-cols-2 md:grid-cols-4 xl:grid-cols-6 gap-4 items-start':
@@ -1,15 +1,16 @@
1
1
  <script setup lang="ts">
2
2
  import type { Component } from 'vue'
3
-
4
3
  import {
5
4
  CheckCircleIcon,
6
5
  ExclamationTriangleIcon,
7
6
  LightBulbIcon,
7
+ SparklesIcon,
8
8
  } from '@heroicons/vue/24/solid'
9
9
  import { onMounted, onUnmounted, ref } from 'vue'
10
10
  import { useEventBus } from '../../composables/event-bus'
11
11
  import ScaleTransition from './transitions/ScaleTransition.vue'
12
12
 
13
+ /** Notification interface */
13
14
  interface NotifProps {
14
15
  imgSrc?: string
15
16
  imgIcon?: Component
@@ -18,56 +19,97 @@ interface NotifProps {
18
19
  ctaText?: string
19
20
  ctaLink?: string
20
21
  ctaAction?: () => void
21
- type?: 'info' | 'warning' | 'success'
22
+ /** Add your new 'secret' type */
23
+ type?: 'info' | 'warning' | 'success' | 'secret'
24
+ /** Notification timeout in milliseconds */
22
25
  time?: number
23
26
  }
27
+
28
+ /** Our global event bus (replace with your own logic if needed) */
24
29
  const eventBus = useEventBus()
30
+
31
+ /** Current displayed notification */
25
32
  const currentNotif = ref<NotifProps | null>(null)
26
- let currentTimeout: any | null = null
33
+
34
+ /** Progress percentage (0 to 100) for the notification’s life */
35
+ const progress = ref(0)
36
+
37
+ /** References to setTimeout / setInterval so we can clear them properly */
38
+ let hideTimeout: ReturnType<typeof setTimeout> | null = null
39
+ let progressInterval: ReturnType<typeof setInterval> | null = null
40
+
41
+ /**
42
+ * Primary logic when a 'SendNotif' event is called.
43
+ * - Clears any existing notification first
44
+ * - Sets up the new notification
45
+ * - Starts a progress bar
46
+ */
27
47
  function onCall(data: NotifProps) {
28
- if (currentNotif.value !== null) {
29
- hideNotif()
30
- }
31
- const actualIcon = ref(data.imgIcon)
32
- if (data.imgIcon === undefined) {
33
- if (data.type === 'info') {
34
- actualIcon.value = LightBulbIcon
35
- }
36
- else if (data.type === 'warning') {
37
- actualIcon.value = ExclamationTriangleIcon
38
- }
39
- else if (data.type === 'success') {
40
- actualIcon.value = CheckCircleIcon
41
- }
42
- }
48
+ // If there's an existing notification, remove it first
49
+ hideNotif()
50
+
51
+ // Ensure a minimum of 1s if time is too short or undefined
43
52
  if (!data.time || data.time < 1000) {
44
53
  data.time = 5000
45
54
  }
46
55
 
56
+ // Automatically compute an icon if none is provided
57
+ if (!data.imgIcon) {
58
+ if (data.type === 'info') data.imgIcon = LightBulbIcon
59
+ else if (data.type === 'warning') data.imgIcon = ExclamationTriangleIcon
60
+ else if (data.type === 'success') data.imgIcon = CheckCircleIcon
61
+ else if (data.type === 'secret') data.imgIcon = SparklesIcon
62
+ }
63
+
64
+ // Set the new notification
47
65
  currentNotif.value = {
48
- imgSrc: data.imgSrc,
49
- imgIcon: actualIcon.value,
50
- title: data.title,
51
- content: data.content,
52
- ctaText: data.ctaText,
53
- ctaLink: data.ctaLink,
54
- time: data.time,
55
- type: data.type,
56
- ctaAction: data.ctaAction,
66
+ ...data,
57
67
  }
58
68
 
59
- currentTimeout = setTimeout(hideNotif, currentNotif.value.time)
69
+ // (A) Hide the notification after the specified time
70
+ hideTimeout = setTimeout(() => hideNotif(), data.time)
71
+
72
+ // (B) Animate the progress bar from 0 to 100% within that time
73
+ progress.value = 0
74
+ progressInterval = setInterval(() => {
75
+ if (currentNotif.value && data.time) {
76
+ // update progress based on a 100ms tick
77
+ progress.value += (100 / (data.time / 100))
78
+ // if progress hits or exceeds 100, hide
79
+ if (progress.value >= 100) {
80
+ hideNotif()
81
+ }
82
+ }
83
+ }, 100)
60
84
  }
61
85
 
86
+ /**
87
+ * Clears everything related to the current notification
88
+ */
62
89
  function hideNotif() {
63
90
  currentNotif.value = null
64
- if (currentTimeout !== null) {
65
- clearTimeout(currentTimeout)
91
+ progress.value = 0
92
+
93
+ if (hideTimeout) {
94
+ clearTimeout(hideTimeout)
95
+ hideTimeout = null
96
+ }
97
+ if (progressInterval) {
98
+ clearInterval(progressInterval)
99
+ progressInterval = null
66
100
  }
67
101
  }
102
+
103
+ /**
104
+ * Setup: Listen to the global event bus
105
+ */
68
106
  onMounted(() => {
69
107
  eventBus.on('SendNotif', onCall)
70
108
  })
109
+
110
+ /**
111
+ * Cleanup: remove event listeners
112
+ */
71
113
  onUnmounted(() => {
72
114
  eventBus.off('SendNotif', onCall)
73
115
  })
@@ -78,17 +120,20 @@ onUnmounted(() => {
78
120
  <div
79
121
  v-if="currentNotif !== null"
80
122
  id="base-notif"
81
- class="p-2 mb-4 fixed bottom-4 right-8 !z-[2000] bg-fv-neutral-50/[.6] dark:bg-neutral-800/[.6]"
123
+ class="p-2 mb-4 fixed bottom-4 right-8 !z-[2000] bg-fv-neutral-50/[.6] dark:bg-neutral-800/[.6] rounded-lg border"
82
124
  role="alert"
83
125
  :class="{
84
- 'text-fv-neutral-800 border border-fv-neutral-300 rounded-lg dark:text-fv-neutral-400 dark:border-fv-neutral-600':
126
+ 'text-fv-neutral-800 border-fv-neutral-300 dark:text-fv-neutral-400 dark:border-fv-neutral-600':
85
127
  currentNotif.type === 'info',
86
- 'text-red-800 border border-red-300 rounded-lg dark:text-red-300 dark:border-red-800':
128
+ 'text-red-800 border-red-300 dark:text-red-300 dark:border-red-800':
87
129
  currentNotif.type === 'warning',
88
- 'text-green-800 border border-green-300 rounded-lg dark:text-green-300 dark:border-green-800':
130
+ 'text-green-800 border-green-300 dark:text-green-300 dark:border-green-800':
89
131
  currentNotif.type === 'success',
132
+ 'text-fuchsia-800 border-fuchsia-300 dark:text-fuchsia-300 dark:border-fuchsia-800':
133
+ currentNotif.type === 'secret',
90
134
  }"
91
135
  >
136
+ <!-- Title + icon or image -->
92
137
  <div class="flex items-center gap-2">
93
138
  <img
94
139
  v-if="currentNotif.imgSrc"
@@ -103,11 +148,24 @@ onUnmounted(() => {
103
148
  />
104
149
  <h3 class="text-lg font-medium" v-text="currentNotif.title" />
105
150
  </div>
151
+
152
+ <!-- Optional content -->
106
153
  <div
107
154
  v-if="currentNotif.content"
108
- class="mt-2text-sm"
155
+ class="mt-2 text-sm"
109
156
  v-text="currentNotif.content"
110
157
  />
158
+
159
+ <!-- Progress bar (3px) -->
160
+ <div class="relative mt-3 h-[3px] bg-gray-200 rounded-full overflow-hidden">
161
+ <!-- We re-use text color (text-*) as background or define a custom color -->
162
+ <div
163
+ class="absolute left-0 top-0 h-full bg-current transition-[width]"
164
+ :style="{ width: `${progress}%` }"
165
+ />
166
+ </div>
167
+
168
+ <!-- CTA row (if you need more buttons, just extend it) -->
111
169
  <div class="flex justify-end gap-2 pt-3">
112
170
  <button
113
171
  type="button"
@@ -115,6 +173,7 @@ onUnmounted(() => {
115
173
  aria-label="Close"
116
174
  @click="hideNotif"
117
175
  >
176
+ <!-- i18n example, or plain text like "Dismiss" -->
118
177
  {{ $t("dismiss_cta") }}
119
178
  </button>
120
179
  </div>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fy-/fws-vue",
3
- "version": "2.1.42",
3
+ "version": "2.1.44",
4
4
  "author": "Florian 'Fy' Gasquez <m@fy.to>",
5
5
  "license": "MIT",
6
6
  "homepage": "https://github.com/fy-to/FWJS#readme",