This commit is contained in:
Aidan Timson
2025-11-04 12:50:54 +00:00
parent cf9f4afcdc
commit c762f12ffd
2 changed files with 41 additions and 21 deletions

View File

@@ -1,8 +1,27 @@
import { tinykeys } from "tinykeys"; import { tinykeys } from "tinykeys";
import { canOverrideAlphanumericInput } from "../dom/can-override-input"; import { canOverrideAlphanumericInput } from "../dom/can-override-input";
/**
* A function to handle a keyboard shortcut.
*/
export type ShortcutHandler = (event: KeyboardEvent) => void; export type ShortcutHandler = (event: KeyboardEvent) => void;
/**
* Configuration for a keyboard shortcut.
*/
export interface ShortcutConfig {
/**
* The handler function to call when the shortcut is triggered.
*/
handler: ShortcutHandler;
/**
* If true, the shortcut will be triggered even when the user is selecting text.
* By default (false), shortcuts are blocked during text selection to avoid
* interrupting copy/paste operations.
*/
allowWhenTextSelected?: boolean;
}
interface ShortcutEntry { interface ShortcutEntry {
/** /**
* The key that the shortcut is registered to. * The key that the shortcut is registered to.
@@ -17,25 +36,26 @@ interface ShortcutEntry {
/** /**
* Register keyboard shortcuts using tinykeys. * Register keyboard shortcuts using tinykeys.
* *
* @param shortcuts - Key combinations mapped to handler functions. * @param shortcuts - Key combinations mapped to shortcut configurations.
* @returns A function to remove the shortcuts. * @returns A function to remove the shortcuts.
*/ */
function registerShortcuts( function registerShortcuts(
shortcuts: Record<string, ShortcutHandler> shortcuts: Record<string, ShortcutConfig>
): () => void { ): () => void {
const wrappedShortcuts: Record<string, ShortcutHandler> = {}; const wrappedShortcuts: Record<string, ShortcutHandler> = {};
Object.entries(shortcuts).forEach(([key, handler]) => { Object.entries(shortcuts).forEach(([key, config]) => {
wrappedShortcuts[key] = (event: KeyboardEvent) => { wrappedShortcuts[key] = (event: KeyboardEvent) => {
// Don't capture the event if the user is focused on an input field // Don't capture the event if the user is focused on an input field
if (!canOverrideAlphanumericInput(event.composedPath())) { if (!canOverrideAlphanumericInput(event.composedPath())) {
return; return;
} }
// Don't capture the event if the user is selecting text // Don't capture the event if the user is selecting text to avoid
if (window.getSelection()?.toString()) { // interrupting copy/paste operations or text manipulation
if (!config.allowWhenTextSelected && window.getSelection()?.toString()) {
return; return;
} }
handler(event); config.handler(event);
}; };
}); });
@@ -51,12 +71,12 @@ export class ShortcutManager {
/** /**
* Add a group of keyboard shortcuts to the manager. * Add a group of keyboard shortcuts to the manager.
* *
* @param shortcuts - Key combinations mapped to handler functions. * @param shortcuts - Key combinations mapped to shortcut configurations.
* Uses tinykeys syntax. See https://github.com/jamiebuilds/tinykeys#usage. * Uses tinykeys syntax. See https://github.com/jamiebuilds/tinykeys#usage.
*/ */
public add(shortcuts: Record<string, ShortcutHandler>) { public add(shortcuts: Record<string, ShortcutConfig>) {
Object.entries(shortcuts).forEach(([key, handler]) => { Object.entries(shortcuts).forEach(([key, config]) => {
const disposer = registerShortcuts({ [key]: handler }); const disposer = registerShortcuts({ [key]: config });
const entry: ShortcutEntry = { key, disposer }; const entry: ShortcutEntry = { key, disposer };
this.shortcutEntries.push(entry); this.shortcutEntries.push(entry);
}); });

View File

@@ -64,19 +64,19 @@ export default <T extends Constructor<HassElement>>(superClass: T) =>
const shortcutManager = new ShortcutManager(); const shortcutManager = new ShortcutManager();
shortcutManager.add({ shortcutManager.add({
// Those are for latin keyboards that have e, c, m keys // Those are for latin keyboards that have e, c, m keys
e: (ev) => this._showQuickBar(ev), e: { handler: (ev) => this._showQuickBar(ev) },
c: (ev) => this._showQuickBar(ev, QuickBarMode.Command), c: { handler: (ev) => this._showQuickBar(ev, QuickBarMode.Command) },
m: (ev) => this._createMyLink(ev), m: { handler: (ev) => this._createMyLink(ev) },
a: (ev) => this._showVoiceCommandDialog(ev), a: { handler: (ev) => this._showVoiceCommandDialog(ev) },
d: (ev) => this._showQuickBar(ev, QuickBarMode.Device), d: { handler: (ev) => this._showQuickBar(ev, QuickBarMode.Device) },
// Workaround see https://github.com/jamiebuilds/tinykeys/issues/130 // Workaround see https://github.com/jamiebuilds/tinykeys/issues/130
"Shift+?": (ev) => this._showShortcutDialog(ev), "Shift+?": { handler: (ev) => this._showShortcutDialog(ev) },
// Those are fallbacks for non-latin keyboards that don't have e, c, m keys (qwerty-based shortcuts) // Those are fallbacks for non-latin keyboards that don't have e, c, m keys (qwerty-based shortcuts)
KeyE: (ev) => this._showQuickBar(ev), KeyE: { handler: (ev) => this._showQuickBar(ev) },
KeyC: (ev) => this._showQuickBar(ev, QuickBarMode.Command), KeyC: { handler: (ev) => this._showQuickBar(ev, QuickBarMode.Command) },
KeyM: (ev) => this._createMyLink(ev), KeyM: { handler: (ev) => this._createMyLink(ev) },
KeyA: (ev) => this._showVoiceCommandDialog(ev), KeyA: { handler: (ev) => this._showVoiceCommandDialog(ev) },
KeyD: (ev) => this._showQuickBar(ev, QuickBarMode.Device), KeyD: { handler: (ev) => this._showQuickBar(ev, QuickBarMode.Device) },
}); });
} }