'use client'
import { useState } from 'react'
import { AlertDialog, Button, Stack, Text } from '@hina-ui/react'
export default function Demo() {
const [removed, setRemoved] = useState(false)
async function remove() {
await new Promise(resolve => setTimeout(resolve, 800))
setRemoved(true)
}
return (
<Stack gap="sm" align="start">
<AlertDialog
title="删除这篇文章?"
description="删除后无法恢复,文章下的评论也会一并删除。"
tone="danger"
confirmText="删除"
onConfirm={remove}
>
<Button variant="outline" tone="danger">
删除文章
</Button>
</AlertDialog>
{removed && (
<Text tone="muted" size="sm">
文章已删除。
</Text>
)}
</Stack>
)
}
tsx
用法
import { AlertDialog } from '@hina-ui/react'
ts
确认对话框只问一个问题:title 是问题本身,description 补充后果,底部固定一对按钮,文字可以用 cancelText 与 confirmText 替换。children 是触发器。点击遮罩不会关闭它,按 Esc 或者点「取消」才会;它没有右上角的关闭按钮,回答本身就是关闭的方式。
'use client'
import { useState } from 'react'
import { AlertDialog, Button, Stack, Text } from '@hina-ui/react'
export default function Demo() {
const [published, setPublished] = useState(false)
return (
<Stack gap="sm" align="start">
<AlertDialog
title="发布这篇文章?"
description="发布之后所有人都能看到它。"
confirmText="发布"
cancelText="再想想"
onConfirm={() => setPublished(true)}
>
<Button variant="outline" tone="neutral">
发布
</Button>
</AlertDialog>
{published && (
<Text tone="muted" size="sm">
文章已发布。
</Text>
)}
</Stack>
)
}
tsx
示例
危险操作
tone="danger" 把「确定」换成危险色,用于删除这类不可撤销的操作。
import { AlertDialog, Button } from '@hina-ui/react'
export default function Demo() {
return (
<AlertDialog
title="注销账号?"
description="账号与所有数据会在 30 天后永久删除,期间登录即可撤销。"
tone="danger"
confirmText="注销"
>
<Button variant="outline" tone="danger">
注销账号
</Button>
</AlertDialog>
)
}
tsx
确认倒计时
confirmDelay 指定确认前的等待秒数,默认为 0。每次打开时重新计时,期间确认按钮禁用并显示剩余秒数;结束后恢复原文案与可点击状态,取消和 Esc 在等待期间仍可用。
打开期间修改 confirmDelay 会按新值重新计时,设为 0 立即解除等待。正小数向上取整,非正数或非有限值按 0 处理。确认失败后可以直接重试。
import { AlertDialog, Button } from '@hina-ui/react'
export default function Demo() {
return (
<AlertDialog
title="确认操作?"
description="倒计时结束后,确认按钮可以点击。"
tone="danger"
confirmText="确认"
confirmDelay={3}
>
<Button variant="outline" tone="neutral">
确认倒计时
</Button>
</AlertDialog>
)
}
tsx
异步确认
onConfirm 接收确认处理函数。返回 Promise 或 PromiseLike 时,对话框等待其完成再关闭:期间「确定」显示加载指示,「取消」与 Esc 都不可用。
同步抛错或异步拒绝由组件内部捕获,并通过 onError 回调传出原始错误。对话框保持打开并恢复按钮,可以直接重试;使用 onError 更新错误提示。
'use client'
import { useState } from 'react'
import { AlertDialog, Button, Stack, Switch, Text } from '@hina-ui/react'
export default function Demo() {
const [fail, setFail] = useState(false)
const [status, setStatus] = useState('')
async function archive() {
setStatus('')
await new Promise(resolve => setTimeout(resolve, 1000))
if (fail) {
throw new Error('归档失败,请稍后再试。')
}
setStatus('已归档。')
}
function handleError(error: unknown) {
setStatus(error instanceof Error ? error.message : String(error))
}
return (
<Stack gap="md" align="start">
<Switch checked={fail} onCheckedChange={setFail}>
模拟失败
</Switch>
<AlertDialog
title="归档这个项目?"
description="归档后项目变为只读,可以随时恢复。"
confirmText="归档"
onConfirm={archive}
onError={handleError}
content={
status && (
<Text tone="muted" size="sm">
{status}
</Text>
)
}
>
<Button variant="outline" tone="neutral">
归档项目
</Button>
</AlertDialog>
</Stack>
)
}
tsx
受控
open 可受控。省略 children 时不渲染触发器,对话框只能从外部打开。
'use client'
import { useState } from 'react'
import { AlertDialog, Button, Inline } from '@hina-ui/react'
export default function Demo() {
const [open, setOpen] = useState(false)
return (
<Inline gap="sm">
<Button variant="outline" tone="neutral" onClick={() => setOpen(true)}>
从外部打开
</Button>
<AlertDialog
open={open}
onOpenChange={setOpen}
title="离开当前页面?"
description="未保存的修改会丢失。"
/>
</Inline>
)
}
tsx
尺寸与位置
size 默认 sm,宽 384 像素,md 为 448 像素;placement 与 Dialog 相同,不设置时宽屏居中、窄屏贴底。
import { AlertDialog, Button, Inline } from '@hina-ui/react'
export default function Demo() {
return (
<Inline gap="sm">
<AlertDialog title="清空回收站?" description="回收站里的 12 个文件会被永久删除。" size="md">
<Button variant="outline" tone="neutral">
md 尺寸
</Button>
</AlertDialog>
<AlertDialog
title="清空回收站?"
description="回收站里的 12 个文件会被永久删除。"
placement="center"
>
<Button variant="outline" tone="neutral">
始终居中
</Button>
</AlertDialog>
<AlertDialog
title="清空回收站?"
description="回收站里的 12 个文件会被永久删除。"
placement="bottom"
>
<Button variant="outline" tone="neutral">
始终贴底
</Button>
</AlertDialog>
</Inline>
)
}
tsx
行为
- 打开时焦点落在「取消」按钮上,误按回车不会执行操作;关闭后焦点回到触发器。
- 点击遮罩不关闭;Esc 关闭,等同于取消。
- 打开期间页面停止滚动,焦点被限制在面板内。
- 「确定」的处理函数返回 Promise 时,对话框在其结束前处于忙碌状态,成功后关闭;失败时调用
onError并保持打开。
无障碍
- 面板是
role="alertdialog",标题与说明分别关联到aria-labelledby与aria-describedby。 - 忙碌期间面板带有
aria-busy。 - 按钮文字取自当前语言,可以替换。
API
Props
| 属性 | 类型 | 默认值 | 说明 |
|---|---|---|---|
title | string | — | 必填。问题本身 |
description | string | — | 补充说明 |
confirmText | string | 当前语言 | 「确定」按钮的文字 |
confirmDelay | number | 0 | 每次打开后的确认等待秒数 |
cancelText | string | 当前语言 | 「取消」按钮的文字 |
tone | 'accent' | 'danger' | 'accent' | 「确定」按钮的色调 |
size | 'sm' | 'md' | 'sm' | 面板的最大宽度 |
placement | 'center' | 'bottom' | — | 不设置时随屏幕宽度变化 |
onConfirm | () => unknown | — | 确认处理函数,可返回 Promise 或 PromiseLike |
open | boolean | — | 是否打开,可受控 |
className | string | — | 追加至面板的类名 |
内容属性
| 属性 | 说明 |
|---|---|
children | 触发器 |
content | 说明与按钮之间的内容 |
回调
| 回调 | 参数 | 说明 |
|---|---|---|
onConfirm | — | 点击「确定」;处理函数返回 Promise 时等待其完成 |
onCancel | — | 点击「取消」 |
onError | unknown | 确认失败的原始错误,弹窗保持打开 |