129 lines
2.2 KiB
Vue
129 lines
2.2 KiB
Vue
<template>
|
|
<Transition
|
|
:name="cn.b()"
|
|
@before-leave="$emit('close')"
|
|
@after-leave="$emit('destroy')"
|
|
>
|
|
<UiAlert
|
|
v-show="visible"
|
|
:id="id"
|
|
:class="[cn.b(), verticalSide, horizontalSide]"
|
|
:title="title"
|
|
:type="type"
|
|
@mouseenter="clearTimer"
|
|
@mouseleave="startTimer"
|
|
>
|
|
<slot />
|
|
</UiAlert>
|
|
</Transition>
|
|
</template>
|
|
|
|
<script setup lang="ts">
|
|
import type { NotificationPlacement, NotificationType } from './types'
|
|
import { computed, onMounted, ref } from 'vue'
|
|
|
|
export interface Props {
|
|
duration?: number
|
|
id?: string | number
|
|
offset?: number
|
|
placement?: NotificationPlacement
|
|
type?: NotificationType
|
|
title?: string
|
|
zIndex?: number
|
|
}
|
|
|
|
defineOptions({
|
|
name: 'UiNotification',
|
|
})
|
|
|
|
const props = withDefaults(defineProps<Props>(), {
|
|
type: 'neutral',
|
|
duration: 5000,
|
|
offset: 0,
|
|
placement: 'top-right',
|
|
zIndex: 0,
|
|
})
|
|
|
|
defineEmits(['close', 'destroy'])
|
|
|
|
const cn = useClassname('ui-notification')
|
|
|
|
const timerId = ref()
|
|
const visible = ref(false)
|
|
|
|
const verticalSide = computed(() => props.placement.split('-')[0])
|
|
const horizontalSide = computed(() => props.placement.split('-')[1])
|
|
|
|
function close() {
|
|
visible.value = false
|
|
}
|
|
|
|
function startTimer() {
|
|
if (props.duration > 0) {
|
|
timerId.value = setTimeout(() => {
|
|
if (visible.value)
|
|
close()
|
|
}, props.duration)
|
|
}
|
|
}
|
|
|
|
function clearTimer() {
|
|
if (timerId.value)
|
|
clearTimeout(timerId.value)
|
|
}
|
|
|
|
onMounted(() => {
|
|
startTimer()
|
|
visible.value = true
|
|
})
|
|
|
|
defineExpose({
|
|
visible,
|
|
close,
|
|
})
|
|
</script>
|
|
|
|
<style lang="scss">
|
|
.ui-notification {
|
|
position: fixed;
|
|
transition-duration: $transition-duration;
|
|
transition-property: opacity, transform, left, right, top, bottom;
|
|
z-index: calc(8000 + v-bind('zIndex'));
|
|
width: 400px;
|
|
transform-origin: right top;
|
|
|
|
@include mobile {
|
|
width: calc(100% - 32px);
|
|
}
|
|
|
|
&.top {
|
|
top: calc(v-bind('`${offset}px`') + 80px);
|
|
|
|
@include mobile {
|
|
top: auto;
|
|
bottom: v-bind('`${offset}px`');
|
|
}
|
|
}
|
|
|
|
&.bottom {
|
|
bottom: v-bind('`${offset}px`');
|
|
}
|
|
|
|
&.left {
|
|
left: 32px;
|
|
|
|
@include mobile {
|
|
left: 16px;
|
|
}
|
|
}
|
|
|
|
&.right {
|
|
right: 32px;
|
|
|
|
@include mobile {
|
|
right: 16px;
|
|
}
|
|
}
|
|
}
|
|
</style>
|