ketekny-ui-kit 1.0.47 → 1.0.48
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.
- package/package.json +1 -1
- package/src/ui/kMessageNew.vue +69 -2
package/package.json
CHANGED
package/src/ui/kMessageNew.vue
CHANGED
|
@@ -89,13 +89,34 @@
|
|
|
89
89
|
</button>
|
|
90
90
|
</div>
|
|
91
91
|
</div>
|
|
92
|
+
|
|
93
|
+
<div
|
|
94
|
+
v-if="showDismissProgress"
|
|
95
|
+
:class="['mt-1 flex items-center justify-end gap-1 text-[10px] font-medium leading-none', colors.label]"
|
|
96
|
+
aria-hidden="true"
|
|
97
|
+
>
|
|
98
|
+
<Clock3 class="h-2.5 w-2.5" />
|
|
99
|
+
<span>{{ dismissSecondsLeft }}s</span>
|
|
100
|
+
</div>
|
|
101
|
+
|
|
102
|
+
<div
|
|
103
|
+
v-if="showDismissProgress"
|
|
104
|
+
:class="['pointer-events-none absolute inset-x-0 bottom-0 h-1', progressTracks[type]]"
|
|
105
|
+
aria-hidden="true"
|
|
106
|
+
/>
|
|
107
|
+
<div
|
|
108
|
+
v-if="showDismissProgress"
|
|
109
|
+
:class="['pointer-events-none absolute bottom-0 left-0 h-1', progressBars[type]]"
|
|
110
|
+
:style="{ width: `${dismissElapsedPercent}%` }"
|
|
111
|
+
aria-hidden="true"
|
|
112
|
+
/>
|
|
92
113
|
</div>
|
|
93
114
|
</Transition>
|
|
94
115
|
</template>
|
|
95
116
|
|
|
96
117
|
<script setup>
|
|
97
118
|
import { computed, onBeforeUnmount, ref, useSlots, watch } from 'vue'
|
|
98
|
-
import { X as CloseIcon, Info, CircleCheckBig, TriangleAlert, OctagonX } from 'lucide-vue-next'
|
|
119
|
+
import { X as CloseIcon, Info, CircleCheckBig, TriangleAlert, OctagonX, Clock3 } from 'lucide-vue-next'
|
|
99
120
|
|
|
100
121
|
// ─── Color config ────────────────────────────────────────────────────────────
|
|
101
122
|
|
|
@@ -298,7 +319,9 @@ const slots = useSlots()
|
|
|
298
319
|
const internalVisible = ref(true)
|
|
299
320
|
const messageRef = ref(null)
|
|
300
321
|
const dismissTimer = ref(null)
|
|
322
|
+
const dismissRaf = ref(null)
|
|
301
323
|
const dismissStartedAt = ref(0)
|
|
324
|
+
const dismissRemainingAtStart = ref(0)
|
|
302
325
|
const dismissRemaining = ref(0)
|
|
303
326
|
|
|
304
327
|
const isVisible = computed(() => (props.visible !== null ? props.visible : internalVisible.value))
|
|
@@ -312,12 +335,53 @@ const dismissAfterMs = computed(() => {
|
|
|
312
335
|
const value = Number(props.dismissAfter)
|
|
313
336
|
return Number.isFinite(value) && value > 0 ? value : 0
|
|
314
337
|
})
|
|
338
|
+
const showDismissProgress = computed(() => isVisible.value && dismissAfterMs.value > 0)
|
|
339
|
+
const dismissElapsedPercent = computed(() => {
|
|
340
|
+
if (dismissAfterMs.value <= 0) return 0
|
|
341
|
+
return Math.min(100, Math.max(0, ((dismissAfterMs.value - dismissRemaining.value) / dismissAfterMs.value) * 100))
|
|
342
|
+
})
|
|
343
|
+
const dismissSecondsLeft = computed(() => Math.max(0, Math.ceil(dismissRemaining.value / 1000)))
|
|
344
|
+
|
|
345
|
+
const progressTracks = {
|
|
346
|
+
info: 'bg-sky-200/60 dark:bg-sky-900/55',
|
|
347
|
+
success: 'bg-emerald-200/60 dark:bg-emerald-900/55',
|
|
348
|
+
warning: 'bg-amber-200/60 dark:bg-amber-900/55',
|
|
349
|
+
error: 'bg-rose-200/60 dark:bg-rose-900/55',
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
const progressBars = {
|
|
353
|
+
info: 'bg-sky-500/70 dark:bg-sky-400/80',
|
|
354
|
+
success: 'bg-emerald-500/70 dark:bg-emerald-400/80',
|
|
355
|
+
warning: 'bg-amber-500/80 dark:bg-amber-400/85',
|
|
356
|
+
error: 'bg-rose-500/70 dark:bg-rose-400/80',
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
function stopDismissRaf() {
|
|
360
|
+
if (dismissRaf.value) {
|
|
361
|
+
cancelAnimationFrame(dismissRaf.value)
|
|
362
|
+
dismissRaf.value = null
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
function syncDismissRemaining() {
|
|
367
|
+
if (!dismissTimer.value) return
|
|
368
|
+
|
|
369
|
+
const elapsed = Date.now() - dismissStartedAt.value
|
|
370
|
+
dismissRemaining.value = Math.max(0, dismissRemainingAtStart.value - elapsed)
|
|
371
|
+
|
|
372
|
+
if (dismissRemaining.value > 0) {
|
|
373
|
+
dismissRaf.value = requestAnimationFrame(syncDismissRemaining)
|
|
374
|
+
} else {
|
|
375
|
+
dismissRaf.value = null
|
|
376
|
+
}
|
|
377
|
+
}
|
|
315
378
|
|
|
316
379
|
function clearDismissTimer() {
|
|
317
380
|
if (dismissTimer.value) {
|
|
318
381
|
clearTimeout(dismissTimer.value)
|
|
319
382
|
dismissTimer.value = null
|
|
320
383
|
}
|
|
384
|
+
stopDismissRaf()
|
|
321
385
|
}
|
|
322
386
|
|
|
323
387
|
function startDismissTimer(duration) {
|
|
@@ -325,9 +389,12 @@ function startDismissTimer(duration) {
|
|
|
325
389
|
if (!isVisible.value || duration <= 0) return
|
|
326
390
|
|
|
327
391
|
dismissRemaining.value = duration
|
|
392
|
+
dismissRemainingAtStart.value = duration
|
|
328
393
|
dismissStartedAt.value = Date.now()
|
|
394
|
+
dismissRaf.value = requestAnimationFrame(syncDismissRemaining)
|
|
329
395
|
dismissTimer.value = setTimeout(() => {
|
|
330
396
|
dismissTimer.value = null
|
|
397
|
+
stopDismissRaf()
|
|
331
398
|
dismissRemaining.value = 0
|
|
332
399
|
close()
|
|
333
400
|
}, duration)
|
|
@@ -337,7 +404,7 @@ function pauseDismissTimer() {
|
|
|
337
404
|
if (!dismissTimer.value || dismissAfterMs.value <= 0) return
|
|
338
405
|
|
|
339
406
|
const elapsed = Date.now() - dismissStartedAt.value
|
|
340
|
-
dismissRemaining.value = Math.max(0,
|
|
407
|
+
dismissRemaining.value = Math.max(0, dismissRemainingAtStart.value - elapsed)
|
|
341
408
|
clearDismissTimer()
|
|
342
409
|
}
|
|
343
410
|
|