fix(workbar): keep scroll arrows clear of tab close buttons
Reserve space on both sides of the tab viewport for overflow arrows so they cannot cover tab close controls. Keep that space while arrows are hidden or disabled to avoid layout shifts. Add Electron coverage for arrow bounds and native closing at the right viewport edge; update tab layout documentation. Validation: 19 focused tests passed; typecheck and lint passed. Full suite did not complete before interruption and reported failures in supervisor settings and supervisor layout tests. Release note: Fix workbar scroll arrows covering tab close buttons when many tabs are open.
This commit is contained in:
+1
-1
@@ -371,7 +371,7 @@
|
||||
- 已打开实例保留 `tablist`、`tab`、`tabpanel`、方向键、Home、End 和焦点恢复语义。
|
||||
实例过多时 Tab 区单行横向滚动,“+”保持可见,不换行或改成下拉框。不可关闭实例不渲染
|
||||
关闭按钮,也不响应关闭快捷命令。
|
||||
“+”位于滚动容器外并占用固定宽度;滚动箭头限定在剩余的页签区域内,不能覆盖添加入口。
|
||||
“+”位于滚动容器外并占用固定宽度;溢出时左右滚动箭头在滚动视口两侧各占独立宽度,不能覆盖页签、关闭按钮或添加入口。箭头隐藏或禁用时保留占位,避免悬停或滚动引起布局跳动。
|
||||
- 当前工作栏 Tab 必须同时使用连续内容表面、可见边框、顶部指示、图标或较高字重中的至少
|
||||
两种信号;悬停反馈不得强于当前选中态。Tab 关闭按钮保留固定布局空间,默认仅在对应 Tab 悬停或
|
||||
`focus-within` 时显示;无悬停或粗指针设备始终显示,键盘与辅助技术始终可访问。
|
||||
|
||||
@@ -182,7 +182,7 @@ stdio fallback。首期远程终端随 SSH Channel 结束,不提供跨 SSH 或
|
||||
- 每个实例使用独立 `tab` 和 `tabpanel`。
|
||||
- “+”固定在 Tab 行末尾,位于 `tablist` 和页签滚动容器之外,占用独立宽度。
|
||||
- Tab 过多时实例区域单行横向滚动,不换行、不自动隐藏实例。“+”在 `300px` 工作栏内也保持可见和可点击。
|
||||
- 标签全部可见时隐藏左右滚动按钮;溢出时左右箭头透明浮动于页签区域两端,不占标签布局宽度,也不进入“+”的点击区域。鼠标移入标签栏时显示可滚动方向的箭头,移出后隐藏;键盘聚焦箭头时显示,无悬停能力的设备保持可滚动方向的箭头可见。悬停箭头时显示轻微底色。点击滚动约一屏,边界方向禁用并隐藏,且不拦截下方标签的指针操作。切换或新增激活标签时将其滚入可见区域。未激活标签使用低对比度灰蓝底色和细边框,激活标签保留亮底与强调色顶线。
|
||||
- 标签全部可见时移除左右滚动按钮及其占位;溢出时左右箭头在滚动视口两侧各占独立的 28px 宽度,不覆盖标签、关闭按钮或“+”。鼠标移入标签栏时显示可滚动方向的箭头,移出后隐藏;键盘聚焦箭头时显示,无悬停能力的设备保持可滚动方向的箭头可见。悬停箭头时显示轻微底色。点击滚动约一屏,边界方向禁用并隐藏;溢出期间隐藏和禁用均保留占位,避免布局跳动。切换或新增激活标签时将整个标签(含关闭按钮)滚入可见区域。未激活标签使用低对比度灰蓝底色和细边框,激活标签保留亮底与强调色顶线。
|
||||
- 终端 Tab 默认使用递增标题,例如“终端 1”“终端 2”;状态栏持续显示项目、Host、
|
||||
工作目录和 Shell。用户可以重命名终端。
|
||||
- 任务中心、工作区、成果及新建终端的默认标题随界面语言切换,恢复布局时也使用当前语言。
|
||||
|
||||
@@ -5055,9 +5055,9 @@ button > svg {
|
||||
}
|
||||
|
||||
.workbar-shell__scroll-button {
|
||||
position: absolute;
|
||||
z-index: 2;
|
||||
bottom: 5px;
|
||||
flex: 0 0 28px;
|
||||
align-self: flex-end;
|
||||
margin-bottom: 5px;
|
||||
background: transparent;
|
||||
opacity: 0;
|
||||
pointer-events: none;
|
||||
@@ -5070,14 +5070,6 @@ button > svg {
|
||||
pointer-events: auto;
|
||||
}
|
||||
|
||||
.workbar-shell__scroll-button:first-child {
|
||||
left: 0;
|
||||
}
|
||||
|
||||
.workbar-shell__scroll-button:last-child {
|
||||
right: 0;
|
||||
}
|
||||
|
||||
.workbar-shell__scroll-button:hover:not(:disabled) {
|
||||
background: var(--surface-muted);
|
||||
color: var(--text-primary);
|
||||
|
||||
@@ -130,6 +130,10 @@ app.whenReady().then(async () => {
|
||||
for (const many of [false, true]) {
|
||||
if (many) { await click('#many-tabs'); await wait('document.querySelectorAll("[role=tab]").length===16') }
|
||||
await addVisible()
|
||||
assert(await js(`(() => {
|
||||
const s=${scroll}.getBoundingClientRect(),buttons=[...document.querySelectorAll('.workbar-shell__scroll-button')];
|
||||
return buttons.length===2 && buttons[0].getBoundingClientRect().right<=s.left && buttons[1].getBoundingClientRect().left>=s.right;
|
||||
})()`), 'Scroll arrows must reserve space outside the tab viewport')
|
||||
await click('.workbar-shell__add')
|
||||
await wait('!!document.querySelector(".workbar-shell__catalog") && document.activeElement.matches("[data-workbar-app-choice]")')
|
||||
await key('Escape')
|
||||
@@ -160,7 +164,19 @@ app.whenReady().then(async () => {
|
||||
await click('.workbar-shell__catalog-choice:has(.workbar-shell__app-icon--browser)')
|
||||
await wait('document.querySelectorAll("[role=tab]").length===17 && !document.querySelector(".workbar-shell__catalog")')
|
||||
await addVisible()
|
||||
observations.push({ locale, theme, workbar: 'default and 16 long tabs; native add/catalog/create, arrows, Home/End' })
|
||||
await key('Left')
|
||||
await wait('document.querySelector(".workbar-shell__tab-item:nth-last-child(2) [role=tab]").getAttribute("aria-selected")==="true"')
|
||||
await js(`${scroll}.scrollLeft=0`)
|
||||
await settle()
|
||||
await js(`(() => {
|
||||
const tab=document.querySelector('.workbar-shell__tab-item--active'),s=${scroll};
|
||||
s.scrollLeft+=tab.getBoundingClientRect().right-s.getBoundingClientRect().right;
|
||||
})()`)
|
||||
await wait('!document.querySelector(".workbar-shell__scroll-button:last-child").disabled')
|
||||
const closingId = await js('document.querySelector(".workbar-shell__tab-item--active [role=tab]").id')
|
||||
await click('.workbar-shell__tab-item--active .workbar-shell__tab-close')
|
||||
await wait(`document.querySelectorAll('[role=tab]').length===16 && !document.getElementById(${JSON.stringify(closingId)})`)
|
||||
observations.push({ locale, theme, workbar: 'default and 16 long tabs; native add/catalog/create, arrows, Home/End, close at right viewport edge with right arrow enabled' })
|
||||
|
||||
await win.loadURL(`${process.env.GB_LAYOUT_URL}?surface=settings&locale=${locale}&theme=${theme}`)
|
||||
await wait('!!document.querySelector(".model-connection-detail select")')
|
||||
|
||||
Reference in New Issue
Block a user