Demos
How to import
import { Button, Popconfirm, Radio, RadioGroup, Space, Toast } from '@fibrous/semi-ui-solid';
Basic Usage
Popconfirm is based on the Tooltip component. Children support the same type as Tooltip. For details, please refer to Tooltip Cautions
Code
import { Button, Popconfirm, Toast } from '@fibrous/semi-ui-solid';
export default function PopconfirmDemo() {
const onConfirm = () => {
Toast.success('确认保存!');
};
const onCancel = () => {
Toast.warning('取消保存!');
};
return (
<Popconfirm title="确定是否要保存此修改?" content="此修改将不可逆" onConfirm={onConfirm} onCancel={onCancel}>
{(trigger) => <Button {...trigger()}>保存</Button>}
</Popconfirm>
);
}Type collocation
Developers can use scenario-based okType, cancelType and icon to match different styles of bubble confirmation boxes. icon takes a JSX element; null or false renders no icon.
Code
import { createSignal, For } from 'solid-js';
import { IconAlertTriangle } from '@fibrous/semi-icons-solid';
import { Button, Popconfirm, Radio, RadioGroup, type RadioChangeEvent } from '@fibrous/semi-ui-solid';
type TypeKey = 'default' | 'warning' | 'danger' | 'tertiary';
const keys: TypeKey[] = ['default', 'warning', 'danger', 'tertiary'];
// The default icon keeps its theme color, so no color is set for it
const iconColors: Record<TypeKey, string | undefined> = {
default: undefined,
warning: 'var(--semi-color-warning)',
danger: 'var(--semi-color-danger)',
tertiary: 'var(--semi-color-tertiary)',
};
export default function PopconfirmDemo() {
const [type, setType] = createSignal<TypeKey>('default');
const [visible, setVisible] = createSignal(true);
const changeType = (event: RadioChangeEvent) => {
const key = keys.find((item) => item === event.target.value);
if (key) {
setType(key);
}
};
return (
<div>
<RadioGroup type="button" onChange={changeType} value={type()} style={{ 'margin-top': '14px', 'margin-bottom': '14px' }}>
<For each={keys}>
{(key) => (
<Radio value={key}>
<span style={{ color: `var(--semi-color-${key === 'default' ? 'primary' : key})` }}>{key}</span>
</Radio>
)}
</For>
</RadioGroup>
<div>
<Popconfirm visible={visible()} onVisibleChange={(value) => setVisible(value)} trigger="custom" okType={type() === 'danger' ? 'danger' : 'primary'} icon={<IconAlertTriangle size="extra-large" style={{ color: iconColors[type()] }} />} title="确定是否要保存此修改?" content="此修改将不可逆">
{(trigger) => (
<Button {...trigger()} onClick={() => setVisible(!visible())}>
点击此处
</Button>
)}
</Popconfirm>
</div>
</div>
);
}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
import { Button, Popconfirm } from '@fibrous/semi-ui-solid';
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}>
{(trigger) => <Button {...trigger()}>保存</Button>}
</Popconfirm>
);
}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.
content accepts a function whose { initialFocusRef } argument carries initialFocusRef, a ref callback: bind it with ref={initialFocusRef} to the focusable DOM or component, 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
import { Button, Popconfirm, Space } from '@fibrous/semi-ui-solid';
export default function PopconfirmDemo() {
return (
<Space>
<Popconfirm title="确定是否要保存此修改?" content="此修改将不可逆" okButtonProps={{ autoFocus: true, type: 'danger' }}>
{(trigger) => <Button {...trigger()}>确认聚焦</Button>}
</Popconfirm>
<Popconfirm title="确定是否要保存此修改?" content="此修改将不可逆" cancelButtonProps={{ autoFocus: true }}>
{(trigger) => <Button {...trigger()}>取消聚焦</Button>}
</Popconfirm>
<Popconfirm title="确定是否要保存此修改?" content={(renderProps) => <input ref={renderProps.initialFocusRef} placeholder="focus here" />}>
{(trigger) => <Button {...trigger()}>内容聚焦</Button>}
</Popconfirm>
</Space>
);
}Use with Tooltip or Popover
Please refer to Use with Tooltip/Popover
API Reference
Props
| Property | Description | Type | Default |
|---|---|---|---|
| afterClose | Called after the card has closed and its leave animation has finished. | Type() => void | - |
| afterOpen | Called after the card has opened and its enter animation has finished, or right after opening when there is no enter animation, controlled or not. An open that is closed before the enter animation ends does not call it. | TooltipProps['afterOpen'] | - |
| aria-label | aria-label of the dialog. When given, it takes precedence over the title. | string | - |
| aria-labelledby | aria-labelledby of the dialog. Defaults to the title, or to the content without a title. | string | - |
| arrowPointAtCenter | Whether the arrow points at the center of the trigger. Requires showArrow. Defaults to true. | boolean | - |
| arrowStyle | Border color, background color and border opacity of the arrow. | ArrowStyle | - |
| autoAdjustOverflow | Whether the popup flips automatically when it would be clipped. Defaults to true. | boolean | - |
| cancelButtonProps | Props of the cancel button. autoFocus focuses it on open; class, style and other props land on the button. | PopconfirmButtonProps | - |
| cancelText | Cancel button text. Defaults to the locale entry Popconfirm.cancel. | string | - |
| cancelType | Cancel button type. Defaults to tertiary. | ButtonType | "tertiary" |
| children | The trigger. Use the function form (getTriggerProps) => JSX.Element and spread {...getTriggerProps()} onto the focusable element, which gets the focus, keyboard and ARIA attributes. Any other form is wrapped in the span of Tooltip. | TooltipProps['children'] | - |
| class | Class name of the confirmation card (.semi-popconfirm), not of the popup. | string | - |
| clickToHide | Whether a click on the popup or anything inside it closes it. | TooltipProps['clickToHide'] | - |
| closeOnEsc | Whether Esc closes the open card, wherever focus is. Defaults to true. With trigger custom it only calls onEscKeyDown, and the caller decides whether to close. | TooltipProps['closeOnEsc'] | - |
| condition | Whether it may be triggered, defaults to true. 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 requests the close through onVisibleChange, and the card closes once the caller writes back. | boolean | - |
| content | Content of the card: a JSX element or a function. The function receives { initialFocusRef }; pass initialFocusRef as the ref of the element that takes focus on open. | TooltipProps['content'] | - |
| contentClassName | Class name of the content container. | ClassValue | - |
| defaultVisible | Whether it is initially shown when uncontrolled. | boolean | - |
| disableArrowKeyDown | Whether the arrow keys on the trigger are prevented from moving focus into the card. | TooltipProps['disableArrowKeyDown'] | - |
| disabled | Whether it is disabled, defaults to false. A disabled Popconfirm renders only the trigger and never opens. | boolean | false |
| disableFocusListener | Whether keyboard focus is ignored when trigger is hover. | boolean | - |
| guardFocus | Whether Tab cycles inside the card while focus is in it. Defaults to true. | TooltipProps['guardFocus'] | - |
| icon | Custom icon, a JSX element. The default warning icon shows when omitted; null or false renders no icon. | JSX.Element | - |
| keepDOM | Whether the content stays mounted while closed. | boolean | - |
| motion | Whether the popup animates when it appears and hides. Defaults to true. | boolean | - |
| mouseEnterDelay | Milliseconds before it shows after the pointer enters. Applies when trigger is hover or focus. | number | - |
| mouseLeaveDelay | Milliseconds before it hides after the pointer leaves. Applies when trigger is hover or focus. | number | - |
| okButtonProps | Props of the confirm button. autoFocus focuses it on open; class, style and other props land on the button. | PopconfirmButtonProps | - |
| okText | Confirm button text. Defaults to the locale entry Popconfirm.confirm (确定 in zh_CN, Confirm in en_US). | string | - |
| okType | Confirm button type. Defaults to primary. | ButtonType | "primary" |
| onCancel | Called with the native MouseEvent when the cancel or close button is clicked. A returned Promise keeps the button loading and closes the card once it settles; a rejection keeps the card open. Any other return value is ignored. | Type(e: MouseEvent) => unknown | - |
| onClickOutSide | Called with the native MouseEvent when the popup is shown and the mouse is pressed outside the trigger and the popup. | TooltipProps['onClickOutSide'] | - |
| onConfirm | Called with the native MouseEvent when the confirm button is clicked. A returned Promise keeps the button loading and closes the card once it settles; a rejection keeps the card open. Any other return value is ignored. | Type(e: MouseEvent) => unknown | - |
| onEscKeyDown | Called with the native KeyboardEvent when Esc is pressed while the popup is open. | TooltipProps['onEscKeyDown'] | - |
| onOpen | Called once per opening when the card is shown, as its enter animation starts, before afterOpen, controlled or not. A hover open withdrawn because the pointer already left does not call it. | TooltipProps['onOpen'] | - |
| onVisibleChange | Called with the requested visibility. With a controlled visible it reports only requests from user interaction, not changes the caller makes to visible. | Type(visible: boolean) => void | - |
| position | Popup position. Defaults to bottomLeft (bottomRight in RTL). | Position | - |
| prefixCls | Class prefix of the confirmation card. Defaults to semi-popconfirm. | string | "semi-popconfirm" |
| ref | Callback that receives the handle, which only has focusTrigger. It is called once when the component is created. | TypeRef<PopconfirmHandle> | - |
| returnFocusOnClose | Whether focus returns to the trigger after closing. Defaults to true. It has no effect when trigger is custom, and works as usual with a controlled visible. | TooltipProps['returnFocusOnClose'] | - |
| role | ARIA role of the popup. Defaults to dialog whatever the trigger. | string | "dialog" |
| showArrow | Whether the arrow triangle is shown. Defaults to false. | boolean | - |
| showCloseIcon | Whether the close button in the top-right corner is shown. Defaults to true. It calls onCancel. | boolean | true |
| spacing | Distance in pixels between the popup and the trigger. An object sets the horizontal and vertical distances. | number | { x: number; y: number } | - |
| stopPropagation | Whether click events on the popup stop bubbling, defaults to true. Clicks inside the card never reach document listeners. | boolean | string | true |
| style | Style of the confirmation card, as an object with kebab-case keys or as a string. | JSX.CSSProperties | string | - |
| title | Title, a string or a JSX element. With a title, the dialog is named by it (see aria-labelledby). | JSX.Element | - |
| trigger | When it shows: hover, focus, click, contextMenu or custom. Defaults to click. With a controlled visible, trigger events only request opening or closing through onVisibleChange. | Trigger | "click" |
| visible | Controlled visibility. When given, this value decides whether it shows. Clicking the trigger, confirming, cancelling, clicking outside and Esc only request a change through onVisibleChange, which takes effect once the caller writes visible back. | boolean | - |
Methods
| Method | Description | Type |
|---|---|---|
| focusTrigger | Moves focus to the trigger element. It only takes effect when returnFocusOnClose is true and trigger is not custom. When disabled, there is no trigger element to focus and the call does nothing. | Type() => void |
class and style land on the confirmation card, and the props in okButtonProps and cancelButtonProps land on their buttons (except autoFocus); ref receives the focusTrigger handle. Popconfirm has no root DOM element, so other undeclared native attributes and events are dropped.
Accessibility
ARIA
- Whatever the trigger,
hoverandfocusincluded, the Popconfirm popup has roledialog, named bytitle(bycontentwhen there is notitle); theroleprop overrides it - For the rest, please refer to Popover
Keyboard and focus
- Popconfirm must have trigger, trigger can be focused, use
Enterkey 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
autoFocusto the objectcancelButtonProps) - 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
autoFocusto the objectokButtonProps)
- 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
- Keyboard users can dismiss Popconfirm by pressing
Escand 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 iscustom) - When it is opened, after the user clicks
Escin the blank space in Popconfirm, the focus will also return to the trigger (except when trigger iscustom)
Design Tokens
| Sass variable | Value | Usage |
|---|---|---|
| $color-popconfirm_header-text | var(--semi-color-text-0) | 标题文字颜色 |
| $color-popconfirm_body-text | var(--semi-color-text-2) | 正文文字颜色 |
| $color-popconfirm_header_alert-icon | var(--semi-color-warning) | 警示图标颜色 |
| $width-popconfirm-icon | 24px | 图标宽度 |
| $width-popconfirm-maxWidth | 400px | 整体最大宽度 |
| $spacing-popconfirm-top | 24px | 顶部内边距 |
| $spacing-popconfirm-bottom | 20px | 底部内边距 |
| $spacing-popconfirm_with_arrow-padding | 12px | 顶部内边距 |
| $spacing-popconfirm_header_title-marginBottom | 8px | header 标题底部外边距 |
| $spacing-popconfirm_body_p-padding | 0 | body 正文内边距 |
| $spacing-popconfirm_body_p-margin | 0 | body 正文外边距 |
| $spacing-popconfirm_footer-marginTop | 25px | footer 顶部外边距 |
| $spacing-popconfirm_footer_btn-marginRight | 8px | footer 按钮右侧外边距 |
| $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-marginRight | 12px | header 图标的右侧外边距 |
| $font-popconfirm_header_title-fontWeight | $font-weight-bold | header 标题字重 |
| $radius-popconfirm-popover | var(--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.