element-plus/packages/notification/src/index.vue

174 lines
4.6 KiB
Vue
Raw Normal View History

<template>
<transition name="el-notification-fade" @after-leave="onClose">
<div
2020-08-06 17:53:58 +08:00
v-show="visible"
:id="id"
:class="['el-notification', customClass, horizontalClass]"
:style="positionStyle"
role="alert"
@mouseenter="clearTimer()"
@mouseleave="startTimer()"
@click="click"
>
2020-08-06 17:53:58 +08:00
<i
v-if="type || iconClass"
class="el-notification__icon"
:class="[typeClass, iconClass]"
></i>
<div
class="el-notification__group"
:class="{ 'is-with-icon': typeClass || iconClass }"
>
<h2 class="el-notification__title" v-text="title"></h2>
<div v-show="message" class="el-notification__content" :style="!!title ? null : 'margin: 0'">
<slot>
<p v-if="!dangerouslyUseHTMLString">{{ message }}</p>
<!-- Caution here, message could've been compromized, nerver use user's input as message -->
<!-- eslint-disable-next-line -->
<p v-else v-html="message"></p>
</slot>
</div>
2020-08-06 17:53:58 +08:00
<div
v-if="showClose"
class="el-notification__closeBtn el-icon-close"
@click.stop="close"
></div>
</div>
</div>
</transition>
</template>
<script lang="ts">
import { defineComponent, computed, ref, PropType } from 'vue'
2020-08-06 00:09:09 +08:00
// notificationVM is an alias of vue.VNode
2020-11-26 23:26:03 +08:00
import type { NotificationVM } from './notification.type'
import { EVENT_CODE } from '../../utils/aria'
import { on, off } from '../../utils/dom'
const TypeMap: Indexable<string> = {
success: 'success',
info: 'info',
warning: 'warning',
error: 'error',
}
export default defineComponent({
name: 'ElNotification',
props: {
customClass: { type: String, default: '' },
2020-08-06 00:09:09 +08:00
dangerouslyUseHTMLString: { type: Boolean, default: false },
duration: { type: Number, default: 4500 },
iconClass: { type: String, default: '' },
id: { type: String, default: '' },
2020-08-06 00:09:09 +08:00
message: {
type: [String, Object] as PropType<string | NotificationVM>,
default: '',
},
offset: { type: Number, default: 0 },
onClick: {
type: Function as PropType<() => void>,
default: () => void 0,
},
onClose: {
type: Function as PropType<() => void>,
required: true,
},
position: {
type: String as PropType<'top-right' | 'top-left' | 'bottom-right' | 'bottom-left'>,
default: 'top-right',
},
showClose: { type: Boolean, default: true },
title: { type: String, default: '' },
type: { type: String, default: '' },
2020-08-06 00:09:09 +08:00
zIndex: { type: Number, default: 0 },
},
emits: ['close', 'click'],
setup(props) {
2020-08-06 00:09:09 +08:00
const typeClass = computed(() => {
const type = props.type
return type && TypeMap[type] ? `el-icon-${TypeMap[type]}` : ''
})
2020-08-06 00:09:09 +08:00
const horizontalClass = computed(() => {
return props.position.indexOf('right') > 1 ? 'right' : 'left'
})
2020-08-06 00:09:09 +08:00
const verticalProperty = computed(() => {
return props.position.startsWith('top') ? 'top' : 'bottom'
})
2020-08-06 00:09:09 +08:00
const positionStyle = computed(() => {
return {
[verticalProperty.value]: `${props.offset}px`,
}
})
2020-08-06 17:53:58 +08:00
const visible = ref(false)
2020-08-06 00:09:09 +08:00
const closed = ref(false)
const timer = ref(null)
2020-08-06 00:09:09 +08:00
return {
horizontalClass,
typeClass,
positionStyle,
verticalProperty,
visible,
closed,
timer,
}
},
mounted() {
if (this.duration > 0) {
this.timer = setTimeout(() => {
if (!this.closed) {
this.close()
}
}, this.duration)
}
2020-08-06 17:53:58 +08:00
this.visible = true
on(document, 'keydown', this.keydown)
},
beforeUnmount() {
off(document, 'keydown', this.keydown)
},
methods: {
// start counting down to destroy notification instance
startTimer() {
if (this.duration > 0) {
this.timer = setTimeout(() => {
if (!this.closed) {
this.close()
}
}, this.duration)
}
},
// clear timer
clearTimer() {
clearTimeout(this.timer)
this.timer = null
},
// Event handlers
click() {
this?.onClick()
},
close() {
this.visible = false
this.closed = true
this.timer = null
},
keydown({ code }: KeyboardEvent) {
if (code === EVENT_CODE.delete || code === EVENT_CODE.backspace) {
this.clearTimer() // press delete/backspace clear timer
} else if (code === EVENT_CODE.esc) {
// press esc to close the notification
if (!this.closed) {
this.close()
}
} else {
this.startTimer() // resume timer
}
},
},
})
</script>