UNPKG

yy-menu

Version:

A traditional menu system for web apps inspired by Electron

261 lines (249 loc) 8.08 kB
/** * Handles all keyboard input for the menu and user-registered keys */ export const localAccelerator = { init: function () { if (!localAccelerator.menuKeys) { localAccelerator.menuKeys = {} localAccelerator.keys = {} document.body.addEventListener('keydown', (e) => localAccelerator.keydown(localAccelerator, e)) document.body.addEventListener('keyup', (e) => localAccelerator.keyup(localAccelerator, e)) } }, /** * clear all user-registered keys */ clearKeys: function () { localAccelerator.keys = {} }, /** * Register a shortcut key for use by an open menu * @param {KeyCodes} letter * @param {MenuItem} menuItem * @param {boolean} applicationMenu * @private */ registerMenuShortcut: function (letter, menuItem) { if (letter) { const keyCode = (menuItem.menu.applicationMenu ? 'alt+' : '') + letter localAccelerator.menuKeys[localAccelerator.prepareKey(keyCode)] = (e) => { menuItem.handleClick(e) e.stopPropagation() e.preventDefault() } } }, /** * Register special shortcut keys for menu * @param {MenuItem} menuItem * @private */ registerMenuSpecial: function (menu) { localAccelerator.menuKeys['escape'] = () => menu.closeAll() localAccelerator.menuKeys['enter'] = (e) => menu.enter(e) localAccelerator.menuKeys['space'] = (e) => menu.enter(e) localAccelerator.menuKeys['arrowright'] = (e) => menu.move(e, 'right') localAccelerator.menuKeys['arrowleft'] = (e) => menu.move(e, 'left') localAccelerator.menuKeys['arrowup'] = (e) => menu.move(e, 'up') localAccelerator.menuKeys['arrowdown'] = (e) => menu.move(e, 'down') }, /** * special key registration for alt * @param {function} pressed * @param {function} released * @private */ registerAlt: function (pressed, released) { localAccelerator.alt = { pressed, released } }, /** * Removes menu shortcuts * @private */ unregisterMenuShortcuts: function () { localAccelerator.menuKeys = {} }, /** * Keycodes definition. In the form of modifier[+modifier...]+key * <p>For example: ctrl+shift+e</p> * <p>KeyCodes are case insensitive (i.e., shift+a is the same as Shift+A). And spaces are removed</p> * <p>You can assign more than one key to the same shortcut by using a | between the keys (e.g., 'shift+a | ctrl+a')</p> * <pre> * Modifiers: * ctrl, alt, shift, meta, (ctrl aliases: command, control, commandorcontrol) * </pre> * <pre> * Keys: * escape, 0-9, minus, equal, backspace, tab, a-z, backetleft, bracketright, semicolon, quote, * backquote, backslash, comma, period, slash, numpadmultiply, space, capslock, f1-f24, pause, * scrolllock, printscreen, home, arrowup, arrowleft, arrowright, arrowdown, pageup, pagedown, * end, insert, delete, enter, shiftleft, shiftright, ctrlleft, ctrlright, altleft, altright, shiftleft, * shiftright, numlock, numpad... * </pre> * For OS-specific codes and a more detailed explanation see {@link https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/code}. Also note that 'Digit' and 'Key' are removed from the code to make it easier to type. * * @typedef {string} localAccelerator~KeyCodes */ /** * translate a user-provided keycode * @param {KeyCodes} keyCode * @return {KeyCodes} formatted and sorted keyCode * @private */ prepareKey: function (keyCode) { const keys = [] let split keyCode += '' if (keyCode.length > 1 && keyCode.indexOf('|') !== -1) { split = keyCode.split('|') } else { split = [keyCode] } for (let code of split) { let key = '' let modifiers = [] code = code.toLowerCase().replace(' ', '') if (code.indexOf('+') !== -1) { const split = code.split('+') for (let i = 0; i < split.length - 1; i++) { let modifier = split[i] modifier = modifier.replace('commandorcontrol', 'ctrl') modifier = modifier.replace('command', 'ctrl') modifier = modifier.replace('control', 'ctrl') modifiers.push(modifier) } modifiers = modifiers.sort((a, b) => { return a[0] > b[0] ? 1 : a[0] < b[0] ? -1 : 0 }) for (let part of modifiers) { key += part + '+' } key += split[split.length - 1] } else { key = code } keys.push(key) } return keys }, /** * Make the KeyCode pretty for printing on the menu * @param {KeyCode} keyCode * @return {string} * @private */ prettifyKey: function (keyCode) { let key = '' const codes = localAccelerator.prepareKey(keyCode) for (let i = 0; i < codes.length; i++) { const keyCode = codes[i] if (keyCode.indexOf('+') !== -1) { const split = keyCode.toLowerCase().split('+') for (let i = 0; i < split.length - 1; i++) { let modifier = split[i] key += modifier[0].toUpperCase() + modifier.substr(1) + '+' } key += split[split.length - 1].toUpperCase() } else { key = keyCode.toUpperCase() } if (i !== codes.length - 1) { key += ' or ' } } return key }, /** * register a key as a global accelerator * @param {KeyCodes} keyCode (e.g., Ctrl+shift+E) * @param {function} callback */ register: function (keyCode, callback) { const keys = localAccelerator.prepareKey(keyCode) for (let key of keys) { localAccelerator.keys[key] = (e) => { callback(e) e.preventDefault() e.stopPropagation() } } }, keyup: function (accelerator, e) { if (localAccelerator.alt && (e.code === 'AltLeft' || e.code === 'AltRight')) { localAccelerator.alt.released() localAccelerator.alt.isPressed = false } }, keydown: function (accelerator, e) { if (localAccelerator.alt && !localAccelerator.alt.isPressed && (e.code === 'AltLeft' || e.code === 'AltRight')) { localAccelerator.alt.pressed() localAccelerator.alt.isPressed = true e.preventDefault() } const modifiers = [] if (e.altKey) { modifiers.push('alt') } if (e.ctrlKey) { modifiers.push('ctrl') } if (e.metaKey) { modifiers.push('meta') } if (e.shiftKey) { modifiers.push('shift') } let keyCode = '' for (let modifier of modifiers) { keyCode += modifier + '+' } let translate = e.code.toLowerCase() translate = translate.replace('digit', '') translate = translate.replace('key', '') keyCode += translate if (localAccelerator.menuKeys[keyCode]) { localAccelerator.menuKeys[keyCode](e, localAccelerator) } else if (localAccelerator.keys[keyCode]) { localAccelerator.keys[keyCode](e, localAccelerator) } } }