iter(g107): 新增二次确认气泡 popconfirm
This commit is contained in:
@@ -58,3 +58,4 @@ export { tour } from './tour.js';
|
||||
export { watermark } from './watermark.js';
|
||||
export { affix } from './affix.js';
|
||||
export { anchor } from './anchor.js';
|
||||
export { popconfirm } from './popconfirm.js';
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -8,7 +8,7 @@ let seq = 0; // 全局自增序号:保证每个弹层 id 唯一,供 aria-con
|
||||
/*** 气泡卡片:包裹触发器,默认点击开合;内容可交互,点外部 / Esc / 再次点触发器收起 ***/
|
||||
export function popover(props: PopoverProps, trigger: HTMLElement): HTMLElement {
|
||||
// —— 状态集中声明 ——
|
||||
const open = signal(false); // 弹层显隐
|
||||
const open = props.open ?? signal(false); // 弹层显隐:外部传了信号就交给它控制(实现受控开合)
|
||||
let releaseEsc: (() => void) | null = null; // Esc 栈注销函数:展开期间占一层,收起即释放
|
||||
const place = signal(props.placement ?? 'bottom'); // 实际生效方位:打开时按视口空间自动翻转
|
||||
const id = `ua-pop-${++seq}`; // 弹层唯一 id
|
||||
|
||||
@@ -16,6 +16,7 @@ export interface PopoverProps {
|
||||
trigger?: 'click' | 'hover'; // 触发方式;缺省 click(hover 模式带 delay 延迟)
|
||||
delay?: number; // hover 模式出场延迟毫秒;缺省 150
|
||||
onOpenChange?: (open: boolean) => void; // 开合回调
|
||||
open?: Signal<boolean>; // 受控开合信号;不传则组件内部维护,传了外部可编程开合
|
||||
}
|
||||
|
||||
/*** 按钮入参 ***/
|
||||
@@ -704,4 +705,17 @@ export interface AnchorProps {
|
||||
onChange?: (href: string) => void; // 跳转后的回调(埋点或联动滚动)
|
||||
}
|
||||
|
||||
/*** 二次确认气泡入参 ***/
|
||||
export interface PopconfirmProps {
|
||||
title: string; // 气泡标题,通常写成一句问句
|
||||
description?: string; // 补充说明:影响范围 / 不可撤销提示
|
||||
confirmText?: string; // 确认按钮文案;缺省「确定」
|
||||
cancelText?: string; // 取消按钮文案;缺省「取消」
|
||||
variant?: 'default' | 'danger'; // 确认按钮语义色;缺省 default,删除类操作传 danger
|
||||
placement?: 'top' | 'bottom'; // 气泡方位;缺省 top(不盖住被操作的行)
|
||||
onConfirm: () => void; // 点确定后的回调(弹层已收起)
|
||||
onCancel?: () => void; // 取消 / 点外部 / Esc 收起时的回调
|
||||
open?: Signal<boolean>; // 受控开合信号;不传则内部维护
|
||||
}
|
||||
|
||||
export type { ColumnDef, RowKey }; // 再导出常用内核类型,方便调用方
|
||||
|
||||
Reference in New Issue
Block a user