Animated Toast Stack
在堆叠区域管理临时通知,支持操作与手势关闭。
适用场景
- 当界面需要以下行为时使用 Animated Toast Stack:在堆叠区域管理临时通知,支持操作与手势关闭。
安装
选择安装方式,用法示例将同步更新导入路径。
$ bunx --bun shadcn-vue@2.8.2 add https://orvui.efu.me/r/animated-toast-stack.json
首次使用时,将安装的 src/components/orvui/styles.css 导入项目样式入口。
示例
预览
Open a real toast
Toasts render fixed on the screen. Change position to open a toast from that edge.
packages/vue/src/motion/animated-toast-stack/demo.vue
<script setup lang="ts">
import { onBeforeUnmount, ref } from "vue";
import { Check, LoaderCircle, Sparkles, X } from "lucide-vue-next";
import {
AnimatedToastStack,
useAnimatedToastStack,
type ToastInput,
type ToastPosition,
} from "@/components/orvui/motion/animated-toast-stack";
const positions: ToastPosition[] = [
"top-left",
"top-center",
"top-right",
"bottom-left",
"bottom-center",
"bottom-right",
];
const examples: (ToastInput & { label: string })[] = [
{ label: "Title only", status: "success", title: "Saved", duration: 0 },
{
label: "Promise",
status: "loading",
title: "Publishing component",
description: "Bundling source, preview, and registry metadata.",
duration: 0,
},
{
label: "Success",
status: "success",
title: "Component published",
description: "Registry endpoint and raw source are available.",
},
{
label: "Error",
status: "error",
title: "Snapshot failed",
description: "Retry after the browser target settles.",
},
];
const position = ref<ToastPosition>("bottom-right"),
{ toasts, showToast, updateToast, dismissToast, clearToasts } =
useAnimatedToastStack({ defaultDuration: 3600, limit: 5 }),
timers = new Set<ReturnType<typeof setTimeout>>();
function openToast(input: ToastInput) {
const id = showToast(input);
if (input.status !== "loading") return;
const timer = setTimeout(() => {
timers.delete(timer);
updateToast(id, {
status: "success",
title: "Publish complete",
description: "Toast updated in-place from loading to success.",
duration: 3200,
});
}, 1800);
timers.add(timer);
}
function moveStack(value: ToastPosition) {
position.value = value;
showToast({
status: "info",
title: "Position changed",
description: `New toasts open from ${value}.`,
});
}
onBeforeUnmount(() => {
for (const timer of timers) clearTimeout(timer);
timers.clear();
});
</script>
<template>
<div class="flex min-h-72 w-full flex-col items-center justify-center gap-6">
<AnimatedToastStack
:toasts="toasts"
:position="position"
placement="fixed"
:max-visible="4"
:class-names="{ surface: 'bg-card/95' }"
:icons="{ neutral: Sparkles }"
@dismiss="dismissToast"
/>
<div class="flex flex-col items-center gap-2 text-center">
<p class="text-sm font-medium text-foreground">Open a real toast</p>
<p class="max-w-sm text-xs leading-5 text-muted-foreground">
Toasts render fixed on the screen. Change position to open a toast from
that edge.
</p>
</div>
<div class="flex flex-wrap items-center justify-center gap-2">
<button
v-for="example in examples"
:key="example.label"
type="button"
class="inline-flex h-9 items-center gap-2 rounded-full border border-border bg-card px-4 text-xs font-medium text-foreground transition-colors press hover:border-(--color-border-strong)"
@click="openToast(example)"
>
<component
:is="
example.status === 'loading'
? LoaderCircle
: example.status === 'success'
? Check
: X
"
class="h-3.5 w-3.5"
/>{{ example.label }}</button
><button
type="button"
class="inline-flex h-9 items-center rounded-full px-4 text-xs font-medium text-muted-foreground press hover:bg-foreground/[0.06] hover:text-foreground"
@click="clearToasts"
>
Clear
</button>
</div>
<div class="flex flex-wrap items-center justify-center gap-1.5">
<button
v-for="option in positions"
:key="option"
type="button"
:class="[
'rounded-full px-2.5 py-1 text-[11px] font-medium transition-colors press',
position === option
? 'bg-foreground text-background'
: 'bg-foreground/[0.04] text-muted-foreground hover:bg-foreground/[0.08] hover:text-foreground',
]"
@click="moveStack(option)"
>
{{ option }}
</button>
</div>
</div>
</template>
packages/vue/src/motion/animated-toast-stack/src/AnimatedToastStack.vue
状态与事件
此示例组合没有声明成对的受控属性与 update 事件。使用已列出的属性、原生事件和插槽组合内容;不要为它添加未公开的 v-model。
API 参考
toastsAnimatedToast[]—position?"top-left" | "top-center" | "top-right" | "bottom-left" | "bottom-center" | "bottom-right""bottom-right"placement?"static" | "fixed" | "absolute"(props.fixed ? "fixed" : "static")fixed?booleanfalseportal?booleanundefinedportalRoot?null | Element—maxVisible?number4className?string—classNames?Partial<Record<"action" | "description" | "content" | "root" | "item" | "surface" | "iconWrap" | "title" | "close" | "progress", string>>—icons?Partial<Record<ToastStatus, any>>—renderToast?(toast: AnimatedToast) => VNodeChild—事件
dismiss[id: string]@dismiss插槽
toast—作用域参数: toast
#toast无障碍
为交互控件提供明确的无障碍名称。保留文档中的键盘路径、可见焦点与减少动态效果状态;自定义插槽应继续保留所替代部件的语义。
常见问题
- 如何配置 Animated Toast Stack?
- 通过 toasts、position、placement、fixed、portal、portalRoot、maxVisible、className、classNames、icons、renderToast 配置 Animated Toast Stack;API 表列出其类型与默认值。
- 如何连接到应用状态?
- 监听 dismiss,将组件连接到应用状态。
- 可以在服务端渲染吗?
- 可以在 Vue SSR 应用中正常渲染。服务端与客户端的初始 props 应保持一致;浏览器测量与交互资源在挂载后启动。
更新于
来源与许可 ↗MIT · 0.1.1