Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 | 3x 6x 3x 3x 3x 3x 3x 4x 3x 1x 3x 3x 3x 3x 2x 2x 3x 3x 3x 1x 1x 1x | <template>
<Teleport to="body">
<Transition name="snackbar">
<div
v-if="show"
class="fixed bottom-6 left-1/2 -translate-x-1/2 z-50 flex items-center gap-2 px-4 py-2.5 rounded-full text-xs font-medium shadow-lg shadow-ctp-crust/30 whitespace-nowrap"
:class="bannerClass"
>
<span class="w-1.5 h-1.5 rounded-full shrink-0" :class="dotClass" />
<span>{{ message }}</span>
</div>
</Transition>
</Teleport>
</template>
<script setup lang="ts">
import { ref, watch, computed, onMounted } from 'vue'
import { useOffline } from '../../composables/useOffline'
import { reconnectAttempt } from '../../services/websocket'
const props = defineProps<{ connected: boolean }>()
const { isOnline } = useOffline()
const show = ref(false)
let hideTimeout: ReturnType<typeof setTimeout> | null = null
const status = computed<'offline' | 'syncing' | 'connected'>(() => {
if (!isOnline.value) return 'offline'
if (!props.connected) return 'syncing'
return 'connected'
})
const bannerClass = computed(() => ({
'bg-ctp-red/90 text-ctp-base': status.value === 'offline',
'bg-ctp-surface1 text-ctp-yellow border border-ctp-yellow/30': status.value === 'syncing',
'bg-ctp-green/90 text-ctp-base': status.value === 'connected',
}))
const dotClass = computed(() => ({
'bg-ctp-base': status.value === 'offline' || status.value === 'connected',
'bg-ctp-yellow animate-pulse': status.value === 'syncing',
}))
const message = computed(() => {
if (status.value === 'offline') return 'Kein Internet — Offline gespeichert'
Eif (status.value === 'syncing') {
return reconnectAttempt.value > 0
? `Erneut verbinden… Versuch ${reconnectAttempt.value}`
: 'Verbindung wird hergestellt…'
}
return 'Verbunden'
})
onMounted(() => {
if (status.value !== 'connected') show.value = true
})
watch(status, (next, prev) => {
Iif (hideTimeout) clearTimeout(hideTimeout)
show.value = true
Iif (next === 'connected' && prev !== 'connected') {
hideTimeout = setTimeout(() => { show.value = false }, 2000)
}
})
</script>
<style scoped>
.snackbar-enter-active,
.snackbar-leave-active {
transition: all 0.3s cubic-bezier(0.32, 0.72, 0, 1);
}
.snackbar-enter-from,
.snackbar-leave-to {
opacity: 0;
transform: translateX(-50%) translateY(16px);
}
</style>
|