2020-08-04 19:03:20 +08:00
|
|
|
<template>
|
2021-01-28 19:19:28 +08:00
|
|
|
<transition name="el-notification-fade" @after-leave="onClose">
|
2020-08-04 19:03:20 +08:00
|
|
|
<div
|
2020-08-06 17:53:58 +08:00
|
|
|
v-show="visible"
|
2020-08-04 19:03:20 +08:00
|
|
|
: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 }"
|
|
|
|
>
|
2020-08-04 19:03:20 +08:00
|
|
|
<h2 class="el-notification__title" v-text="title"></h2>
|
2021-01-18 20:38:34 +08:00
|
|
|
<div v-show="message" class="el-notification__content" :style="!!title ? null : 'margin: 0'">
|
2020-08-04 19:03:20 +08:00
|
|
|
<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>
|
2020-08-04 19:03:20 +08:00
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</transition>
|
|
|
|
</template>
|
|
|
|
<script lang="ts">
|
2020-08-19 17:05:56 +08:00
|
|
|
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'
|
2020-09-22 21:01:10 +08:00
|
|
|
import { EVENT_CODE } from '../../utils/aria'
|
2020-08-04 19:03:20 +08:00
|
|
|
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 },
|
2020-08-04 19:03:20 +08:00
|
|
|
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',
|
|
|
|
},
|
2020-08-04 19:03:20 +08:00
|
|
|
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 },
|
2020-08-04 19:03:20 +08:00
|
|
|
},
|
|
|
|
emits: ['close', 'click'],
|
|
|
|
setup(props) {
|
|
|
|
|
2020-08-06 00:09:09 +08:00
|
|
|
const typeClass = computed(() => {
|
|
|
|
const type = props.type
|
2020-08-04 19:03:20 +08:00
|
|
|
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-04 19:03:20 +08:00
|
|
|
})
|
|
|
|
|
2020-08-06 00:09:09 +08:00
|
|
|
const verticalProperty = computed(() => {
|
|
|
|
return props.position.startsWith('top') ? 'top' : 'bottom'
|
2020-08-04 19:03:20 +08:00
|
|
|
})
|
|
|
|
|
2020-08-06 00:09:09 +08:00
|
|
|
const positionStyle = computed(() => {
|
|
|
|
return {
|
|
|
|
[verticalProperty.value]: `${props.offset}px`,
|
|
|
|
}
|
2020-08-04 19:03:20 +08:00
|
|
|
})
|
|
|
|
|
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-04 19:03:20 +08:00
|
|
|
|
2020-08-06 00:09:09 +08:00
|
|
|
return {
|
|
|
|
horizontalClass,
|
|
|
|
typeClass,
|
|
|
|
positionStyle,
|
|
|
|
verticalProperty,
|
|
|
|
visible,
|
|
|
|
closed,
|
|
|
|
timer,
|
|
|
|
}
|
2020-08-04 19:03:20 +08:00
|
|
|
},
|
|
|
|
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
|
2020-08-04 19:03:20 +08:00
|
|
|
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() {
|
2021-01-28 19:19:28 +08:00
|
|
|
this.visible = false
|
2020-08-04 19:03:20 +08:00
|
|
|
this.closed = true
|
|
|
|
this.timer = null
|
|
|
|
},
|
2020-09-22 21:01:10 +08:00
|
|
|
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) {
|
2020-08-04 19:03:20 +08:00
|
|
|
// press esc to close the notification
|
|
|
|
if (!this.closed) {
|
|
|
|
this.close()
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
this.startTimer() // resume timer
|
|
|
|
}
|
|
|
|
},
|
|
|
|
},
|
|
|
|
})
|
|
|
|
</script>
|