import { Shortcut } from './shortcut'; /** * Contains keyboard and mouse events binded on each Block by Block Manager */ /** * ShortcutData interface * Each shortcut must have name and handler * `name` is a shortcut, like 'CMD+K', 'CMD+B' etc * `handler` is a callback * @interface ShortcutData */ export interface ShortcutData { /** * Shortcut name * Ex. CMD+I, CMD+B .... */ name: string; /** * Shortcut handler */ handler(event: KeyboardEvent): void; /** * Element handler should be added for */ on: HTMLElement | Document; } /** * Get a human-readable description of an element for error messages * @param element - The element to describe * @returns A string description of the element */ const getElementDescription = (element: HTMLElement | Document): string => { if (element instanceof HTMLElement) { return element.tagName.toLowerCase() + (element.className ? `.${element.className}` : ''); } if (element instanceof Document) { return 'document'; } return String(element); }; /** * @class Shortcut * @classdesc Allows to register new shortcut * * Internal Shortcuts Module */ class ShortcutsClass { /** * All registered shortcuts * @type {Map} */ private registeredShortcuts: Map = new Map(); /** * Register shortcut * @param shortcut - shortcut options */ public add(shortcut: ShortcutData): void { const foundShortcut = this.findShortcut(shortcut.on, shortcut.name); if (foundShortcut) { const elementDescription = getElementDescription(shortcut.on); throw Error( `Shortcut ${shortcut.name} is already registered for ${elementDescription}. Please remove it before add a new handler.` ); } const newShortcut = new Shortcut({ name: shortcut.name, on: shortcut.on, callback: shortcut.handler, }); const shortcuts = this.registeredShortcuts.get(shortcut.on) || []; this.registeredShortcuts.set(shortcut.on, [...shortcuts, newShortcut]); } /** * Remove shortcut * @param element - Element shortcut is set for * @param name - shortcut name */ public remove(element: HTMLElement | Document, name: string): void { const shortcut = this.findShortcut(element, name); if (!shortcut) { return; } shortcut.remove(); const shortcuts = this.registeredShortcuts.get(element); if (!shortcuts) { return; } const filteredShortcuts = shortcuts.filter(el => el !== shortcut); if (filteredShortcuts.length === 0) { this.registeredShortcuts.delete(element); return; } this.registeredShortcuts.set(element, filteredShortcuts); } /** * Get Shortcut instance if exist * @param element - Element shorcut is set for * @param shortcut - shortcut name * @returns {number} index - shortcut index if exist */ private findShortcut(element: HTMLElement | Document, shortcut: string): Shortcut | void { const shortcuts = this.registeredShortcuts.get(element) || []; return shortcuts.find(({ name }) => name === shortcut); } } export const Shortcuts = new ShortcutsClass();