mirror of
https://github.com/home-assistant/frontend.git
synced 2025-11-11 03:51:07 +00:00
Compare commits
18 Commits
copilot/fi
...
tinykeys-1
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b17dc53d27 | ||
|
|
ae9f194868 | ||
|
|
c762f12ffd | ||
|
|
cf9f4afcdc | ||
|
|
9f394a4ed6 | ||
|
|
9da1835a57 | ||
|
|
8d3c36236f | ||
|
|
b4d6ee105f | ||
|
|
ffcb78bc7b | ||
|
|
6f7be230d8 | ||
|
|
f5fa606ac2 | ||
|
|
b18c14117d | ||
|
|
e64366ea61 | ||
|
|
4c3e1430bf | ||
|
|
2019bb8ebc | ||
|
|
523c26a11a | ||
|
|
3a7d106bcc | ||
|
|
b6a841d109 |
66
src/common/keyboard/shortcuts.ts
Normal file
66
src/common/keyboard/shortcuts.ts
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
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 {
|
||||||
|
handler: ShortcutHandler;
|
||||||
|
/**
|
||||||
|
* If true, allows shortcuts even when text is selected.
|
||||||
|
* Default is false to avoid interrupting copy/paste.
|
||||||
|
*/
|
||||||
|
allowWhenTextSelected?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register keyboard shortcuts using tinykeys.
|
||||||
|
* Automatically blocks shortcuts in input fields and during text selection.
|
||||||
|
*/
|
||||||
|
function registerShortcuts(
|
||||||
|
shortcuts: Record<string, ShortcutConfig>
|
||||||
|
): () => void {
|
||||||
|
const wrappedShortcuts: Record<string, ShortcutHandler> = {};
|
||||||
|
|
||||||
|
Object.entries(shortcuts).forEach(([key, config]) => {
|
||||||
|
wrappedShortcuts[key] = (event: KeyboardEvent) => {
|
||||||
|
if (!canOverrideAlphanumericInput(event.composedPath())) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!config.allowWhenTextSelected && window.getSelection()?.toString()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
config.handler(event);
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
return tinykeys(window, wrappedShortcuts);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Manages keyboard shortcuts registration and cleanup.
|
||||||
|
*/
|
||||||
|
export class ShortcutManager {
|
||||||
|
private _disposer?: () => void;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register keyboard shortcuts.
|
||||||
|
* Uses tinykeys syntax: https://github.com/jamiebuilds/tinykeys#usage
|
||||||
|
*/
|
||||||
|
public add(shortcuts: Record<string, ShortcutConfig>) {
|
||||||
|
this._disposer = registerShortcuts(shortcuts);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove all registered shortcuts.
|
||||||
|
*/
|
||||||
|
public remove() {
|
||||||
|
this._disposer?.();
|
||||||
|
this._disposer = undefined;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,5 +1,4 @@
|
|||||||
import type { PropertyValues } from "lit";
|
import type { PropertyValues } from "lit";
|
||||||
import { tinykeys } from "tinykeys";
|
|
||||||
import memoizeOne from "memoize-one";
|
import memoizeOne from "memoize-one";
|
||||||
import { isComponentLoaded } from "../common/config/is_component_loaded";
|
import { isComponentLoaded } from "../common/config/is_component_loaded";
|
||||||
import { mainWindow } from "../common/dom/get_main_window";
|
import { mainWindow } from "../common/dom/get_main_window";
|
||||||
@@ -12,9 +11,9 @@ import type { Constructor, HomeAssistant } from "../types";
|
|||||||
import { storeState } from "../util/ha-pref-storage";
|
import { storeState } from "../util/ha-pref-storage";
|
||||||
import { showToast } from "../util/toast";
|
import { showToast } from "../util/toast";
|
||||||
import type { HassElement } from "./hass-element";
|
import type { HassElement } from "./hass-element";
|
||||||
|
import { ShortcutManager } from "../common/keyboard/shortcuts";
|
||||||
import { extractSearchParamsObject } from "../common/url/search-params";
|
import { extractSearchParamsObject } from "../common/url/search-params";
|
||||||
import { showVoiceCommandDialog } from "../dialogs/voice-command-dialog/show-ha-voice-command-dialog";
|
import { showVoiceCommandDialog } from "../dialogs/voice-command-dialog/show-ha-voice-command-dialog";
|
||||||
import { canOverrideAlphanumericInput } from "../common/dom/can-override-input";
|
|
||||||
import { showShortcutsDialog } from "../dialogs/shortcuts/show-shortcuts-dialog";
|
import { showShortcutsDialog } from "../dialogs/shortcuts/show-shortcuts-dialog";
|
||||||
import type { Redirects } from "../panels/my/ha-panel-my";
|
import type { Redirects } from "../panels/my/ha-panel-my";
|
||||||
|
|
||||||
@@ -62,21 +61,22 @@ export default <T extends Constructor<HassElement>>(superClass: T) =>
|
|||||||
}
|
}
|
||||||
|
|
||||||
private _registerShortcut() {
|
private _registerShortcut() {
|
||||||
tinykeys(window, {
|
const shortcutManager = new ShortcutManager();
|
||||||
|
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) },
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -87,7 +87,6 @@ export default <T extends Constructor<HassElement>>(superClass: T) =>
|
|||||||
private _showVoiceCommandDialog(e: KeyboardEvent) {
|
private _showVoiceCommandDialog(e: KeyboardEvent) {
|
||||||
if (
|
if (
|
||||||
!this.hass?.enableShortcuts ||
|
!this.hass?.enableShortcuts ||
|
||||||
!canOverrideAlphanumericInput(e.composedPath()) ||
|
|
||||||
!this._conversation(this.hass.config.components)
|
!this._conversation(this.hass.config.components)
|
||||||
) {
|
) {
|
||||||
return;
|
return;
|
||||||
@@ -105,7 +104,7 @@ export default <T extends Constructor<HassElement>>(superClass: T) =>
|
|||||||
e: KeyboardEvent,
|
e: KeyboardEvent,
|
||||||
mode: QuickBarMode = QuickBarMode.Entity
|
mode: QuickBarMode = QuickBarMode.Entity
|
||||||
) {
|
) {
|
||||||
if (!this._canShowQuickBar(e)) {
|
if (!this._canShowQuickBar()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -118,7 +117,7 @@ export default <T extends Constructor<HassElement>>(superClass: T) =>
|
|||||||
}
|
}
|
||||||
|
|
||||||
private _showShortcutDialog(e: KeyboardEvent) {
|
private _showShortcutDialog(e: KeyboardEvent) {
|
||||||
if (!this._canShowQuickBar(e)) {
|
if (!this._canShowQuickBar()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -131,10 +130,7 @@ export default <T extends Constructor<HassElement>>(superClass: T) =>
|
|||||||
}
|
}
|
||||||
|
|
||||||
private async _createMyLink(e: KeyboardEvent) {
|
private async _createMyLink(e: KeyboardEvent) {
|
||||||
if (
|
if (!this.hass?.enableShortcuts) {
|
||||||
!this.hass?.enableShortcuts ||
|
|
||||||
!canOverrideAlphanumericInput(e.composedPath())
|
|
||||||
) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -193,11 +189,7 @@ export default <T extends Constructor<HassElement>>(superClass: T) =>
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private _canShowQuickBar(e: KeyboardEvent) {
|
private _canShowQuickBar() {
|
||||||
return (
|
return this.hass?.user?.is_admin && this.hass.enableShortcuts;
|
||||||
this.hass?.user?.is_admin &&
|
|
||||||
this.hass.enableShortcuts &&
|
|
||||||
canOverrideAlphanumericInput(e.composedPath())
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user