iter(g143): 页签栏支持右侧操作区

This commit is contained in:
HIsheR
2026-10-26 15:38:50 +08:00
parent 5b8472eb1f
commit 3058da101d
8 changed files with 55 additions and 6 deletions
+8
View File
@@ -1500,6 +1500,14 @@
"acceptance": "space 新增 split:仅在 2 个以上子元素间插入分隔符,分隔符 aria-hidden、弱化色、不可选中;文档与演示同步;npm run check 全绿", "acceptance": "space 新增 split:仅在 2 个以上子元素间插入分隔符,分隔符 aria-hidden、弱化色、不可选中;文档与演示同步;npm run check 全绿",
"notes": "自补:既有组件可用性细节", "notes": "自补:既有组件可用性细节",
"status": "done" "status": "done"
},
{
"id": "g143",
"title": "页签栏支持右侧操作区 extra",
"motivation": "「新建 / 设置」这类页签级操作只能塞进页签数组或浮在页签栏外,垂直不对齐、分隔线断掉;调用方还得自己包一层 flex",
"acceptance": "tabs 新增 extra:与页签同一行、共用一条分隔线,不传时 DOM 结构不变(纯增量);文档 props/steps 与演示同步;npm run check 全绿",
"notes": "自补:既有组件可用性细节",
"status": "open"
} }
] ]
} }
+8 -2
View File
@@ -1,6 +1,6 @@
{ {
"$schema": "ui-arch-registry-index/0.1", "$schema": "ui-arch-registry-index/0.1",
"generatedAt": "2026-10-26T07:37:18.841Z", "generatedAt": "2026-10-26T07:38:33.907Z",
"repoRoot": ".", "repoRoot": ".",
"packages": [ "packages": [
{ {
@@ -1385,6 +1385,11 @@
"k": 1, "k": 1,
"d": "关闭页签回调:组件不自行移除,凭 value 从数据源里摘掉后重绘" "d": "关闭页签回调:组件不自行移除,凭 value 从数据源里摘掉后重绘"
}, },
{
"n": "extra",
"t": "unknown",
"d": "页签栏右侧操作区内容(按钮 / 链接):与页签同一行、共用一条分隔线;缺省不渲染且页签栏结构不变"
},
{ {
"n": "closable", "n": "closable",
"t": "boolean", "t": "boolean",
@@ -1397,7 +1402,8 @@
"tabs 只渲染页签头;页签内容用响应式函数按信号切换,如 () => tab() === 'a' ? panelA : panelB", "tabs 只渲染页签头;页签内容用响应式函数按信号切换,如 () => tab() === 'a' ? panelA : panelB",
"点击或方向键(←→ 环形)、Home/End(直达首/末页签)切换,滑动指示条自动跟随;disabled: true 的页签置灰不可点", "点击或方向键(←→ 环形)、Home/End(直达首/末页签)切换,滑动指示条自动跟随;disabled: true 的页签置灰不可点",
"需要可关闭页签时给该项加 closable: true,× 或聚焦后按 Delete 触发 onClose(value);组件不改数据源,调用方从数组里摘掉该项再重绘", "需要可关闭页签时给该项加 closable: true,× 或聚焦后按 Delete 触发 onClose(value);组件不改数据源,调用方从数组里摘掉该项再重绘",
"键盘可访问:Tab 聚焦激活页签,←→ 在启用页签间环形移动,Home/End 跳首个/末个启用页签" "键盘可访问:Tab 聚焦激活页签,←→ 在启用页签间环形移动,Home/End 跳首个/末个启用页签",
"页签栏右侧要放「新建 / 设置」这类操作就传 extra(按钮或链接):它与页签同一行共用一条分隔线,不传时 DOM 结构保持不变"
] ]
}, },
{ {
+5 -1
View File
@@ -522,7 +522,11 @@ const content = h(
'sec-tabs-modal', 'sec-tabs-modal',
'标签页与弹窗', '标签页与弹窗',
'tabs 滑动指示条 + 方向键切换(←→),禁用页签置灰,closable 页签带 ×(聚焦后 Delete 也能关,关闭只回调、由数据源摘除后重绘);modal 支持 Esc / 遮罩 / × 关闭(closeOnOverlay: false 时点遮罩不关、防填一半误触),弹窗内的下拉展开时 Esc 先收下拉、再按才关弹窗(逐层关闭)。', 'tabs 滑动指示条 + 方向键切换(←→),禁用页签置灰,closable 页签带 ×(聚焦后 Delete 也能关,关闭只回调、由数据源摘除后重绘);modal 支持 Esc / 遮罩 / × 关闭(closeOnOverlay: false 时点遮罩不关、防填一半误触),弹窗内的下拉展开时 Esc 先收下拉、再按才关弹窗(逐层关闭)。',
tabs({ tabs: [{ value: 'a', label: '概览' }, { value: 'b', label: '明细' }, { value: 'c', label: '设置' }, { value: 'd', label: '已停用', disabled: true }], active: activeTab }), tabs({
tabs: [{ value: 'a', label: '概览' }, { value: 'b', label: '明细' }, { value: 'c', label: '设置' }, { value: 'd', label: '已停用', disabled: true }],
active: activeTab,
extra: button({ label: '新建页签', variant: 'ghost', onClick: () => toast.info('右侧操作区与页签同一行') }), // extra 放页签栏右侧
}),
h('span', { class: 'out' }, () => `当前页签:${activeTab()}`), h('span', { class: 'out' }, () => `当前页签:${activeTab()}`),
renderList(h('div', { class: 'row' }), () => { renderList(h('div', { class: 'row' }), () => {
const items = closableTabs(); const items = closableTabs();
+3 -1
View File
@@ -1,6 +1,6 @@
# @ui-arch 组件库 Agent 文档(无图形版) # @ui-arch 组件库 Agent 文档(无图形版)
> 自动生成于 2026-10-26T07:37:18.841Z,数据源:各包 ua.manifest.json,勿手改。人类版文档站:apps/docs/index.html。 > 自动生成于 2026-10-26T07:38:33.907Z,数据源:各包 ua.manifest.json,勿手改。人类版文档站:apps/docs/index.html。
> 组件 = 普通函数,传 props 返回 DOM;状态用 signal 双向绑定:读 signal() 写 signal(v)。 > 组件 = 普通函数,传 props 返回 DOM;状态用 signal 双向绑定:读 signal() 写 signal(v)。
## 快速上手 ## 快速上手
@@ -691,6 +691,7 @@ const pct = signal(0); progress({ value: pct }); setInterval(() => pct((pct() +
- active:Signal<string> —— 当前激活页签 value 信号,双向绑定 - active:Signal<string> —— 当前激活页签 value 信号,双向绑定
- onChange(回调):(value: string) => void —— 切换回调 - onChange(回调):(value: string) => void —— 切换回调
- onClose(回调):(value: string) => void —— 关闭页签回调:组件不自行移除,凭 value 从数据源里摘掉后重绘 - onClose(回调):(value: string) => void —— 关闭页签回调:组件不自行移除,凭 value 从数据源里摘掉后重绘
- extra:unknown —— 页签栏右侧操作区内容(按钮 / 链接):与页签同一行、共用一条分隔线;缺省不渲染且页签栏结构不变
- closable:boolean —— 页签项上的开关:true 时渲染关闭按钮(×),关闭按钮不进 Tab 序但可用鼠标点;缺省不可关 - closable:boolean —— 页签项上的开关:true 时渲染关闭按钮(×),关闭按钮不进 Tab 序但可用鼠标点;缺省不可关
示例: 示例:
@@ -706,6 +707,7 @@ const tab = signal('a'); tabs({ tabs: [{ value: 'a', label: '概览', closable:
3. 点击或方向键(←→ 环形)、Home/End(直达首/末页签)切换,滑动指示条自动跟随;disabled: true 的页签置灰不可点 3. 点击或方向键(←→ 环形)、Home/End(直达首/末页签)切换,滑动指示条自动跟随;disabled: true 的页签置灰不可点
4. 需要可关闭页签时给该项加 closable: true,× 或聚焦后按 Delete 触发 onClose(value);组件不改数据源,调用方从数组里摘掉该项再重绘 4. 需要可关闭页签时给该项加 closable: true,× 或聚焦后按 Delete 触发 onClose(value);组件不改数据源,调用方从数组里摘掉该项再重绘
5. 键盘可访问:Tab 聚焦激活页签,←→ 在启用页签间环形移动,Home/End 跳首个/末个启用页签 5. 键盘可访问:Tab 聚焦激活页签,←→ 在启用页签间环形移动,Home/End 跳首个/末个启用页签
6. 页签栏右侧要放「新建 / 设置」这类操作就传 extra(按钮或链接):它与页签同一行共用一条分隔线,不传时 DOM 结构保持不变
现场演示:apps/docs/demo.html#sec-tabs-modal 现场演示:apps/docs/demo.html#sec-tabs-modal
+3 -1
View File
@@ -64,7 +64,9 @@ export function tabs(props: TabsProps): HTMLElement {
next?.click(); next?.click();
}); });
const root = h('div', { class: 'ua-tabs' }, head); const extra = props.extra ? h('div', { class: 'ua-tabs-extra' }, props.extra) : null; // 右侧操作区:与页签同一行
const bar = extra ? h('div', { class: 'ua-tabs-bar' }, head, extra) : head; // 有操作区才包一层,无操作区结构不变
const root = h('div', { class: 'ua-tabs' }, bar);
effect(sync); effect(sync);
requestAnimationFrame(sync); // 挂载后再量一次布局,否则指示条宽度为 0 requestAnimationFrame(sync); // 挂载后再量一次布局,否则指示条宽度为 0
trackSize(root, sync); // 容器变宽 / 窗口缩放后重算,指示条不会停在旧宽度 trackSize(root, sync); // 容器变宽 / 窗口缩放后重算,指示条不会停在旧宽度
+1
View File
@@ -347,6 +347,7 @@ export interface TabsProps {
active?: Signal<string>; // 当前激活页签的 value 信号,双向绑定 active?: Signal<string>; // 当前激活页签的 value 信号,双向绑定
onChange?: (value: string) => void; // 切换回调 onChange?: (value: string) => void; // 切换回调
onClose?: (value: string) => void; // 关闭页签回调:组件不自行移除,凭 value 从数据源里摘掉 onClose?: (value: string) => void; // 关闭页签回调:组件不自行移除,凭 value 从数据源里摘掉
extra?: unknown; // 页签栏右侧操作区内容(按钮 / 链接);缺省不渲染,页签栏结构不变
} }
/*** 弹窗入参 ***/ /*** 弹窗入参 ***/
+7 -1
View File
@@ -1256,6 +1256,11 @@
"k": 1, "k": 1,
"d": "关闭页签回调:组件不自行移除,凭 value 从数据源里摘掉后重绘" "d": "关闭页签回调:组件不自行移除,凭 value 从数据源里摘掉后重绘"
}, },
{
"n": "extra",
"t": "unknown",
"d": "页签栏右侧操作区内容(按钮 / 链接):与页签同一行、共用一条分隔线;缺省不渲染且页签栏结构不变"
},
{ {
"n": "closable", "n": "closable",
"t": "boolean", "t": "boolean",
@@ -1268,7 +1273,8 @@
"tabs 只渲染页签头;页签内容用响应式函数按信号切换,如 () => tab() === 'a' ? panelA : panelB", "tabs 只渲染页签头;页签内容用响应式函数按信号切换,如 () => tab() === 'a' ? panelA : panelB",
"点击或方向键(←→ 环形)、Home/End(直达首/末页签)切换,滑动指示条自动跟随;disabled: true 的页签置灰不可点", "点击或方向键(←→ 环形)、Home/End(直达首/末页签)切换,滑动指示条自动跟随;disabled: true 的页签置灰不可点",
"需要可关闭页签时给该项加 closable: true,× 或聚焦后按 Delete 触发 onClose(value);组件不改数据源,调用方从数组里摘掉该项再重绘", "需要可关闭页签时给该项加 closable: true,× 或聚焦后按 Delete 触发 onClose(value);组件不改数据源,调用方从数组里摘掉该项再重绘",
"键盘可访问:Tab 聚焦激活页签,←→ 在启用页签间环形移动,Home/End 跳首个/末个启用页签" "键盘可访问:Tab 聚焦激活页签,←→ 在启用页签间环形移动,Home/End 跳首个/末个启用页签",
"页签栏右侧要放「新建 / 设置」这类操作就传 extra(按钮或链接):它与页签同一行共用一条分隔线,不传时 DOM 结构保持不变"
] ]
}, },
{ {
+20
View File
@@ -4419,3 +4419,23 @@ h5.ua-type-title { font-size: var(--ua-font-size-md); }
font-size: var(--ua-font-size-sm); font-size: var(--ua-font-size-sm);
user-select: none; user-select: none;
} }
/* —— 页签栏右侧操作区:与页签同一行,分隔线由整条 bar 承担 —— */
.ua-tabs-bar {
display: flex;
align-items: flex-end;
justify-content: space-between;
gap: var(--ua-space-3);
border-bottom: 1px solid var(--ua-color-border);
}
.ua-tabs-bar .ua-tabs-head {
flex: 1 1 auto;
border-bottom: 0; /* 线移到 bar 上,操作区也和页签栏同一条线 */
}
.ua-tabs-extra {
display: flex;
flex: 0 0 auto;
align-items: center;
gap: var(--ua-space-2);
padding-bottom: var(--ua-space-1);
}