2 * The default mapping of unique id to shortcut key.
3 * @type {Object<string, string>}
10 function reverseMap(map) {
12 for (const [key, value] of Object.entries(map)) {
13 reversed[value] = key;
19 * @extends {Component}
24 this.container = this.$el;
25 this.mapById = defaultMap;
26 this.mapByShortcut = reverseMap(this.mapById);
28 this.hintsShowing = false;
29 // TODO - Allow custom key maps
30 // TODO - Allow turning off shortcuts
31 // TODO - Roll out to interface elements
32 // TODO - Hide hints on focus, scroll, click
34 this.setupListeners();
38 window.addEventListener('keydown', event => {
40 if (event.target.closest('input, select, textarea')) {
44 const shortcutId = this.mapByShortcut[event.key];
46 const wasHandled = this.runShortcut(shortcutId);
48 event.preventDefault();
53 window.addEventListener('keydown', event => {
54 if (event.key === '?') {
55 this.hintsShowing ? this.hideHints() : this.showHints();
56 this.hintsShowing = !this.hintsShowing;
62 * Run the given shortcut, and return a boolean to indicate if the event
63 * was successfully handled by a shortcut action.
68 const el = this.container.querySelector(`[data-shortcut="${id}"]`);
69 console.info('Shortcut run', el);
74 if (el.matches('input, textarea, select')) {
79 if (el.matches('a, button')) {
84 console.error(`Shortcut attempted to be ran for element type that does not have handling setup`, el);
90 const shortcutEls = this.container.querySelectorAll('[data-shortcut]');
91 for (const shortcutEl of shortcutEls) {
92 const id = shortcutEl.getAttribute('data-shortcut');
93 const key = this.mapById[id];
94 this.showHintLabel(shortcutEl, key);
98 showHintLabel(targetEl, key) {
99 const targetBounds = targetEl.getBoundingClientRect();
100 const label = document.createElement('div');
101 label.classList.add('shortcut-hint');
102 label.textContent = key;
103 this.container.append(label);
105 const labelBounds = label.getBoundingClientRect();
107 label.style.insetInlineStart = `${((targetBounds.x + targetBounds.width) - (labelBounds.width + 12))}px`;
108 label.style.insetBlockStart = `${(targetBounds.y + (targetBounds.height - labelBounds.height) / 2)}px`;
112 const hints = this.container.querySelectorAll('.shortcut-hint');
113 for (const hint of hints) {
119 export default Shortcuts;