对话框
一个覆盖在整个页面之上的弹出层。
安装
pnpm dlx shadcn@latest add @lumi-ui/dialog
将以下工具类添加到你的 globals.css 中:
@utility animate-fade-up {
@apply transition-all duration-200 ease-[cubic-bezier(0.25,1,0.5,1)];
&[data-starting-style],
&[data-ending-style] {
opacity: 0;
scale: 0.98;
translate: 0 0.5rem;
}
}
@utility animate-fade-down {
@apply transition-all duration-200 ease-[cubic-bezier(0.25,1,0.5,1)];
&[data-starting-style],
&[data-ending-style] {
opacity: 0;
scale: 0.98;
translate: 0 -0.5rem;
}
}
@utility animate-fade-zoom {
@apply transition-all duration-200 ease-[cubic-bezier(0.25,1,0.5,1)];
&[data-starting-style],
&[data-ending-style] {
opacity: 0;
scale: 0.94;
}
}
@utility animate-fade {
@apply transition-all duration-200;
&[data-starting-style],
&[data-ending-style] {
opacity: 0;
}
}基本用法
import {
Dialog,
DialogClose,
DialogDescription,
DialogFooter,
DialogHeader,
DialogContent,
DialogTitle,
DialogTrigger,
} from "@/components/ui/dialog";
export function DialogDemo() {
return (
<Dialog>
<DialogTrigger>打开对话框</DialogTrigger>
<DialogContent showCloseButton>
<DialogHeader>
<DialogTitle>确定要执行此操作吗?</DialogTitle>
<DialogDescription>
此操作无法撤销。这将永久删除你的账户,并从我们的服务器中移除你的数据。
</DialogDescription>
</DialogHeader>
<DialogFooter>
<DialogClose>取消</DialogClose>
<DialogClose>继续</DialogClose>
</DialogFooter>
</DialogContent>
</Dialog>
);
}组件结构
<Dialog>
<DialogTrigger />
<DialogContent>
<DialogHeader>
<DialogTitle />
<DialogDescription />
</DialogHeader>
{/* 内容 */}
<DialogFooter>
<DialogClose />
</DialogFooter>
</DialogContent>
</Dialog>样式变体
DialogContent 的布局属性决定了视觉样式和进场动画:
responsive(默认):移动端为底部抽屉,桌面端为居中模态框。center:带向上淡入动画的居中模态框。top:带向下淡入动画的顶部对齐模态框。scrollable:针对内容内部滚动的容器优化。stacked:位移和缩放,用于处理嵌套对话框流程。element-outside:用于 UI 元素位于主容器之外的情况。
实用示例
滚动处理
内部滚动
将 DialogContent 设置为 layout="scrollable"。使用 ScrollArea 或 overflow-y-auto 处理内容。滚动区域之外的元素保持固定。
元素在外部
当控件应位于弹出层之外时,使用 layout="element-outside"。
视口滚动
对于非常长的内容,使用 DialogViewport 作为滚动容器。这允许整个对话框随页面滚动。
自定义实现
关闭确认
对 DialogContent 和 AlertDialogContent 使用 layout="stacked",以优雅地处理嵌套确认流程。
受控模式
手动管理 open 状态,适用于外部触发器或异步关闭逻辑。
const [open, setOpen] = React.useState(false);
<Dialog open={open} onOpenChange={setOpen}>
<DialogTrigger>打开</DialogTrigger>
<DialogContent>
<form onSubmit={async () => {
await submitData();
setOpen(false);
}}>
...
</form>
</DialogContent>
</Dialog>从下拉菜单触发
从 DropdownMenu 打开对话框时,请使用受控状态,以避免焦点管理和嵌套问题。
分离的触发器与载荷
当 DialogTrigger 和 Dialog 无法嵌套在同一个根节点下时,使用 createDialogHandle 将它们链接起来。
const demoDialog = createDialogHandle();
<DialogTrigger handle={demoDialog}>打开</DialogTrigger>
<Dialog handle={demoDialog}>...</Dialog>动态内容
单个对话框可以处理多个触发器,并根据载荷动态渲染内容。
受控多触发器
当外部控制多个触发器时,在 onOpenChange 中使用 eventDetails.source 来识别当前活动的触发器。