The R/F/D toolbar shortcuts were only wired in PageControls, but the recipes page uses its own RecipePageControls in recipes.js. The shared toolbar still rendered the R keycap and the shortcuts sheet listed it, yet no keydown handler was registered, so pressing R did nothing. Extract the shortcut handling into a shared ActionShortcuts module and register it from both PageControls and RecipePageControls. Registration is idempotent per event-manager source, so rebuilding controls cannot fire the action twice. F/D still pass through on recipes because the page has no fetch/download buttons. Add regression coverage for the recipes shortcut.
73 lines
2.2 KiB
JavaScript
73 lines
2.2 KiB
JavaScript
// ActionShortcuts.js - Shared R/F/D keyboard shortcuts for the actions toolbar
|
|
import { eventManager } from '../../utils/EventManager.js';
|
|
import { isTypingContext } from '../../utils/uiHelpers.js';
|
|
|
|
/**
|
|
* Event manager source shared by every page controls implementation
|
|
* (PageControls and the recipes page's RecipePageControls).
|
|
*/
|
|
export const ACTION_SHORTCUTS_SOURCE = 'pageControls-actions';
|
|
|
|
const ACTION_BY_KEY = Object.freeze({
|
|
r: 'refresh',
|
|
f: 'fetch',
|
|
d: 'download'
|
|
});
|
|
|
|
/**
|
|
* Handle a keydown event for the actions toolbar shortcuts
|
|
* (R = refresh, F = fetch metadata, D = download).
|
|
*
|
|
* The shortcut reuses the button's own click handler, so it needs no
|
|
* page-specific knowledge beyond the data-action attribute.
|
|
*
|
|
* @param {KeyboardEvent} e
|
|
* @returns {boolean} True when the event was handled and propagation should stop
|
|
*/
|
|
export function handleActionShortcut(e) {
|
|
// Plain letters only — leave modified combos (Ctrl/Cmd/Alt) alone
|
|
if (e.ctrlKey || e.metaKey || e.altKey) {
|
|
return false;
|
|
}
|
|
|
|
// Don't hijack keys while typing in a text entry context
|
|
if (isTypingContext(e.target)) {
|
|
return false;
|
|
}
|
|
|
|
const action = ACTION_BY_KEY[e.key.toLowerCase()];
|
|
if (!action) {
|
|
return false;
|
|
}
|
|
|
|
// The button may not exist on this page (e.g. recipes has no
|
|
// fetch/download) — let other handlers run in that case
|
|
const button = document.querySelector(`[data-action="${action}"]`);
|
|
if (!button) {
|
|
return false;
|
|
}
|
|
|
|
e.preventDefault();
|
|
// Native disabled buttons ignore .click(), so an in-progress
|
|
// refresh is safe
|
|
button.click();
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Register the shared action shortcuts with the centralized event manager.
|
|
*
|
|
* Re-registering the same source replaces the previous handler so a page that
|
|
* rebuilds its controls (or a test creating several managers) does not fire
|
|
* the action twice.
|
|
*
|
|
* @param {string} [source] - Event manager source identifier
|
|
*/
|
|
export function registerActionShortcuts(source = ACTION_SHORTCUTS_SOURCE) {
|
|
eventManager.removeHandler('keydown', source);
|
|
eventManager.addHandler('keydown', source, (e) => handleActionShortcut(e), {
|
|
priority: 90,
|
|
skipWhenModalOpen: true
|
|
});
|
|
}
|