1
1
<template >
2
- <transition name =" va-toast-fade" >
2
+ <Transition name =" va-toast-fade" @after-leave = " onHidden " >
3
3
<div
4
4
v-show =" visible"
5
5
ref =" rootElement"
38
38
/>
39
39
</div >
40
40
</div >
41
- </transition >
41
+ </Transition >
42
42
</template >
43
43
44
44
<script lang="ts">
@@ -47,6 +47,7 @@ import { PropType, ref, computed, onMounted, shallowRef, defineComponent, Comput
47
47
import { useComponentPresetProp , useColors , useTimer , useTextColor , useTranslation , useTranslationProp , useNumericProp } from ' ../../composables'
48
48
49
49
import { ToastPosition } from ' ./types'
50
+ import { useToastService } from ' ./hooks/useToastService'
50
51
51
52
import { StringWithAutocomplete } from ' ../../utils/types/prop-type'
52
53
</script >
@@ -78,15 +79,15 @@ const props = defineProps({
78
79
icon: { type: String , default: ' close' },
79
80
customClass: { type: String , default: ' ' },
80
81
duration: { type: [Number , String ], default: 5000 },
81
- color: { type: String , default: ' ' },
82
+ color: { type: String , default: ' primary ' },
82
83
closeable: { type: Boolean , default: true },
83
84
onClose: { type: Function },
84
85
onClick: { type: Function },
85
86
multiLine: { type: Boolean , default: false },
86
87
position: {
87
88
type: String as PropType <ToastPosition >,
88
89
default: ' top-right' ,
89
- validator : (value : string ) => [' top-right' , ' top-left' , ' bottom-right' , ' bottom-left' ].includes (value ),
90
+ validator : (value : string ) => [' top-right' , ' top-center ' , ' top- left' , ' bottom-right' , ' bottom-center ' , ' bottom-left' ].includes (value ),
90
91
},
91
92
render: { type: Function },
92
93
ariaCloseLabel: useTranslationProp (' $t:close' ),
@@ -107,20 +108,30 @@ const durationComputed = useNumericProp('duration') as ComputedRef<number>
107
108
108
109
const visible = ref (false )
109
110
111
+ const {
112
+ yOffset,
113
+ updateYOffset,
114
+ } = useToastService (props )
115
+
116
+ const positionObject = computed (() => ({
117
+ vertical: props .position .includes (' top' ) ? ' top' : ' bottom' ,
118
+ horizontal: props .position .includes (' center' ) ? ' center' : props .position .includes (' right' ) ? ' right' : ' left' ,
119
+ }))
120
+
110
121
const getPositionStyle = () => {
111
- const vertical = props . position . includes ( ' top ' ) ? ' top ' : ' bottom '
112
- const horizontal = props . position . includes ( ' center ' ) ? ' center ' : props . position . includes ( ' right ' ) ? ' right ' : ' left '
122
+ const vertical = positionObject . value . vertical
123
+ const horizontal = positionObject . value . horizontal
113
124
114
125
if (horizontal === ' center' ) {
115
126
return {
116
- [vertical ]: ` ${offsetYComputed .value }px ` ,
127
+ [vertical ]: ` ${offsetYComputed .value + yOffset . value }px ` ,
117
128
left: ' 50%' ,
118
- transform : ' translateX( -50%) ' ,
129
+ ' --va-toast-x-shift ' : ' -50%' ,
119
130
}
120
131
}
121
132
122
133
return {
123
- [vertical ]: ` ${offsetYComputed .value }px ` ,
134
+ [vertical ]: ` ${offsetYComputed .value + yOffset . value }px ` ,
124
135
[horizontal ]: ` ${offsetXComputed .value }px ` ,
125
136
}
126
137
}
@@ -129,6 +140,7 @@ const toastClasses = computed(() => [
129
140
props .customClass ,
130
141
props .multiLine ? ' va-toast--multiline' : ' ' ,
131
142
props .inline ? ' va-toast--inline' : ' ' ,
143
+ [` va-toast--${props .position } ` ],
132
144
])
133
145
134
146
const toastStyles = computed (() => ({
@@ -163,14 +175,16 @@ const onToastClick = () => {
163
175
164
176
const onToastClose = () => {
165
177
visible .value = false
178
+ updateYOffset ()
179
+ }
166
180
167
- rootElement .value ?.addEventListener (' transitionend' , destroyElement )
168
-
181
+ const onHidden = () => {
169
182
if (typeof props .onClose === ' function' ) {
170
183
props .onClose ()
171
184
} else {
172
185
emit (' on-close' )
173
186
}
187
+ destroyElement ()
174
188
}
175
189
176
190
const timer = useTimer ()
@@ -193,6 +207,10 @@ onMounted(() => {
193
207
@import " variables" ;
194
208
195
209
.va-toast {
210
+ --va-toast-x-shift : 0px ;
211
+ --va-toast-animation-x-shift : 0px ;
212
+ --va-toast-animation-y-shift : 100% ;
213
+
196
214
position : fixed ;
197
215
box-sizing : border-box ;
198
216
width : var (--va-toast-width );
@@ -207,26 +225,30 @@ onMounted(() => {
207
225
overflow : hidden ;
208
226
z-index : var (--va-toast-z-index );
209
227
font-family : var (--va-font-family );
228
+ transform : translateX (var (--va-toast-x-shift ));
210
229
211
- & --inline {
212
- position : static ;
230
+ & --top-right ,
231
+ & --bottom-right {
232
+ --va-toast-animation-x-shift : 100% ;
213
233
}
214
234
215
- & --multiline {
216
- min-height : 70px ;
235
+ & --top-left ,
236
+ & --bottom-left {
237
+ --va-toast-animation-x-shift : -100% ;
217
238
}
218
239
219
- & --right {
220
- right : 16px ;
240
+ & --top-left ,
241
+ & --top-center ,
242
+ & --top-right {
243
+ --va-toast-animation-y-shift : -100% ;
221
244
}
222
245
223
- & --left {
224
- left : 16 px ;
246
+ & --inline {
247
+ position : static ;
225
248
}
226
249
227
- & __group {
228
- margin-left : var (--va-toast-group-margin-left );
229
- margin-right : var (--va-toast-group-margin-right );
250
+ & --multiline {
251
+ min-height : 70px ;
230
252
}
231
253
232
254
& __title {
@@ -269,19 +291,14 @@ onMounted(() => {
269
291
}
270
292
}
271
293
272
- .va-toast-fade-enter {
273
- & .right {
274
- right : 0 ;
275
- transform : translateX (100% );
294
+ .va-toast-fade {
295
+ & -enter-from {
296
+ transform : translateX (calc (var (--va-toast-animation-x-shift ) + var (--va-toast-x-shift )));
276
297
}
277
298
278
- & .left {
279
- left : 0 ;
280
- transform : translateX ( -100 % ) ;
299
+ & -leave-to {
300
+ transform : translateY ( var ( --va-toast-animation-y-shift )) ;
301
+ opacity : 0 ;
281
302
}
282
303
}
283
-
284
- .va-toast-fade-leave-active {
285
- opacity : 0 ;
286
- }
287
304
</style >
0 commit comments