Carry over what only the win7 tree had: middle-click close, ARIA, menu fix
The deleted win7 tree held a few behaviours the modern tree never got: - Context menu: the first ArrowUp in a fresh menu skipped the last item (Delete in the row menu, Disconnect in the tab menu) — stepping from -1 landed one short. The index math moves to core/menu-nav.ts stepIndex(), checked by menu-nav.spec.ts (bun test; verified to fail on the old logic). Items get ids and the menu exposes aria-activedescendant, so screen readers follow the arrow keys while focus stays on the menu. - Middle-click closes a tab, as in browsers. auxclick also fires for the right button, so only button 1 counts; closeWorkspace already refuses the last tab. A middle press is swallowed so WebView2 doesn't start autoscroll over the scrollable strip. The hover ✕ stays. - ARIA: the tab strip is a tablist of role=tab buttons with aria-selected (the in-tab ✕ is taken out of the a11y tree and tab order — Ctrl/Cmd+W and the tab menu remain the keyboard paths); transfer bars are progressbars with value and a name. Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -10,6 +10,7 @@ import {
|
||||
import { NgIcon } from '@ng-icons/core';
|
||||
import { ContextMenuService, type MenuItem } from '@core/context-menu.service';
|
||||
import { FocusService } from '@core/focus.service';
|
||||
import { stepIndex } from '@core/menu-nav';
|
||||
import { remPx } from '@core/metrics';
|
||||
|
||||
/** Rough per-item metrics in REM, used only to decide whether the menu must
|
||||
@@ -38,6 +39,7 @@ const EDGE_GAP = 6; // px on purpose: a viewport-edge margin, not a UI size
|
||||
#menuEl
|
||||
role="menu"
|
||||
tabindex="-1"
|
||||
[attr.aria-activedescendant]="activeIndex() >= 0 ? 'ctx-item-' + activeIndex() : null"
|
||||
(keydown)="onKeydown($event)"
|
||||
[style.left.px]="position().x"
|
||||
[style.top.px]="position().y"
|
||||
@@ -50,6 +52,7 @@ const EDGE_GAP = 6; // px on purpose: a viewport-edge margin, not a UI size
|
||||
<button
|
||||
type="button"
|
||||
role="menuitem"
|
||||
[attr.id]="'ctx-item-' + $index"
|
||||
[title]="item.tooltip ?? ''"
|
||||
[disabled]="!!item.disabled"
|
||||
[class.bg-accent]="$index === activeIndex() && !item.disabled"
|
||||
@@ -163,18 +166,9 @@ export class ContextMenu {
|
||||
}
|
||||
}
|
||||
|
||||
/** Move the highlight by one selectable item, skipping separators and
|
||||
* disabled entries, wrapping. */
|
||||
private step(items: MenuItem[], delta: number): void {
|
||||
const count = items.length;
|
||||
if (!count) return;
|
||||
let index = this.activeIndex();
|
||||
for (let hops = 0; hops < count; hops++) {
|
||||
index = (index + delta + count) % count;
|
||||
if (items[index].type !== 'separator' && !items[index].disabled) {
|
||||
this.activeIndex.set(index);
|
||||
return;
|
||||
}
|
||||
}
|
||||
/** Move the highlight by one selectable item (see stepIndex). */
|
||||
private step(items: MenuItem[], delta: 1 | -1): void {
|
||||
const selectable = items.map((i) => i.type !== 'separator' && !i.disabled);
|
||||
this.activeIndex.set(stepIndex(selectable, this.activeIndex(), delta));
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user