Dialog 对话框Dialog · confirm()
打断当前的事,要人先回应:确认一个操作、补充一两项信息。它是模态的,出现时页面其余部分不可用,所以只在必须打断时才用;只是告知结果请用 Toast。
同一张悬浮卡片
Dialog、Drawer、Sheet、Panel 不是四套东西,而是同一张悬浮卡片(FloatingCard)在不同位置、不同尺寸下的用法:一张圆角卡片,和视口四周留一圈缝,从停靠的那一侧滑入。没有阴影,模态时靠遮罩压暗背景,非模态时靠描边和页面分开。遮罩、焦点圈、滚动锁、Esc、进退场都在 FloatingCard 里,只此一份;卡片里面统一用 FloatingHeader / FloatingBody / FloatingFooter 三个结构件来搭。
| 组件 | 停靠位置 | 模态 | 用在哪 |
|---|---|---|---|
| Dialog 本页 | 居中 | 模态 | 确认、简短的表单 |
| Drawer | 贴着四边之一 | 模态 | 详情、较长的表单、导航 |
| Sheet | 贴着底边 | 模态 | 手机上代替对话框和抽屉 |
| Panel | 贴着四边之一 | 非模态 | 检查器、筛选面板 |
结构件
Dialog 自带一套“图标 + 标题 + 说明 + 等分按钮”的排版,够用时直接传属性即可(见下文)。需要自己排版的居中卡片,可以直接用 FloatingCard 配三个结构件。
基本用法
图标在标题左侧,下面是说明,底部一到两个等分撑满的按钮。Esc 或点遮罩关闭;关闭后焦点回到打开它的按钮上。
图标的状态色
tone 只改变图标色块的颜色,默认 accent。
居中变体
没有图标的简短确认框用 align="center",标题和说明居中。
尺寸
宽度预设 sm 320、md 400(默认)、lg 560,也可以给数字或 CSS 长度。高度随内容,超出视口时卡片内部滚动。
放表单
children 放在说明和按钮之间。closable 在右上角加一个关闭按钮;initialFocus 指定打开后焦点落在哪个元素上。
alertdialog
需要用户立刻回应、不能随手关掉的打断,用 role="alertdialog" 并关掉 dismissible:Esc 和点遮罩都不再关闭,只能点按钮。给最安全的那个按钮加 data-autofocus,让焦点一开始就落在它上面。
从触发点进场
居中的对话框从打开它的地方放大出来,关闭时缩回同一个点:用指针点开的从点按处,用键盘(Tab 到按钮再按回车)打开的从按钮中心;通过快捷键打开、没有触发元素时(焦点在页面空白处或输入框里)从屏幕中心。快捷键按下时焦点可能还停在某个按钮上,这时传 origin="center" 明确要求从屏幕中心出来;也可以传视口坐标 { x, y }。手机宽度下也一样。
confirm()
最常见的“确定吗?”不需要自己管开合状态:await confirm(...) 返回一个 Promise,确认得到 true,取消、Esc、点遮罩得到 false。它渲染的是 alertdialog;tone 为 danger 时确认按钮用危险色,焦点默认落在“取消”上。在应用根部挂一个 <Confirmer />(本站已挂)。
规格
- 面板
- 圆角 32 · 内边距 24 · 最大宽 400
- 图标
- 40 × 40 色块 · 在标题左侧
- 居中变体
- 无图标 · 标题与说明居中
- 进场
- 从触发点放大:scale 0.6 → 1 · spring-soft 曲线 303ms + 100ms 淡入;没有触发点时从屏幕中心
- 遮罩
- 160ms · 变暗 + 模糊 8px
- 退场
- 160ms ease-in · 缩回进场的那个点
接口
Dialog
| 属性 | 类型 | 默认值 | 说明 |
|---|---|---|---|
title | ReactNode | — | 标题,自动成为对话框的名字。 |
description | ReactNode | — | 说明,自动成为对话框的描述。 |
icon | ReactNode | — | 标题左侧的图标位(40×40 的色块)。 |
tone | 'neutral' | 'accent' | 'success' | 'warning' | 'danger' | 'info' | 'accent' | 图标色块的状态色。 |
actions | ReactNode | — | 底部操作区:放一到两个按钮,等分撑满。 |
children | ReactNode | — | 说明和按钮之间的自定义内容。 |
size | 'sm' | 'md' | 'lg' | number | string | 'md' | 宽度预设:320 / 400 / 560;或数字(px)、CSS 长度。 |
align | 'start' | 'center' | 'start' | center:标题与说明居中。 |
role | 'dialog' | 'alertdialog' | 'dialog' | 需要用户立刻回应的打断用 alertdialog。 |
closable | boolean | false | 右上角带一个关闭按钮。 |
closeLabel | string | '关闭' | 关闭按钮的读屏名称。 |
origin | 'trigger' | 'center' | { x: number; y: number } | 'trigger' | 从哪里放大进场、缩回哪里。trigger:点按处 / 键盘焦点元素的中心,没有触发元素时屏幕中心;center:屏幕中心;也可以给视口坐标。 |
open / defaultOpen | boolean | false | 开合,受控 / 非受控。 |
onOpenChange | (open: boolean) => void | — | 开合变化时(Esc、点遮罩、关闭按钮、拖拽关闭都会触发)。 |
dismissible | boolean | true | 允许 Esc 和点遮罩关闭。 |
gap | number | 12(窄屏 8) | 卡片到视口边缘的间隙(px)。 |
initialFocus | RefObject<HTMLElement> | — | 模态时打开后焦点落在哪。默认是卡片里带 data-autofocus 的元素,没有就落在卡片本身。 |
restoreFocus | boolean | true | 关闭后把焦点还给打开前的元素。 |
container | HTMLElement | null | — | 渲染到哪个容器,默认 body。 |
onExited | () => void | — | 退场动画结束、节点卸载后。 |
aria-label / aria-labelledby / aria-describedby | string | — | 不传时自动接上 FloatingHeader 的标题和说明。 |
confirm(options)
| 属性 | 类型 | 默认值 | 说明 |
|---|---|---|---|
title* | ReactNode | — | 标题。也可以直接 confirm('一句话')。 |
description | ReactNode | — | 说明。 |
confirmText | ReactNode | '确定' | 确认按钮的文字。 |
cancelText | ReactNode | '取消' | 取消按钮的文字。 |
tone | DialogTone | — | danger:确认按钮用危险色,默认焦点落在“取消”上。 |
icon | ReactNode | — | 图标。不传图标时默认用居中变体。 |
size | 'sm' | 'md' | 'lg' | number | string | — | 同 Dialog。 |
align | 'start' | 'center' | — | 默认:有图标靠左,没有图标居中。 |
dismissible | boolean | true | 允许 Esc / 点遮罩关闭(按“取消”算)。 |
origin | 'trigger' | 'center' | { x: number; y: number } | 'trigger' | 同 Dialog:从哪里放大进场。 |
FloatingHeader
| 属性 | 类型 | 默认值 | 说明 |
|---|---|---|---|
title | ReactNode | — | 标题。自动成为卡片的 aria-labelledby。 |
description | ReactNode | — | 标题下面的一行说明。自动成为卡片的 aria-describedby。 |
icon | ReactNode | — | 标题左边的图标位(40×40 的色块)。 |
level | 1 | 2 | 3 | 4 | 5 | 6 | 2 | 标题的层级。 |
closable | boolean | — | 是否带关闭按钮。默认跟随卡片的 dismissible。 |
closeLabel | string | '关闭' | 关闭按钮的读屏名称。 |
onClose | () => void | — | 点关闭按钮时;不传则请求卡片关闭。 |
children | ReactNode | — | 放在关闭按钮前面的额外操作。 |
FloatingBody / FloatingFooter
| 属性 | 类型 | 默认值 | 说明 |
|---|---|---|---|
padded | boolean | true | 是否带内边距。内容自己通栏(列表、表格)时关掉。 |
align | 'start' | 'end' | 'between' | 'stretch' | 'end' | 按钮的排法:靠左、靠右、两端、等分撑满。 |
无障碍
role="dialog"(或alertdialog)加aria-modal="true";名字和描述自动取自 title 和 description,没有可见标题时必须给aria-label。- 打开时焦点移进对话框:
initialFocus指定的元素 → 带data-autofocus的元素 → 对话框本身。Tab 和 Shift Tab 被圈在对话框里循环。 - Esc 或点遮罩关闭(
dismissible={false}时都不行);关闭后焦点回到打开它的元素。 - 打开期间页面背景锁定滚动,遮罩后面的内容不可点。
- alertdialog 会让读屏立刻读出标题和说明,只用于必须马上回应的事。破坏性操作把初始焦点放在“取消”上,confirm() 的 danger 已经这样做了。
- 减弱动效时进退场直接完成。