Skip to content

Commit 304d28b

Browse files
Fix sidebar Enter/o not focusing the note list on filtered views
Two issues: 1. Pressing Enter on sidebar filter buttons (Todo, Pinned, etc.) triggered both the keyboard nav handler (focus notes pane) and the button's native Enter→click (re-select filter, set focus back to sidebar). Fix: prevent default Enter/Space on filter buttons so the nav handler controls focus exclusively. 2. When the selected note wasn't in the current filtered list (e.g. selected from All Notes, then switched to Todo), the notes pane handler tried to focus a non-existent row and fell back to focusing the scroll container. Fix: detect the mismatch and fall back to selecting the first visible note instead. Also removed the scroll container focus fallback entirely — if no note row exists to focus, do nothing since focusedPane store state handles keyboard routing. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent dbca18a commit 304d28b

3 files changed

Lines changed: 41 additions & 6 deletions

File tree

app/src/features/notes/ui/notes-pane-utils.ts

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -152,10 +152,7 @@ export function focusNotesPaneTarget(scrollContainer: HTMLDivElement | null) {
152152
if (selectedRow) {
153153
selectedRow.scrollIntoView({ block: "nearest" });
154154
selectedRow.focus({ preventScroll: true });
155-
return;
156155
}
157-
158-
scrollContainer?.focus({ preventScroll: true });
159156
});
160157
});
161158
}

app/src/features/notes/ui/notes-pane.tsx

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -260,7 +260,18 @@ export function NotesPane({
260260
useEffect(() => {
261261
const handleFocusNotesPane = (event: Event) => {
262262
const customEvent = event as CustomEvent<FocusNotesPaneDetail>;
263-
const selection = customEvent.detail?.selection ?? "selected";
263+
let selection = customEvent.detail?.selection ?? "selected";
264+
265+
// If the selected note isn't in the current filtered list, fall back
266+
// to selecting the first visible note instead of focusing an empty
267+
// scroll container.
268+
if (
269+
selection === "selected" &&
270+
selectedNoteId &&
271+
!filteredNotes.some((n) => n.id === selectedNoteId)
272+
) {
273+
selection = "first";
274+
}
264275

265276
if (selection === "first") {
266277
if (isNotesPlaceholderData) {
@@ -269,7 +280,9 @@ export function NotesPane({
269280
}
270281

271282
setIsSearchFocused(false);
283+
setFocusedPane("notes");
272284
selectFirstVisibleNote();
285+
focusNotesPaneTarget(scrollContainerRef.current);
273286
return;
274287
}
275288

@@ -283,7 +296,13 @@ export function NotesPane({
283296
return () => {
284297
window.removeEventListener(FOCUS_NOTES_PANE_EVENT, handleFocusNotesPane);
285298
};
286-
}, [isNotesPlaceholderData, selectFirstVisibleNote, setFocusedPane]);
299+
}, [
300+
filteredNotes,
301+
isNotesPlaceholderData,
302+
selectFirstVisibleNote,
303+
selectedNoteId,
304+
setFocusedPane,
305+
]);
287306

288307
useEffect(() => {
289308
setShowHeaderBorder((scrollContainerRef.current?.scrollTop ?? 0) > 0);

app/src/features/shell/ui/sidebar-notes-section.tsx

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { type MouseEvent } from "react";
1+
import { type KeyboardEvent, type MouseEvent } from "react";
22
import { LogicalPosition } from "@tauri-apps/api/dpi";
33
import { Menu } from "@tauri-apps/api/menu";
44
import {
@@ -50,6 +50,19 @@ async function showTrashContextMenu(
5050
}
5151
}
5252

53+
/**
54+
* Prevent the native button Enter/Space → click behavior so that the
55+
* parent `<nav>` keyboard handler controls focus transitions exclusively.
56+
* Without this, pressing Enter on a filter button fires both the nav
57+
* handler (which focuses the notes pane) and the button's synthetic
58+
* click (which re-selects the filter and sets focus back to sidebar).
59+
*/
60+
function preventButtonKeyboardClick(event: KeyboardEvent<HTMLButtonElement>) {
61+
if (event.key === "Enter" || event.key === " ") {
62+
event.preventDefault();
63+
}
64+
}
65+
5366
export function NotesSection({
5467
archivedCount,
5568
isFocused,
@@ -169,6 +182,7 @@ export function NotesSection({
169182
className={sidebarItemClasses(isTodayActive, isFocused)}
170183
onClick={onSelectToday}
171184
onFocus={onSidebarRowFocus}
185+
onKeyDown={preventButtonKeyboardClick}
172186
ref={(element) => onRowRef("filter:today", element)}
173187
data-comet-sidebar-active={isTodayActive ? "true" : undefined}
174188
type="button"
@@ -186,6 +200,7 @@ export function NotesSection({
186200
className={sidebarItemClasses(isTodoActive, isFocused)}
187201
onClick={onSelectTodo}
188202
onFocus={onSidebarRowFocus}
203+
onKeyDown={preventButtonKeyboardClick}
189204
ref={(element) => onRowRef("filter:todo", element)}
190205
data-comet-sidebar-active={isTodoActive ? "true" : undefined}
191206
type="button"
@@ -207,6 +222,7 @@ export function NotesSection({
207222
className={sidebarItemClasses(isPinnedActive, isFocused)}
208223
onClick={onSelectPinned}
209224
onFocus={onSidebarRowFocus}
225+
onKeyDown={preventButtonKeyboardClick}
210226
ref={(element) => onRowRef("filter:pinned", element)}
211227
data-comet-sidebar-active={isPinnedActive ? "true" : undefined}
212228
type="button"
@@ -222,6 +238,7 @@ export function NotesSection({
222238
className={sidebarItemClasses(isUntaggedActive, isFocused)}
223239
onClick={onSelectUntagged}
224240
onFocus={onSidebarRowFocus}
241+
onKeyDown={preventButtonKeyboardClick}
225242
ref={(element) => onRowRef("filter:untagged", element)}
226243
data-comet-sidebar-active={isUntaggedActive ? "true" : undefined}
227244
type="button"
@@ -241,6 +258,7 @@ export function NotesSection({
241258
className={sidebarItemClasses(isArchiveActive, isFocused)}
242259
onClick={onSelectArchive}
243260
onFocus={onSidebarRowFocus}
261+
onKeyDown={preventButtonKeyboardClick}
244262
ref={(element) => onRowRef("filter:archive", element)}
245263
data-comet-sidebar-active={isArchiveActive ? "true" : undefined}
246264
type="button"
@@ -257,6 +275,7 @@ export function NotesSection({
257275
onClick={onSelectTrash}
258276
onContextMenu={(event) => handleTrashContextMenu(event)}
259277
onFocus={onSidebarRowFocus}
278+
onKeyDown={preventButtonKeyboardClick}
260279
ref={(element) => onRowRef("filter:trash", element)}
261280
data-comet-sidebar-active={isTrashActive ? "true" : undefined}
262281
type="button"

0 commit comments

Comments
 (0)