fix(host): native button semantics for rows; path editor inert under the create dialog

Rows keep their NATIVE button role inside a listitem seat (role overrides
exposed selectable folders as passive list items to assistive technology),
the crumb trail becomes a navigation landmark, and the path-edit input joins
the parentInert derivation so the nested create dialog cannot be undercut
through it.
This commit is contained in:
creatixchu
2026-07-28 23:50:05 +08:00
parent 6add9a8eef
commit af049cceaf
3 changed files with 53 additions and 40 deletions

View File

@@ -140,7 +140,13 @@
background: var(--dsw-alias-border-l3);
}
.rowSeat {
display: flex;
flex: none;
}
.row {
width: 100%;
display: flex;
align-items: center;
gap: 4px;

View File

@@ -71,21 +71,23 @@ function LevelColumn({ entries, selectedPath, busy, onPick, wide }: {
{entries.filter(entry => !entry.hidden).map((entry) => {
const selected = entry.path === selectedPath
return (
<button
key={entry.path}
type="button"
role="listitem"
aria-current={selected || undefined}
className={clsx(css.row, selected && css.rowSelected)}
disabled={busy}
onClick={() => { onPick(entry) }}
>
{selected
? <IconFolderOpen16 size={16} className={css.rowIconSelected} />
: <IconFolderClose16 size={16} className={css.rowIcon} />}
<span className={css.rowName}>{entry.name}</span>
<IconChevronRightOutline14 size={12} className={css.rowChevron} />
</button>
// The wrapper carries the list semantics; the row keeps its NATIVE
// button role so assistive technology exposes an actionable control.
<span key={entry.path} role="listitem" className={css.rowSeat}>
<button
type="button"
aria-current={selected || undefined}
className={clsx(css.row, selected && css.rowSelected)}
disabled={busy}
onClick={() => { onPick(entry) }}
>
{selected
? <IconFolderOpen16 size={16} className={css.rowIconSelected} />
: <IconFolderClose16 size={16} className={css.rowIcon} />}
<span className={css.rowName}>{entry.name}</span>
<IconChevronRightOutline14 size={12} className={css.rowChevron} />
</button>
</span>
)
})}
</div>
@@ -260,7 +262,7 @@ export function DirectoryBrowser({ open, listDirectory, createDirectory, onOpen,
{pathDraft === null
? (
<>
<span className={css.crumbTrail} ref={crumbTrailRef}>
<span className={css.crumbTrail} role="navigation" ref={crumbTrailRef}>
{crumbs.map((crumb, index) => (
<span key={crumb.path} className={css.crumbSeat}>
{index > 0 && <IconChevronRightOutline14 size={12} className={css.crumbChevron} />}
@@ -292,7 +294,7 @@ export function DirectoryBrowser({ open, listDirectory, createDirectory, onOpen,
value={pathDraft}
aria-label={t('browser.editPath')}
autoFocus
disabled={busy}
disabled={parentInert}
onChange={(event) => { setPathDraft(event.target.value) }}
onKeyDown={(event) => {
if (event.key === 'Enter') {