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 { canOverrideAlphanumericInput } from "../dom/can-override-input";
/**
* A function to handle a keyboard shortcut.
*/
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 {
/**
* The key that the shortcut is registered to.
@@ -17,25 +36,26 @@ interface ShortcutEntry {
/**
* 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.
*/
function registerShortcuts(
shortcuts: Record<string, ShortcutHandler>
shortcuts: Record<string, ShortcutConfig>
): () => void {
const wrappedShortcuts: Record<string, ShortcutHandler> = {};
Object.entries(shortcuts).forEach(([key, handler]) => {
Object.entries(shortcuts).forEach(([key, config]) => {
wrappedShortcuts[key] = (event: KeyboardEvent) => {
// Don't capture the event if the user is focused on an input field
if (!canOverrideAlphanumericInput(event.composedPath())) {
return;
}
// Don't capture the event if the user is selecting text
if (window.getSelection()?.toString()) {
// Don't capture the event if the user is selecting text to avoid
// interrupting copy/paste operations or text manipulation
if (!config.allowWhenTextSelected && window.getSelection()?.toString()) {
return;
}
handler(event);
config.handler(event);
};
});
@@ -51,12 +71,12 @@ export class ShortcutManager {
/**
* 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.
*/
public add(shortcuts: Record<string, ShortcutHandler>) {
Object.entries(shortcuts).forEach(([key, handler]) => {
const disposer = registerShortcuts({ [key]: handler });
public add(shortcuts: Record<string, ShortcutConfig>) {
Object.entries(shortcuts).forEach(([key, config]) => {
const disposer = registerShortcuts({ [key]: config });
const entry: ShortcutEntry = { key, disposer };
this.shortcutEntries.push(entry);
});

View File

@@ -64,19 +64,19 @@ export default <T extends Constructor<HassElement>>(superClass: T) =>
const shortcutManager = new ShortcutManager();
shortcutManager.add({
// Those are for latin keyboards that have e, c, m keys
e: (ev) => this._showQuickBar(ev),
c: (ev) => this._showQuickBar(ev, QuickBarMode.Command),
m: (ev) => this._createMyLink(ev),
a: (ev) => this._showVoiceCommandDialog(ev),
d: (ev) => this._showQuickBar(ev, QuickBarMode.Device),
e: { handler: (ev) => this._showQuickBar(ev) },
c: { handler: (ev) => this._showQuickBar(ev, QuickBarMode.Command) },
m: { handler: (ev) => this._createMyLink(ev) },
a: { handler: (ev) => this._showVoiceCommandDialog(ev) },
d: { handler: (ev) => this._showQuickBar(ev, QuickBarMode.Device) },
// 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)
KeyE: (ev) => this._showQuickBar(ev),
KeyC: (ev) => this._showQuickBar(ev, QuickBarMode.Command),
KeyM: (ev) => this._createMyLink(ev),
KeyA: (ev) => this._showVoiceCommandDialog(ev),
KeyD: (ev) => this._showQuickBar(ev, QuickBarMode.Device),
KeyE: { handler: (ev) => this._showQuickBar(ev) },
KeyC: { handler: (ev) => this._showQuickBar(ev, QuickBarMode.Command) },
KeyM: { handler: (ev) => this._createMyLink(ev) },
KeyA: { handler: (ev) => this._showVoiceCommandDialog(ev) },
KeyD: { handler: (ev) => this._showQuickBar(ev, QuickBarMode.Device) },
});
}