diff --git a/app/javascript/mastodon/components/hotkeys/index.tsx b/app/javascript/mastodon/components/hotkeys/index.tsx index 751ec01fe5..481057bd7d 100644 --- a/app/javascript/mastodon/components/hotkeys/index.tsx +++ b/app/javascript/mastodon/components/hotkeys/index.tsx @@ -109,8 +109,8 @@ const hotkeyMatcherMap = { mention: just('m'), open: any('enter', 'o'), openProfile: just('p'), - moveDown: just('j'), - moveUp: just('k'), + moveDown: any('j', 'pagedown'), + moveUp: any('k', 'pageup'), moveToTop: just('0'), toggleHidden: just('x'), toggleSensitive: just('h'), @@ -147,9 +147,15 @@ const hotkeyMatcherMap = { type HotkeyName = keyof typeof hotkeyMatcherMap; -export type HandlerMap = Partial< - Record void> ->; +type HandlerFunction = + // When a handler returns a boolean, it should indicate whether the + // hotkey was handled (i.e. it resulted in an action). + // If `false` is returned, `preventDefault` and `stopPropagation` + // will not be called on the keyboard event, restoring the key's + // native behaviour. + ((event: KeyboardEvent) => boolean) | ((event: KeyboardEvent) => void); + +export type HandlerMap = Partial>; export function useHotkeys(handlers: HandlerMap) { const ref = useRef(null); @@ -184,7 +190,7 @@ export function useHotkeys(handlers: HandlerMap) { const matchCandidates: { // A candidate will be have an undefined handler if it's matched, // but handled in a parent component rather than this one. - handler: ((event: KeyboardEvent) => void) | undefined; + handler: HandlerFunction | undefined; priority: number; }[] = []; @@ -209,9 +215,11 @@ export function useHotkeys(handlers: HandlerMap) { const bestMatchingHandler = matchCandidates.at(0)?.handler; if (bestMatchingHandler) { - bestMatchingHandler(event); - event.stopPropagation(); - event.preventDefault(); + const wasHandled = bestMatchingHandler(event); + if (wasHandled !== false) { + event.stopPropagation(); + event.preventDefault(); + } } // Add last keypress to buffer diff --git a/app/javascript/mastodon/features/keyboard_shortcuts/index.jsx b/app/javascript/mastodon/features/keyboard_shortcuts/index.jsx index dc0e8cb4f8..efc63ddfea 100644 --- a/app/javascript/mastodon/features/keyboard_shortcuts/index.jsx +++ b/app/javascript/mastodon/features/keyboard_shortcuts/index.jsx @@ -68,7 +68,7 @@ class KeyboardShortcuts extends ImmutablePureComponent { - enter, o + , o @@ -88,11 +88,11 @@ class KeyboardShortcuts extends ImmutablePureComponent { - k + k, - j + j, @@ -112,15 +112,15 @@ class KeyboardShortcuts extends ImmutablePureComponent { - alt+n + +n - alt+x + +x - backspace + @@ -128,7 +128,7 @@ class KeyboardShortcuts extends ImmutablePureComponent { - esc + diff --git a/app/javascript/mastodon/features/ui/index.jsx b/app/javascript/mastodon/features/ui/index.jsx index 4910950b44..22332eae6e 100644 --- a/app/javascript/mastodon/features/ui/index.jsx +++ b/app/javascript/mastodon/features/ui/index.jsx @@ -508,7 +508,8 @@ class UI extends PureComponent { if (currentItemIndex === -1) { focusColumn(1); } else { - focusItemSibling(currentItemIndex, -1); + const wasHandled = focusItemSibling(currentItemIndex, -1); + return wasHandled; } }; @@ -517,7 +518,8 @@ class UI extends PureComponent { if (currentItemIndex === -1) { focusColumn(1); } else { - focusItemSibling(currentItemIndex, 1); + const wasHandled = focusItemSibling(currentItemIndex, 1); + return wasHandled; } }; diff --git a/app/javascript/mastodon/features/ui/util/focusUtils.ts b/app/javascript/mastodon/features/ui/util/focusUtils.ts index c632a0a51d..80e9cb9768 100644 --- a/app/javascript/mastodon/features/ui/util/focusUtils.ts +++ b/app/javascript/mastodon/features/ui/util/focusUtils.ts @@ -159,13 +159,12 @@ export function focusItemSibling(index: number, direction: 1 | -1) { ); if (!siblingItem) { - return; + return false; } // If sibling element is empty, we skip it if (siblingItem.matches(':empty')) { - focusItemSibling(index + direction, direction); - return; + return focusItemSibling(index + direction, direction); } // Check if the sibling is a post or a 'follow suggestions' widget @@ -184,5 +183,8 @@ export function focusItemSibling(index: number, direction: 1 | -1) { }); targetElement.focus(); + return true; + } else { + return false; } } diff --git a/app/javascript/mastodon/locales/en.json b/app/javascript/mastodon/locales/en.json index 8df8070ed1..44e2c9aa17 100644 --- a/app/javascript/mastodon/locales/en.json +++ b/app/javascript/mastodon/locales/en.json @@ -819,6 +819,12 @@ "keyboard_shortcuts.heading": "Keyboard shortcuts", "keyboard_shortcuts.home": "Open home timeline", "keyboard_shortcuts.hotkey": "Hotkey", + "keyboard_shortcuts.keys.alt": "Alt", + "keyboard_shortcuts.keys.backspace": "Backspace", + "keyboard_shortcuts.keys.enter": "Enter", + "keyboard_shortcuts.keys.esc": "Esc", + "keyboard_shortcuts.keys.page_down": "Page Down", + "keyboard_shortcuts.keys.page_up": "Page Up", "keyboard_shortcuts.legend": "Display this legend", "keyboard_shortcuts.load_more": "Focus \"Load more\" button", "keyboard_shortcuts.local": "Open local timeline",