iter(g107): 新增二次确认气泡 popconfirm

This commit is contained in:
HIsheR
2026-10-26 13:59:48 +08:00
parent 137bf87d83
commit 48f94659e4
14 changed files with 287 additions and 11 deletions
+51
View File
@@ -0,0 +1,51 @@
import { effect, h, signal } from '@ui-arch/frame';
import { button } from './button.js';
import { popover } from './popover.js';
import type { PopconfirmProps } from './types.js';
/*** 二次确认气泡:危险操作就地确认,不打断页面;确定后收起再回调,取消 / 点外部 / Esc 都走 onCancel ***/
export function popconfirm(props: PopconfirmProps, trigger: HTMLElement): HTMLElement {
// —— 状态集中声明 ——
const open = props.open ?? signal(false); // 开合信号:自己持有一份,确认与取消都要能写回
let accepted = false; // 本次收起是否由「确定」触发:用来区分确认与取消
/*** 收起弹层:写回信号即触发 popover 的显隐与 Esc 栈清理 ***/
const close = (): void => { open(false); };
/*** 确认:先收起再回调,避免回调里同步改数据时弹层还开着 ***/
const accept = (): void => {
accepted = true;
close();
props.onConfirm();
};
const body = h(
'div',
{ class: 'ua-popconfirm' },
h('div', { class: 'ua-popconfirm-text' }, props.description ?? '该操作不可撤销,确定继续吗?'),
h(
'div',
{ class: 'ua-popconfirm-actions' },
button({ label: props.cancelText ?? '取消', variant: 'ghost', onClick: () => close() }),
button({ label: props.confirmText ?? '确定', variant: props.variant === 'danger' ? 'danger' : 'primary', onClick: () => accept() }),
),
);
const wrap = popover({
title: props.title,
content: body,
placement: props.placement ?? 'top', // 缺省压在上方:确认气泡不该盖住被操作的那一行
trigger: 'click',
open,
onOpenChange: (next) => {
if (next || accepted) { accepted = false; return; } // 展开、或刚由确认收起:不回调取消
props.onCancel?.(); // 点外部 / Esc 收起等同取消
},
}, trigger);
effect(() => {
if (!open()) return; // 卫语句:收起时不动焦点
requestAnimationFrame(() => (body.querySelector('button') as HTMLElement | null)?.focus()); // 展开即落在「取消」上,误按回车不会误伤
});
return wrap;
}