208 lines
5.1 KiB
Vue
Raw Normal View History

2019-11-16 10:21:14 +03:00
<template>
<div
@click="tryClose"
data-notify="container"
class="alert alert-notify alert-dismissible"
:class="[
{ 'alert-with-icon': icon },
verticalAlign,
horizontalAlign,
alertType
]"
role="alert"
:style="customPosition"
data-notify-position="top-center"
>
<template v-if="icon || $slots.icon">
<slot name="icon">
<span class="alert-icon" data-notify="icon">
<i :class="icon"></i>
</span>
</slot>
</template>
<span class="alert-text">
<span v-if="title" class="title">
<b>{{ title }}<br/></b>
</span>
<span v-if="message" v-html="message"></span>
<content-render
v-if="!message && component"
:component="component"
></content-render>
</span>
<slot name="dismiss-icon">
<button type="button"
class="close"
data-dismiss="alert"
aria-label="Close"
@click="close">
<span aria-hidden="true">×</span>
</button>
</slot>
</div>
</template>
<script>
export default {
name: 'notification',
components: {
contentRender: {
props: ['component'],
render: h => h(this.component)
}
},
props: {
message: String,
title: {
type: String,
description: 'Notification title'
},
icon: {
type: String,
description: 'Notification icon'
},
verticalAlign: {
type: String,
default: 'top',
validator: value => {
let acceptedValues = ['top', 'bottom'];
return acceptedValues.indexOf(value) !== -1;
},
description: 'Vertical alignment of notification (top|bottom)'
},
horizontalAlign: {
type: String,
default: 'right',
validator: value => {
let acceptedValues = ['left', 'center', 'right'];
return acceptedValues.indexOf(value) !== -1;
},
description: 'Horizontal alignment of notification (left|center|right)'
},
type: {
type: String,
default: 'info',
validator: value => {
let acceptedValues = [
'default',
'info',
'primary',
'danger',
'warning',
'success'
];
return acceptedValues.indexOf(value) !== -1;
},
description: 'Notification type of notification (default|info|primary|danger|warning|success)'
},
timeout: {
type: Number,
default: 5000,
validator: value => {
return value >= 0;
},
description: 'Notification timeout (closes after X milliseconds). Default is 5000 (5s)'
},
timestamp: {
type: Date,
default: () => new Date(),
description: 'Notification timestamp (used internally to handle notification removal correctly)'
},
component: {
type: [Object, Function],
description: 'Custom content component. Cane be a `.vue` component or render function'
},
showClose: {
type: Boolean,
default: true,
description: 'Whether to show close button'
},
closeOnClick: {
type: Boolean,
default: true,
description: 'Whether to close notification when clicking it\' body'
},
clickHandler: {
type: Function,
description: 'Custom notification click handler'
}
},
data() {
return {
elmHeight: 0
};
},
computed: {
hasIcon() {
return this.icon && this.icon.length > 0;
},
alertType() {
return `alert-${this.type}`;
},
customPosition() {
let initialMargin = 20;
let alertHeight = this.elmHeight + 10;
let sameAlertsCount = this.$notifications.state.filter(alert => {
return (
alert.horizontalAlign === this.horizontalAlign &&
alert.verticalAlign === this.verticalAlign &&
alert.timestamp <= this.timestamp
);
}).length;
if (this.$notifications.settings.overlap) {
sameAlertsCount = 1;
}
let pixels = (sameAlertsCount - 1) * alertHeight + initialMargin;
let styles = {};
if (this.verticalAlign === 'top') {
styles.top = `${pixels}px`;
} else {
styles.bottom = `${pixels}px`;
}
return styles;
}
},
methods: {
close() {
this.$emit('close', this.timestamp);
},
tryClose(evt) {
if (this.clickHandler) {
this.clickHandler(evt, this);
}
if (this.closeOnClick) {
this.close();
}
}
},
mounted() {
this.elmHeight = this.$el.clientHeight;
if (this.timeout) {
setTimeout(this.close, this.timeout);
}
}
};
</script>
<style lang="scss">
.notifications .alert {
position: fixed;
z-index: 10000;
&[data-notify='container'] {
max-width: 500px;
}
&.center {
margin: 0 auto;
}
&.left {
left: 20px;
}
&.right {
right: 20px;
}
}
</style>