vue-toastflow 0.0.5 → 0.0.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +16 -2
- package/src/components/Toast.vue +658 -658
- package/src/components/ToastContainer.vue +352 -352
- package/src/toast.ts +9 -9
|
@@ -1,352 +1,352 @@
|
|
|
1
|
-
<script setup lang="ts">
|
|
2
|
-
import { computed, inject, onMounted, onUnmounted, ref, watch } from "vue";
|
|
3
|
-
import Toast from "./Toast.vue";
|
|
4
|
-
import type {
|
|
5
|
-
ToastAnimation,
|
|
6
|
-
ToastConfig,
|
|
7
|
-
ToastId,
|
|
8
|
-
ToastInstance,
|
|
9
|
-
ToastPosition,
|
|
10
|
-
ToastStore,
|
|
11
|
-
} from "toastflow-core";
|
|
12
|
-
import { toastStoreKey } from "../symbols";
|
|
13
|
-
import { getToastStore } from "../toast";
|
|
14
|
-
|
|
15
|
-
const positions: ToastPosition[] = [
|
|
16
|
-
"top-left",
|
|
17
|
-
"top-center",
|
|
18
|
-
"top-right",
|
|
19
|
-
"bottom-left",
|
|
20
|
-
"bottom-center",
|
|
21
|
-
"bottom-right",
|
|
22
|
-
];
|
|
23
|
-
|
|
24
|
-
const injectedStore = inject<ToastStore | null>(toastStoreKey, null);
|
|
25
|
-
const store: ToastStore = injectedStore ?? getToastStore();
|
|
26
|
-
|
|
27
|
-
const toasts = ref<ToastInstance[]>([]);
|
|
28
|
-
|
|
29
|
-
// event-driven keys
|
|
30
|
-
const progressResetMap = ref<Record<ToastId, number>>({});
|
|
31
|
-
const duplicateMap = ref<Record<ToastId, number>>({});
|
|
32
|
-
const updateMap = ref<Record<ToastId, number>>({});
|
|
33
|
-
|
|
34
|
-
let unsubscribeState: (() => void) | null = null;
|
|
35
|
-
let unsubscribeEvents: (() => void) | null = null;
|
|
36
|
-
|
|
37
|
-
onMounted(function () {
|
|
38
|
-
unsubscribeState = store.subscribe(function (state) {
|
|
39
|
-
toasts.value = state.toasts;
|
|
40
|
-
});
|
|
41
|
-
|
|
42
|
-
unsubscribeEvents = store.subscribeEvents(function (event) {
|
|
43
|
-
if (event.kind === "timer-reset") {
|
|
44
|
-
progressResetMap.value[event.id] = Math.random();
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
if (event.kind === "duplicate") {
|
|
48
|
-
duplicateMap.value[event.id] = Math.random();
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
if (event.kind === "update") {
|
|
52
|
-
updateMap.value[event.id] = Math.random();
|
|
53
|
-
}
|
|
54
|
-
});
|
|
55
|
-
});
|
|
56
|
-
|
|
57
|
-
onUnmounted(function () {
|
|
58
|
-
if (unsubscribeState) {
|
|
59
|
-
unsubscribeState();
|
|
60
|
-
}
|
|
61
|
-
if (unsubscribeEvents) {
|
|
62
|
-
unsubscribeEvents();
|
|
63
|
-
}
|
|
64
|
-
});
|
|
65
|
-
|
|
66
|
-
function getProgressResetKey(id: ToastId): number {
|
|
67
|
-
return progressResetMap.value[id] ?? 0;
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
function getDuplicateKey(id: ToastId): number {
|
|
71
|
-
return duplicateMap.value[id] ?? 0;
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
function getUpdateKey(id: ToastId): number {
|
|
75
|
-
return updateMap.value[id] ?? 0;
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
const grouped = computed(function () {
|
|
79
|
-
const byPos: Record<ToastPosition, ToastInstance[]> = {
|
|
80
|
-
"top-left": [],
|
|
81
|
-
"top-center": [],
|
|
82
|
-
"top-right": [],
|
|
83
|
-
"bottom-left": [],
|
|
84
|
-
"bottom-center": [],
|
|
85
|
-
"bottom-right": [],
|
|
86
|
-
};
|
|
87
|
-
|
|
88
|
-
for (const toast of toasts.value) {
|
|
89
|
-
byPos[toast.position].push(toast);
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
return byPos;
|
|
93
|
-
});
|
|
94
|
-
|
|
95
|
-
const baseConfig: ToastConfig = store.getConfig();
|
|
96
|
-
const stackConfigs = ref<Record<ToastPosition, ToastConfig>>({
|
|
97
|
-
"top-left": { ...baseConfig, position: "top-left" },
|
|
98
|
-
"top-center": { ...baseConfig, position: "top-center" },
|
|
99
|
-
"top-right": { ...baseConfig, position: "top-right" },
|
|
100
|
-
"bottom-left": { ...baseConfig, position: "bottom-left" },
|
|
101
|
-
"bottom-center": { ...baseConfig, position: "bottom-center" },
|
|
102
|
-
"bottom-right": { ...baseConfig, position: "bottom-right" },
|
|
103
|
-
});
|
|
104
|
-
|
|
105
|
-
const globalZIndex = computed(function () {
|
|
106
|
-
if (!toasts.value.length) {
|
|
107
|
-
return baseConfig.zIndex;
|
|
108
|
-
}
|
|
109
|
-
return Math.max(...toasts.value.map((toast) => toast.zIndex));
|
|
110
|
-
});
|
|
111
|
-
|
|
112
|
-
function stackConfig(position: ToastPosition): ToastConfig {
|
|
113
|
-
return stackConfigs.value[position] ?? { ...baseConfig, position };
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
function stackStyle(position: ToastPosition): Record<string, string> {
|
|
117
|
-
const { offset, width } = stackConfig(position);
|
|
118
|
-
// Lock the stack width, so it doesn't collapse when leaving items get absolute-positioned
|
|
119
|
-
const style: Record<string, string> = { width, maxWidth: "100%" };
|
|
120
|
-
|
|
121
|
-
if (position.startsWith("top-")) {
|
|
122
|
-
style.top = offset;
|
|
123
|
-
}
|
|
124
|
-
if (position.startsWith("bottom-")) {
|
|
125
|
-
style.bottom = offset;
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
if (position.endsWith("left")) {
|
|
129
|
-
style.left = offset;
|
|
130
|
-
} else if (position.endsWith("right")) {
|
|
131
|
-
style.right = offset;
|
|
132
|
-
} else if (position.endsWith("center")) {
|
|
133
|
-
style.left = "50%";
|
|
134
|
-
style.transform = "translateX(-50%)";
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
return style;
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
function stackAlignClass(position: ToastPosition): string {
|
|
141
|
-
if (position.endsWith("left")) {
|
|
142
|
-
return "tf-toast-stack--left";
|
|
143
|
-
}
|
|
144
|
-
if (position.endsWith("center")) {
|
|
145
|
-
return "tf-toast-stack--center";
|
|
146
|
-
}
|
|
147
|
-
return "tf-toast-stack--right";
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
function stackAxisClass(position: ToastPosition): string | null {
|
|
151
|
-
if (position.startsWith("bottom-")) {
|
|
152
|
-
return "tf-toast-stack-inner--bottom";
|
|
153
|
-
}
|
|
154
|
-
return null;
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
function handleDismiss(id: ToastId) {
|
|
158
|
-
store.dismiss(id);
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
function beforeLeave(el: Element) {
|
|
162
|
-
const element = el as HTMLElement;
|
|
163
|
-
const parent = element.parentElement;
|
|
164
|
-
if (!parent || parent.children.length <= 1) {
|
|
165
|
-
return;
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
const top = element.offsetTop;
|
|
169
|
-
const parentHeight = parent.clientHeight;
|
|
170
|
-
const parentWidth = parent.clientWidth;
|
|
171
|
-
|
|
172
|
-
parent.style.minHeight = `${parentHeight}px`;
|
|
173
|
-
element.style.position = "absolute";
|
|
174
|
-
element.style.width = `${parentWidth}px`;
|
|
175
|
-
element.style.left = "0";
|
|
176
|
-
element.style.right = "0";
|
|
177
|
-
element.style.top = `${top}px`;
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
function afterLeave(el: Element) {
|
|
181
|
-
const element = el as HTMLElement;
|
|
182
|
-
const parent = element.parentElement;
|
|
183
|
-
if (parent) {
|
|
184
|
-
parent.style.minHeight = "";
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
element.style.position = "";
|
|
188
|
-
element.style.width = "";
|
|
189
|
-
element.style.left = "";
|
|
190
|
-
element.style.right = "";
|
|
191
|
-
element.style.top = "";
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
watch(
|
|
195
|
-
toasts,
|
|
196
|
-
function (current) {
|
|
197
|
-
const ids = new Set(
|
|
198
|
-
current.map(function (toast) {
|
|
199
|
-
return toast.id;
|
|
200
|
-
}),
|
|
201
|
-
);
|
|
202
|
-
|
|
203
|
-
for (const key of Object.keys(progressResetMap.value)) {
|
|
204
|
-
if (!ids.has(key as ToastId)) {
|
|
205
|
-
delete progressResetMap.value[key as ToastId];
|
|
206
|
-
}
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
for (const key of Object.keys(duplicateMap.value)) {
|
|
210
|
-
if (!ids.has(key as ToastId)) {
|
|
211
|
-
delete duplicateMap.value[key as ToastId];
|
|
212
|
-
}
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
for (const key of Object.keys(updateMap.value)) {
|
|
216
|
-
if (!ids.has(key as ToastId)) {
|
|
217
|
-
delete updateMap.value[key as ToastId];
|
|
218
|
-
}
|
|
219
|
-
}
|
|
220
|
-
},
|
|
221
|
-
{ deep: false },
|
|
222
|
-
);
|
|
223
|
-
|
|
224
|
-
function animationForToast(toast: ToastInstance): Partial<ToastAnimation> {
|
|
225
|
-
return {
|
|
226
|
-
...baseConfig.animation,
|
|
227
|
-
...toast.animation,
|
|
228
|
-
};
|
|
229
|
-
}
|
|
230
|
-
|
|
231
|
-
watch(
|
|
232
|
-
grouped,
|
|
233
|
-
function (byPos) {
|
|
234
|
-
const next = { ...stackConfigs.value };
|
|
235
|
-
|
|
236
|
-
(Object.keys(byPos) as ToastPosition[]).forEach(function (position) {
|
|
237
|
-
const first = byPos[position][0];
|
|
238
|
-
if (first) {
|
|
239
|
-
next[position] = {
|
|
240
|
-
...baseConfig,
|
|
241
|
-
...first,
|
|
242
|
-
position,
|
|
243
|
-
animation: {
|
|
244
|
-
...baseConfig.animation,
|
|
245
|
-
...first.animation,
|
|
246
|
-
},
|
|
247
|
-
};
|
|
248
|
-
}
|
|
249
|
-
});
|
|
250
|
-
|
|
251
|
-
stackConfigs.value = next;
|
|
252
|
-
},
|
|
253
|
-
{ deep: false },
|
|
254
|
-
);
|
|
255
|
-
</script>
|
|
256
|
-
|
|
257
|
-
<template>
|
|
258
|
-
<div class="tf-toast-root" :style="{ zIndex: globalZIndex }">
|
|
259
|
-
<div
|
|
260
|
-
v-for="position in positions"
|
|
261
|
-
:key="position"
|
|
262
|
-
class="tf-toast-stack"
|
|
263
|
-
:class="stackAlignClass(position)"
|
|
264
|
-
:style="stackStyle(position)"
|
|
265
|
-
>
|
|
266
|
-
<TransitionGroup
|
|
267
|
-
:name="stackConfig(position).animation.name"
|
|
268
|
-
@before-leave="beforeLeave"
|
|
269
|
-
@after-leave="afterLeave"
|
|
270
|
-
tag="div"
|
|
271
|
-
:class="['tf-toast-stack-inner', stackAxisClass(position)]"
|
|
272
|
-
:style="{ gap: stackConfig(position).gap }"
|
|
273
|
-
>
|
|
274
|
-
<div
|
|
275
|
-
v-for="toast in grouped[position]"
|
|
276
|
-
:key="toast.id"
|
|
277
|
-
class="tf-toast-item"
|
|
278
|
-
:style="{ width: toast.width, maxWidth: '100%' }"
|
|
279
|
-
:data-position="toast.position"
|
|
280
|
-
>
|
|
281
|
-
<slot
|
|
282
|
-
v-if="$slots.default"
|
|
283
|
-
:toast="toast"
|
|
284
|
-
:progressResetKey="getProgressResetKey(toast.id)"
|
|
285
|
-
:duplicateKey="getDuplicateKey(toast.id)"
|
|
286
|
-
:updateKey="getUpdateKey(toast.id)"
|
|
287
|
-
:bumpAnimationClass="animationForToast(toast).bump"
|
|
288
|
-
:clearAllAnimationClass="animationForToast(toast).clearAll"
|
|
289
|
-
:updateAnimationClass="animationForToast(toast).update"
|
|
290
|
-
:dismiss="handleDismiss"
|
|
291
|
-
/>
|
|
292
|
-
|
|
293
|
-
<Toast
|
|
294
|
-
v-else
|
|
295
|
-
:toast="toast"
|
|
296
|
-
:progressResetKey="getProgressResetKey(toast.id)"
|
|
297
|
-
:duplicateKey="getDuplicateKey(toast.id)"
|
|
298
|
-
:updateKey="getUpdateKey(toast.id)"
|
|
299
|
-
:bumpAnimationClass="animationForToast(toast).bump"
|
|
300
|
-
:clearAllAnimationClass="animationForToast(toast).clearAll"
|
|
301
|
-
:updateAnimationClass="animationForToast(toast).update"
|
|
302
|
-
@dismiss="handleDismiss"
|
|
303
|
-
/>
|
|
304
|
-
</div>
|
|
305
|
-
</TransitionGroup>
|
|
306
|
-
</div>
|
|
307
|
-
</div>
|
|
308
|
-
</template>
|
|
309
|
-
|
|
310
|
-
<style scoped>
|
|
311
|
-
.tf-toast-root {
|
|
312
|
-
pointer-events: none;
|
|
313
|
-
position: fixed;
|
|
314
|
-
inset: 0;
|
|
315
|
-
}
|
|
316
|
-
|
|
317
|
-
.tf-toast-stack {
|
|
318
|
-
position: absolute;
|
|
319
|
-
height: 100%;
|
|
320
|
-
display: flex;
|
|
321
|
-
align-items: flex-start;
|
|
322
|
-
}
|
|
323
|
-
|
|
324
|
-
.tf-toast-stack-inner {
|
|
325
|
-
display: flex;
|
|
326
|
-
flex-direction: column;
|
|
327
|
-
position: relative;
|
|
328
|
-
width: 100%;
|
|
329
|
-
}
|
|
330
|
-
|
|
331
|
-
.tf-toast-stack-inner--bottom {
|
|
332
|
-
min-height: 100%;
|
|
333
|
-
justify-content: flex-end;
|
|
334
|
-
}
|
|
335
|
-
|
|
336
|
-
.tf-toast-stack--left {
|
|
337
|
-
justify-content: flex-start;
|
|
338
|
-
}
|
|
339
|
-
|
|
340
|
-
.tf-toast-stack--center {
|
|
341
|
-
justify-content: center;
|
|
342
|
-
}
|
|
343
|
-
|
|
344
|
-
.tf-toast-stack--right {
|
|
345
|
-
justify-content: flex-end;
|
|
346
|
-
}
|
|
347
|
-
|
|
348
|
-
.tf-toast-item {
|
|
349
|
-
pointer-events: auto;
|
|
350
|
-
width: 100%;
|
|
351
|
-
}
|
|
352
|
-
</style>
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import { computed, inject, onMounted, onUnmounted, ref, watch } from "vue";
|
|
3
|
+
import Toast from "./Toast.vue";
|
|
4
|
+
import type {
|
|
5
|
+
ToastAnimation,
|
|
6
|
+
ToastConfig,
|
|
7
|
+
ToastId,
|
|
8
|
+
ToastInstance,
|
|
9
|
+
ToastPosition,
|
|
10
|
+
ToastStore,
|
|
11
|
+
} from "toastflow-core";
|
|
12
|
+
import { toastStoreKey } from "../symbols";
|
|
13
|
+
import { getToastStore } from "../toast";
|
|
14
|
+
|
|
15
|
+
const positions: ToastPosition[] = [
|
|
16
|
+
"top-left",
|
|
17
|
+
"top-center",
|
|
18
|
+
"top-right",
|
|
19
|
+
"bottom-left",
|
|
20
|
+
"bottom-center",
|
|
21
|
+
"bottom-right",
|
|
22
|
+
];
|
|
23
|
+
|
|
24
|
+
const injectedStore = inject<ToastStore | null>(toastStoreKey, null);
|
|
25
|
+
const store: ToastStore = injectedStore ?? getToastStore();
|
|
26
|
+
|
|
27
|
+
const toasts = ref<ToastInstance[]>([]);
|
|
28
|
+
|
|
29
|
+
// event-driven keys
|
|
30
|
+
const progressResetMap = ref<Record<ToastId, number>>({});
|
|
31
|
+
const duplicateMap = ref<Record<ToastId, number>>({});
|
|
32
|
+
const updateMap = ref<Record<ToastId, number>>({});
|
|
33
|
+
|
|
34
|
+
let unsubscribeState: (() => void) | null = null;
|
|
35
|
+
let unsubscribeEvents: (() => void) | null = null;
|
|
36
|
+
|
|
37
|
+
onMounted(function () {
|
|
38
|
+
unsubscribeState = store.subscribe(function (state) {
|
|
39
|
+
toasts.value = state.toasts;
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
unsubscribeEvents = store.subscribeEvents(function (event) {
|
|
43
|
+
if (event.kind === "timer-reset") {
|
|
44
|
+
progressResetMap.value[event.id] = Math.random();
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
if (event.kind === "duplicate") {
|
|
48
|
+
duplicateMap.value[event.id] = Math.random();
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
if (event.kind === "update") {
|
|
52
|
+
updateMap.value[event.id] = Math.random();
|
|
53
|
+
}
|
|
54
|
+
});
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
onUnmounted(function () {
|
|
58
|
+
if (unsubscribeState) {
|
|
59
|
+
unsubscribeState();
|
|
60
|
+
}
|
|
61
|
+
if (unsubscribeEvents) {
|
|
62
|
+
unsubscribeEvents();
|
|
63
|
+
}
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
function getProgressResetKey(id: ToastId): number {
|
|
67
|
+
return progressResetMap.value[id] ?? 0;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function getDuplicateKey(id: ToastId): number {
|
|
71
|
+
return duplicateMap.value[id] ?? 0;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function getUpdateKey(id: ToastId): number {
|
|
75
|
+
return updateMap.value[id] ?? 0;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
const grouped = computed(function () {
|
|
79
|
+
const byPos: Record<ToastPosition, ToastInstance[]> = {
|
|
80
|
+
"top-left": [],
|
|
81
|
+
"top-center": [],
|
|
82
|
+
"top-right": [],
|
|
83
|
+
"bottom-left": [],
|
|
84
|
+
"bottom-center": [],
|
|
85
|
+
"bottom-right": [],
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
for (const toast of toasts.value) {
|
|
89
|
+
byPos[toast.position].push(toast);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
return byPos;
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
const baseConfig: ToastConfig = store.getConfig();
|
|
96
|
+
const stackConfigs = ref<Record<ToastPosition, ToastConfig>>({
|
|
97
|
+
"top-left": { ...baseConfig, position: "top-left" },
|
|
98
|
+
"top-center": { ...baseConfig, position: "top-center" },
|
|
99
|
+
"top-right": { ...baseConfig, position: "top-right" },
|
|
100
|
+
"bottom-left": { ...baseConfig, position: "bottom-left" },
|
|
101
|
+
"bottom-center": { ...baseConfig, position: "bottom-center" },
|
|
102
|
+
"bottom-right": { ...baseConfig, position: "bottom-right" },
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
const globalZIndex = computed(function () {
|
|
106
|
+
if (!toasts.value.length) {
|
|
107
|
+
return baseConfig.zIndex;
|
|
108
|
+
}
|
|
109
|
+
return Math.max(...toasts.value.map((toast) => toast.zIndex));
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
function stackConfig(position: ToastPosition): ToastConfig {
|
|
113
|
+
return stackConfigs.value[position] ?? { ...baseConfig, position };
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
function stackStyle(position: ToastPosition): Record<string, string> {
|
|
117
|
+
const { offset, width } = stackConfig(position);
|
|
118
|
+
// Lock the stack width, so it doesn't collapse when leaving items get absolute-positioned
|
|
119
|
+
const style: Record<string, string> = { width, maxWidth: "100%" };
|
|
120
|
+
|
|
121
|
+
if (position.startsWith("top-")) {
|
|
122
|
+
style.top = offset;
|
|
123
|
+
}
|
|
124
|
+
if (position.startsWith("bottom-")) {
|
|
125
|
+
style.bottom = offset;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
if (position.endsWith("left")) {
|
|
129
|
+
style.left = offset;
|
|
130
|
+
} else if (position.endsWith("right")) {
|
|
131
|
+
style.right = offset;
|
|
132
|
+
} else if (position.endsWith("center")) {
|
|
133
|
+
style.left = "50%";
|
|
134
|
+
style.transform = "translateX(-50%)";
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
return style;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
function stackAlignClass(position: ToastPosition): string {
|
|
141
|
+
if (position.endsWith("left")) {
|
|
142
|
+
return "tf-toast-stack--left";
|
|
143
|
+
}
|
|
144
|
+
if (position.endsWith("center")) {
|
|
145
|
+
return "tf-toast-stack--center";
|
|
146
|
+
}
|
|
147
|
+
return "tf-toast-stack--right";
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
function stackAxisClass(position: ToastPosition): string | null {
|
|
151
|
+
if (position.startsWith("bottom-")) {
|
|
152
|
+
return "tf-toast-stack-inner--bottom";
|
|
153
|
+
}
|
|
154
|
+
return null;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
function handleDismiss(id: ToastId) {
|
|
158
|
+
store.dismiss(id);
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
function beforeLeave(el: Element) {
|
|
162
|
+
const element = el as HTMLElement;
|
|
163
|
+
const parent = element.parentElement;
|
|
164
|
+
if (!parent || parent.children.length <= 1) {
|
|
165
|
+
return;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
const top = element.offsetTop;
|
|
169
|
+
const parentHeight = parent.clientHeight;
|
|
170
|
+
const parentWidth = parent.clientWidth;
|
|
171
|
+
|
|
172
|
+
parent.style.minHeight = `${parentHeight}px`;
|
|
173
|
+
element.style.position = "absolute";
|
|
174
|
+
element.style.width = `${parentWidth}px`;
|
|
175
|
+
element.style.left = "0";
|
|
176
|
+
element.style.right = "0";
|
|
177
|
+
element.style.top = `${top}px`;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
function afterLeave(el: Element) {
|
|
181
|
+
const element = el as HTMLElement;
|
|
182
|
+
const parent = element.parentElement;
|
|
183
|
+
if (parent) {
|
|
184
|
+
parent.style.minHeight = "";
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
element.style.position = "";
|
|
188
|
+
element.style.width = "";
|
|
189
|
+
element.style.left = "";
|
|
190
|
+
element.style.right = "";
|
|
191
|
+
element.style.top = "";
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
watch(
|
|
195
|
+
toasts,
|
|
196
|
+
function (current) {
|
|
197
|
+
const ids = new Set(
|
|
198
|
+
current.map(function (toast) {
|
|
199
|
+
return toast.id;
|
|
200
|
+
}),
|
|
201
|
+
);
|
|
202
|
+
|
|
203
|
+
for (const key of Object.keys(progressResetMap.value)) {
|
|
204
|
+
if (!ids.has(key as ToastId)) {
|
|
205
|
+
delete progressResetMap.value[key as ToastId];
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
for (const key of Object.keys(duplicateMap.value)) {
|
|
210
|
+
if (!ids.has(key as ToastId)) {
|
|
211
|
+
delete duplicateMap.value[key as ToastId];
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
for (const key of Object.keys(updateMap.value)) {
|
|
216
|
+
if (!ids.has(key as ToastId)) {
|
|
217
|
+
delete updateMap.value[key as ToastId];
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
},
|
|
221
|
+
{ deep: false },
|
|
222
|
+
);
|
|
223
|
+
|
|
224
|
+
function animationForToast(toast: ToastInstance): Partial<ToastAnimation> {
|
|
225
|
+
return {
|
|
226
|
+
...baseConfig.animation,
|
|
227
|
+
...toast.animation,
|
|
228
|
+
};
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
watch(
|
|
232
|
+
grouped,
|
|
233
|
+
function (byPos) {
|
|
234
|
+
const next = { ...stackConfigs.value };
|
|
235
|
+
|
|
236
|
+
(Object.keys(byPos) as ToastPosition[]).forEach(function (position) {
|
|
237
|
+
const first = byPos[position][0];
|
|
238
|
+
if (first) {
|
|
239
|
+
next[position] = {
|
|
240
|
+
...baseConfig,
|
|
241
|
+
...first,
|
|
242
|
+
position,
|
|
243
|
+
animation: {
|
|
244
|
+
...baseConfig.animation,
|
|
245
|
+
...first.animation,
|
|
246
|
+
},
|
|
247
|
+
};
|
|
248
|
+
}
|
|
249
|
+
});
|
|
250
|
+
|
|
251
|
+
stackConfigs.value = next;
|
|
252
|
+
},
|
|
253
|
+
{ deep: false },
|
|
254
|
+
);
|
|
255
|
+
</script>
|
|
256
|
+
|
|
257
|
+
<template>
|
|
258
|
+
<div class="tf-toast-root" :style="{ zIndex: globalZIndex }">
|
|
259
|
+
<div
|
|
260
|
+
v-for="position in positions"
|
|
261
|
+
:key="position"
|
|
262
|
+
class="tf-toast-stack"
|
|
263
|
+
:class="stackAlignClass(position)"
|
|
264
|
+
:style="stackStyle(position)"
|
|
265
|
+
>
|
|
266
|
+
<TransitionGroup
|
|
267
|
+
:name="stackConfig(position).animation.name"
|
|
268
|
+
@before-leave="beforeLeave"
|
|
269
|
+
@after-leave="afterLeave"
|
|
270
|
+
tag="div"
|
|
271
|
+
:class="['tf-toast-stack-inner', stackAxisClass(position)]"
|
|
272
|
+
:style="{ gap: stackConfig(position).gap }"
|
|
273
|
+
>
|
|
274
|
+
<div
|
|
275
|
+
v-for="toast in grouped[position]"
|
|
276
|
+
:key="toast.id"
|
|
277
|
+
class="tf-toast-item"
|
|
278
|
+
:style="{ width: toast.width, maxWidth: '100%' }"
|
|
279
|
+
:data-position="toast.position"
|
|
280
|
+
>
|
|
281
|
+
<slot
|
|
282
|
+
v-if="$slots.default"
|
|
283
|
+
:toast="toast"
|
|
284
|
+
:progressResetKey="getProgressResetKey(toast.id)"
|
|
285
|
+
:duplicateKey="getDuplicateKey(toast.id)"
|
|
286
|
+
:updateKey="getUpdateKey(toast.id)"
|
|
287
|
+
:bumpAnimationClass="animationForToast(toast).bump"
|
|
288
|
+
:clearAllAnimationClass="animationForToast(toast).clearAll"
|
|
289
|
+
:updateAnimationClass="animationForToast(toast).update"
|
|
290
|
+
:dismiss="handleDismiss"
|
|
291
|
+
/>
|
|
292
|
+
|
|
293
|
+
<Toast
|
|
294
|
+
v-else
|
|
295
|
+
:toast="toast"
|
|
296
|
+
:progressResetKey="getProgressResetKey(toast.id)"
|
|
297
|
+
:duplicateKey="getDuplicateKey(toast.id)"
|
|
298
|
+
:updateKey="getUpdateKey(toast.id)"
|
|
299
|
+
:bumpAnimationClass="animationForToast(toast).bump"
|
|
300
|
+
:clearAllAnimationClass="animationForToast(toast).clearAll"
|
|
301
|
+
:updateAnimationClass="animationForToast(toast).update"
|
|
302
|
+
@dismiss="handleDismiss"
|
|
303
|
+
/>
|
|
304
|
+
</div>
|
|
305
|
+
</TransitionGroup>
|
|
306
|
+
</div>
|
|
307
|
+
</div>
|
|
308
|
+
</template>
|
|
309
|
+
|
|
310
|
+
<style scoped>
|
|
311
|
+
.tf-toast-root {
|
|
312
|
+
pointer-events: none;
|
|
313
|
+
position: fixed;
|
|
314
|
+
inset: 0;
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
.tf-toast-stack {
|
|
318
|
+
position: absolute;
|
|
319
|
+
height: 100%;
|
|
320
|
+
display: flex;
|
|
321
|
+
align-items: flex-start;
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
.tf-toast-stack-inner {
|
|
325
|
+
display: flex;
|
|
326
|
+
flex-direction: column;
|
|
327
|
+
position: relative;
|
|
328
|
+
width: 100%;
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
.tf-toast-stack-inner--bottom {
|
|
332
|
+
min-height: 100%;
|
|
333
|
+
justify-content: flex-end;
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
.tf-toast-stack--left {
|
|
337
|
+
justify-content: flex-start;
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
.tf-toast-stack--center {
|
|
341
|
+
justify-content: center;
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
.tf-toast-stack--right {
|
|
345
|
+
justify-content: flex-end;
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
.tf-toast-item {
|
|
349
|
+
pointer-events: auto;
|
|
350
|
+
width: 100%;
|
|
351
|
+
}
|
|
352
|
+
</style>
|