ember-keyboard
ember-keyboard copied to clipboard
Provide support for scopes
Hallo,
I'm currently working on an app that utilizes a custom service for shortcuts. This service includes a scope that indicates the app's current state. It would greatly benefit me to have a centrally managed scope for easier migration. Is there a solution available that I might have missed?
My current ShortcutsService;
import RouterService from '@ember/routing/router-service';
import Service, { inject as service } from '@ember/service';
import { tracked } from '@glimmer/tracking';
import CommandPaletteService from './command-palette';
import EventBusService from './event-bus';
import { ShortCutScope, ShortCut } from '../types/app';
/**
* ShortcutsService
*/
export default class ShortcutsService extends Service {
@service declare router: RouterService;
@service declare eventBus: EventBusService;
// prettier-ignore
@service('command-palette') declare commandPaletteService: CommandPaletteService;
// Defaults
@tracked scope = ShortCutScope.All;
// prettier-ignore
shortcuts: ShortCut[] = [
{shortcut: 'command+1', scope: ShortCutScope.All, callback: () => this.router.transitionTo('documents') },
{shortcut: 'command+2', scope: ShortCutScope.All, callback: () => this.router.transitionTo('import') },
{shortcut: 'command+3', scope: ShortCutScope.All, callback: () => this.router.transitionTo('senders') },
{shortcut: 'command+,', scope: ShortCutScope.All, callback: () => this.router.transitionTo('settings') },
{shortcut: 'command+k', scope: ShortCutScope.All, callback: () => this.commandPaletteService.open() },
{shortcut: 'escape', scope: ShortCutScope.CommandPalette, callback: () => this.commandPaletteService.close() },
{shortcut: 'arrowup', scope: ShortCutScope.CommandPalette, callback: () => this.commandPaletteService.indexChange(false) },
{shortcut: 'arrowdown', scope: ShortCutScope.CommandPalette, callback: () => this.commandPaletteService.indexChange(true) },
];
// Id's of input elements that should not be filtered
excludedInputIds = ['command-palette-input'];
/**
* Setup the shortcuts
* @returns {void}
*/
setup(): void {
document.addEventListener('keydown', (event) => {
this.handleKeyDown(event);
});
}
/**
* Set the scope
* @param {ShortCutScope} scope
*/
setScope(scope: ShortCutScope): void {
this.scope = scope;
}
/**
* Get the scope
* @returns {ShortCutScope}
*/
get getScope(): ShortCutScope {
return this.scope;
}
/**
* Handle key down event
* @param {KeyboardEvent} event
*/
handleKeyDown(event: KeyboardEvent): void {
if (this.filterEvent(event)) {
return;
}
const shortcut = this.findShortcut(event);
if (shortcut) {
event.preventDefault();
event.stopPropagation();
shortcut.callback();
}
}
/**
* Filter the event for input elements
*
* @param {KeyboardEvent} event
*/
filterEvent(event: KeyboardEvent): boolean {
const target = event.target as HTMLElement;
if (target.tagName === 'INPUT' || target.tagName === 'TEXTAREA') {
if (this.excludedInputIds.includes(target.id)) {
return false;
}
return true;
}
return false;
}
/**
* Find a shortcut
* @param {KeyboardEvent} event
* @returns {ShortCut | undefined}
*/
findShortcut(event: KeyboardEvent): ShortCut | undefined {
const key = event.key.toLowerCase();
const isCommand = event.metaKey || event.ctrlKey;
const shortcut = `${isCommand ? 'command+' : ''}${key}`;
return this.shortcuts.find((s) => {
return (
s.shortcut === shortcut &&
(s.scope === this.scope || s.scope === ShortCutScope.All)
);
});
}
}
// Don't remove this declaration: this is what enables TypeScript to resolve
// this service using `Owner.lookup('service:shortcuts')`, as well
// as to check when you pass the service name as an argument to the decorator,
// like `@service('shortcuts') declare altName: ShortcutsService;`.
declare module '@ember/service' {
interface Registry {
shortcuts: ShortcutsService;
}
}