对话框

一个覆盖在整个页面之上的弹出层。

安装

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 元素位于主容器之外的情况。
Positions
Behaviors

实用示例

滚动处理

内部滚动

DialogContent 设置为 layout="scrollable"。使用 ScrollAreaoverflow-y-auto 处理内容。滚动区域之外的元素保持固定。

元素在外部

当控件应位于弹出层之外时,使用 layout="element-outside"

视口滚动

对于非常长的内容,使用 DialogViewport 作为滚动容器。这允许整个对话框随页面滚动。

自定义实现

关闭确认

DialogContentAlertDialogContent 使用 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 打开对话框时,请使用受控状态,以避免焦点管理和嵌套问题。

分离的触发器与载荷

DialogTriggerDialog 无法嵌套在同一个根节点下时,使用 createDialogHandle 将它们链接起来。

分离的触发器
const demoDialog = createDialogHandle();
 
<DialogTrigger handle={demoDialog}>打开</DialogTrigger>
<Dialog handle={demoDialog}>...</Dialog>

动态内容

单个对话框可以处理多个触发器,并根据载荷动态渲染内容。

受控多触发器

当外部控制多个触发器时,在 onOpenChange 中使用 eventDetails.source 来识别当前活动的触发器。

API 参考

基础组件

组件描述
Dialog组合对话框的所有部分。不渲染自己的 HTML 元素。
DialogTrigger用于打开对话框的按钮。
DialogPortal将弹出层移动到 DOM 不同位置的传送门元素。渲染 <div> 元素。
DialogBackdrop显示在弹出层下方的遮罩层。渲染 <div> 元素。
DialogViewport对话框弹出层的定位容器,可设为可滚动。渲染 <div> 元素。
DialogPopup对话框内容的容器。必须在 DialogPortal 内使用。渲染 <div> 元素。
DialogHeader用于便捷布局的样式化容器。渲染 <div> 元素。
DialogTitle标记对话框的标题。渲染 <h2> 元素。
DialogDescription包含对话框附加信息的段落。渲染 <p> 元素。
DialogFooter用于便捷布局的样式化容器。渲染 <div> 元素。
DialogClose用于关闭对话框的按钮。渲染 <button> 元素。

复合组件

组件描述
DialogContent标准用法。组合 Portal、Backdrop、Viewport 和 Popup。
DialogElementOutsideContentDialogContent 的扩展,适用于弹出层外部存在元素的 UI。

DialogContent 布局变体

变体描述
responsive移动端底部抽屉;桌面端居中模态框。
center带向上淡入过渡的居中模态框。
top带向下淡入过渡的顶部对齐模态框。
scrollable用于内部滚动的固定页头/页脚布局。
stacked支持嵌套对话框的缩放和偏移。
element-outside支持弹出框外的交互元素。