[Glitch] Add hotkeys PageUp and PageDown for list navigation

Port ce3fdf73f3bac6e2bb559399cbefe63d94fe0a2d to glitch-soc

Signed-off-by: Claire <claire.github-309c@sitedethib.com>
This commit is contained in:
diondiondion 2026-06-03 15:26:43 +02:00 committed by Claire
parent a880a02c1f
commit e61a97443f
4 changed files with 33 additions and 21 deletions

View File

@ -110,8 +110,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'),
@ -148,9 +148,15 @@ const hotkeyMatcherMap = {
type HotkeyName = keyof typeof hotkeyMatcherMap;
export type HandlerMap = Partial<
Record<HotkeyName, (event: KeyboardEvent) => 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<Record<HotkeyName, HandlerFunction>>;
export function useHotkeys<T extends HTMLElement>(handlers: HandlerMap) {
const ref = useRef<T>(null);
@ -185,7 +191,7 @@ export function useHotkeys<T extends HTMLElement>(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;
}[] = [];
@ -210,9 +216,11 @@ export function useHotkeys<T extends HTMLElement>(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

View File

@ -72,7 +72,7 @@ class KeyboardShortcuts extends ImmutablePureComponent {
<td><FormattedMessage id='keyboard_shortcuts.bookmark' defaultMessage='to bookmark' /></td>
</tr>
<tr>
<td><kbd>enter</kbd>, <kbd>o</kbd></td>
<td><FormattedMessage id='keyboard_shortcuts.keys.enter' defaultMessage='Enter' tagName='kbd' />, <kbd>o</kbd></td>
<td><FormattedMessage id='keyboard_shortcuts.enter' defaultMessage='to open status' /></td>
</tr>
<tr>
@ -92,11 +92,11 @@ class KeyboardShortcuts extends ImmutablePureComponent {
<td><FormattedMessage id='keyboard_shortcuts.toggle_sensitivity' defaultMessage='to show/hide media' /></td>
</tr>
<tr>
<td><kbd>k</kbd></td>
<td><kbd>k</kbd>, <FormattedMessage id='keyboard_shortcuts.keys.page_up' defaultMessage='Page Up' tagName='kbd' /></td>
<td><FormattedMessage id='keyboard_shortcuts.up' defaultMessage='to move up in the list' /></td>
</tr>
<tr>
<td><kbd>j</kbd></td>
<td><kbd>j</kbd>, <FormattedMessage id='keyboard_shortcuts.keys.page_down' defaultMessage='Page Down' tagName='kbd' /></td>
<td><FormattedMessage id='keyboard_shortcuts.down' defaultMessage='to move down in the list' /></td>
</tr>
<tr>
@ -116,15 +116,15 @@ class KeyboardShortcuts extends ImmutablePureComponent {
<td><FormattedMessage id='keyboard_shortcuts.compose' defaultMessage='to focus the compose textarea' /></td>
</tr>
<tr>
<td><kbd>alt</kbd>+<kbd>n</kbd></td>
<td><FormattedMessage id='keyboard_shortcuts.keys.alt' defaultMessage='Alt' tagName='kbd' />+<kbd>n</kbd></td>
<td><FormattedMessage id='keyboard_shortcuts.toot' defaultMessage='to start a brand new post' /></td>
</tr>
<tr>
<td><kbd>alt</kbd>+<kbd>x</kbd></td>
<td><FormattedMessage id='keyboard_shortcuts.keys.alt' defaultMessage='Alt' tagName='kbd' />+<kbd>x</kbd></td>
<td><FormattedMessage id='keyboard_shortcuts.spoilers' defaultMessage='to show/hide CW field' /></td>
</tr>
<tr>
<td><kbd>backspace</kbd></td>
<td><FormattedMessage id='keyboard_shortcuts.keys.backspace' defaultMessage='Backspace' tagName='kbd' /></td>
<td><FormattedMessage id='keyboard_shortcuts.back' defaultMessage='to navigate back' /></td>
</tr>
<tr>
@ -136,7 +136,7 @@ class KeyboardShortcuts extends ImmutablePureComponent {
<td><FormattedMessage id='keyboard_shortcuts.secondary_toot' defaultMessage='to send toot using secondary privacy setting' /></td>
</tr>
<tr>
<td><kbd>esc</kbd></td>
<td><FormattedMessage id='keyboard_shortcuts.keys.esc' defaultMessage='Esc' tagName='kbd' /></td>
<td><FormattedMessage id='keyboard_shortcuts.unfocus' defaultMessage='to un-focus compose textarea/search' /></td>
</tr>
<tr>

View File

@ -554,7 +554,8 @@ class UI extends PureComponent {
if (currentItemIndex === -1) {
focusColumn(1);
} else {
focusItemSibling(currentItemIndex, -1);
const wasHandled = focusItemSibling(currentItemIndex, -1);
return wasHandled;
}
};
@ -563,7 +564,8 @@ class UI extends PureComponent {
if (currentItemIndex === -1) {
focusColumn(1);
} else {
focusItemSibling(currentItemIndex, 1);
const wasHandled = focusItemSibling(currentItemIndex, 1);
return wasHandled;
}
};

View File

@ -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;
}
}