写得真好,期待下一章。
这段剧情和原作不一样吧?
已收藏。
'use client'
import { useState } from 'react'
import { Button, Inline, Popconfirm, Stack, Text } from '@hina-ui/react'
export default function Demo() {
const [comments, setComments] = useState([
'写得真好,期待下一章。',
'这段剧情和原作不一样吧?',
'已收藏。',
])
function remove(index: number) {
setComments(current => current.filter((_, position) => position !== index))
}
return (
<Stack gap="sm" align="stretch" className="w-96">
{comments.map((comment, index) => (
<Inline key={comment} align="center" justify="between" gap="sm">
<Text size="sm">{comment}</Text>
<Popconfirm
title="删除这条评论?"
description="删除后无法恢复。"
tone="danger"
confirmText="删除"
onConfirm={() => remove(index)}
>
<Button size="sm" variant="ghost" tone="neutral">
删除
</Button>
</Popconfirm>
</Inline>
))}
{!comments.length ? (
<Text tone="muted" size="sm">
没有评论了。
</Text>
) : null}
</Stack>
)
}
tsx
用法
import { Popconfirm } from '@hina-ui/react'
ts
气泡确认框在触发器旁边问一个问题,适合删除一行、撤销一次操作这类范围小、就地可见的确认;后果重、需要打断当前任务的确认用 AlertDialog。title 是问题,description 补充后果,底部一对按钮的文字可以用 cancelText 与 confirmText 替换。children 是触发器。
'use client'
import { useState } from 'react'
import { Button, Popconfirm, Stack, Text } from '@hina-ui/react'
export default function Demo() {
const [done, setDone] = useState(false)
return (
<Stack gap="sm" align="start">
<Popconfirm title="把全部通知标记为已读?" onConfirm={() => setDone(true)}>
<Button variant="outline" tone="neutral">
全部已读
</Button>
</Popconfirm>
{done ? (
<Text tone="muted" size="sm">
已全部标记为已读。
</Text>
) : null}
</Stack>
)
}
tsx
示例
危险操作
tone="danger" 把「确定」换成危险色,用于删除这类不可撤销的操作。
import { Button, Popconfirm } from '@hina-ui/react'
export default function Demo() {
return (
<Popconfirm
title="移除这位成员?"
description="移除后对方将无法访问这个项目。"
tone="danger"
confirmText="移除"
>
<Button variant="outline" tone="danger">
移除成员
</Button>
</Popconfirm>
)
}
tsx
异步确认
onConfirm 回调返回 Promise 时,气泡等它结束再收回:期间「确定」显示加载指示,「取消」、Esc 与点击外部都不可用;处理函数抛出错误时气泡保持打开。
'use client'
import { useState } from 'react'
import { Button, Popconfirm, Stack, Switch, Text } from '@hina-ui/react'
export default function Demo() {
const [fail, setFail] = useState(false)
const [status, setStatus] = useState('')
async function retry() {
setStatus('')
await new Promise(resolve => setTimeout(resolve, 1000))
if (fail) {
setStatus('重试失败,请稍后再试。')
throw new Error('retry failed')
}
setStatus('已重新发送。')
}
return (
<Stack gap="md" align="start">
<Switch checked={fail} onCheckedChange={setFail}>
模拟失败
</Switch>
<Popconfirm
title="重新发送这封邮件?"
confirmText="重发"
onConfirm={retry}
content={
status ? (
<Text tone="muted" size="sm">
{status}
</Text>
) : null
}
>
<Button variant="outline" tone="neutral">
重新发送
</Button>
</Popconfirm>
</Stack>
)
}
tsx
位置
side 与 align 与 Popover 相同,默认在触发器正下方,空间不足时翻转到相反一侧。
import { Button, Inline, Popconfirm } from '@hina-ui/react'
const sides = ['top', 'right', 'bottom', 'left'] as const
const names = { top: '上方', right: '右侧', bottom: '下方', left: '左侧' }
export default function Demo() {
return (
<Inline gap="sm">
{sides.map(side => (
<Popconfirm key={side} title="确认这个位置?" side={side}>
<Button variant="outline" tone="neutral">
{names[side]}
</Button>
</Popconfirm>
))}
</Inline>
)
}
tsx
受控
open 可受控,可以从外部打开或者收回。
'use client'
import { useState } from 'react'
import { Button, Inline, Popconfirm } 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>
<Popconfirm
open={open}
onOpenChange={setOpen}
title="清空草稿?"
description="草稿里的内容会被丢弃。"
>
<Button variant="outline" tone="neutral">
清空草稿
</Button>
</Popconfirm>
</Inline>
)
}
tsx
行为
- 打开后焦点落在「取消」按钮上,误按回车不会执行操作;收回后焦点回到触发器。
- Esc 与点击外部只是收回气泡,不调用
onCancel;点「取消」才调用。 - 打开期间页面停止滚动,触发器保持按下时的样式。
onConfirm返回 Promise 时,气泡在其结束前处于忙碌状态,结束后收回;抛出错误则保持打开。
无障碍
- 面板是
role="dialog",名称取自触发器,问题与说明一起关联到aria-describedby。 - 忙碌期间面板带有
aria-busy。 - 按钮文字取自当前语言,可以替换。
API
Props
| 属性 | 类型 | 默认值 | 说明 |
|---|---|---|---|
title | string | — | 必填。问题本身 |
description | string | — | 补充说明 |
confirmText | string | 当前语言 | 「确定」按钮的文字 |
cancelText | string | 当前语言 | 「取消」按钮的文字 |
tone | 'accent' | 'danger' | 'accent' | 「确定」按钮的色调 |
side | 'top' | 'right' | 'bottom' | 'left' | 'bottom' | 浮出的方向 |
align | 'start' | 'center' | 'end' | 'center' | 与触发器的对齐方式 |
sideOffset | number | 8 | 与触发器的距离,像素 |
open | boolean | — | 是否打开,可受控 |
className | string | — | 追加至面板的类名 |
内容属性
| 属性 | 说明 |
|---|---|
children | 触发器 |
content | 说明与按钮之间的内容 |
回调
| 回调 | 参数 | 说明 |
|---|---|---|
onConfirm | — | 点击「确定」;处理函数返回 Promise 时等待其完成 |
onCancel | — | 点击「取消」 |