This commit is contained in:
sguenter 2023-12-27 19:01:12 +01:00
parent da535c5de2
commit c86abe5ff7

View File

@ -1,24 +1,21 @@
import { HotkeyDefinitionType, specialKeys } from './HotkeyDefinitionType';
import { HotkeyListener } from './HotkeyListener';
import { JsonHelper, ObjectHelper } from '@ainias42/js-helper';
import { JsonHelper, ObjectHelper, Override } from '@ainias42/js-helper';
import { HotkeyEntry } from './HotkeyEntry';
import { SubKeyType } from './SubKeyType';
import { HotkeyPressedMap } from './HotkeyPressedMap';
import { HotkeyConfigWithOptionals } from './HotkeyConfigWithOptionals';
export class HotkeyManager<HotkeyConfig extends Record<string, HotkeyEntry<string>>> {
export class HotKeyManager<HotkeyConfig extends Record<string, HotkeyEntry<string>>> {
private keyPressedMap = new Map<string, boolean>();
private hotkeys: HotkeyConfig;
private hotkeysPressedMap: HotkeyPressedMap<HotkeyConfig>;
private hotKeys: HotkeyConfig;
private hotKeysPressedMap: HotkeyPressedMap<HotkeyConfig>;
private enabled = true;
private ignoreFormElements = true;
private readonly ignoreFormElements: boolean = true;
constructor(hotkeys: HotkeyConfigWithOptionals<HotkeyConfig>, ignoreFormElements?: boolean) {
this.hotkeys = ObjectHelper.entries(hotkeys).reduce((acc, [key, value]) => {
acc[key] = { subKeys: {}, callbacks: [], ...value } as unknown as HotkeyConfig[typeof key];
return acc;
}, {} as HotkeyConfig);
this.hotkeysPressedMap = this.generateHotkeyPressedMap();
this.hotKeys = {} as HotkeyConfig;
this.addHotKeys(hotkeys);
if (ignoreFormElements !== undefined) {
this.ignoreFormElements = ignoreFormElements;
@ -29,7 +26,8 @@ export class HotkeyManager<HotkeyConfig extends Record<string, HotkeyEntry<strin
return (
element instanceof HTMLInputElement ||
element instanceof HTMLSelectElement ||
element instanceof HTMLTextAreaElement
element instanceof HTMLTextAreaElement ||
(element instanceof HTMLElement && element.contentEditable === 'true')
);
}
@ -38,7 +36,7 @@ export class HotkeyManager<HotkeyConfig extends Record<string, HotkeyEntry<strin
}
private static checkAffections(keyDefinitions: HotkeyDefinitionType[], key: string): boolean {
return keyDefinitions.some((keyDefinition) => HotkeyManager.checkAffection(keyDefinition, key));
return keyDefinitions.some((keyDefinition) => HotKeyManager.checkAffection(keyDefinition, key));
}
private static checkAffection(keyDefinition: HotkeyDefinitionType, key: string): boolean {
@ -48,11 +46,28 @@ export class HotkeyManager<HotkeyConfig extends Record<string, HotkeyEntry<strin
);
}
addHotKeys<NewHotkeyConfig extends Record<string, HotkeyEntry<string>>>(
hotkeys: HotkeyConfigWithOptionals<NewHotkeyConfig>,
) {
type NewConfig = Override<HotkeyConfig, NewHotkeyConfig>;
this.hotKeys = {
...this.hotKeys,
...ObjectHelper.entries(hotkeys).reduce((acc, [key, value]) => {
acc[key] = { subKeys: {}, callbacks: [], ...value } as unknown as NewConfig[typeof key];
return acc;
}, {} as NewConfig),
};
this.hotKeysPressedMap = this.generateHotkeyPressedMap();
return this as unknown as HotKeyManager<NewConfig>;
}
addListener<HotkeyName extends keyof HotkeyConfig>(
hotkeyName: HotkeyName,
callback: HotkeyListener<SubKeyType<HotkeyConfig, HotkeyName>>,
) {
const { callbacks } = this.hotkeys[hotkeyName];
const { callbacks } = this.hotKeys[hotkeyName];
callbacks.push(callback);
return () => {
const index = callbacks.indexOf(callback);
@ -71,15 +86,15 @@ export class HotkeyManager<HotkeyConfig extends Record<string, HotkeyEntry<strin
}
addHotKeyDefinition(hotkey: keyof HotkeyConfig, definition: HotkeyDefinitionType) {
this.hotkeys[hotkey].keys.push(definition);
this.hotKeys[hotkey].keys.push(definition);
}
removeHotkeyDefinition(hotkey: keyof HotkeyConfig, definition: HotkeyDefinitionType) {
this.hotkeys[hotkey].keys = this.hotkeys[hotkey].keys.filter((key) => !HotkeyManager.isEqual(key, definition));
removeHotKeyDefinition(hotkey: keyof HotkeyConfig, definition: HotkeyDefinitionType) {
this.hotKeys[hotkey].keys = this.hotKeys[hotkey].keys.filter((key) => !HotKeyManager.isEqual(key, definition));
}
setHotkeyDefinitions(hotkey: keyof HotkeyConfig, definitions: HotkeyDefinitionType[]) {
this.hotkeys[hotkey].keys = definitions;
setHotKeyDefinitions(hotkey: keyof HotkeyConfig, definitions: HotkeyDefinitionType[]) {
this.hotKeys[hotkey].keys = definitions;
}
addSubKeyDefinition<HotkeyName extends keyof HotkeyConfig>(
@ -87,7 +102,7 @@ export class HotkeyManager<HotkeyConfig extends Record<string, HotkeyEntry<strin
subKey: SubKeyType<HotkeyConfig, HotkeyName>,
definition: HotkeyDefinitionType,
) {
this.hotkeys[hotkey].subKeys[subKey as string].push(definition);
this.hotKeys[hotkey].subKeys[subKey as string].push(definition);
}
removeSubKeyDefinition<HotkeyName extends keyof HotkeyConfig>(
@ -95,8 +110,8 @@ export class HotkeyManager<HotkeyConfig extends Record<string, HotkeyEntry<strin
subKey: SubKeyType<HotkeyConfig, HotkeyName>,
definition: HotkeyDefinitionType,
) {
const { subKeys } = this.hotkeys[hotkey];
subKeys[subKey as string] = subKeys[subKey as string].filter((key) => !HotkeyManager.isEqual(key, definition));
const { subKeys } = this.hotKeys[hotkey];
subKeys[subKey as string] = subKeys[subKey as string].filter((key) => !HotKeyManager.isEqual(key, definition));
}
setSubKeyDefinitions<HotkeyName extends keyof HotkeyConfig>(
@ -104,16 +119,16 @@ export class HotkeyManager<HotkeyConfig extends Record<string, HotkeyEntry<strin
subKey: SubKeyType<HotkeyConfig, HotkeyName>,
definitions: HotkeyDefinitionType[],
) {
this.hotkeys[hotkey].subKeys[subKey as string] = definitions;
this.hotKeys[hotkey].subKeys[subKey as string] = definitions;
}
getConfig() {
return this.hotkeys;
return this.hotKeys;
}
addKeyListeners() {
window.addEventListener('keydown', (e) => {
if (this.ignoreFormElements && HotkeyManager.isFormElement(e.target)) {
if (this.ignoreFormElements && HotKeyManager.isFormElement(e.target)) {
return;
}
@ -134,16 +149,16 @@ export class HotkeyManager<HotkeyConfig extends Record<string, HotkeyEntry<strin
});
}
getHotkeysPressedMap() {
return this.hotkeysPressedMap;
getHotKeysPressedMap() {
return this.hotKeysPressedMap;
}
isHotkeyPressed(hotkey: keyof HotkeyConfig) {
return this.hotkeysPressedMap[hotkey].isPressed;
isHotKeyPressed(hotkey: keyof HotkeyConfig) {
return this.hotKeysPressedMap[hotkey].isPressed;
}
isSubKeyPressed<Hotkey extends keyof HotkeyConfig>(hotkey: Hotkey, subkey: SubKeyType<HotkeyConfig, Hotkey>) {
return this.hotkeysPressedMap[hotkey].isPressed && this.hotkeysPressedMap[hotkey].subKeys[subkey];
return this.hotKeysPressedMap[hotkey].isPressed && this.hotKeysPressedMap[hotkey].subKeys[subkey];
}
private checkHotkeyDefinitions(keyDefinitions: HotkeyDefinitionType[]): boolean {
@ -165,7 +180,7 @@ export class HotkeyManager<HotkeyConfig extends Record<string, HotkeyEntry<strin
}
private generateHotkeyPressedMap() {
return ObjectHelper.entries(this.hotkeys).reduce((acc, [key, value]) => {
return ObjectHelper.entries(this.hotKeys).reduce((acc, [key, value]) => {
const isPressed = this.checkHotkeyDefinitions(value.keys);
acc[key] = {
isPressed,
@ -183,8 +198,8 @@ export class HotkeyManager<HotkeyConfig extends Record<string, HotkeyEntry<strin
}
private triggerListenerFor(key: keyof HotkeyConfig, event: KeyboardEvent) {
const pressedEntry = this.hotkeysPressedMap[key];
this.hotkeys[key].callbacks.forEach((callback) =>
const pressedEntry = this.hotKeysPressedMap[key];
this.hotKeys[key].callbacks.forEach((callback) =>
callback({ event, subKeys: pressedEntry.subKeys, isPressed: pressedEntry.isPressed }),
);
}
@ -192,9 +207,9 @@ export class HotkeyManager<HotkeyConfig extends Record<string, HotkeyEntry<strin
private checkHotkeys(event: KeyboardEvent) {
const hotkeysToTrigger: (keyof HotkeyConfig)[] = [];
const oldMap = this.hotkeysPressedMap;
this.hotkeysPressedMap = this.generateHotkeyPressedMap();
const hotkeysToCheckAffection = ObjectHelper.entries(this.hotkeysPressedMap).filter(([key, value]) => {
const oldMap = this.hotKeysPressedMap;
this.hotKeysPressedMap = this.generateHotkeyPressedMap();
const hotkeysToCheckAffection = ObjectHelper.entries(this.hotKeysPressedMap).filter(([key, value]) => {
const oldValue = oldMap[key];
if (
oldValue.isPressed !== value.isPressed ||
@ -209,12 +224,12 @@ export class HotkeyManager<HotkeyConfig extends Record<string, HotkeyEntry<strin
// Check for repeated pressed. Nothing changed, but key is pressed again
const eventKey = event.key.toLowerCase();
hotkeysToCheckAffection.forEach(([key]) => {
const keyDefinition = this.hotkeys[key];
if (HotkeyManager.checkAffections(keyDefinition.keys, eventKey)) {
const keyDefinition = this.hotKeys[key];
if (HotKeyManager.checkAffections(keyDefinition.keys, eventKey)) {
hotkeysToTrigger.push(key);
} else {
ObjectHelper.values(keyDefinition.subKeys).forEach((subKeyDefinitions) => {
if (HotkeyManager.checkAffections(subKeyDefinitions, eventKey)) {
if (HotKeyManager.checkAffections(subKeyDefinitions, eventKey)) {
hotkeysToTrigger.push(key);
}
});