Remove DialogVant/NotifyVant and add Toast and Sheet components with useToast, useReveal, and usePageTransition composables. Rework Navigation, RegisterView, and styles across both apps; rebuild dist assets. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
90 lines
2.1 KiB
Vue
90 lines
2.1 KiB
Vue
<!--
|
|
Renders the shared toast queue (see composables/useToast.ts). Mounted once
|
|
in App.vue. Toasts drop in from the top with a spring, the stack slides to
|
|
make room, and a closing toast shrinks up and fades out.
|
|
-->
|
|
<template>
|
|
<Teleport to="body">
|
|
<div class="toast-host">
|
|
<TransitionGroup name="toast">
|
|
<ToastCard v-for="item in toasts" :key="item.id" :item="item" @close="dismiss(item.id)" />
|
|
</TransitionGroup>
|
|
</div>
|
|
</Teleport>
|
|
</template>
|
|
|
|
<script setup lang="ts">
|
|
import ToastCard from './ToastCard.vue'
|
|
import { useToast } from '../../composables/useToast'
|
|
|
|
const { toasts, dismiss } = useToast()
|
|
</script>
|
|
|
|
<style lang="less">
|
|
.toast-host {
|
|
position: fixed;
|
|
top: 0;
|
|
left: 0;
|
|
right: 0;
|
|
z-index: 3000;
|
|
display: flex;
|
|
flex-direction: column;
|
|
align-items: center;
|
|
gap: 8px;
|
|
padding: calc(12px + env(safe-area-inset-top, 0px)) 14px 0;
|
|
direction: rtl;
|
|
pointer-events: none;
|
|
|
|
> * {
|
|
max-width: 420px;
|
|
}
|
|
}
|
|
|
|
// Enter: drop from above, slightly small and blurred, then overshoot into place.
|
|
.toast-host .toast.toast-enter-active {
|
|
transition:
|
|
transform 0.6s cubic-bezier(0.2, 1.35, 0.4, 1),
|
|
opacity 0.35s ease,
|
|
filter 0.4s ease;
|
|
}
|
|
.toast-host .toast.toast-enter-from {
|
|
transform: translateY(-120%) scale(0.88);
|
|
opacity: 0;
|
|
filter: blur(6px);
|
|
}
|
|
|
|
// Leave: taken out of flow so the stack can glide up behind it.
|
|
.toast-host .toast.toast-leave-active {
|
|
position: absolute;
|
|
left: 14px;
|
|
right: 14px;
|
|
margin: 0 auto;
|
|
transition:
|
|
transform 0.32s cubic-bezier(0.5, 0, 0.75, 0),
|
|
opacity 0.28s ease,
|
|
filter 0.3s ease;
|
|
}
|
|
.toast-host .toast.toast-leave-to {
|
|
transform: translateY(-40%) scale(0.9);
|
|
opacity: 0;
|
|
filter: blur(4px);
|
|
}
|
|
|
|
.toast-host .toast.toast-move {
|
|
transition: transform 0.45s cubic-bezier(0.2, 1.2, 0.4, 1);
|
|
}
|
|
|
|
@media (prefers-reduced-motion: reduce) {
|
|
.toast-host .toast.toast-enter-active,
|
|
.toast-host .toast.toast-leave-active,
|
|
.toast-host .toast.toast-move {
|
|
transition: opacity 0.2s ease;
|
|
}
|
|
.toast-host .toast.toast-enter-from,
|
|
.toast-host .toast.toast-leave-to {
|
|
transform: none;
|
|
filter: none;
|
|
}
|
|
}
|
|
</style>
|