Feedback · Popconfirm

Popconfirm

Used when the operation of the target element requires further confirmation from the user. Compared with Popover, it has a built-in series of configurable action buttons. Compared with Modal, it does not force full-screen centering, and the interaction is lighter.

Demos

How to import

<script setup lang="ts">
    import { Popconfirm } from '@fibrous/semi-ui-vue';
</script>

Basic Usage

Popconfirm is based on the Tooltip component. Children support the same type as Tooltip. For details, please refer to Tooltip Cautions

Code
<script setup lang="ts">
    import { Button, Popconfirm, Toast } from '@fibrous/semi-ui-vue';

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

    const onCancel = () => {
        Toast.warning('取消保存!');
    };
</script>

<template>
    <Popconfirm @cancel="onCancel" @confirm="onConfirm">
        <Button>保存</Button>
        <template #title>确定是否要保存此修改?</template>
        <template #content>此修改将不可逆</template>
    </Popconfirm>
</template>

Type collocation

Developers can use scenario-based okType/cancelType and the #icon slot to match different styles of bubble confirmation boxes. Without an #icon slot the default warning icon is shown; an empty #icon slot renders no icon.

Code
<script setup lang="ts">
    import { ref } from 'vue';
    import { IconAlertTriangle } from '@fibrous/semi-icons-vue';
    import { Button, Popconfirm, Radio, RadioGroup, type RadioValue } from '@fibrous/semi-ui-vue';

    type TypeKey = 'default' | 'warning' | 'danger' | 'tertiary';

    const typeMap: Record<TypeKey, { okType?: 'danger'; color?: string }> = {
        default: {},
        warning: { color: 'var(--semi-color-warning)' },
        danger: { okType: 'danger', color: 'var(--semi-color-danger)' },
        tertiary: { color: 'var(--semi-color-tertiary)' },
    };
    const keys = Object.keys(typeMap) as TypeKey[];
    const type = ref<TypeKey>('default');
    const visible = ref(true);

    const changeType = (value: RadioValue | undefined) => {
        const key = keys.find((item) => item === value);
        if (key) type.value = key;
    };
</script>

<template>
    <div>
        <RadioGroup :modelValue="type" :style="{ marginTop: '14px', marginBottom: '14px' }" type="button" @update:modelValue="changeType">
            <Radio v-for="key in keys" :key="key" :value="key">
                <span :style="{ color: `var(--semi-color-${key === 'default' ? 'primary' : key})` }">{{ key }}</span>
            </Radio>
        </RadioGroup>
        <div>
            <Popconfirm v-model:visible="visible" :okType="typeMap[type].okType" trigger="custom">
                <Button @click="visible = !visible">点击此处</Button>
                <template #icon>
                    <IconAlertTriangle size="extra-large" :style="{ color: typeMap[type].color }" />
                </template>
                <template #title>确定是否要保存此修改?</template>
                <template #content>此修改将不可逆</template>
            </Popconfirm>
        </div>
    </div>
</template>

Delay hide

onConfirm and onCancel can be closed after click through return Promise.
When onCancel and onConfirm are triggered, the corresponding Button will automatically switch to loading: true
promise solve will close the bubble confirmation box, the bubble will remain when promise reject, and button loading will automatically switch to false

Code
<script setup lang="ts">
    import { Button, Popconfirm } from '@fibrous/semi-ui-vue';

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

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

<template>
    <Popconfirm @cancel="onCancel" @confirm="onConfirm">
        <Button>保存</Button>
        <template #title>确定是否要保存此修改?</template>
        <template #content>此修改将不可逆</template>
    </Popconfirm>
</template>

Initialize the Focus Position of Popup Layer

okButtonProps and cancelButtonProps support passing in the autoFocus parameter, which will automatically focus at this position when the panel is opened.

The #content slot receives { initialFocusRef } as its scope. Bind it to the focusable DOM or component with :ref, and it will automatically focus at this position when the panel is opened. When a button autoFocus and initialFocusRef are both given, the button wins.

Code
<script setup lang="ts">
    import { Button, Popconfirm, Space } from '@fibrous/semi-ui-vue';
</script>

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

Use with Tooltip or Popover

Please refer to Use with Tooltip/Popover

API Reference

Props

PropertyDescriptionTypeDefault
cancelButtonPropsProps of the cancel button. autoFocus focuses it on open; class and style land on the button.PopconfirmButtonPropssemiGlobalDefault("Popconfirm", "cancelButtonProps")
cancelTextCancel button text. Defaults to the locale entry Popconfirm.cancel.stringsemiGlobalDefault("Popconfirm", "cancelText")
cancelTypeType of the cancel button.TypesemiDefault("Popconfirm", "cancelType", "tertiary")
defaultVisibleWhether it is initially shown when uncontrolled.booleansemiGlobalDefault("Popconfirm", "defaultVisible")
disabledWhether it is disabled. A disabled Popconfirm renders only the trigger and never opens.booleansemiDefault("Popconfirm", "disabled", false)
okButtonPropsProps of the confirm button. autoFocus focuses it on open; class and style land on the button.PopconfirmButtonPropssemiGlobalDefault("Popconfirm", "okButtonProps")
okTextConfirm button text. Defaults to the locale entry Popconfirm.confirm.stringsemiGlobalDefault("Popconfirm", "okText")
okTypeType of the confirm button.TypesemiDefault("Popconfirm", "okType", "primary")
showCloseIconWhether the close button in the corner is shown.booleansemiDefault("Popconfirm", "showCloseIcon", true)
titlePlain-text title, which also names the dialog. The #title slot wins when both are given. It is a declared prop, so it never lands on an element as the native title attribute.stringundefined
onCancelCalled when the cancel or close button is clicked. A returned Promise keeps the button loading and closes the card once it resolves; a rejection keeps it open. This remains a function prop so its result can be read.Type((event: MouseEvent) => void | Promise<unknown>)undefined
onConfirmCalled when the confirm button is clicked. A returned Promise keeps the button loading and closes the card once it resolves; a rejection keeps it open. This remains a function prop so its result can be read.Type((event: MouseEvent) => void | Promise<unknown>)undefined
ariaLabelaria-label of the dialog. When given, it takes precedence over the title.stringsemiGlobalDefault("Popconfirm", "ariaLabel")
ariaLabelledbyaria-labelledby of the dialog. Defaults to the title, or to the content without a title.stringsemiGlobalDefault("Popconfirm", "ariaLabelledby")
ariaDescribedbyaria-describedby of the dialog. Defaults to the content when there are both a title and content.stringsemiGlobalDefault("Popconfirm", "ariaDescribedby")
roleARIA role of the popup. Defaults to dialog.stringsemiDefault("Popconfirm", "role", "dialog")
autoAdjustOverflowWhether the popup flips automatically when it would be clipped.booleansemiGlobalDefault("Popconfirm", "autoAdjustOverflow")
visibleWhether it is shown, used with v-model:visible. When given, this value decides; trigger keeps its value and the trigger events still work.booleansemiGlobalDefault("Popconfirm", "visible")
positionPopup position. Defaults to bottomLeft (bottomRight in RTL).Type"top" | "bottom" | "left" | "right" | "topLeft" | "topRight" | "leftTop" | "leftBottom" | "rightTop" | "rightBottom" | "bottomLeft" | "bottomRight" | "leftTopOver" | "rightTopOver" | "leftBottomOver" | "rightBottomOver"semiGlobalDefault("Popconfirm", "position")
motionWhether the popup is animated.booleansemiGlobalDefault("Popconfirm", "motion")
mouseEnterDelayMilliseconds before it shows after the pointer enters. Applies to the hover and focus triggers.numbersemiGlobalDefault("Popconfirm", "mouseEnterDelay")
mouseLeaveDelayMilliseconds before it hides after the pointer leaves. Applies to the hover and focus triggers.numbersemiGlobalDefault("Popconfirm", "mouseLeaveDelay")
triggerWhen it shows: hover, focus, click, contextMenu or custom."hover" | "focus" | "click" | "custom" | "contextMenu"semiDefault("Popconfirm", "trigger", "click")
contentClassNameClass name of the content container.string | unknown[]semiGlobalDefault("Popconfirm", "contentClassName")
showArrowWhether the arrow is shown.booleansemiGlobalDefault("Popconfirm", "showArrow")
spacingDistance in pixels between the popup and the trigger. An object sets the horizontal and vertical distances.number | { x: number; y: number; }semiGlobalDefault("Popconfirm", "spacing")
stopPropagationWhether click, mousedown and focus in/out events on the popup stop propagating. Clicks inside the card never propagate.string | booleansemiDefault("Popconfirm", "stopPropagation", true)
arrowStyleBorder color, background color and border opacity of the arrow.ArrowStylesemiGlobalDefault("Popconfirm", "arrowStyle")
arrowPointAtCenterWhether the arrow points at the center of the trigger. Requires showArrow.booleansemiGlobalDefault("Popconfirm", "arrowPointAtCenter")
prefixClsClass prefix of the confirmation card.stringsemiDefault("Popconfirm", "prefixCls", cssClasses.PREFIX)
closeOnEscWhether Esc closes the open card. With trigger custom it only emits escKeyDown and the caller decides whether to close.booleansemiGlobalDefault("Popconfirm", "closeOnEsc")
guardFocusWhether Tab cycles inside the card while focus is in it.booleansemiGlobalDefault("Popconfirm", "guardFocus")
returnFocusOnCloseWhether focus returns to the trigger after closing. It also works with a controlled visible, and has no effect with trigger custom.booleansemiGlobalDefault("Popconfirm", "returnFocusOnClose")
clickToHideWhether a click on the popup or anything inside it closes it.booleansemiGlobalDefault("Popconfirm", "clickToHide")
disableArrowKeyDownWhether the arrow keys on the trigger are prevented from moving focus into the card.booleansemiGlobalDefault("Popconfirm", "disableArrowKeyDown")
disableFocusListenerWhether keyboard focus is ignored when trigger is hover.booleansemiGlobalDefault("Popconfirm", "disableFocusListener")
keepDOMWhether the content stays mounted while closed.booleansemiGlobalDefault("Popconfirm", "keepDOM")
conditionWhether it may be triggered. Only an explicit false disables the hover, click and focus triggers. Changing it from true to false closes an open card at once; with a controlled visible it only emits update:visible and visibleChange(false), and the card closes once the caller writes back.booleansemiGlobalDefault("Popconfirm", "condition")

Events

EventDescriptionCallback parameters
openEmitted once per opening when the card is shown, as its enter animation starts, before afterOpen, controlled or not; not emitted for a hover open withdrawn because the pointer already left.[]
update:visibleEmitted when it should show or hide, for v-model:visible to write back.[visible: boolean]
visibleChangeEmitted when the visibility changes. After a click on the trigger it fires before the card appears. With a controlled visible it reports only requests from user interaction, not changes the parent makes to visible.[visible: boolean]
clickOutSideEmitted when the mouse is pressed outside of the trigger and the open card. Only for trigger click, contextMenu or custom.[event: MouseEvent]
escKeyDownEmitted when Esc is pressed while the card is open and closeOnEsc is true (the default).[event: KeyboardEvent]
afterOpenEmitted after the card has opened and its enter animation has finished, or right after opening when there is no enter animation, controlled or not; not emitted for an open that is closed before the enter animation ends.[]
afterCloseEmitted after the card has closed and its leave animation has finished.[]

Slots

SlotDescriptionScope parameters
defaultThe trigger.any
titleTitle of the card.any
contentContent of the card. The scope parameter initialFocusRef binds to the element that receives focus on open. An empty slot renders no content area.TypeRenderContentProps<FocusTarget>
iconCustom icon. The default warning icon shows when omitted; an empty slot renders no icon.any

Exposes

NameDescriptionType
focusTriggerFocuses the trigger. Only works when returnFocusOnClose is true and trigger is not custom; does nothing when disabled.Type() => void

class and style land on the confirmation card, and the class and style in okButtonProps and cancelButtonProps land on the buttons; other undeclared attributes are discarded. aria-label, aria-labelledby and aria-describedby are declared props and land on the role="dialog" element. When the subtree is cached by <KeepAlive> (deactivated), the popup is treated as closed and update:visible and visibleChange are emitted with false; a popup whose visible is one-way bound to true is hidden with the cached subtree and shows again once reactivated.

When visible is controlled, clicking or hovering the trigger emits update:visible and visibleChange for the caller to write back with v-model:visible; changes the parent makes to visible itself emit neither, and returnFocusOnClose works as usual. With a one-way :visible and no write-back, confirming or cancelling does not return focus to the trigger at once; focus goes back when the card has really closed.

Accessibility

ARIA

  • Whatever the trigger, hover and focus included, the Popconfirm popup has role dialog, named by title (by content when there is no title); the role prop overrides it
  • For the rest, please refer to Popover

Keyboard and focus

  • Popconfirm must have trigger, trigger can be focused, use Enter key to open Popconfirm
  • After Popconfirm is activated, press the arrow key ⬇️ to move the focus to Popconfirm. The initial focus of Popconfirm should follow several principles:
    • If the Popconfirm contains the last step of an irreversible process, such as: deleting data, etc., then this initial focus is preferably on the least destructive interactable element, such as: the cancel button (by passing the autoFocus to the object cancelButtonProps)
    • If you only read text in Popconfirm, it is recommended to set the initial focus on the most likely interactive elements, such as: confirm button (implemented by passing autoFocus to the object okButtonProps )
  • Keyboard users can dismiss Popconfirm by pressing Esc and focus should return to the trigger. After the user closes the Pop through the interactive element within the Popconfirm, the focus should also return to the trigger (except when trigger is custom)
  • When it is opened, after the user clicks Esc in the blank space in Popconfirm, the focus will also return to the trigger (except when trigger is custom)

Design Tokens

These are the component's internal Sass variables and cannot be overridden. To customize styles, override the --semi-* CSS variables (see the Design Token page) or write unlayered CSS rules.
Sass variableValueUsage
$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

  • Why does the Popconfirm floating layer lose its width and wrap unexpectedly when the width is not enough near the screen border?

    After Chromium 104, the wrapping rendering strategy when the width of the screen border text is not enough has changed. For details, see issue #1022, the semi-side has been This problem was fixed in v2.17.0.