wip bsky client for the web & android
bbell.vt3e.cat
1<script setup lang="ts">
2import { useToastStore } from '@/stores/toast'
3
4function onBeforeLeave(el: Element) {
5 const htmlEl = el as HTMLElement
6 const { offsetTop, offsetLeft, offsetWidth } = htmlEl
7 htmlEl.style.top = `${offsetTop}px`
8 htmlEl.style.left = `${offsetLeft}px`
9 htmlEl.style.width = `${offsetWidth}px`
10}
11const toast = useToastStore()
12</script>
13
14<template>
15 <Transition tag="div" name="stack">
16 <div v-if="toast.toasts.length" class="toast-stack">
17 <TransitionGroup name="toast" @before-leave="onBeforeLeave">
18 <div
19 v-for="t in toast.toasts"
20 :key="t.id"
21 :class="['toast', t.type]"
22 @click="toast.handleClick(t.id)"
23 >
24 <div class="icon">
25 <component :is="t.icon" />
26 </div>
27 <span class="message">{{ t.message }}</span>
28 </div>
29 </TransitionGroup>
30 </div>
31 </Transition>
32</template>
33
34<style scoped lang="scss">
35.toast-stack {
36 position: absolute;
37 bottom: calc(5rem + env(safe-area-inset-bottom));
38 left: 0.5rem;
39 z-index: 1000;
40
41 display: flex;
42 flex-direction: column;
43 align-items: flex-start;
44 gap: 0.75rem;
45
46 width: 100%;
47 pointer-events: none;
48}
49@media (min-width: 800px) {
50 .toast-stack {
51 position: fixed;
52 bottom: 1.5rem;
53 }
54}
55
56.toast {
57 --accent-colour: var(--accent);
58 max-width: var(--content-width);
59 background-color: hsla(var(--surface0) / 1);
60
61 display: inline-flex;
62 align-items: center;
63 justify-content: center;
64
65 padding: 0.25rem 0.5rem;
66 padding-right: 1rem;
67 gap: 0.5rem;
68 border-radius: 2rem;
69
70 cursor: pointer;
71 pointer-events: all;
72 user-select: none;
73
74 max-height: 5rem;
75 overflow: hidden;
76
77 border: 1px solid hsla(var(--accent-colour) / 0.25);
78
79 .icon {
80 font-size: 1.5rem;
81 display: flex;
82 align-items: center;
83 justify-content: center;
84 color: hsl(var(--accent-colour));
85 }
86
87 &.success {
88 --accent-colour: var(--green);
89 }
90 &.error {
91 --accent-colour: var(--red);
92 }
93 &.info {
94 --accent-colour: var(--blue);
95 }
96}
97
98.stack-enter-active,
99.stack-leave-active {
100 transition: opacity 0.2s ease;
101}
102.stack-enter-from,
103.stack-leave-to {
104 opacity: 0;
105}
106
107.toast-move,
108.toast-enter-active,
109.toast-leave-active {
110 transition: all 0.25s ease;
111}
112
113.toast-enter-from {
114 opacity: 0;
115 transform: translateY(1rem) scale(0.95);
116}
117
118.toast-leave-to {
119 opacity: 0;
120 transform: translateX(-2rem) scale(0.95);
121 filter: blur(2px);
122 max-height: 0;
123 padding-top: 0;
124 padding-bottom: 0;
125 margin-top: -0.75rem;
126 border-width: 0;
127}
128</style>