fix(folder-browser): arrow navigation only without modifiers (#174)

Skip column/list arrow handling when any modifier is down. Detect modifiers
via nativeEvent and getModifierState (WebKit/WebView), and match arrow keys
by both key and code. On row buttons, preventDefault for plain arrows only
to avoid native focus/scroll stealing navigation. Filter field ArrowDown uses
the same modifier check.
This commit is contained in:
cucadmuh
2026-04-13 21:13:10 +03:00
committed by GitHub
parent ec021516c7
commit e75fda168e
+35 -1
View File
@@ -66,6 +66,35 @@ function entryToTrack(e: SubsonicDirectoryEntry): Track {
}; };
} }
function isFolderBrowserArrowKey(e: React.KeyboardEvent): boolean {
return (
e.key === 'ArrowUp' ||
e.key === 'ArrowDown' ||
e.key === 'ArrowLeft' ||
e.key === 'ArrowRight' ||
e.code === 'ArrowUp' ||
e.code === 'ArrowDown' ||
e.code === 'ArrowLeft' ||
e.code === 'ArrowRight'
);
}
/** Modifiers from native event + getModifierState (WebKit/WebView can miss flags on the synthetic event). */
function folderBrowserHasKeyModifiers(e: React.KeyboardEvent): boolean {
const n = e.nativeEvent;
if (n.ctrlKey || n.altKey || n.shiftKey || n.metaKey) return true;
if (typeof n.getModifierState === 'function') {
return (
n.getModifierState('Control') ||
n.getModifierState('Alt') ||
n.getModifierState('Shift') ||
n.getModifierState('Meta') ||
n.getModifierState('OS')
);
}
return false;
}
export default function FolderBrowser() { export default function FolderBrowser() {
const { t } = useTranslation(); const { t } = useTranslation();
const [columns, setColumns] = useState<Column[]>([]); const [columns, setColumns] = useState<Column[]>([]);
@@ -449,6 +478,7 @@ export default function FolderBrowser() {
}); });
return; return;
} }
if (isFolderBrowserArrowKey(e) && folderBrowserHasKeyModifiers(e)) return;
if (!['ArrowUp', 'ArrowDown', 'ArrowLeft', 'ArrowRight', 'Enter'].includes(key)) return; if (!['ArrowUp', 'ArrowDown', 'ArrowLeft', 'ArrowRight', 'Enter'].includes(key)) return;
setKeyboardNavActive(true); setKeyboardNavActive(true);
const current = keyboardPos ?? fallbackNavPos(columns); const current = keyboardPos ?? fallbackNavPos(columns);
@@ -718,7 +748,7 @@ export default function FolderBrowser() {
requestAnimationFrame(() => wrapperRef.current?.focus({ preventScroll: true })); requestAnimationFrame(() => wrapperRef.current?.focus({ preventScroll: true }));
return; return;
} }
if (e.key === 'ArrowDown') { if (e.key === 'ArrowDown' && !folderBrowserHasKeyModifiers(e)) {
e.preventDefault(); e.preventDefault();
e.stopPropagation(); e.stopPropagation();
const rowIndex = preferredRowIndex(colIndex); const rowIndex = preferredRowIndex(colIndex);
@@ -777,6 +807,10 @@ export default function FolderBrowser() {
if (item.isDir) handleDirClick(colIndex, item); if (item.isDir) handleDirClick(colIndex, item);
else handleFileClick(colIndex, item); else handleFileClick(colIndex, item);
}} }}
onKeyDown={e => {
if (!isFolderBrowserArrowKey(e) || folderBrowserHasKeyModifiers(e)) return;
e.preventDefault();
}}
onContextMenu={e => { onContextMenu={e => {
setKeyboardPos({ colIndex, rowIndex }); setKeyboardPos({ colIndex, rowIndex });
onRowContextMenu(e, colIndex, rowIndex, col, item); onRowContextMenu(e, colIndex, rowIndex, col, item);