Skip to content

Commit faf96f0

Browse files
feat($Toast): new Vue plugin Vuetify Toast
1 parent 7a44a3f commit faf96f0

File tree

4 files changed

+348
-0
lines changed

4 files changed

+348
-0
lines changed

src/plugins/toast/Toast.vue

Lines changed: 176 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,176 @@
1+
<template>
2+
<v-snackbar :timeout="timeout"
3+
:color="color"
4+
:bottom="y === 'bottom'"
5+
:top="y === 'top'"
6+
:left="x === 'left'"
7+
:right="x === 'right'"
8+
:multi-line="multiLine"
9+
:vertical="vertical"
10+
v-model="active"
11+
class="v-application vts"
12+
:class="classes"
13+
@click="dismiss"
14+
role="alert">
15+
<v-icon dark left v-if="!!icon" class="vts__icon" :color="iconColor">{{ icon }}</v-icon>
16+
<div class="vts__message" :class="{ 'vts__message--padded': showClose && !closeText }">
17+
<div v-html="message"/>
18+
<slot/>
19+
</div>
20+
<v-btn :icon="!closeText"
21+
:text="!!closeText"
22+
class="vts__close"
23+
:class="{ 'vts__close--icon': !closeText }"
24+
:color="closeColor"
25+
v-if="showClose"
26+
@click="close">
27+
<v-icon v-if="!closeText">{{ closeIcon }}</v-icon>
28+
<span v-if="!!closeText">{{ closeText }}</span>
29+
</v-btn>
30+
</v-snackbar>
31+
</template>
32+
33+
<script>
34+
export default {
35+
props: {
36+
x: {
37+
type: String,
38+
default: 'right'
39+
},
40+
y: {
41+
type: String,
42+
default: 'bottom'
43+
},
44+
color: {
45+
type: String,
46+
default: 'info'
47+
},
48+
icon: {
49+
type: String,
50+
default: ''
51+
},
52+
iconColor: {
53+
type: String,
54+
default: ''
55+
},
56+
classes: {
57+
type: [String, Object, Array],
58+
default: ''
59+
},
60+
message: {
61+
type: String,
62+
default: ''
63+
},
64+
timeout: {
65+
type: Number,
66+
default: 3000
67+
},
68+
dismissable: {
69+
type: Boolean,
70+
default: true
71+
},
72+
multiLine: {
73+
type: Boolean,
74+
default: false
75+
},
76+
vertical: {
77+
type: Boolean,
78+
default: false
79+
},
80+
showClose: {
81+
type: Boolean,
82+
default: false
83+
},
84+
closeText: {
85+
type: String,
86+
default: ''
87+
},
88+
closeIcon: {
89+
type: String,
90+
default: 'close'
91+
},
92+
closeColor: {
93+
type: String,
94+
default: ''
95+
}
96+
},
97+
data: () => ({
98+
active: false
99+
}),
100+
mounted () {
101+
this.$nextTick(() => this.show())
102+
},
103+
watch: {
104+
active: function (isActive, wasActive) {
105+
this.$emit('statusChange', isActive, wasActive)
106+
}
107+
},
108+
methods: {
109+
show () {
110+
this.active = true
111+
},
112+
close () {
113+
this.active = false
114+
},
115+
dismiss () {
116+
if (this.dismissable) {
117+
this.close()
118+
}
119+
}
120+
}
121+
}
122+
</script>
123+
124+
<!--suppress CssUnusedSymbol -->
125+
<style>
126+
.vts {
127+
max-width: none !important;
128+
width: auto !important;
129+
}
130+
131+
.vts .v-snack__content {
132+
justify-content: flex-start;
133+
}
134+
135+
.vts__icon {
136+
margin-right: 12px;
137+
}
138+
139+
.vts__message {
140+
margin-right: auto;
141+
}
142+
143+
.vts__close {
144+
margin: 0 -8px 0 24px !important;
145+
justify-self: flex-end;
146+
}
147+
148+
.vts.v-snack--vertical .vts__icon {
149+
margin: 0 0 12px !important;
150+
}
151+
152+
.vts.v-snack--vertical .v-snack__content {
153+
padding-bottom: 16px !important;
154+
}
155+
156+
.vts.v-snack--vertical .vts__message--padded {
157+
padding: 12px 0 0;
158+
}
159+
160+
.vts.v-snack--vertical .vts__icon + .vts__message {
161+
padding-top: 0;
162+
}
163+
164+
.vts.v-snack--vertical .vts__close {
165+
margin: 12px 0 -8px !important;
166+
}
167+
168+
.vts.v-snack--vertical .vts__close--icon {
169+
margin: 0 !important;
170+
position: absolute;
171+
right: 4px;
172+
top: 4px;
173+
transform: scale(0.75);
174+
padding: 4px !important;
175+
}
176+
</style>

src/plugins/toast/index.d.ts

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
// eslint-disable-next-line no-unused-vars
2+
import { PluginFunction } from 'vue'
3+
4+
export interface VuetifyToastUseOptions extends VuetifyToastObject {
5+
property?: string
6+
}
7+
8+
declare const VuetifyToastPlugin: VuetifyToastPlugin
9+
export default VuetifyToastPlugin
10+
11+
export interface VuetifyToastPlugin {
12+
install: PluginFunction<VuetifyToastUseOptions>
13+
}
14+
15+
export interface VuetifyToastObject {
16+
x?: string
17+
y?: string
18+
color?: string
19+
icon?: string
20+
iconColor?: string
21+
classes?: string | object | Array<object | string>
22+
timeout?: number
23+
dismissable?: boolean
24+
multiLine?: boolean
25+
vertical?: boolean
26+
showClose?: boolean
27+
closeText?: string
28+
closeIcon?: string
29+
closeColor?: string
30+
queueable?: boolean
31+
slot?: Array<any>
32+
shorts?: any
33+
}
34+
35+
interface VuetifyToastShow {
36+
(message: string, options?: VuetifyToastObject): void
37+
}
38+
39+
interface VuetifyToastMethods extends VuetifyToastShow {
40+
getCmp: any
41+
clearQueue: any
42+
43+
[key: string]: VuetifyToastShow
44+
}
45+
46+
declare module 'vue/types/vue' {
47+
interface Vue {
48+
$toast: VuetifyToastMethods
49+
}
50+
}

src/plugins/toast/index.js

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
import Toast from './Toast.vue'
2+
3+
function init (Vue, globalOptions = {}) {
4+
let cmp = null
5+
const queue = []
6+
const property = globalOptions.property || '$toast'
7+
8+
function createCmp (options) {
9+
const component = new Vue(Toast)
10+
const componentOptions = { ...Vue.prototype[property].globalOptions, ...options }
11+
12+
if (componentOptions.slot) {
13+
component.$slots.default = componentOptions.slot
14+
delete componentOptions.slot
15+
}
16+
17+
Object.assign(component, componentOptions)
18+
document.body.appendChild(component.$mount().$el)
19+
20+
return component
21+
}
22+
23+
function show (message, options = {}) {
24+
if (cmp) {
25+
const isQueueable = options.queueable !== undefined ? options.queueable : globalOptions.queueable
26+
27+
if (isQueueable) {
28+
queue.push({ message, options })
29+
} else {
30+
cmp.close()
31+
queue.unshift({ message, options })
32+
}
33+
34+
return
35+
}
36+
37+
options.message = message
38+
cmp = createCmp(options)
39+
cmp.$on('statusChange', (isActive, wasActive) => {
40+
if (wasActive && !isActive) {
41+
cmp.$nextTick(() => {
42+
cmp.$destroy()
43+
cmp = null
44+
45+
if (queue.length) {
46+
const toast = queue.shift()
47+
show(toast.message, toast.options)
48+
}
49+
})
50+
}
51+
})
52+
}
53+
54+
function shorts (options) {
55+
const colors = ['success', 'info', 'error', 'warning']
56+
const methods = {}
57+
58+
colors.forEach(color => {
59+
methods[color] = (message, options) => show(message, { color, ...options })
60+
})
61+
62+
if (options.shorts) {
63+
for (const key in options.shorts) {
64+
// noinspection JSUnfilteredForInLoop
65+
const localOptions = options.shorts[key]
66+
// noinspection JSUnfilteredForInLoop
67+
methods[key] = (message, options) => show(message, { ...localOptions, ...options })
68+
}
69+
}
70+
71+
return methods
72+
}
73+
74+
function getCmp () {
75+
return cmp
76+
}
77+
78+
function clearQueue () {
79+
return queue.splice(0, queue.length)
80+
}
81+
82+
// noinspection JSUnusedGlobalSymbols
83+
Vue.prototype[property] = Object.assign(show, {
84+
globalOptions,
85+
getCmp,
86+
clearQueue,
87+
...shorts(globalOptions)
88+
})
89+
}
90+
91+
if (typeof window !== 'undefined' && window.Vue) {
92+
window.Vue.use(init)
93+
}
94+
95+
export default init

src/plugins/vuetify/index.ts

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,35 @@ import { darkTheme, lightTheme } from '@/plugins/vuetify/theme'
55
import Afrikaans from '@/i18n/afrikaans'
66
import English from '@/i18n/english'
77
import SimplifiedChinese from '@/i18n/simplified-chinese'
8+
import VuetifyToast from '@/plugins/toast'
89

910
Vue.use(Vuetify)
11+
Vue.use(VuetifyToast, {
12+
x: 'center',
13+
y: 'top',
14+
color: 'info',
15+
icon: 'info',
16+
iconColor: '',
17+
classes: [
18+
'body-2'
19+
],
20+
timeout: 5000,
21+
dismissable: false,
22+
multiLine: false,
23+
vertical: false,
24+
queueable: false,
25+
showClose: false,
26+
closeText: '',
27+
closeIcon: 'close',
28+
closeColor: '',
29+
slot: [],
30+
shorts: {
31+
custom: {
32+
// color: 'purple'
33+
}
34+
},
35+
property: '$toast'
36+
})
1037

1138
export default new Vuetify({
1239
lang: {

0 commit comments

Comments
 (0)