反馈类

气泡确认框

目标元素的操作需要用户进一步的确认时使用。与 Popover 相比它内置了一系列可配置的操作按钮,与 Modal 相比它不强制全屏居中显示,交互也更轻量

代码演示

如何引入

import { Popconfirm } from '@fibrous/semi-ui-react';

基本使用

Popconfirm 底层基于 Tooltip 封装,Children 支持类型同 Tooltip,注意事项详情可查阅 Tooltip注意事项

代码
import { Popconfirm, Button, Toast } from '@fibrous/semi-ui-react';

export default function PopconfirmDemo() {
    const onConfirm = () => {
        Toast.success('确认保存!');
    };

    const onCancel = () => {
        Toast.warning('取消保存!');
    };
    return (
        <Popconfirm title="确定是否要保存此修改?" content="此修改将不可逆" onConfirm={onConfirm} onCancel={onCancel}>
            <Button>保存</Button>
        </Popconfirm>
    );
}

类型搭配

开发者可以基于场景使用 okType/cancelType/icon 等参数搭配出不同风格的气泡式确认框。

代码
import { useState } from 'react';
import { IconAlertTriangle } from '@fibrous/semi-icons-react';
import { Popconfirm, Radio, RadioGroup, Button, type RadioChangeEvent } from '@fibrous/semi-ui-react';

export default function PopconfirmDemo() {
    const typeMap = {
        default: {
            icon: <IconAlertTriangle size="extra-large" />,
        },
        warning: {
            icon: <IconAlertTriangle style={{ color: 'var(--semi-color-warning)' }} size="extra-large" />,
        },
        danger: {
            okType: 'danger' as const,
            icon: <IconAlertTriangle style={{ color: 'var(--semi-color-danger)' }} size="extra-large" />,
        },
        tertiary: {
            icon: <IconAlertTriangle style={{ color: 'var(--semi-color-tertiary)' }} size="extra-large" />,
        },
    };

    type TypeKey = keyof typeof typeMap;
    const keys = Object.keys(typeMap) as TypeKey[];
    const [type, setType] = useState<TypeKey>('default');
    const [visible, setVisible] = useState(true);

    const changeType = (e: RadioChangeEvent) => {
        const type = e && e.target && e.target.value;
        if (type && keys.includes(type)) {
            setType(type);
        }
    };

    return (
        <div>
            <RadioGroup type="button" onChange={changeType} value={type} style={{ marginTop: 14, marginBottom: 14 }}>
                {keys.map((key) => (
                    <Radio key={key} value={key}>
                        <span style={{ color: `var(--semi-color-${key === 'default' ? 'primary' : key})` }}>{key}</span>
                    </Radio>
                ))}
            </RadioGroup>
            <div>
                <Popconfirm {...typeMap[type]} visible={visible} onVisibleChange={setVisible} trigger="custom" title="确定是否要保存此修改?" content="此修改将不可逆">
                    <Button onClick={() => setVisible(!visible)}>点击此处</Button>
                </Popconfirm>
            </div>
        </div>
    );
}

延时关闭

onConfirm、onCancel 可以通过 return Promise 实现点击后延时关闭。
onCancel、onConfirm 被触发时,对应的 Button 会自动切换为 loading: true, promise solve 会关闭气泡确认框, promise reject时气泡依然保留,同时 button loading 自动切换为 false

代码
import { Popconfirm, Button } from '@fibrous/semi-ui-react';

export default function PopconfirmDemo() {
    const onConfirm = () => {
        return new Promise<void>((resolve) => {
            setTimeout(() => {
                console.log('resolve,close popconfirm');
                resolve();
            }, 2000);
        });
    };

    const onCancel = () => {
        return new Promise((_resolve, reject) => {
            setTimeout(() => {
                console.log('reject, popconfirm still exist');
                reject();
            }, 2000);
        });
    };

    return (
        <Popconfirm title="确定是否要保存此修改?" content="此修改将不可逆" onConfirm={onConfirm} onCancel={onCancel}>
            <Button>保存</Button>
        </Popconfirm>
    );
}

初始化弹出层焦点位置

okButtonProps 和 cancelButtonProps 支持传入 autoFocus 参数,传入后打开面板时会自动聚焦在该位置。

content 支持传入函数,它的入参是一个对象,将 initialFocusRef 绑定在可聚焦 DOM 或组件上,打开面板时会自动聚焦在该位置。

代码
import { Button, Popconfirm, Space } from '@fibrous/semi-ui-react';

export default function PopconfirmDemo() {
    return (
        <Space>
            <Popconfirm
                title="确定是否要保存此修改?"
                content="此修改将不可逆"
                okButtonProps={{
                    autoFocus: true,
                    type: 'danger',
                }}
            >
                <Button>确认聚焦</Button>
            </Popconfirm>
            <Popconfirm
                title="确定是否要保存此修改?"
                content="此修改将不可逆"
                cancelButtonProps={{
                    autoFocus: true,
                }}
            >
                <Button>取消聚焦</Button>
            </Popconfirm>
            <Popconfirm
                title="确定是否要保存此修改?"
                content={({ initialFocusRef }) => {
                    return <input ref={initialFocusRef} placeholder="focus here" />;
                }}
            >
                <Button>内容聚焦</Button>
            </Popconfirm>
        </Space>
    );
}

搭配 Tooltip 或 Popover 使用

请参考搭配使用

API 参考

Props

属性说明类型默认值
afterClose确认框关闭且离场动画结束后调用。Type() => void-
afterOpen确认框打开且进场动画结束后调用;没有进场动画时打开后即调用,受控与非受控相同;进场结束前被关闭的打开不调用。TooltipProps['afterOpen']-
aria-label对话框的 aria-label,传入后优先于标题。string-
aria-labelledby对话框的 aria-labelledby,缺省指向标题,没有标题时指向内容。string-
arrowPointAtCenter箭头是否指向触发元素中心,需要同时开启 showArrow,缺省为 true。boolean-
arrowStyle箭头的边框色、背景色与边框透明度。ArrowStyle-
autoAdjustOverflow弹出层被遮挡时是否自动调整方向,缺省为 true。boolean-
cancelButtonProps取消按钮的属性。autoFocus 为 true 时打开后聚焦该按钮,className、style 与其他属性落在按钮上。ButtonProps-
cancelText取消按钮文字,缺省取语言包 Popconfirm.cancel(zh_CN 为 取消,en_US 为 Cancel)。string-
cancelType取消按钮类型。ButtonType"tertiary"
children触发元素。传入元素时由它接收触发事件与 ARIA 属性;文本等非元素内容会被包在 Tooltip 的 span 中。React.ReactNode-
className确认卡片(.semi-popconfirm)的类名,不落在弹出层上。string-
clickToHide点击弹出层及其内部任一元素时是否关闭。TooltipProps['clickToHide']-
closeOnEsc弹出层打开期间按 Esc 是否关闭确认框,与焦点位置无关,由最上层的弹出层响应,缺省为 true;trigger 为 custom 时只调用 onEscKeyDown;visible 受控时另以 onVisibleChange(false) 请求关闭,由使用方决定是否关闭。TooltipProps['closeOnEsc']-
condition是否允许触发显示,缺省为 true;只有显式设为 false 时,hover、click、focus 等触发行为才不生效。由 true 变为 false 时立即关闭已显示的确认框;visible 受控时只通过 onVisibleChange 请求关闭,使用方回写后才关闭。boolean-
content确认框内容,可以是 ReactNode,也可以是函数。函数的参数是 { initialFocusRef },把 initialFocusRef 作为 ref 绑定到打开时需要聚焦的元素上。TooltipProps['content']-
contentClassName弹出层内的内容容器(.semi-popover)的类名,与确认卡片的 className 分开设置。string | any[]-
defaultVisible非受控时初始是否展示。boolean-
disableArrowKeyDown是否禁用在触发元素上按方向键把焦点移入确认框,缺省为 false。TooltipProps['disableArrowKeyDown']-
disabled是否禁用;禁用时只渲染触发元素,不弹出确认框。booleanfalse
disableFocusListenertrigger 为 hover 时,是否不响应键盘聚焦打开,缺省为 false。boolean-
guardFocus焦点位于确认框内时,Tab 是否在其中循环,缺省为 true。TooltipProps['guardFocus']-
icon自定义图标,接受 ReactNode;传入 null 或 false 时不显示图标。React.ReactNodeType<IconAlertTriangle size="extra-large" />
keepDOM关闭时是否保留内容而不销毁,缺省为 false。boolean-
motion弹出层出现与隐藏时是否有动画,缺省为 true。boolean-
mouseEnterDelay指针移入后延迟显示的毫秒数,缺省为 50,仅 trigger 为 hover 或 focus 时生效。number-
mouseLeaveDelay指针移出后延迟隐藏的毫秒数,缺省为 50,仅 trigger 为 hover 或 focus 时生效。number-
okButtonProps确认按钮的属性。autoFocus 为 true 时打开后聚焦该按钮,className、style 与其他属性落在按钮上。ButtonProps-
okText确认按钮文字,缺省取语言包 Popconfirm.confirm(zh_CN 为 确定,en_US 为 Confirm)。string-
okType确认按钮类型。ButtonType"primary"
onCancel点击取消按钮或关闭按钮时调用,参数是原生 MouseEvent。返回 Promise 时按钮保持加载,落定后才关闭确认框,reject 时保留确认框;返回其他值会被忽略。Type(e: MouseEvent) => Promise<any> | void-
onClickOutSide弹出层处于展示状态时,点击非触发元素、非弹层内部的区域时调用,参数是原生 MouseEvent。TooltipProps['onClickOutSide']-
onConfirm点击确认按钮时调用,参数是原生 MouseEvent。返回 Promise 时按钮保持加载,落定后才关闭确认框,reject 时保留确认框;返回其他值会被忽略。Type(e: MouseEvent) => Promise<any> | void-
onEscKeyDown弹出层打开期间按 Esc 时调用,参数是原生 KeyboardEvent。TooltipProps['onEscKeyDown']-
onOpen确认框显示、进场动画开始时调用,每次打开一次,早于 afterOpen,受控与非受控相同;悬停打开后指针已离开而撤回的打开不调用。TooltipProps['onOpen']-
onVisibleChange显示状态请求变化时调用,参数是请求的显示状态。visible 受控时只报告用户交互发起的请求,使用方自己改变 visible 不触发。Type(visible: boolean) => void-
position弹出方向,可选 top、topLeft、topRight、left、leftTop、leftBottom、right、rightTop、rightBottom、bottom、bottomLeft、bottomRight,缺省为 bottomLeft(RTL 下为 bottomRight)。Position-
prefixCls确认卡片的类名前缀。string"semi-popconfirm"
ref接收句柄的引用,句柄只有 focusTrigger 方法。TypeReact.Ref<PopconfirmHandle>-
returnFocusOnClose关闭后焦点是否回到触发元素,缺省为 true;trigger 为 custom 时不生效,受控的 visible 照常生效。TooltipProps['returnFocusOnClose']-
role弹出层的 ARIA role,与 trigger 无关。string"dialog"
showArrow是否显示箭头三角形,缺省为 false。boolean-
showCloseIcon是否显示右上角关闭按钮。关闭按钮触发 onCancel。booleantrue
spacing弹出层与触发元素的距离,单位为像素,缺省为 4,开启 showArrow 时为 10;对象形式分别设置水平与垂直方向。number | { x: number; y: number }-
stopPropagation是否阻止弹出层上的点击事件冒泡。确认卡片内的点击始终不会传到文档上的监听器。boolean | stringtrue
style确认卡片的内联样式。CSSProperties-
title标题,可以是字符串或 ReactNode。有标题时,对话框以标题命名(见 aria-labelledby)。React.ReactNode-
trigger触发展示的时机,可选 hover、focus、click、contextMenu 或 custom。visible 受控时,触发器事件只通过 onVisibleChange 请求打开或关闭。Trigger"click"
visible受控的展示状态。传入后由该值决定是否显示;点击触发器、确认、取消、点击外部与 Esc 只通过 onVisibleChange 请求变化,由使用方回写 visible 后才生效。boolean-

Methods

方法说明类型
focusTrigger让触发元素获得焦点。仅在 returnFocusOnClose 为 true 且 trigger 不是 custom 时生效;disabled 时没有触发元素可聚焦,调用无效果。Type() => void

className、style 落在确认卡片上,okButtonProps、cancelButtonProps 中的 className、style 与其他属性落在对应按钮上(autoFocus 除外)。aria-label、aria-labelledby 落在 role="dialog" 的弹出层元素上,有标题和内容时该元素还通过 aria-describedby 指向内容。其他未声明的属性会被丢弃。

Accessibility

ARIA

  • 无论 trigger 是什么(包括 hover、focus),Popconfirm 的弹出层都具有 dialog role,以 title 命名(没有 title 时以 content 命名);可用 role 属性覆盖
  • 其余语义化请参考 Popover

键盘和焦点

  • Popconfirm 必须带有触发器,触发器可被聚焦,使用 Enter 键打开 Popconfirm
  • Popconfirm 激活后,按下方向键 ⬇️ 将焦点移动到 Popconfirm 上。Popconfirm 的初始焦点应当遵循以下几个原则:
    • 如果 Popconfirm 内包含一个不可逆转过程的最后一个步骤,比如:删除数据等,那么这个初始焦点最好放在破坏性最小的可交互元素上,如:关闭按钮 ( 通过向对象 cancelButtonProps 中传入 autoFocus 实现)
    • 如果 Popconfirm 内仅为阅读文本,那么建议将初始焦点设置在最可能常用的交互元素上,如:确定按钮 ( 通过向对象 okButtonProps 中传入 autoFocus 实现)
  • 键盘用户能够通过按 Esc 关闭 Popconfirm,并且焦点应该返回到触发器上。用户通过 Popconfirm 内的交互元素关闭该 Pop 后,焦点也应当返回到触发器上(trigger 为 custom 时除外)
  • 打开的情况下,用户点击 Popconfirm 内的空白处 Esc 后,焦点也会回到触发器上 (trigger 为 custom 时除外)

设计变量

下表是组件内部的 Sass 变量,不能覆写。定制样式请覆写 --semi-* CSS 变量(见 Design Token 页),或写不分层的 CSS 规则。
Sass 变量值用法
$color-popconfirm_header-textvar(--semi-color-text-0)标题文字颜色
$color-popconfirm_body-textvar(--semi-color-text-2)正文文字颜色
$color-popconfirm_header_alert-iconvar(--semi-color-warning)警示图标颜色
$width-popconfirm-icon24px图标宽度
$width-popconfirm-maxWidth400px整体最大宽度
$spacing-popconfirm-top24px顶部内边距
$spacing-popconfirm-bottom20px底部内边距
$spacing-popconfirm_with_arrow-padding12px顶部内边距
$spacing-popconfirm_header_title-marginBottom8pxheader 标题底部外边距
$spacing-popconfirm_body_p-padding0body 正文内边距
$spacing-popconfirm_body_p-margin0body 正文外边距
$spacing-popconfirm_footer-marginTop25pxfooter 顶部外边距
$spacing-popconfirm_footer_btn-marginRight8pxfooter 按钮右侧外边距
$spacing-popconfirm_popover_with_arrow_inner-padding($spacing-popconfirm-top - $spacing-popconfirm_with_arrow-padding) ($spacing-popconfirm-top - $spacing-popconfirm_with_arrow-padding) ($spacing-popconfirm-top - $spacing-popconfirm_with_arrow-padding) ($spacing-popconfirm-bottom - $spacing-popconfirm_with_arrow-padding)带三角形箭头时的内边距
$spacing-popconfirm_popover_with_arrow_inner_rtl-padding($spacing-popconfirm-top - $spacing-popconfirm_with_arrow-padding) ($spacing-popconfirm-bottom - $spacing-popconfirm_with_arrow-padding) ($spacing-popconfirm-top - $spacing-popconfirm_with_arrow-padding) ($spacing-popconfirm-top - $spacing-popconfirm_with_arrow-padding)带三角形箭头时的内边距(rtl)
$spacing-popconfirm_header_icon-marginRight12pxheader 图标的右侧外边距
$font-popconfirm_header_title-fontWeight$font-weight-boldheader 标题字重
$radius-popconfirm-popovervar(--semi-border-radius-medium)气泡确认框圆角大小

FAQ

  • 为什么 Popconfirm 浮层在靠近屏幕边界宽度不够时,丢失宽度意外换行?
    在 chromium 104 后 对于屏幕边界文本宽度不够时的换行渲染策略发生变化,详细原因可查看 issue #1022,semi侧已经在v2.17.0版本修复了这个问题。