fix: fall back when initial-focus target is not focusable yet
ci / gitleaks (push) Successful in 1m26s
ci / docker (push) Successful in 7m22s

Task detail opens with taskDetailBusy=true, so the close button is
disabled at the moment focusIntoPanel runs; focusing it silently failed
and left focus on body (Esc then never reached the sheet). Skip targets
that are disabled/hidden/inert without relying on layout metrics (keeps
jsdom tests valid), falling back to first focusable or the panel itself.
This commit is contained in:
2026-09-23 09:01:42 +08:00
parent 28a97d9f5a
commit 136acbc59c
2 changed files with 6 additions and 2 deletions
+1 -1
View File
@@ -1879,7 +1879,7 @@ onUnmounted(() => {
</main>
<div v-if="selectedTask || habitDetailOpen || calendarDetailOpen" class="pane-resizer pane-resizer--detail" role="separator" :aria-label="detailSeparatorLabel" aria-orientation="vertical" aria-valuemin="300" :aria-valuemax="detailMaxWidth" :aria-valuenow="detailWidth" tabindex="0" @pointerdown="startPaneResize('detail',$event)" @lostpointercapture="handlePaneLostPointerCapture" @keydown="resizePaneWithKeyboard('detail',$event)" />
<AppSheet v-if="selectedTask" :open="true" :modal="compactLayout" variant="detail" panel-class="detail" title-id="task-detail-title" initial-focus="button[aria-label='关闭详情']" :close-on-scrim="compactLayout" :busy="taskDetailBusy" @close="closeTaskDetail" @submit.prevent="saveSelectedTaskChanges">
<AppSheet v-if="selectedTask" :open="true" :modal="compactLayout" variant="detail" panel-class="detail" title-id="task-detail-title" initial-focus=".detail-head button" :close-on-scrim="compactLayout" :busy="taskDetailBusy" @close="closeTaskDetail" @submit.prevent="saveSelectedTaskChanges">
<div class="detail-head"><span id="task-detail-title">任务详情</span><button class="icon" type="button" :disabled="taskDetailBusy" aria-label="关闭详情" @click="closeTaskDetail"><X/></button></div>
<fieldset class="detail-form" :disabled="taskDetailBusy">
<div class="detail-title"><button class="task-check detail-task-check" type="button" :aria-label="selectedTask.completed ? `重新打开${selectedTask.title}` : `完成${selectedTask.title}`" :aria-pressed="selectedTask.completed" @click="toggle(selectedTask)"><span class="task-check-mark" :class="`p${selectedTask.priority}`"><Check v-if="selectedTask.completed" /></span></button><textarea v-model="selectedTask.title" rows="2" aria-label="任务标题"/></div>
+5 -1
View File
@@ -57,7 +57,11 @@ function keydown(event: KeyboardEvent) {
function focusIntoPanel() {
if (!panel.value?.contains(document.activeElement)) {
const target = props.initialFocus ? panel.value?.querySelector<HTMLElement>(props.initialFocus) : null
;(target ?? focusables()[0] ?? panel.value)?.focus()
const usable = !!target && !target.matches(':disabled')
&& !target.closest('[hidden],[aria-hidden="true"],[inert]')
&& (() => { const s = window.getComputedStyle(target); return s.display !== 'none' && s.visibility !== 'hidden' })()
const el = usable ? target : (focusables()[0] ?? panel.value)
el?.focus()
}
}
async function activate() {