iter(g107): 新增二次确认气泡 popconfirm
This commit is contained in:
@@ -1212,6 +1212,14 @@
|
|||||||
"acceptance": "新增 watch.ts 的 watchViewport(root, host, handler):滚动 / 缩放时若 root 已脱离文档就一次性摘掉两个监听并停止回调,否则转交 handler 并首帧同步一次;四个组件改用该助手,行为与原来一致(首帧同步保留);npm run check 全绿",
|
"acceptance": "新增 watch.ts 的 watchViewport(root, host, handler):滚动 / 缩放时若 root 已脱离文档就一次性摘掉两个监听并停止回调,否则转交 handler 并首帧同步一次;四个组件改用该助手,行为与原来一致(首帧同步保留);npm run check 全绿",
|
||||||
"notes": "自补:一致性与泄漏治理;一处实现四处复用,后续新增长期监听组件照抄",
|
"notes": "自补:一致性与泄漏治理;一处实现四处复用,后续新增长期监听组件照抄",
|
||||||
"status": "done"
|
"status": "done"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "g107",
|
||||||
|
"title": "新增二次确认气泡 popconfirm",
|
||||||
|
"motivation": "删除 / 发布这类危险操作只有两条路:直接执行(误点就没了)或弹 modal(打断视线、还要再找一次按钮)。中间档位「就地确认」缺失,而 popover 的 open 状态是组件私有的,调用方连编程收起都做不到,没法自己拼",
|
||||||
|
"acceptance": "PopoverProps 新增受控 open 信号(不传行为不变);新增 popconfirm(props, trigger):title/description/confirmText/cancelText/variant/placement/onConfirm/onCancel/open,确定先收起再回调,取消 / 点外部 / Esc 走 onCancel,展开即聚焦「取消」;组件 58→59,manifest/docs/README/AGENTS/ARCHITECTURE/demo 同步;npm run check 全绿",
|
||||||
|
"notes": "自补:组件补充;复用 popover + button,不重复实现定位与 Esc 栈",
|
||||||
|
"status": "open"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
+70
-2
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"$schema": "ui-arch-registry-index/0.1",
|
"$schema": "ui-arch-registry-index/0.1",
|
||||||
"generatedAt": "2026-10-26T05:56:43.149Z",
|
"generatedAt": "2026-10-26T05:59:36.078Z",
|
||||||
"repoRoot": ".",
|
"repoRoot": ".",
|
||||||
"packages": [
|
"packages": [
|
||||||
{
|
{
|
||||||
@@ -201,7 +201,8 @@
|
|||||||
"checkboxGroup(props):多选组,value 为数组信号,min/max 用禁用边界项实现(不抛错),checkAll 支持半选且只统计未禁用项",
|
"checkboxGroup(props):多选组,value 为数组信号,min/max 用禁用边界项实现(不抛错),checkAll 支持半选且只统计未禁用项",
|
||||||
"affix(props, ...children):固钉,滚过 offset 后内容转 fixed 贴顶、外层保留占位高度,固定后宽度随容器自适应",
|
"affix(props, ...children):固钉,滚过 offset 后内容转 fixed 贴顶、外层保留占位高度,固定后宽度随容器自适应",
|
||||||
"multiSelect(props):多选下拉,标签式回显 + 可搜索弹层,点选不收起便于连续多选,max 到限后未选项禁用",
|
"multiSelect(props):多选下拉,标签式回显 + 可搜索弹层,点选不收起便于连续多选,max 到限后未选项禁用",
|
||||||
"anchor(props):锚点导航,竖排区块链接,滚动时按目标位置高亮当前区块(aria-current 同步),点击按 offset 偏移量平滑定位并回写 hash;container 可指定内滚容器"
|
"anchor(props):锚点导航,竖排区块链接,滚动时按目标位置高亮当前区块(aria-current 同步),点击按 offset 偏移量平滑定位并回写 hash;container 可指定内滚容器",
|
||||||
|
"popconfirm(props, trigger):二次确认气泡,挂在任意触发器上,就地确认危险操作;确定后先收起再回调,取消 / 点外部 / Esc 走 onCancel,展开即聚焦「取消」"
|
||||||
],
|
],
|
||||||
"keyTypes": [
|
"keyTypes": [
|
||||||
"ButtonProps",
|
"ButtonProps",
|
||||||
@@ -1227,6 +1228,11 @@
|
|||||||
"t": "HTMLElement",
|
"t": "HTMLElement",
|
||||||
"r": 1,
|
"r": 1,
|
||||||
"d": "第二个参数:被包裹的触发器元素"
|
"d": "第二个参数:被包裹的触发器元素"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"n": "open",
|
||||||
|
"t": "Signal<boolean>",
|
||||||
|
"d": "受控开合信号;不传则内部维护,传了外部可编程开合(popconfirm 即基于它)"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"example": "popover({ title: '筛选条件', content: h('div', null, '任意可交互内容') }, button({ label: '筛选' }))",
|
"example": "popover({ title: '筛选条件', content: h('div', null, '任意可交互内容') }, button({ label: '筛选' }))",
|
||||||
@@ -2882,6 +2888,68 @@
|
|||||||
"页面有吸顶导航时把它的高度传给 offset,跳转与高亮判定都会预留这段距离",
|
"页面有吸顶导航时把它的高度传给 offset,跳转与高亮判定都会预留这段距离",
|
||||||
"滚动容器不是整页时用 container 传入该元素;点击走平滑滚动,系统开启「减少动效」时改为瞬移,hash 用 replaceState 回写不堆历史"
|
"滚动容器不是整页时用 container 传入该元素;点击走平滑滚动,系统开启「减少动效」时改为瞬移,hash 用 replaceState 回写不堆历史"
|
||||||
]
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "popconfirm",
|
||||||
|
"title": "二次确认 Popconfirm",
|
||||||
|
"demo": "sec-popconfirm",
|
||||||
|
"props": [
|
||||||
|
{
|
||||||
|
"n": "title",
|
||||||
|
"t": "string",
|
||||||
|
"r": 1,
|
||||||
|
"d": "气泡标题,通常写成一句问句(如「删除这条记录?」)"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"n": "description",
|
||||||
|
"t": "string",
|
||||||
|
"d": "补充说明:影响范围、是否可撤销;缺省给一句通用提示"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"n": "confirmText",
|
||||||
|
"t": "string",
|
||||||
|
"d": "确认按钮文案;缺省「确定」"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"n": "cancelText",
|
||||||
|
"t": "string",
|
||||||
|
"d": "取消按钮文案;缺省「取消」"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"n": "variant",
|
||||||
|
"t": "'default' | 'danger'",
|
||||||
|
"d": "确认按钮语义色;删除类操作传 danger,缺省 default"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"n": "placement",
|
||||||
|
"t": "'top' | 'bottom'",
|
||||||
|
"d": "气泡方位;缺省 top,压在触发器上方不盖住被操作的行"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"n": "onConfirm",
|
||||||
|
"t": "() => void",
|
||||||
|
"k": 1,
|
||||||
|
"d": "点确定后的回调(此时弹层已收起)"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"n": "onCancel",
|
||||||
|
"t": "() => void",
|
||||||
|
"k": 1,
|
||||||
|
"d": "取消 / 点外部 / Esc 收起时的回调"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"n": "open",
|
||||||
|
"t": "Signal<boolean>",
|
||||||
|
"d": "受控开合信号;不传则内部维护,传了可由外部编程开合"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"example": "popconfirm({ title: '删除这条记录?', description: '删除后无法恢复', variant: 'danger', confirmText: '删除', onConfirm: () => toast.success('已删除') }, button({ label: '删除', variant: 'danger' }))",
|
||||||
|
"steps": [
|
||||||
|
"把要保护的触发器元素(按钮 / 图标)作为第二个参数传进去,返回的包裹节点替换原位置",
|
||||||
|
"写清 title(问句)与 description(影响范围),删除类操作把 variant 设成 danger,确认按钮会自动变成危险色",
|
||||||
|
"点确定先收起弹层再触发 onConfirm,回调里可以放心同步改数据;取消、点外部、Esc 一律走 onCancel",
|
||||||
|
"展开时焦点自动落在「取消」上,误按回车不会执行危险操作;需要外部控制开合时传 open 信号"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -21,7 +21,7 @@
|
|||||||
|
|
||||||
- `packages/core/table-core`(@ui-arch/table-core):Headless 表格逻辑内核
|
- `packages/core/table-core`(@ui-arch/table-core):Headless 表格逻辑内核
|
||||||
- `packages/core/frame`(@ui-arch/frame):signal + hyperscript 微框架
|
- `packages/core/frame`(@ui-arch/frame):signal + hyperscript 微框架
|
||||||
- `packages/skin/ui`(@ui-arch/ui):58 个 DOM 组件(表单 / 反馈 / 浮层 / 导航 / 表格),样式只消费令牌
|
- `packages/skin/ui`(@ui-arch/ui):59 个 DOM 组件(表单 / 反馈 / 浮层 / 导航 / 表格),样式只消费令牌
|
||||||
- `packages/design/tokens`(@ui-arch/tokens):设计令牌
|
- `packages/design/tokens`(@ui-arch/tokens):设计令牌
|
||||||
- `packages/tools/cli`(@ui-arch/cli):`ua` 命令行 + MCP 注册表服务(private)
|
- `packages/tools/cli`(@ui-arch/cli):`ua` 命令行 + MCP 注册表服务(private)
|
||||||
- `apps/demo`(@ui-arch/demo):交互演示,产物 apps/demo/dist/demo.html
|
- `apps/demo`(@ui-arch/demo):交互演示,产物 apps/demo/dist/demo.html
|
||||||
|
|||||||
+1
-1
@@ -34,7 +34,7 @@ ui-arch-mvp/
|
|||||||
│ │ ├── table-core/ # 表格状态内核
|
│ │ ├── table-core/ # 表格状态内核
|
||||||
│ │ └── frame/ # 信号 + hyperscript 微框架
|
│ │ └── frame/ # 信号 + hyperscript 微框架
|
||||||
│ ├── skin/ # 皮肤层(DOM 组件,可发布)
|
│ ├── skin/ # 皮肤层(DOM 组件,可发布)
|
||||||
│ │ └── ui/ # 58 个组件 + ui.css
|
│ │ └── ui/ # 59 个组件 + ui.css
|
||||||
│ ├── design/ # 资产层(可发布)
|
│ ├── design/ # 资产层(可发布)
|
||||||
│ │ └── tokens/ # --ua-* 设计令牌
|
│ │ └── tokens/ # --ua-* 设计令牌
|
||||||
│ └── tools/ # 工程层(private,不发布)
|
│ └── tools/ # 工程层(private,不发布)
|
||||||
|
|||||||
@@ -12,13 +12,13 @@ npm run docs # 生成人类文档站 apps/docs/index.html + Agent 全
|
|||||||
npm run changelog # 依据 git 提交史重写 CHANGELOG.md(发版前跑;changelog:check 可查漂移)
|
npm run changelog # 依据 git 提交史重写 CHANGELOG.md(发版前跑;changelog:check 可查漂移)
|
||||||
```
|
```
|
||||||
|
|
||||||
## 组件(共 58 个)
|
## 组件(共 59 个)
|
||||||
|
|
||||||
<!-- ua:components -->
|
<!-- ua:components -->
|
||||||
accordion · affix · alert · anchor · autoComplete · avatar · backTop · badge · breadcrumb · button · carousel · card · cascader · checkbox · checkboxGroup · colorPicker · dataTable · datePicker · descriptions · divider · drawer · empty · form · image · inputNumber · list · menu · modal · multiSelect · notification · pagination · popover · progress · radioGroup · rate · result · segmented · select · skeleton · slider · space · spinner · splitter · statistic · stepper · switchButton · tabs · tag · textField · textarea · timeline · toast · tooltip · tour · transfer · tree · upload · watermark
|
accordion · affix · alert · anchor · autoComplete · avatar · backTop · badge · breadcrumb · button · carousel · card · cascader · checkbox · checkboxGroup · colorPicker · dataTable · datePicker · descriptions · divider · drawer · empty · form · image · inputNumber · list · menu · modal · multiSelect · notification · pagination · popover · popconfirm · progress · radioGroup · rate · result · segmented · select · skeleton · slider · space · spinner · splitter · statistic · stepper · switchButton · tabs · tag · textField · textarea · timeline · toast · tooltip · tour · transfer · tree · upload · watermark
|
||||||
<!-- /ua:components -->
|
<!-- /ua:components -->
|
||||||
|
|
||||||
分组:表单(button / textField / inputNumber / textarea / checkbox / checkboxGroup / colorPicker / switchButton / select / multiSelect / radioGroup / slider / rate / autoComplete / datePicker / segmented / upload / form + formItem / transfer / cascader)、反馈(alert / toast / notification / result / spinner / progress / skeleton / badge / empty / statistic)、浮层(tooltip / popover / modal / drawer / menu)、导航(tabs / breadcrumb / pagination / accordion / stepper / tree / anchor)、数据展示(dataTable / descriptions / card / list / timeline / tag / avatar / image / carousel)、其他(space / divider / splitter / backTop / affix / tour / watermark)。
|
分组:表单(button / textField / inputNumber / textarea / checkbox / checkboxGroup / colorPicker / switchButton / select / multiSelect / radioGroup / slider / rate / autoComplete / datePicker / segmented / upload / form + formItem / transfer / cascader)、反馈(alert / toast / notification / result / spinner / progress / skeleton / badge / empty / statistic)、浮层(tooltip / popover / popconfirm / modal / drawer / menu)、导航(tabs / breadcrumb / pagination / accordion / stepper / tree / anchor)、数据展示(dataTable / descriptions / card / list / timeline / tag / avatar / image / carousel)、其他(space / divider / splitter / backTop / affix / tour / watermark)。
|
||||||
|
|
||||||
## 可访问性约定
|
## 可访问性约定
|
||||||
|
|
||||||
@@ -32,7 +32,7 @@ accordion · affix · alert · anchor · autoComplete · avatar · backTop · ba
|
|||||||
|---|---|
|
|---|---|
|
||||||
| `packages/core/frame` | @ui-arch/frame:signal / computed / effect + hyperscript(无虚拟 DOM) |
|
| `packages/core/frame` | @ui-arch/frame:signal / computed / effect + hyperscript(无虚拟 DOM) |
|
||||||
| `packages/core/table-core` | @ui-arch/table-core:headless 表格(filter → sort → paginate 派生管线) |
|
| `packages/core/table-core` | @ui-arch/table-core:headless 表格(filter → sort → paginate 派生管线) |
|
||||||
| `packages/skin/ui` | @ui-arch/ui:58 个 DOM 组件 + ui.css(只消费 `--ua-*` 令牌) |
|
| `packages/skin/ui` | @ui-arch/ui:59 个 DOM 组件 + ui.css(只消费 `--ua-*` 令牌) |
|
||||||
| `packages/design/tokens` | @ui-arch/tokens:设计令牌(暗色只换值不换名) |
|
| `packages/design/tokens` | @ui-arch/tokens:设计令牌(暗色只换值不换名) |
|
||||||
| `packages/tools/cli` | @ui-arch/cli:`ua` 命令行与 stdio MCP server |
|
| `packages/tools/cli` | @ui-arch/cli:`ua` 命令行与 stdio MCP server |
|
||||||
| `apps/demo` | 交互演示(esbuild 打包成单文件) |
|
| `apps/demo` | 交互演示(esbuild 打包成单文件) |
|
||||||
|
|||||||
@@ -45,6 +45,7 @@ import {
|
|||||||
modal,
|
modal,
|
||||||
multiSelect,
|
multiSelect,
|
||||||
pagination,
|
pagination,
|
||||||
|
popconfirm,
|
||||||
popover,
|
popover,
|
||||||
progress,
|
progress,
|
||||||
radioGroup,
|
radioGroup,
|
||||||
@@ -1192,6 +1193,21 @@ const content = h(
|
|||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
section(
|
||||||
|
'sec-popconfirm',
|
||||||
|
'二次确认',
|
||||||
|
'popconfirm 把确认放在触发点旁边而不是弹窗里:确定先收起再回调(回调里改数据不会看到弹层还挂着),取消 / 点外部 / Esc 一律走 onCancel;展开时焦点落在「取消」上,误按回车不会删东西。',
|
||||||
|
popconfirm({
|
||||||
|
title: '删除这条记录?',
|
||||||
|
description: '删除后不可恢复,关联的审批记录会一并清空。',
|
||||||
|
variant: 'danger',
|
||||||
|
confirmText: '删除',
|
||||||
|
cancelText: '再想想',
|
||||||
|
onConfirm: () => toast.success('已删除(弹层先收起再回调)'),
|
||||||
|
onCancel: () => toast.info('已取消'),
|
||||||
|
}, button({ label: '删除记录', variant: 'danger' })),
|
||||||
|
popconfirm({ title: '发布到生产?', description: '发布后 1 分钟内全量生效。', confirmText: '发布', onConfirm: () => toast.success('已发布') }, button({ label: '发布', variant: 'ghost' })),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
// 引导漫游实例:根节点需挂到 body(fixed 定位,不参与布局)
|
// 引导漫游实例:根节点需挂到 body(fixed 定位,不参与布局)
|
||||||
|
|||||||
+32
-1
@@ -1,6 +1,6 @@
|
|||||||
# @ui-arch 组件库 Agent 文档(无图形版)
|
# @ui-arch 组件库 Agent 文档(无图形版)
|
||||||
|
|
||||||
> 自动生成于 2026-10-26T05:56:43.149Z,数据源:各包 ua.manifest.json,勿手改。人类版文档站:apps/docs/index.html。
|
> 自动生成于 2026-10-26T05:59:36.078Z,数据源:各包 ua.manifest.json,勿手改。人类版文档站:apps/docs/index.html。
|
||||||
> 组件 = 普通函数,传 props 返回 DOM;状态用 signal 双向绑定:读 signal() 写 signal(v)。
|
> 组件 = 普通函数,传 props 返回 DOM;状态用 signal 双向绑定:读 signal() 写 signal(v)。
|
||||||
|
|
||||||
## 快速上手
|
## 快速上手
|
||||||
@@ -605,6 +605,7 @@ tooltip({ content: '保存后立即生效' }, button({ label: '保存', onClick:
|
|||||||
- delay(默认 150):number —— hover 模式出场延迟毫秒;缺省 150
|
- delay(默认 150):number —— hover 模式出场延迟毫秒;缺省 150
|
||||||
- onOpenChange(回调):(open: boolean) => void —— 开合回调
|
- onOpenChange(回调):(open: boolean) => void —— 开合回调
|
||||||
- triggerEl(第二个参数)(必填):HTMLElement —— 第二个参数:被包裹的触发器元素
|
- triggerEl(第二个参数)(必填):HTMLElement —— 第二个参数:被包裹的触发器元素
|
||||||
|
- open:Signal<boolean> —— 受控开合信号;不传则内部维护,传了外部可编程开合(popconfirm 即基于它)
|
||||||
|
|
||||||
示例:
|
示例:
|
||||||
|
|
||||||
@@ -1590,6 +1591,35 @@ anchor({ items: [{ href: '#sec-button', title: '按钮' }, { href: '#sec-card',
|
|||||||
|
|
||||||
现场演示:apps/docs/demo.html#sec-anchor
|
现场演示:apps/docs/demo.html#sec-anchor
|
||||||
|
|
||||||
|
### popconfirm — 二次确认 Popconfirm
|
||||||
|
|
||||||
|
参数:
|
||||||
|
|
||||||
|
- title(必填):string —— 气泡标题,通常写成一句问句(如「删除这条记录?」)
|
||||||
|
- description:string —— 补充说明:影响范围、是否可撤销;缺省给一句通用提示
|
||||||
|
- confirmText:string —— 确认按钮文案;缺省「确定」
|
||||||
|
- cancelText:string —— 取消按钮文案;缺省「取消」
|
||||||
|
- variant:'default' | 'danger' —— 确认按钮语义色;删除类操作传 danger,缺省 default
|
||||||
|
- placement:'top' | 'bottom' —— 气泡方位;缺省 top,压在触发器上方不盖住被操作的行
|
||||||
|
- onConfirm(回调):() => void —— 点确定后的回调(此时弹层已收起)
|
||||||
|
- onCancel(回调):() => void —— 取消 / 点外部 / Esc 收起时的回调
|
||||||
|
- open:Signal<boolean> —— 受控开合信号;不传则内部维护,传了可由外部编程开合
|
||||||
|
|
||||||
|
示例:
|
||||||
|
|
||||||
|
```
|
||||||
|
popconfirm({ title: '删除这条记录?', description: '删除后无法恢复', variant: 'danger', confirmText: '删除', onConfirm: () => toast.success('已删除') }, button({ label: '删除', variant: 'danger' }))
|
||||||
|
```
|
||||||
|
|
||||||
|
手把手:
|
||||||
|
|
||||||
|
1. 把要保护的触发器元素(按钮 / 图标)作为第二个参数传进去,返回的包裹节点替换原位置
|
||||||
|
2. 写清 title(问句)与 description(影响范围),删除类操作把 variant 设成 danger,确认按钮会自动变成危险色
|
||||||
|
3. 点确定先收起弹层再触发 onConfirm,回调里可以放心同步改数据;取消、点外部、Esc 一律走 onCancel
|
||||||
|
4. 展开时焦点自动落在「取消」上,误按回车不会执行危险操作;需要外部控制开合时传 open 信号
|
||||||
|
|
||||||
|
现场演示:apps/docs/demo.html#sec-popconfirm
|
||||||
|
|
||||||
API 速览:
|
API 速览:
|
||||||
|
|
||||||
- button(props):按钮,variant = primary | ghost | danger,disabled 支持布尔或信号禁用;type 缺省 button(表单内不隐式提交)
|
- button(props):按钮,variant = primary | ghost | danger,disabled 支持布尔或信号禁用;type 缺省 button(表单内不隐式提交)
|
||||||
@@ -1652,6 +1682,7 @@ API 速览:
|
|||||||
- affix(props, ...children):固钉,滚过 offset 后内容转 fixed 贴顶、外层保留占位高度,固定后宽度随容器自适应
|
- affix(props, ...children):固钉,滚过 offset 后内容转 fixed 贴顶、外层保留占位高度,固定后宽度随容器自适应
|
||||||
- multiSelect(props):多选下拉,标签式回显 + 可搜索弹层,点选不收起便于连续多选,max 到限后未选项禁用
|
- multiSelect(props):多选下拉,标签式回显 + 可搜索弹层,点选不收起便于连续多选,max 到限后未选项禁用
|
||||||
- anchor(props):锚点导航,竖排区块链接,滚动时按目标位置高亮当前区块(aria-current 同步),点击按 offset 偏移量平滑定位并回写 hash;container 可指定内滚容器
|
- anchor(props):锚点导航,竖排区块链接,滚动时按目标位置高亮当前区块(aria-current 同步),点击按 offset 偏移量平滑定位并回写 hash;container 可指定内滚容器
|
||||||
|
- popconfirm(props, trigger):二次确认气泡,挂在任意触发器上,就地确认危险操作;确定后先收起再回调,取消 / 点外部 / Esc 走 onCancel,展开即聚焦「取消」
|
||||||
|
|
||||||
一行用法:
|
一行用法:
|
||||||
|
|
||||||
|
|||||||
@@ -107,6 +107,7 @@
|
|||||||
- affix(props, ...children):固钉,滚过 offset 后内容转 fixed 贴顶、外层保留占位高度,固定后宽度随容器自适应
|
- affix(props, ...children):固钉,滚过 offset 后内容转 fixed 贴顶、外层保留占位高度,固定后宽度随容器自适应
|
||||||
- multiSelect(props):多选下拉,标签式回显 + 可搜索弹层,点选不收起便于连续多选,max 到限后未选项禁用
|
- multiSelect(props):多选下拉,标签式回显 + 可搜索弹层,点选不收起便于连续多选,max 到限后未选项禁用
|
||||||
- anchor(props):锚点导航,竖排区块链接,滚动时按目标位置高亮当前区块(aria-current 同步),点击按 offset 偏移量平滑定位并回写 hash;container 可指定内滚容器
|
- anchor(props):锚点导航,竖排区块链接,滚动时按目标位置高亮当前区块(aria-current 同步),点击按 offset 偏移量平滑定位并回写 hash;container 可指定内滚容器
|
||||||
|
- popconfirm(props, trigger):二次确认气泡,挂在任意触发器上,就地确认危险操作;确定后先收起再回调,取消 / 点外部 / Esc 走 onCancel,展开即聚焦「取消」
|
||||||
- 用法: `const checked = signal(false); checkbox({ label: '同意', checked }); const open = signal(false); modal({ title: '确认', open }, '内容')`
|
- 用法: `const checked = signal(false); checkbox({ label: '同意', checked }); const open = signal(false); modal({ title: '确认', open }, '内容')`
|
||||||
- 约定: 皮肤不持有业务状态,一律委托 headless 内核或调用方信号
|
- 约定: 皮肤不持有业务状态,一律委托 headless 内核或调用方信号
|
||||||
- 约定: 表单类组件统一约定:*Props.value/checked/active 为 Signal 双向绑定,onChange/onInput 为回调
|
- 约定: 表单类组件统一约定:*Props.value/checked/active 为 Signal 双向绑定,onChange/onInput 为回调
|
||||||
|
|||||||
@@ -58,3 +58,4 @@ export { tour } from './tour.js';
|
|||||||
export { watermark } from './watermark.js';
|
export { watermark } from './watermark.js';
|
||||||
export { affix } from './affix.js';
|
export { affix } from './affix.js';
|
||||||
export { anchor } from './anchor.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 / 再次点触发器收起 ***/
|
/*** 气泡卡片:包裹触发器,默认点击开合;内容可交互,点外部 / Esc / 再次点触发器收起 ***/
|
||||||
export function popover(props: PopoverProps, trigger: HTMLElement): HTMLElement {
|
export function popover(props: PopoverProps, trigger: HTMLElement): HTMLElement {
|
||||||
// —— 状态集中声明 ——
|
// —— 状态集中声明 ——
|
||||||
const open = signal(false); // 弹层显隐
|
const open = props.open ?? signal(false); // 弹层显隐:外部传了信号就交给它控制(实现受控开合)
|
||||||
let releaseEsc: (() => void) | null = null; // Esc 栈注销函数:展开期间占一层,收起即释放
|
let releaseEsc: (() => void) | null = null; // Esc 栈注销函数:展开期间占一层,收起即释放
|
||||||
const place = signal(props.placement ?? 'bottom'); // 实际生效方位:打开时按视口空间自动翻转
|
const place = signal(props.placement ?? 'bottom'); // 实际生效方位:打开时按视口空间自动翻转
|
||||||
const id = `ua-pop-${++seq}`; // 弹层唯一 id
|
const id = `ua-pop-${++seq}`; // 弹层唯一 id
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ export interface PopoverProps {
|
|||||||
trigger?: 'click' | 'hover'; // 触发方式;缺省 click(hover 模式带 delay 延迟)
|
trigger?: 'click' | 'hover'; // 触发方式;缺省 click(hover 模式带 delay 延迟)
|
||||||
delay?: number; // hover 模式出场延迟毫秒;缺省 150
|
delay?: number; // hover 模式出场延迟毫秒;缺省 150
|
||||||
onOpenChange?: (open: boolean) => void; // 开合回调
|
onOpenChange?: (open: boolean) => void; // 开合回调
|
||||||
|
open?: Signal<boolean>; // 受控开合信号;不传则组件内部维护,传了外部可编程开合
|
||||||
}
|
}
|
||||||
|
|
||||||
/*** 按钮入参 ***/
|
/*** 按钮入参 ***/
|
||||||
@@ -704,4 +705,17 @@ export interface AnchorProps {
|
|||||||
onChange?: (href: string) => void; // 跳转后的回调(埋点或联动滚动)
|
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 }; // 再导出常用内核类型,方便调用方
|
export type { ColumnDef, RowKey }; // 再导出常用内核类型,方便调用方
|
||||||
|
|||||||
@@ -72,7 +72,8 @@
|
|||||||
"checkboxGroup(props):多选组,value 为数组信号,min/max 用禁用边界项实现(不抛错),checkAll 支持半选且只统计未禁用项",
|
"checkboxGroup(props):多选组,value 为数组信号,min/max 用禁用边界项实现(不抛错),checkAll 支持半选且只统计未禁用项",
|
||||||
"affix(props, ...children):固钉,滚过 offset 后内容转 fixed 贴顶、外层保留占位高度,固定后宽度随容器自适应",
|
"affix(props, ...children):固钉,滚过 offset 后内容转 fixed 贴顶、外层保留占位高度,固定后宽度随容器自适应",
|
||||||
"multiSelect(props):多选下拉,标签式回显 + 可搜索弹层,点选不收起便于连续多选,max 到限后未选项禁用",
|
"multiSelect(props):多选下拉,标签式回显 + 可搜索弹层,点选不收起便于连续多选,max 到限后未选项禁用",
|
||||||
"anchor(props):锚点导航,竖排区块链接,滚动时按目标位置高亮当前区块(aria-current 同步),点击按 offset 偏移量平滑定位并回写 hash;container 可指定内滚容器"
|
"anchor(props):锚点导航,竖排区块链接,滚动时按目标位置高亮当前区块(aria-current 同步),点击按 offset 偏移量平滑定位并回写 hash;container 可指定内滚容器",
|
||||||
|
"popconfirm(props, trigger):二次确认气泡,挂在任意触发器上,就地确认危险操作;确定后先收起再回调,取消 / 点外部 / Esc 走 onCancel,展开即聚焦「取消」"
|
||||||
],
|
],
|
||||||
"keyTypes": [
|
"keyTypes": [
|
||||||
"ButtonProps",
|
"ButtonProps",
|
||||||
@@ -1098,6 +1099,11 @@
|
|||||||
"t": "HTMLElement",
|
"t": "HTMLElement",
|
||||||
"r": 1,
|
"r": 1,
|
||||||
"d": "第二个参数:被包裹的触发器元素"
|
"d": "第二个参数:被包裹的触发器元素"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"n": "open",
|
||||||
|
"t": "Signal<boolean>",
|
||||||
|
"d": "受控开合信号;不传则内部维护,传了外部可编程开合(popconfirm 即基于它)"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"example": "popover({ title: '筛选条件', content: h('div', null, '任意可交互内容') }, button({ label: '筛选' }))",
|
"example": "popover({ title: '筛选条件', content: h('div', null, '任意可交互内容') }, button({ label: '筛选' }))",
|
||||||
@@ -2753,6 +2759,68 @@
|
|||||||
"页面有吸顶导航时把它的高度传给 offset,跳转与高亮判定都会预留这段距离",
|
"页面有吸顶导航时把它的高度传给 offset,跳转与高亮判定都会预留这段距离",
|
||||||
"滚动容器不是整页时用 container 传入该元素;点击走平滑滚动,系统开启「减少动效」时改为瞬移,hash 用 replaceState 回写不堆历史"
|
"滚动容器不是整页时用 container 传入该元素;点击走平滑滚动,系统开启「减少动效」时改为瞬移,hash 用 replaceState 回写不堆历史"
|
||||||
]
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "popconfirm",
|
||||||
|
"title": "二次确认 Popconfirm",
|
||||||
|
"demo": "sec-popconfirm",
|
||||||
|
"props": [
|
||||||
|
{
|
||||||
|
"n": "title",
|
||||||
|
"t": "string",
|
||||||
|
"r": 1,
|
||||||
|
"d": "气泡标题,通常写成一句问句(如「删除这条记录?」)"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"n": "description",
|
||||||
|
"t": "string",
|
||||||
|
"d": "补充说明:影响范围、是否可撤销;缺省给一句通用提示"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"n": "confirmText",
|
||||||
|
"t": "string",
|
||||||
|
"d": "确认按钮文案;缺省「确定」"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"n": "cancelText",
|
||||||
|
"t": "string",
|
||||||
|
"d": "取消按钮文案;缺省「取消」"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"n": "variant",
|
||||||
|
"t": "'default' | 'danger'",
|
||||||
|
"d": "确认按钮语义色;删除类操作传 danger,缺省 default"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"n": "placement",
|
||||||
|
"t": "'top' | 'bottom'",
|
||||||
|
"d": "气泡方位;缺省 top,压在触发器上方不盖住被操作的行"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"n": "onConfirm",
|
||||||
|
"t": "() => void",
|
||||||
|
"k": 1,
|
||||||
|
"d": "点确定后的回调(此时弹层已收起)"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"n": "onCancel",
|
||||||
|
"t": "() => void",
|
||||||
|
"k": 1,
|
||||||
|
"d": "取消 / 点外部 / Esc 收起时的回调"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"n": "open",
|
||||||
|
"t": "Signal<boolean>",
|
||||||
|
"d": "受控开合信号;不传则内部维护,传了可由外部编程开合"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"example": "popconfirm({ title: '删除这条记录?', description: '删除后无法恢复', variant: 'danger', confirmText: '删除', onConfirm: () => toast.success('已删除') }, button({ label: '删除', variant: 'danger' }))",
|
||||||
|
"steps": [
|
||||||
|
"把要保护的触发器元素(按钮 / 图标)作为第二个参数传进去,返回的包裹节点替换原位置",
|
||||||
|
"写清 title(问句)与 description(影响范围),删除类操作把 variant 设成 danger,确认按钮会自动变成危险色",
|
||||||
|
"点确定先收起弹层再触发 onConfirm,回调里可以放心同步改数据;取消、点外部、Esc 一律走 onCancel",
|
||||||
|
"展开时焦点自动落在「取消」上,误按回车不会执行危险操作;需要外部控制开合时传 open 信号"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3720,3 +3720,21 @@ button.ua-step-dot:hover {
|
|||||||
transform: none;
|
transform: none;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* —— 二次确认气泡 —— */
|
||||||
|
.ua-popconfirm {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: var(--ua-space-2);
|
||||||
|
max-width: 240px;
|
||||||
|
}
|
||||||
|
.ua-popconfirm-text {
|
||||||
|
color: var(--ua-color-text-muted);
|
||||||
|
font-size: var(--ua-font-size-md);
|
||||||
|
line-height: 1.5;
|
||||||
|
}
|
||||||
|
.ua-popconfirm-actions {
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-end;
|
||||||
|
gap: var(--ua-space-2);
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user