File: /home/imensosw/.trash/node_modules/vue-notification/src/Notifications.vue
<template>
<div
class="vue-notification-group"
:style="styles"
>
<component
:is="componentName"
:name="animationName"
@enter="enter"
@leave="leave"
@after-leave="clean"
>
<div
v-for="item in active"
class="vue-notification-wrapper"
:style="notifyWrapperStyle(item)"
:key="item.id"
:data-id="item.id"
>
<slot
name="body"
:class="[classes, item.type]"
:item="item"
:close="() => destroy(item)"
>
<!-- Default slot template -->
<div
:class="notifyClass(item)"
@click="destroyIfNecessary(item)"
>
<div
v-if="item.title"
class="notification-title"
v-html="item.title"
>
</div>
<div
class="notification-content"
v-html="item.text"
>
</div>
</div>
</slot>
</div>
</component>
</div>
</template>
<script>
import plugin from './index'
import { events } from './events'
import { Id, split, listToDirection } from './util'
import defaults from './defaults'
import VelocityGroup from './VelocityGroup.vue'
import CssGroup from './CssGroup.vue'
import parseNumericValue from './parser'
const STATE = {
IDLE: 0,
DESTROYED: 2
}
const Component = {
name: 'Notifications',
components: {
VelocityGroup,
CssGroup
},
props: {
group: {
type: String,
default: ''
},
width: {
type: [Number, String],
default: 300
},
reverse: {
type: Boolean,
default: false
},
position: {
type: [String, Array],
default: () => {
return defaults.position
}
},
classes: {
type: String,
default: 'vue-notification'
},
animationType: {
type: String,
default: 'css',
validator (value) {
return value === 'css' || value === 'velocity'
}
},
animation: {
type: Object,
default () {
return defaults.velocityAnimation
}
},
animationName: {
type: String,
default: defaults.cssAnimation
},
speed: {
type: Number,
default: 300
},
/* Todo */
cooldown: {
type: Number,
default: 0
},
duration: {
type: Number,
default: 3000
},
delay: {
type: Number,
default: 0
},
max: {
type: Number,
default: Infinity
},
ignoreDuplicates: {
type: Boolean,
default: false
},
closeOnClick: {
type: Boolean,
default: true
}
},
data () {
return {
list: [],
velocity: plugin.params.velocity
}
},
mounted () {
events.$on('add', this.addItem);
events.$on('close', this.closeItem);
},
computed: {
actualWidth () {
return parseNumericValue(this.width)
},
/**
* isVelocityAnimation
*/
isVA () {
return this.animationType === 'velocity'
},
componentName () {
return this.isVA
? 'VelocityGroup'
: 'CssGroup'
},
styles () {
const { x, y } = listToDirection(this.position)
const width = this.actualWidth.value
const suffix = this.actualWidth.type
let styles = {
width: width + suffix,
[y]: '0px'
}
if (x === 'center') {
styles['left'] = `calc(50% - ${width/2}${suffix})`
} else {
styles[x] = '0px'
}
return styles
},
active () {
return this.list.filter(v => v.state !== STATE.DESTROYED)
},
botToTop () {
return this.styles.hasOwnProperty('bottom')
},
},
methods: {
destroyIfNecessary (item) {
if (this.closeOnClick) {
this.destroy(item)
}
},
addItem (event) {
event.group = event.group || ''
if (this.group !== event.group) {
return
}
if (event.clean || event.clear) {
this.destroyAll()
return
}
const duration = typeof event.duration === 'number'
? event.duration
: this.duration
const speed = typeof event.speed === 'number'
? event.speed
: this.speed
const ignoreDuplicates = typeof event.ignoreDuplicates === 'boolean'
? event.ignoreDuplicates
: this.ignoreDuplicates
let { title, text, type, data, id } = event
const item = {
id: id || Id(),
title,
text,
type,
state: STATE.IDLE,
speed,
length: duration + 2 * speed,
data
}
if (duration >= 0) {
item.timer = setTimeout(() => {
this.destroy(item)
}, item.length)
}
let direction = this.reverse
? !this.botToTop
: this.botToTop
let indexToDestroy = -1
const isDuplicate = this.active.some(item => {
return item.title === event.title && item.text === event.text
});
const canAdd = ignoreDuplicates ? !isDuplicate : true;
if (!canAdd) return;
if (direction) {
this.list.push(item)
if (this.active.length > this.max) {
indexToDestroy = 0
}
} else {
this.list.unshift(item)
if (this.active.length > this.max) {
indexToDestroy = this.active.length - 1
}
}
if (indexToDestroy !== -1) {
this.destroy(this.active[indexToDestroy])
}
},
closeItem (id) {
this.destroyById(id)
},
notifyClass (item) {
return [
'vue-notification-template',
this.classes,
item.type
]
},
notifyWrapperStyle (item) {
return this.isVA
? null
: { transition: `all ${item.speed}ms` }
},
destroy (item) {
clearTimeout(item.timer)
item.state = STATE.DESTROYED
if (!this.isVA) {
this.clean()
}
},
destroyById (id) {
const item = this.list.find(v => v.id === id)
if (item) {
this.destroy(item)
}
},
destroyAll () {
this.active.forEach(this.destroy)
},
getAnimation (index, el) {
const animation = this.animation[index]
return typeof animation === 'function'
? animation.call(this, el)
: animation
},
enter ({ el, complete }) {
const animation = this.getAnimation('enter', el)
this.velocity(el, animation, {
duration: this.speed,
complete
})
},
leave ({ el, complete }) {
let animation = this.getAnimation('leave', el)
this.velocity(el, animation, {
duration: this.speed,
complete
})
},
clean () {
this.list = this.list.filter(v => v.state !== STATE.DESTROYED)
}
}
}
export default Component
</script>
<style>
.vue-notification-group {
display: block;
position: fixed;
z-index: 5000;
}
.vue-notification-wrapper {
display: block;
overflow: hidden;
width: 100%;
margin: 0;
padding: 0;
}
.notification-title {
font-weight: 600;
}
.vue-notification-template {
display: block;
box-sizing: border-box;
background: white;
text-align: left;
}
.vue-notification {
display: block;
box-sizing: border-box;
text-align: left;
font-size: 12px;
padding: 10px;
margin: 0 5px 5px;
color: white;
background: #44A4FC;
border-left: 5px solid #187FE7;
}
.vue-notification.warn {
background: #ffb648;
border-left-color: #f48a06;
}
.vue-notification.error {
background: #E54D42;
border-left-color: #B82E24;
}
.vue-notification.success {
background: #68CD86;
border-left-color: #42A85F;
}
.vn-fade-enter-active, .vn-fade-leave-active, .vn-fade-move {
transition: all .5s;
}
.vn-fade-enter, .vn-fade-leave-to {
opacity: 0;
}
</style>