popconfirm — Popconfirm
Delete, submit, publish — operations like these that “are hard to undo once done” need the user to confirm once more. dialog is too heavy (modal, grabs focus, mask); popconfirm pops a small bubble in place: one sentence + two buttons, and it disappears as soon as you confirm. It is a component whose interaction weight lands exactly between tooltip and dialog.
When to use
Section titled “When to use”- Each framework implements
<LhPopconfirm>: deleting a row, undoing an operation, clearing data, submitting an order. - Lightweight, in-place, low-undo-room secondary confirmation.
- Do not use it for: consequences serious enough to need the user’s full attention (use
dialog— the modal mask forces focus); purely informational messages (usetooltip/message); confirmations that need a reason filled in or checkboxes (use adialogwith a form).
Import
Section titled “Import”import { LH_POPCONFIRM_TYPE_DEFAULT, LH_POPCONFIRM_PLACEMENT_DEFAULT, getPopconfirmIconName, getPopconfirmClass, getPopconfirmTriggerAria, getPopconfirmPopperAria, getDefaultDelay, canCloseOnEscape,} from '@lh-ui/share/core/popconfirm'import type { LhPopconfirmType, LhPopconfirmTrigger, LhPopconfirmState } from '@lh-ui/share/core/popconfirm'Design intent
Section titled “Design intent”The default tone is warning, not info: LH_POPCONFIRM_TYPE_DEFAULT = 'warning' — a confirmation bubble appearing almost always means “there is risk”, so the default should be a cautionary tone. info is left for purely informative confirmations (“Mark as read?”), danger for irreversible operations (“This cannot be undone after deletion”).
Three tones, three icons: info → info, warning → warning, danger → error (the icon name is error rather than danger — the icon library’s naming is error; this is a cross-naming mapping). An unknown type falls back to warning.
role="alertdialog" instead of popover’s "dialog": alertdialog is the ARIA concept for “a dialog requiring an immediate user response” — screen readers prioritize announcing it (with alert-like interruptiveness, but requiring the user to make a choice). This fits the “waiting for you to decide” semantics better than dialog.
The confirm/cancel event contract lives in the UI layer: core only provides class names, icons, aria, and delays; the onConfirm / onCancel events, button text, and loading state are all in the UI layer — because they involve component-tree communication (emit), which core never touches through framework APIs.
Delays follow popover: getDefaultDelay defaults to 200/100, identical to popover — a confirmation bubble also allows hover triggering, and the pointer must be able to enter it.
Function table
Section titled “Function table”| Function | Signature | Returns | Purpose |
|---|---|---|---|
getPopconfirmIconName |
(type: LhPopconfirmType) => string |
icon name | unknown type falls back to warning |
getPopconfirmClass |
(state: { type, placement, popperClass? }) => string[] |
class names | tone + position |
getPopconfirmTriggerAria |
(visible, popperId?) => LhAriaAttrs |
aria bag | expanded/controls/haspopup |
getPopconfirmPopperAria |
(id?) => LhAriaAttrs |
aria bag | role=alertdialog |
getDefaultDelay |
(showAfter?, hideAfter?) => { show, hide } |
delays | defaults 200 / 100 |
canCloseOnEscape |
(state: { disabled }) => boolean |
whether allowed | blocks when disabled |
Constants: LH_POPCONFIRM_TYPE_DEFAULT = 'warning', LH_POPCONFIRM_PLACEMENT_DEFAULT = 'top'.
Type definitions
Section titled “Type definitions”export type LhPopconfirmType = 'info' | 'warning' | 'danger'export type LhPopconfirmTrigger = 'click' | 'hover' | 'focus' | 'manual'
export interface LhPopconfirmState { type?: LhPopconfirmType trigger?: LhPopconfirmTrigger placement?: LhPlacement visible?: boolean disabled?: boolean showIcon?: boolean // default true}Accessibility
Section titled “Accessibility”getPopconfirmPopperAria('pc-1')// { role: 'alertdialog', id: 'pc-1' }getPopconfirmTriggerAria(true, 'pc-1')// { 'aria-expanded': true, 'aria-controls': 'pc-1', 'aria-haspopup': 'true' }- Announcement of alertdialog: when a screen reader encounters an alertdialog it immediately announces its content (aria-labelledby or inner text), and the user does not need to go read it actively — this is where it is more forceful than dialog, matching the “waiting for your confirmation” intent.
- Buttons need clear text: “Confirm / Cancel” beats “OK / NO” or icon buttons. Under a
dangertone, the confirm button’s text should state the consequence (“Delete” rather than “Confirm”). - Cancel is the safe direction: should Enter trigger confirm or cancel by default? A common practice is to place initial focus on the cancel button (one Tab + Enter exits safely) — for destructive operations, do not pre-set focus on the confirm button. This is the UI layer’s decision; core does not care.
- Esc = cancel: when
canCloseOnEscapeallows it, Esc should trigger onCancel rather than merely closing — semantically, “closing the confirmation bubble” means “not confirming”. - Icons are aria-hidden: the semantic icons (warning/error) are already conveyed by the text, so the icons themselves are decorative, avoiding screen readers announcing “warning” redundantly.
Usage examples
Section titled “Usage examples”import { getPopconfirmIconName, getPopconfirmClass, LH_POPCONFIRM_TYPE_DEFAULT } from '@lh-ui/share/core/popconfirm'
getPopconfirmIconName('info') // 'info'getPopconfirmIconName('warning') // 'warning'getPopconfirmIconName('danger') // 'error'getPopconfirmIconName('other' as never) // 'warning' (fallback)
getPopconfirmClass({ type: 'danger', placement: 'top' })// ['lh-popconfirm__popper', 'lh-popconfirm__popper--danger', 'lh-popconfirm__popper--top']
LH_POPCONFIRM_TYPE_DEFAULT // 'warning'<!-- Vue3 --><template> <LhPopconfirm title="This cannot be undone after deletion. Delete this row?" type="danger" confirm-text="Delete" cancel-text="Cancel" @confirm="remove(row)" > <template #reference> <LhButton type="danger" size="small">Delete</LhButton> </template> </LhPopconfirm></template>
<script setup>async function remove(row) { // triggered by onConfirm; the component can support confirmLoading: the bubble only closes after the async completes await api.delete(row.id) LhMessage.success('Deleted')}</script>// Async confirmation: while confirming, disable the buttons + show loading; on failure keep the bubble open// This is UI-layer logic: core holds no async state// React: onConfirm/onCancel come in via props; a controlled visible pairs with async confirmation// uniapp: a <view> bubble; button press states use hover-class; the event contract is the same modelStyles and tokens
Section titled “Styles and tokens”| Class name | Semantics |
|---|---|
.lh-popconfirm__popper |
bubble container |
.lh-popconfirm__popper--{info|warning|danger} |
tone (icon color + confirm button color) |
.lh-popconfirm__popper--{top|bottom|…} |
position |
.lh-popconfirm__icon |
tone icon |
.lh-popconfirm__title |
title text |
.lh-popconfirm__actions |
button area (right-aligned) |
The confirm button’s color follows the tone: danger uses --lh-danger-color, warning uses --lh-warning-color, info uses --lh-primary-color.
Caveats
Section titled “Caveats”- The title should state the consequence: “Are you sure you want to delete?” is bad copy (it does not state the consequence); “This cannot be undone after deletion. Delete this row?” is good copy. This is the core of the popconfirm experience.
- Do not nest: a popconfirm inside a popconfirm leaves the user unsure which layer they are confirming. Use a dialog for multi-step confirmations.
- When the trigger is a disabled button: a
disabledbutton does not fire events (a native limitation), so the popconfirm cannot open — the business layer must handle this itself (wrap it in a non-disabled element, or do not render it at all when disabled). - The close timing for async confirmation: while confirm is being processed, both buttons should be disabled + masked against repeated clicks; on failure keep the bubble open and show the error, and only close on success. Synchronously closing the bubble inside onConfirm is a common bug.
- Pairing with message: after a confirmation completes, use
messagefor the success feedback (“Deleted”) — the two form the standard chain of “confirm → execute → feedback”. - Hover triggering on mobile: same as popover — touch screens have no hover. Use click triggering by default across platforms.
role="alertdialog"carries no implicit semantics for the buttons: the button area must be rendered as real<button>s (not div + onclick), so that keyboard users can operate them.
@lh-ui/share/core/popconfirm → getPopconfirmIconName( )(填好参数后点运行)Copyright © 2024-2026 FengMo WenXue