Socket
Socket
Sign inDemoInstall

shortcuts

Package Overview
Dependencies
Maintainers
1
Versions
39
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

shortcuts - npm Package Compare versions

Comparing version 2.0.3 to 3.0.0

446

demo/index.js
/* IMPORT */
import {Shortcut, Shortcuts} from '../dist/index.js';
import Shortcuts from '../src';
/* HELPERS */
const log = document.getElementById ( 'log' );
const yep = message => {
return () => {
console.log ( `✅ ${message}` );
return true;
};
};
function scrollBottom () {
document.body.scrollTop = Number.MAX_SAFE_INTEGER;
}
const nope = message => {
return () => {
console.log ( `❌ ${message}` );
return false;
};
};
function logHandler ( id, event, result ) {
const handledEmoji = !result ? '✅' : '❌';
const shortcuts = id.map ( ( _, index ) => Shortcut.id2accelerator ( id.slice ( index ) ) );
log.innerHTML += `<pre>${handledEmoji} - ${event.type} - ${shortcuts.map ( s => `<kbd>${s}</kbd>` ).join ( ', ' )}</pre>`;
scrollBottom ();
}
/* MAIN - LISTENING */
function logHandled ( shortcut ) {
setTimeout ( () => { // Hacky way to run this after `logHandler`
log.innerHTML += `<pre> → <kbd>${shortcut}</kbd></pre>`;
scrollBottom ();
});
return true;
}
const shortcuts = new Shortcuts ();
function logRecorded ( shortcut ) {
log.innerHTML += `<pre>⏺ - record - <kbd>${shortcut}</kbd></pre>`;
scrollBottom ();
}
shortcuts.add ([
{ shortcut: '0', handler: yep ( '0' ) },
{ shortcut: '1', handler: yep ( '1' ) },
{ shortcut: '2', handler: yep ( '2' ) },
{ shortcut: '3', handler: yep ( '3' ) },
{ shortcut: '4', handler: yep ( '4' ) },
{ shortcut: '5', handler: yep ( '5' ) },
{ shortcut: '6', handler: yep ( '6' ) },
{ shortcut: '7', handler: yep ( '7' ) },
{ shortcut: '8', handler: yep ( '8' ) },
{ shortcut: '9', handler: yep ( '9' ) }
]);
function logClear () {
log.innerHTML = '';
}
shortcuts.add ([
{ shortcut: '5', handler: yep ( '5 (2)' ) },
{ shortcut: '6', handler: yep ( '6 (2)' ) },
{ shortcut: '7', handler: yep ( '7 (2)' ) },
{ shortcut: '8', handler: yep ( '8 (2)' ) },
{ shortcut: '9', handler: yep ( '9 (2)' ) }
]);
function patch ( shortcuts ) {
const _handler = shortcuts.listener.options.handler
shortcuts.listener.options.handler = function ( id, event ) {
const result = _handler.call ( this, id, event );
if ( !shortcuts.recordHandler ) {
logHandler ( id, event, result );
}
return result;
};
}
shortcuts.remove ([
{ shortcut: '7', },
{ shortcut: '8', },
{ shortcut: '9', }
]);
/* INIT */
shortcuts.register ([
{ shortcut: 'A', handler: yep ( 'A' ) },
{ shortcut: 'B', handler: yep ( 'B' ) },
{ shortcut: 'C', handler: yep ( 'C' ) },
{ shortcut: 'D', handler: yep ( 'D' ) },
{ shortcut: 'E', handler: yep ( 'E' ) },
{ shortcut: 'F', handler: yep ( 'F' ) },
{ shortcut: 'G', handler: yep ( 'G' ) },
{ shortcut: 'H', handler: yep ( 'H' ) },
{ shortcut: 'I', handler: yep ( 'I' ) },
{ shortcut: 'J', handler: yep ( 'J' ) },
{ shortcut: 'K', handler: yep ( 'K' ) },
{ shortcut: 'L', handler: yep ( 'L' ) },
{ shortcut: 'M', handler: yep ( 'M' ) },
{ shortcut: 'N', handler: yep ( 'N' ) },
{ shortcut: 'O', handler: yep ( 'O' ) },
{ shortcut: 'P', handler: yep ( 'P' ) },
{ shortcut: 'Q', handler: yep ( 'Q' ) },
{ shortcut: 'R', handler: yep ( 'R' ) },
{ shortcut: 'S', handler: yep ( 'S' ) },
{ shortcut: 'T', handler: yep ( 'T' ) },
{ shortcut: 'U', handler: yep ( 'U' ) },
{ shortcut: 'V', handler: yep ( 'V' ) },
{ shortcut: 'W', handler: yep ( 'W' ) },
{ shortcut: 'X', handler: yep ( 'X' ) },
{ shortcut: 'Y', handler: yep ( 'Y' ) },
{ shortcut: 'Z', handler: yep ( 'Z' ) },
]);
const shortcuts = new Shortcuts ();
shortcuts.register ([
{ shortcut: 'F1', handler: yep ( 'F1' ) },
{ shortcut: 'F2', handler: yep ( 'F2' ) },
{ shortcut: 'F3', handler: yep ( 'F3' ) },
{ shortcut: 'F4', handler: yep ( 'F4' ) },
{ shortcut: 'F5', handler: yep ( 'F5' ) },
{ shortcut: 'F6', handler: yep ( 'F6' ) },
{ shortcut: 'F7', handler: yep ( 'F7' ) },
{ shortcut: 'F8', handler: yep ( 'F8' ) },
{ shortcut: 'F9', handler: yep ( 'F9' ) },
{ shortcut: 'F10', handler: yep ( 'F10' ) },
{ shortcut: 'F11', handler: yep ( 'F11' ) },
{ shortcut: 'F12', handler: yep ( 'F12' ) },
{ shortcut: 'F13', handler: yep ( 'F13' ) },
{ shortcut: 'F14', handler: yep ( 'F14' ) },
{ shortcut: 'F15', handler: yep ( 'F15' ) },
{ shortcut: 'F16', handler: yep ( 'F16' ) },
{ shortcut: 'F17', handler: yep ( 'F17' ) },
{ shortcut: 'F18', handler: yep ( 'F18' ) },
{ shortcut: 'F19', handler: yep ( 'F19' ) },
{ shortcut: 'F20', handler: yep ( 'F20' ) },
{ shortcut: 'F21', handler: yep ( 'F21' ) },
{ shortcut: 'F22', handler: yep ( 'F22' ) },
{ shortcut: 'F23', handler: yep ( 'F23' ) },
{ shortcut: 'F24', handler: yep ( 'F24' ) }
]);
patch ( shortcuts ); // For logging purposes
shortcuts.register ([
{ shortcut: 'Numpad0', handler: yep ( 'Numpad0' ) },
{ shortcut: 'Numpad1', handler: yep ( 'Numpad1' ) },
{ shortcut: 'Numpad2', handler: yep ( 'Numpad2' ) },
{ shortcut: 'Numpad3', handler: yep ( 'Numpad3' ) },
{ shortcut: 'Numpad4', handler: yep ( 'Numpad4' ) },
{ shortcut: 'Numpad5', handler: yep ( 'Numpad5' ) },
{ shortcut: 'Numpad6', handler: yep ( 'Numpad6' ) },
{ shortcut: 'Numpad7', handler: yep ( 'Numpad7' ) },
{ shortcut: 'Numpad8', handler: yep ( 'Numpad8' ) },
{ shortcut: 'Numpad9', handler: yep ( 'Numpad9' ) }
]);
shortcuts.add ([
{ shortcut: 'A', handler: () => logHandled ( 'A' ) },
// { shortcut: 'S', handler: () => logHandled ( 'S' ) },
// { shortcut: 'CmdOrCtrl+K A', handler: () => logHandled ( 'CmdOrCtrl+K A' ) },
{ shortcut: 'Ctrl+A', handler: () => logHandled ( 'Ctrl+A' ) },
{ shortcut: 'Alt+B', handler: () => logHandled ( 'Alt+B' ) },
{ shortcut: 'Shift+C', handler: () => logHandled ( 'Shift+C' ) },
{ shortcut: 'Shift+4', handler: () => logHandled ( 'Shift+4' ) },
{ shortcut: 'Shift+%', handler: () => logHandled ( 'Shift+%' ) },
{ shortcut: 'Cmd+D', handler: () => logHandled ( 'Cmd+D' ) },
{ shortcut: 'Cmd+E', handler: () => logHandled ( 'Cmd+E' ) },
{ shortcut: 'Cmd+E', handler: () => logHandled ( 'Cmd+E (Second)' ) },
{ shortcut: 'Cmd+J', handler: () => logHandled ( 'Cmd+J' ) },
{ shortcut: 'Cmd+L', handler: () => logHandled ( 'Cmd+L' ) },
{ shortcut: '-Cmd+L' },
{ shortcut: 'Ctrl+Space', handler: () => logHandled ( 'Cmd+Space' ) },
{ shortcut: 'Cmd+Backspace', handler: () => logHandled ( 'Cmd+Backspace' ) },
{ shortcut: 'Cmd+Esc', handler: () => logHandled ( 'Cmd+Esc' ) },
{ shortcut: 'Cmd+Alt+Shift+Control+F', handler: () => logHandled ( 'Cmd+Alt+Shift+Control+F' ) },
{ shortcut: 'CmdOrCtrl+K Shift+%', handler: () => logHandled ( 'CmdOrCtrl+K Shift+%' ) },
{ shortcut: 'CmdOrCtrl+B Shift+^ CmdOrCtrl+B Shift+^', handler: () => logHandled ( 'CmdOrCtrl+B Shift+^ CmdOrCtrl+B Shift+^' ) },
{ shortcut: 'CmdOrCtrl+K CmdOrCtrl+K', handler: () => { logClear (); return logHandled ( `CmdOrCtrl+K CmdOrCtrl+K` ); } },
{ shortcut: 'Up Right Down Left', handler: () => logHandled ( 'Up Right Down Left' ) },
// { shortcut: 'Right Down', handler: () => logHandled ( 'Right Down' ) }
{ shortcut: 'Ctrl Ctrl', handler: () => logHandled ( 'Ctrl Ctrl' ) },
{ shortcut: 'Alt+/', handler: () => logHandled ( 'Alt+/' ) },
{ shortcut: 'Alt', handler: () => logHandled ( 'Alt' ) }
shortcuts.register ([
{ shortcut: 'NumpadAdd', handler: yep ( 'NumpadAdd' ) },
{ shortcut: 'NumpadComma', handler: yep ( 'NumpadComma' ) },
{ shortcut: 'NumpadDecimal', handler: yep ( 'NumpadDecimal' ) },
{ shortcut: 'NumpadDivide', handler: yep ( 'NumpadDivide' ) },
{ shortcut: 'NumpadEnter', handler: yep ( 'NumpadEnter' ) },
{ shortcut: 'NumpadEqual', handler: yep ( 'NumpadEqual' ) },
{ shortcut: 'NumpadMultiply', handler: yep ( 'NumpadMultiply' ) },
{ shortcut: 'NumpadSubtract', handler: yep ( 'NumpadSubtract' ) }
]);
shortcuts.remove ([
{ shortcut: 'Cmd+J' }
shortcuts.register ([
{ shortcut: 'Left+Up', handler: nope ( 'Left+Up' ) },
{ shortcut: 'Right+Up', handler: nope ( 'Right+Up' ) },
{ shortcut: 'Left+Down', handler: nope ( 'Left+Down' ) },
{ shortcut: 'Right+Down', handler: nope ( 'Right+Down' ) },
{ shortcut: 'Left+Right', handler: nope ( 'Left+Right' ) },
{ shortcut: 'Up+Down', handler: nope ( 'Up+Down' ) }
]);
/* MANUAL TESTING */
shortcuts.register ([
{ shortcut: 'Down', handler: yep ( 'Down' ) },
{ shortcut: 'Left', handler: yep ( 'Left' ) },
{ shortcut: 'Right', handler: yep ( 'Right' ) },
{ shortcut: 'Up', handler: yep ( 'Up' ) }
]);
// Alphabet
// Digit
// Punctuation
// Shift+Alphabet
// Shift+Digit
// Shift+Punctuation
// Ctrl+Alphabet
// Ctrl+Digit
// Ctrl+Punctuation
// Cmd+Alphabet
// Cmd+Digit
// Cmd+Punctuation
// Alt+Alphabet
// Alt+Digit
// Alt+Punctuation
// Ctrl+Shift+Alphabet
// Ctrl+Shift+Digit
// Ctrl+Shift+Punctuation
// Cmd+Shift+Alphabet
// Cmd+Shift+Digit
// Cmd+Shift+Punctuation
// Alt+Shift+Alphabet
// Alt+Shift+Digit
// Alt+Shift+Punctuation
// Ctrl+Alt+Shift+Cmd+Alphabet
// Ctrl+Alt+Shift+Cmd+Digit
// Ctrl+Alt+Shift+Cmd+Punctuation
// Alt
shortcuts.register ([
{ shortcut: 'Backspace', handler: yep ( 'Backspace' ) },
{ shortcut: 'CapsLock', handler: yep ( 'CapsLock' ) },
{ shortcut: 'Delete', handler: yep ( 'Delete' ) },
{ shortcut: 'End', handler: yep ( 'End' ) },
{ shortcut: 'Enter', handler: yep ( 'Enter' ) },
{ shortcut: 'Escape', handler: yep ( 'Escape' ) },
{ shortcut: 'Home', handler: yep ( 'Home' ) },
{ shortcut: 'Insert', handler: yep ( 'Insert' ) },
{ shortcut: 'PageDown', handler: yep ( 'PageDown' ) },
{ shortcut: 'PageUp', handler: yep ( 'PageUp' ) },
{ shortcut: 'Space', handler: yep ( 'Space' ) },
{ shortcut: 'Tab', handler: yep ( 'Tab' ) }
]);
//FIXME: The following shortcuts make keypress misfire for some reason
// Ctrl+Shift+6 keypress ??
// Ctrl+Shift+- keypress ??
// Ctrl+Shift+[ keypress ??
// Ctrl+Shift+] keypress ??
// Ctrl+Shift+\ keypress ??
shortcuts.register ([
{ shortcut: 'NumLock', handler: yep ( 'NumLock' ) },
{ shortcut: 'ScrollLock', handler: yep ( 'ScrollLock' ) }
]);
/* RECORD */
shortcuts.register ([
{ shortcut: 'AltLeft', handler: nope ( 'AltLeft' ) },
{ shortcut: 'AltRight', handler: nope ( 'AltRight' ) },
{ shortcut: 'Alt', handler: nope ( 'Alt' ) },
{ shortcut: 'OptionLeft', handler: nope ( 'OptionLeft' ) },
{ shortcut: 'OptionRight', handler: nope ( 'OptionRight' ) },
{ shortcut: 'Option', handler: nope ( 'Option' ) },
{ shortcut: 'CmdLeft', handler: nope ( 'CmdLeft' ) },
{ shortcut: 'CmdRight', handler: nope ( 'CmdRight' ) },
{ shortcut: 'Cmd', handler: nope ( 'Cmd' ) },
{ shortcut: 'CommandLeft', handler: nope ( 'CommandLeft' ) },
{ shortcut: 'CommandRight', handler: nope ( 'CommandRight' ) },
{ shortcut: 'Command', handler: nope ( 'Command' ) },
{ shortcut: 'MetaLeft', handler: nope ( 'MetaLeft' ) },
{ shortcut: 'MetaRight', handler: nope ( 'MetaRight' ) },
{ shortcut: 'Meta', handler: nope ( 'Meta' ) },
{ shortcut: 'CtrlLeft', handler: nope ( 'CtrlLeft' ) },
{ shortcut: 'CtrlRight', handler: nope ( 'CtrlRight' ) },
{ shortcut: 'Ctrl', handler: nope ( 'Ctrl' ) },
{ shortcut: 'ControlLeft', handler: nope ( 'ControlLeft' ) },
{ shortcut: 'ControlRight', handler: nope ( 'ControlRight' ) },
{ shortcut: 'Control', handler: nope ( 'Control' ) },
{ shortcut: 'ShiftLeft', handler: nope ( 'ShiftLeft' ) },
{ shortcut: 'ShiftRight', handler: nope ( 'ShiftRight' ) },
{ shortcut: 'Shift', handler: nope ( 'Shift' ) },
{ shortcut: 'CmdLeftOrCtrlLeft', handler: nope ( 'CmdLeftOrCtrlLeft' ) },
{ shortcut: 'CmdRightOrCtrlRight', handler: nope ( 'CmdRightOrCtrlRight' ) },
{ shortcut: 'CmdOrCtrl', handler: nope ( 'CmdOrCtrl' ) },
{ shortcut: 'CommandLeftOrControlLeft', handler: nope ( 'CommandLeftOrControlLeft' ) },
{ shortcut: 'CommandRightOrControlRight', handler: nope ( 'CommandRightOrControlRight' ) },
{ shortcut: 'CommandOrControl', handler: nope ( 'CommandOrControl' ) }
]);
let recordDispose;
shortcuts.register ([
{ shortcut: '!', handler: nope ( 'ExclationMark' ) },
{ shortcut: '"', handler: nope ( 'DoubleQuote' ) },
{ shortcut: '#', handler: nope ( 'Hash' ) },
{ shortcut: '$', handler: nope ( 'Dollar' ) },
{ shortcut: '%', handler: nope ( 'Percent' ) },
{ shortcut: '&', handler: nope ( 'Ampersand' ) },
{ shortcut: '\'', handler: nope ( 'Quote' ) },
{ shortcut: '(', handler: nope ( 'ParenthesisLeft' ) },
{ shortcut: ')', handler: nope ( 'ParenthesisRight' ) },
{ shortcut: '*', handler: nope ( 'Asterisk' ) },
{ shortcut: '+', handler: nope ( 'Plus' ) },
{ shortcut: ',', handler: nope ( 'Comma' ) },
{ shortcut: '-', handler: nope ( 'Minus' ) },
{ shortcut: '.', handler: nope ( 'Period' ) },
{ shortcut: '/', handler: nope ( 'Slash' ) },
{ shortcut: ':', handler: nope ( 'Colon' ) },
{ shortcut: ';', handler: nope ( 'Semicolon' ) },
{ shortcut: '<', handler: nope ( 'LessThan' ) },
{ shortcut: '=', handler: nope ( 'Equal' ) },
{ shortcut: '>', handler: nope ( 'GreaterThan' ) },
{ shortcut: '?', handler: nope ( 'QuestionMark' ) },
{ shortcut: '@', handler: nope ( 'At' ) },
{ shortcut: '[', handler: nope ( 'BracketLeft' ) },
{ shortcut: '\\', handler: nope ( 'Backslash' ) },
{ shortcut: ']', handler: nope ( 'BracketRight' ) },
{ shortcut: '^', handler: nope ( 'Caret' ) },
{ shortcut: '_', handler: nope ( 'Underscore' ) },
{ shortcut: '`', handler: nope ( 'Backquote' ) },
{ shortcut: '{', handler: nope ( 'BraceLeft' ) },
{ shortcut: '|', handler: nope ( 'Pipe' ) },
{ shortcut: '}', handler: nope ( 'BraceRight' ) },
{ shortcut: '~', handler: nope ( 'Tilde' ) }
]);
window.record = () => {
window.unrecord ();
recordDispose = shortcuts.record ( logRecorded );
};
shortcuts.register ([
{ shortcut: 'Shift+Plus', handler: nope ( 'Shift+Plus (Alias)' ) },
{ shortcut: 'Shift+!', handler: nope ( 'Shift+ExclationMark' ) },
{ shortcut: 'Shift+"', handler: nope ( 'Shift+DoubleQuote' ) },
{ shortcut: 'Shift+#', handler: nope ( 'Shift+Hash' ) },
{ shortcut: 'Shift+$', handler: nope ( 'Shift+Dollar' ) },
{ shortcut: 'Shift+%', handler: nope ( 'Shift+Percent' ) },
{ shortcut: 'Shift+&', handler: nope ( 'Shift+Ampersand' ) },
{ shortcut: 'Shift+\'', handler: nope ( 'Shift+Quote' ) },
{ shortcut: 'Shift+(', handler: nope ( 'Shift+ParenthesisLeft' ) },
{ shortcut: 'Shift+)', handler: nope ( 'Shift+ParenthesisRight' ) },
{ shortcut: 'Shift+*', handler: nope ( 'Shift+Asterisk' ) },
{ shortcut: 'Shift++', handler: nope ( 'Shift+Plus' ) },
{ shortcut: 'Shift+,', handler: nope ( 'Shift+Comma' ) },
{ shortcut: 'Shift+-', handler: nope ( 'Shift+Minus' ) },
{ shortcut: 'Shift+.', handler: nope ( 'Shift+Period' ) },
{ shortcut: 'Shift+/', handler: nope ( 'Shift+Slash' ) },
{ shortcut: 'Shift+:', handler: nope ( 'Shift+Colon' ) },
{ shortcut: 'Shift+;', handler: nope ( 'Shift+Semicolon' ) },
{ shortcut: 'Shift+<', handler: nope ( 'Shift+LessThan' ) },
{ shortcut: 'Shift+=', handler: nope ( 'Shift+Equal' ) },
{ shortcut: 'Shift+>', handler: nope ( 'Shift+GreaterThan' ) },
{ shortcut: 'Shift+?', handler: nope ( 'Shift+QuestionMark' ) },
{ shortcut: 'Shift+@', handler: nope ( 'Shift+At' ) },
{ shortcut: 'Shift+[', handler: nope ( 'Shift+BracketLeft' ) },
{ shortcut: 'Shift+\\', handler: nope ( 'Shift+Backslash' ) },
{ shortcut: 'Shift+]', handler: nope ( 'Shift+BracketRight' ) },
{ shortcut: 'Shift+^', handler: nope ( 'Shift+Caret' ) },
{ shortcut: 'Shift+_', handler: nope ( 'Shift+Underscore' ) },
{ shortcut: 'Shift+`', handler: nope ( 'Shift+Backquote' ) },
{ shortcut: 'Shift+{', handler: nope ( 'Shift+BraceLeft' ) },
{ shortcut: 'Shift+|', handler: nope ( 'Shift+Pipe' ) },
{ shortcut: 'Shift+}', handler: nope ( 'Shift+BraceRight' ) },
{ shortcut: 'Shift+~', handler: nope ( 'Shift+Tilde' ) }
]);
window.unrecord = () => {
if ( recordDispose ) recordDispose ();
};
shortcuts.register ([
{ shortcut: 'Shift+1', handler: nope ( 'Shift+1' ) },
{ shortcut: 'Shift+2', handler: nope ( 'Shift+2' ) },
{ shortcut: 'Shift+3', handler: nope ( 'Shift+3' ) },
{ shortcut: 'Shift+4', handler: nope ( 'Shift+4' ) },
{ shortcut: 'Shift+5', handler: nope ( 'Shift+5' ) },
{ shortcut: 'Shift+6', handler: nope ( 'Shift+6' ) },
{ shortcut: 'Shift+7', handler: nope ( 'Shift+7' ) },
{ shortcut: 'Shift+8', handler: nope ( 'Shift+8' ) },
{ shortcut: 'Shift+9', handler: nope ( 'Shift+9' ) },
{ shortcut: 'Shift+0', handler: nope ( 'Shift+0' ) }
]);
shortcuts.register ([
{ shortcut: 'Shift+A', handler: nope ( 'Shift+A' ) },
{ shortcut: 'Shift+B', handler: nope ( 'Shift+B' ) },
{ shortcut: 'Shift+C', handler: nope ( 'Shift+C' ) },
{ shortcut: 'Shift+Z', handler: nope ( 'Shift+Z' ) },
{ shortcut: 'Shift+Y', handler: nope ( 'Shift+Y' ) },
{ shortcut: 'Shift+X', handler: nope ( 'Shift+X' ) }
]);
shortcuts.register ([
{ shortcut: 'ClickLeft', handler: nope ( 'ClickLeft' ) },
{ shortcut: 'ClickRight', handler: nope ( 'ClickRight' ) },
{ shortcut: 'ClickMiddle', handler: nope ( 'ClickMiddle' ) },
{ shortcut: 'MouseLeft', handler: nope ( 'MouseLeft' ) },
{ shortcut: 'MouseRight', handler: nope ( 'MouseRight' ) },
{ shortcut: 'MouseMiddle', handler: nope ( 'MouseMiddle' ) },
{ shortcut: 'Mouse0', handler: nope ( 'Mouse0' ) },
{ shortcut: 'Mouse1', handler: nope ( 'Mouse1' ) },
{ shortcut: 'Mouse2', handler: nope ( 'Mouse2' ) },
{ shortcut: 'Mouse3', handler: nope ( 'Mouse3' ) },
{ shortcut: 'Mouse4', handler: nope ( 'Mouse4' ) },
{ shortcut: 'Mouse5', handler: nope ( 'Mouse5' ) },
{ shortcut: 'Mouse6', handler: nope ( 'Mouse6' ) },
{ shortcut: 'Mouse7', handler: nope ( 'Mouse7' ) },
{ shortcut: 'Mouse8', handler: nope ( 'Mouse8' ) },
{ shortcut: 'Mouse9', handler: nope ( 'Mouse9' ) }
]);
shortcuts.register ([
{ shortcut: 'Alt+F', handler: yep ( 'Alt+F' ) },
{ shortcut: 'Cmd+K Cmd+A', handler: yep ( 'Cmd+K Cmd+A' ) },
{ shortcut: 'Cmd+K B', handler: yep ( 'Cmd+K B' ) },
{ shortcut: 'Cmd+K Alt+C', handler: yep ( 'Cmd+K Alt+C' ) },
{ shortcut: 'Shift+ClickLeft', handler: yep ( 'Shift+ClickLeft' ) },
{ shortcut: 'Cmd+ClickMiddle', handler: yep ( 'Cmd+ClickMiddle' ) },
{ shortcut: 'Alt+ClickLeft Cmd+ClickMiddle', handler: yep ( 'Alt+ClickLeft Cmd+ClickMiddle' ) }
]);
shortcuts.register ([
{ shortcut: 'CmdLeft+CmdRight', handler: yep ( 'CmdLeft+CmdRight' ) },
// { shortcut: 'CmdLeft+CmdRight AltLeft+AltRight', handler: yep ( 'CmdLeft+CmdRight AltLeft+AltRight' ) } //TODO It'd be cool to support this too
]);
// shortcuts.register ( 'Up Up Down Down Left Right Left Right B A', yep ( 'Up Up Down Down Left Right Left Right B A 🚀' ), { konami: true } ); //TODO Maybe support this too
shortcuts.register ([
{ shortcut: 'Left+Up', handler: nope ( 'Left+Up' ) },
{ shortcut: 'Left+Down', handler: nope ( 'Left+Down' ) },
{ shortcut: 'Left+Right', handler: nope ( 'Left+Right' ) },
{ shortcut: 'Up+Down', handler: nope ( 'Up+Down' ) },
{ shortcut: 'Up+Right', handler: nope ( 'Up+Right' ) },
{ shortcut: 'Down+Right', handler: nope ( 'Down+Right' ) }
]);
shortcuts.register ( { shortcut: 'Ctrl+Cmd+Right', handler: yep ( 'Ctrl+Cmd+Right' ) } );
shortcuts.register ([
{ shortcut: '§', handler: yep ( '§' ) },
{ shortcut: '±', handler: yep ( '±' ) },
{ shortcut: 'Shift+§', handler: nope ( 'Shift+§' ) },
{ shortcut: 'Shift+±', handler: nope ( 'Shift+±' ) }
]);
shortcuts.register ( { shortcut: 'Ctrl+Cmd+A', handler: nope ( 'NOT DISPOSED!' ) } )();
shortcuts.start ();
// setTimeout ( () => {
// shortcuts.stop ();
// shortcuts.reset ();
// }, 5000 );
/* EXPORT */
globalThis.shortcuts = shortcuts;
globalThis.S = shortcuts;

@@ -1,13 +0,2 @@

declare const MODIFIER_KEY_BITMASK = 65280;
declare const TRIGGER_KEY_BITMASK = 255;
declare const PLUSES_RE: RegExp;
declare const WHITESPACE_RE: RegExp;
declare const SHORTCUT_RE: RegExp;
declare const RESULT: {
readonly HANDLED: 0;
readonly UNHANDLED: 1;
readonly UNHANDLEABLE: 2;
};
export { MODIFIER_KEY_BITMASK, TRIGGER_KEY_BITMASK };
export { PLUSES_RE, WHITESPACE_RE, SHORTCUT_RE };
export { RESULT };
declare const FORMAT = "long-inflexible-directional";
export { FORMAT };
/* MAIN */
const MODIFIER_KEY_BITMASK = 65280; // Bitmask that includes all modifier keys and none of the triggers
const TRIGGER_KEY_BITMASK = 0b11111111; // Bitmask that includes all trigger keys and none of the modifiers
const PLUSES_RE = /\+{2,}/gi;
const WHITESPACE_RE = /\s+/gi;
const SHORTCUT_RE = /^\s*?(?:(?:^-?|\s|\+)(?:alt|option|cmd|command|meta|ctrl|control|shift|cmdorctrl|commandorcontrol|backspace|capslock|del|delete|down|end|enter|return|esc|escape|home|insert|left|pagedown|pageup|right|space|spacebar|tab|up|plus|\d|[a-z]|f(?:\d|1\d|2[0-4])|numpad\d|[!"#$%&'()*+,./:;<=>?@[\]^_`{|}~-]))+\s*$/i; // Regex that matches a shortcut
const RESULT = {
HANDLED: 0,
UNHANDLED: 1,
UNHANDLEABLE: 2 // No handler caught that, and there are no deeper handlers that could catch that
};
const FORMAT = 'long-inflexible-directional';
/* EXPORT */
export { MODIFIER_KEY_BITMASK, TRIGGER_KEY_BITMASK };
export { PLUSES_RE, WHITESPACE_RE, SHORTCUT_RE };
export { RESULT };
export { FORMAT };

@@ -1,3 +0,16 @@

import Shortcut from './shortcut';
import Shortcuts from './shortcuts';
export { Shortcut, Shortcuts };
import type { Descriptor, Disposer, Handler, Options, Registration } from './types';
declare class Shortcuts {
private registrations;
private shosho;
constructor(options: Options);
get: () => Descriptor[];
add: (descriptor: Descriptor | Descriptor[]) => void;
register: (descriptor: Descriptor | Descriptor[]) => Disposer;
remove: (descriptor: Descriptor | Descriptor[]) => void;
reset: () => void;
trigger: (shortcut: string) => boolean;
start: () => void;
stop: () => void;
}
export default Shortcuts;
export type { Descriptor, Disposer, Handler, Options, Registration };
/* IMPORT */
import Shortcut from './shortcut.js';
import Shortcuts from './shortcuts.js';
import ShoSho from 'shosho';
import { FORMAT } from './constants.js';
import { castArray } from './utils.js';
/* MAIN */
class Shortcuts {
/* CONSTRUCTOR */
constructor(options) {
/* VARIABLES */
this.registrations = [];
/* PUBLIC API */
this.get = () => {
return this.registrations.map(registration => registration.descriptor);
};
this.add = (descriptor) => {
const descriptors = castArray(descriptor);
for (const descriptor of descriptors) {
if (descriptor.shortcut.startsWith('-')) {
this.remove(descriptor);
}
else if (descriptor.handler) {
const dispose = this.shosho.register(descriptor.shortcut, descriptor.handler);
const shortcut = ShoSho.format(descriptor.shortcut, FORMAT);
const registration = { descriptor, dispose, shortcut };
this.registrations.push(registration);
}
else {
console.error(`Cannot register shortcut "${descriptor.shortcut}", which has no handler`);
}
}
};
this.register = (descriptor) => {
this.add(descriptor);
return () => {
this.remove(descriptor);
};
};
this.remove = (descriptor) => {
const descriptors = castArray(descriptor);
for (const descriptor of descriptors) {
const handler = descriptor.handler;
const shortcut = ShoSho.format(descriptor.shortcut.replace(/^-/, ''), FORMAT);
this.registrations = this.registrations.filter(registration => {
if ((registration.shortcut !== shortcut) || (handler && registration.descriptor.handler !== handler)) {
return true;
}
else {
registration.dispose();
return false;
}
});
}
};
this.reset = () => {
this.registrations = [];
this.shosho.reset();
};
this.trigger = (shortcut) => {
return this.shosho.trigger(shortcut);
};
this.start = () => {
this.shosho.start();
};
this.stop = () => {
this.shosho.stop();
};
this.registrations = [];
this.shosho = new ShoSho(options);
}
}
/* EXPORT */
export { Shortcut, Shortcuts };
export default Shortcuts;

@@ -1,33 +0,17 @@

declare type Chord = string;
declare type ChordID = number;
declare type Shortcut = string;
declare type ShortcutID = ChordID[];
declare type Disposer = () => void;
declare type RecordHandler = (shortcut: Shortcut) => void;
declare type ShouldHandleEventFunction = (event: KeyboardEvent) => boolean;
declare type ListenerOptions = {
capture?: boolean;
target?: Node;
handler: (id: ShortcutID, event: KeyboardEvent) => 0 | 1 | 2 | ShortcutID;
shouldHandleEvent?: ShouldHandleEventFunction;
import type { Options } from 'shosho';
type Descriptor = {
handler?: Handler;
shortcut: string;
};
declare type ShortcutsOptions = {
capture?: boolean;
target?: Node;
shortcuts?: ShortcutDescriptor[];
shouldHandleEvent?: ShouldHandleEventFunction;
type Disposer = {
(): void;
};
declare type ShortcutsTree = {
[id: number]: ShortcutsTree;
parent?: ShortcutsTree;
id?: ChordID;
size: number;
handlers: Function[];
type Handler = {
(event?: Event): boolean | void;
};
declare type ShortcutDescriptor = {
handler?: (event: KeyboardEvent) => boolean | void;
type Registration = {
descriptor: Descriptor;
dispose: Disposer;
shortcut: string;
};
export type { Chord, ChordID, Shortcut, ShortcutID };
export type { Disposer, RecordHandler, ShouldHandleEventFunction };
export type { ListenerOptions, ShortcutsOptions, ShortcutsTree, ShortcutDescriptor };
export type { Descriptor, Disposer, Handler, Options, Registration };

@@ -1,2 +0,2 @@

/* MAIN */
/* IMPORT */
export {};

@@ -1,10 +0,2 @@

declare const Utils: {
isArray: (value: unknown) => value is unknown[];
isEqual: (a: number[], b: number[]) => boolean;
isFalsy: (value: unknown) => boolean;
isKeyboardEvent: (event: Event) => event is KeyboardEvent;
isMac: () => boolean;
isTruthy: (value: unknown) => boolean;
memoize: <T, R>(fn: (arg: T) => R) => (arg: T) => R;
};
export default Utils;
declare const castArray: <T>(value: T | T[]) => T[];
export { castArray };
/* MAIN */
const Utils = {
/* API */
isArray: (value) => {
return Array.isArray(value);
},
isEqual: (a, b) => {
if (a.length !== b.length)
return false;
for (let i = 0, l = a.length; i < l; i++) {
if (a[i] !== b[i])
return false;
}
return true;
},
isFalsy: (value) => {
return !value;
},
isKeyboardEvent: (event) => {
return event.type.startsWith('key');
},
isMac: () => {
if (typeof navigator !== 'object')
return false;
return /mac|ipod|iphone|ipad/i.test(navigator.platform);
},
isTruthy: (value) => {
return !!value;
},
memoize: (fn) => {
const cache = new Map();
return (arg) => {
const cached = cache.get(arg);
if (cached || cache.has(arg))
return cached; //TSC
const result = fn(arg);
cache.set(arg, result);
return result;
};
}
const castArray = (value) => {
return Array.isArray(value) ? value : [value];
};
/* EXPORT */
export default Utils;
export { castArray };

@@ -5,41 +5,13 @@ {

"description": "Super performant and feature rich shortcuts management library.",
"version": "2.0.3",
"version": "3.0.0",
"type": "module",
"main": "dist/index.js",
"exports": {
".": {
"import": "./dist/index.js",
"types": "./dist/index.d.ts"
},
"./constants": {
"import": "./dist/constants.js",
"types": "./dist/constants.d.ts"
},
"./maps": {
"import": "./dist/maps.js",
"types": "./dist/maps.d.ts"
}
},
"typesVersions": {
"*": {
"constants": [
"./dist/constants.d.ts"
],
"maps": [
"./dist/maps.d.ts"
]
}
},
"exports": "./dist/index.js",
"types": "./dist/index.d.ts",
"scripts": {
"benchmark": "tsex benchmark",
"benchmark:watch": "tsex benchmark --watch",
"clean": "tsex clean",
"compile": "tsex compile",
"compile:watch": "tsex compile --watch",
"test": "tsex test",
"test:watch": "tsex test --watch",
"demo:build": "cd demo && mkdir -p dist && esbuild --bundle index.js > ./dist/bundle.js",
"demo:build:watch": "cd demo && mkdir -p dist && esbuild --watch --bundle index.js > ./dist/bundle.js",
"demo:serve": "cd demo && open index.html",
"prepublishOnly": "npm run clean && npm run compile && npm run test"
"demo": "vite demo",
"prepublishOnly": "tsex prepare"
},

@@ -50,10 +22,10 @@ "keywords": [

],
"dependencies": {
"shosho": "^1.4.0"
},
"devDependencies": {
"benchloop": "^1.3.2",
"call-spy": "^3.0.0",
"esbuild": "^0.15.13",
"fava": "^0.0.6",
"tsex": "^1.1.2",
"typescript": "^4.8.4"
"tsex": "^2.2.4",
"typescript": "^4.9.5",
"vite": "^4.3.9"
}
}

@@ -5,9 +5,4 @@ # Shortcuts

## Features
This library is just a thin wrapper over [`ShoSho`](https://github.com/fabiospampinato/shosho) that provides a VSCode-like interface for managing shortcuts. Definitely check out [`ShoSho`](https://github.com/fabiospampinato/shosho), which provides more features and it may offer a lower-level but maybe more convenient API for your use case.
- **Super performant**: it's about as fast as it gets, I don't think it's possible to do significantly better than this.
- **Sequences support**: sequences (a.k.a. konami codes), are supported too, so you can bind actions to <kbd>Up Right Down Left</kbd> or whatever shortcuts sequence you want.
- **Shortcut to Accelerator**: supported shortcuts can be converted to their [Electron's Accelerator](https://electronjs.org/docs/api/accelerator) equivalent.
- **Shortcut to symbols**: supported shortcuts can be converted to symbols (e.g. `⌘A`), this is useful for providing shortcut hints in tooltips.
## Install

@@ -21,111 +16,93 @@

This library provides a [`Shortcuts`](#shortcuts-class) class, which will manage your [shortcuts](#shortcut-syntax), and a [`Shortcut`](#shortcut-object) object, which provides some utilities.
The following interface is provided:
### Shortcut Syntax
```ts
type Descriptor = {
handler?: ( event?: Event ): boolean | void,
shortcut: string
};
The following keys can be used when defining a shortcut:
type Disposer = {
(): void
};
- **Modifiers**: <kbd>Alt</kbd>/<kbd>Option</kbd>, <kbd>Cmd</kbd>/<kbd>Command</kbd>/<kbd>Meta</kbd>, <kbd>Ctrl</kbd>/<kbd>Control</kbd>, <kbd>Shift</kbd>, <kbd>CmdOrCtrl</kbd>/<kbd>CommandOrControl</kbd>.
- **Digits**: <kbd>1-9</kbd>.
- **Alphabet letters**: <kbd>A-Z</kbd>.
- **Function keys**: <kbd>F1-F24</kbd>.
- **Numpad digits**: <kbd>Numpad0-Numpad9</kbd>.
- **Special keys**: <kbd>Backspace</kbd>, <kbd>Capslock</kbd>, <kbd>Del</kbd>/<kbd>Delete</kbd>, <kbd>Down</kbd>, <kbd>End</kbd>, <kbd>Enter</kbd>/<kbd>Return</kbd>, <kbd>Esc</kbd>/<kbd>Escape</kbd>, <kbd>Home</kbd>, <kbd>Insert</kbd>, <kbd>Left</kbd>, <kbd>PageDown</kbd>, <kbd>PageUp</kbd>, <kbd>Right</kbd>, <kbd>Space</kbd>/<kbd>Spacebar</kbd>, <kbd>Tab</kbd>, <kbd>Up</kbd>.
- **Punctuation keys**: <kbd>!</kbd>, <kbd>"</kbd>, <kbd>#</kbd>, <kbd>$</kbd>, <kbd>%</kbd>, <kbd>&</kbd>, <kbd>'</kbd>, <kbd>(</kbd>, <kbd>)</kbd>, <kbd>*</kbd>, <kbd>+</kbd>/<kbd>plus</kbd>, <kbd>,</kbd>, <kbd>-</kbd>, <kbd>.</kbd>, <kbd>/</kbd>, <kbd>:</kbd>, <kbd>;</kbd>, <kbd><</kbd>, <kbd>=</kbd>, <kbd>></kbd>, <kbd>?</kbd>, <kbd>@</kbd>, <kbd>[</kbd>, <kbd>\\</kbd>, <kbd>]</kbd>, <kbd>^</kbd>, <kbd>_</kbd>, <kbd>`</kbd>, <kbd>{</kbd>, <kbd>|</kbd>, <kbd>}</kbd>, <kbd>~</kbd>.
type Options = {
capture?: boolean,
target?: Window | Document | HTMLElement | SVGElement | MathMLElement,
shouldHandleEvent?: ( event: Event ) => boolean
};
Other keys are not supported.
- ℹ️ Shortcuts are case insensitive.
- ℹ️ Keys in a single shortcut must be joined by a plus sign (e.g. <kbd>Ctrl+A</kbd>).
- ℹ️ Sequences of shortcuts must be joined by a space (e.g. <kbd>Ctrl+K Ctrl+B</kbd>).
### Shortcuts Class
The Shortcuts class will be used for adding/removing/resetting/recording shortcuts. This is its interface:
```ts
class Shortcuts {
constructor ( options?: { shortcuts?: ShortcutDescriptor[]: capture?: boolean, target?: Node, shouldHandleEvent?: event => boolean } );
get (): ShortcutDescriptor[];
add ( descriptors: ShortcutDescriptor | ShortcutDescriptor[] );
remove ( descriptors: ShortcutDescriptor | ShortcutDescriptor[] );
reset ();
record ( handler: ( shortcut ) => any ): Function;
constructor ( options: Options );
get (): Descriptor[];
add ( descriptor: Descriptor | Descriptor[] ): void;
register ( descriptor: Descriptor | Descriptor[] ): Disposer;
remove ( descriptor: Descriptor | Descriptor[] ): void;
reset (): void;
trigger ( shortcut: string ): boolean;
start (): void;
stop (): void;
}
```
- ℹ️ The `shortcuts` option accepts an optional array of shortcuts descriptors. More on this below.
- ℹ️ The `capture` option governs whether events are attached for the capturing phase or for the bubbling phase of the propagation.
- ℹ️ The `target` option accepts an optional DOM node, where the keyboard evenr listener will be attached to.
- ℹ️ The `shouldHandleEvent` option accepts an optional function which will be used for determining, for each keyboard event, if it should be handled by this library. By default that function is: `event => !event.defaultPrevented`.
This is how you'd use the library:
A shortcut descriptor looks like this:
```ts
{
handler?: ( event: KeyboardEvent ) => boolean | void,
shortcut: string
}
```
import {Shortcuts} from 'shortcuts';
Usage:
// Let's create a new shortcuts manager instance
```ts
import {Shortcuts} from 'shortcuts';
const shortcuts = new Shortcuts ({
capture: true, // Handle events during the capturing phase
target: document, // Listening for events on the document object
shouldHandleEvent ( event ) {
return true; // Handle all possible events
}
});
const shortcuts = new Shortcuts ();
// Let's register and unregister some shortcuts
// Handlers are filtered by shortcut and executed from bottom to top, stopping at the first handler that returns true
function CtrlBHandler () {};
const onCtrlB = () => {};
shortcuts.add ([ // Adding some shortcuts
{ shortcut: 'Ctrl+A', handler: event => {
console.log ( event );
return true; // Returning true because we don't want other handlers for the same shortcut to be called later
}},
{ shortcut: 'Ctrl+B', handler: CtrlBHandler },
{ shortcut: 'CmdOrCtrl+K Shift+B', handler: () => {
// Doing something...
return true; // Returning true because we don't want other handlers for the same shortcut to be called later
}},
{ shortcut: '-Ctrl+A' } // Removing the previous shortcut
shortcuts.add ([
{
shortcut: 'Ctrl+A',
handler: () => {
// Doing something...
return true; // Returning true if we don't want other handlers for the same shortcut to be called later
}
},
{
shortcut: 'Ctrl+B',
handler: onCtrlB
},
{
shortcut: 'CmdOrCtrl+K Shift+B',
handler: () => {
// Doing something...
return true; // Returning true if we don't want other handlers for the same shortcut to be called later
}
},
{
shortcut: '-Ctrl+A' // Removing all previously-registered handlers for "Ctrl+A"
}
]);
shortcuts.remove ({ shortcut: 'Ctrl-B', handler: CtrlBHandler }); // Removing a single handler
shortcuts.remove ({ shortcut: 'Ctrl-A' }); // Removing all handlers bound to this shortcut
shortcuts.remove ({ shortcut: 'Ctrl+B', handler: onCtrlB }); // Removing a specific handler bound to this shortcut
shortcuts.remove ({ shortcut: 'Ctrl+A' }); // Removing all handlers bound to this shortcut
shortcuts.reset (); // Removing all shortcuts
// Let's actually start listening for shortcuts
const dispose = shortcuts.record ( shortcut => { // Recording shortcuts
console.log ( 'Shortcut recorded:', shortcut );
});
shortcuts.start ();
dispose (); // Stopping recording
```
// Let's stop listening for shortcuts
- ℹ️ Handlers are called from the bottom to the top, so an handler defined at the bottom will take precedence over an handler for the same shortcut defined at the top.
- ℹ️ If multiple handlers are defined for the same shortcut all of them are executed until one of them returns `true`.
- ℹ️ Adding a shortcut starting with an hyphen (e.g. `-Ctrl-A`) will actually remove that shortcut.
- ℹ️ While recording no handlers will be called.
shortcuts.stop ();
### Shortcut Object
// Let's dispose of all registered shortcuts
The Shortcut object provides some utilities that you might need in your application. This is its interface:
```ts
const Shortcut = {
shortcut2id ( shortcut: string ): number[];
shortcut2accelerator ( shortcut: string ): string;
shortcut2symbols ( shortcut: string ): string;
};
shortcuts.reset ();
```
Usage:
```ts
import {Shortcut} from 'shortcuts';
Shortcut.shortcut2accelerator ( 'Meta+Del' ); // => 'Cmd+Delete'
Shortcut.shortcut2symbols ( 'Cmd+Shift+A' ); // => '⌘⇧A'
```
## Thanks

@@ -132,0 +109,0 @@

/* MAIN */
const MODIFIER_KEY_BITMASK = 0b11111111_00000000; // Bitmask that includes all modifier keys and none of the triggers
const TRIGGER_KEY_BITMASK = 0b11111111; // Bitmask that includes all trigger keys and none of the modifiers
const FORMAT = 'long-inflexible-directional';
const PLUSES_RE = /\+{2,}/gi;
const WHITESPACE_RE = /\s+/gi;
const SHORTCUT_RE = /^\s*?(?:(?:^-?|\s|\+)(?:alt|option|cmd|command|meta|ctrl|control|shift|cmdorctrl|commandorcontrol|backspace|capslock|del|delete|down|end|enter|return|esc|escape|home|insert|left|pagedown|pageup|right|space|spacebar|tab|up|plus|\d|[a-z]|f(?:\d|1\d|2[0-4])|numpad\d|[!"#$%&'()*+,./:;<=>?@[\]^_`{|}~-]))+\s*$/i; // Regex that matches a shortcut
const RESULT = <const> {
HANDLED: 0, // An handler caught that
UNHANDLED: 1, // No handler caught that, but maybe a deeper one could
UNHANDLEABLE: 2 // No handler caught that, and there are no deeper handlers that could catch that
};
/* EXPORT */
export {MODIFIER_KEY_BITMASK, TRIGGER_KEY_BITMASK}
export {PLUSES_RE, WHITESPACE_RE, SHORTCUT_RE};
export {RESULT};
export {FORMAT};
/* IMPORT */
import Shortcut from './shortcut';
import Shortcuts from './shortcuts';
import ShoSho from 'shosho';
import {FORMAT} from './constants';
import {castArray} from './utils';
import type {Descriptor, Disposer, Handler, Options, Registration} from './types';
/* MAIN */
class Shortcuts {
/* VARIABLES */
private registrations: Registration[] = [];
private shosho: ShoSho;
/* CONSTRUCTOR */
constructor ( options: Options ) {
this.registrations = [];
this.shosho = new ShoSho ( options );
}
/* PUBLIC API */
get = (): Descriptor[] => {
return this.registrations.map ( registration => registration.descriptor );
};
add = ( descriptor: Descriptor | Descriptor[] ): void => {
const descriptors = castArray ( descriptor );
for ( const descriptor of descriptors ) {
if ( descriptor.shortcut.startsWith ( '-' ) ) {
this.remove ( descriptor );
} else if ( descriptor.handler ) {
const dispose = this.shosho.register ( descriptor.shortcut, descriptor.handler );
const shortcut = ShoSho.format ( descriptor.shortcut, FORMAT );
const registration = { descriptor, dispose, shortcut };
this.registrations.push ( registration );
} else {
console.error ( `Cannot register shortcut "${descriptor.shortcut}", which has no handler` );
}
}
};
register = ( descriptor: Descriptor | Descriptor[] ): Disposer => {
this.add ( descriptor );
return (): void => {
this.remove ( descriptor );
};
};
remove = ( descriptor: Descriptor | Descriptor[] ): void => {
const descriptors = castArray ( descriptor );
for ( const descriptor of descriptors ) {
const handler = descriptor.handler;
const shortcut = ShoSho.format ( descriptor.shortcut.replace ( /^-/, '' ), FORMAT );
this.registrations = this.registrations.filter ( registration => {
if ( ( registration.shortcut !== shortcut ) || ( handler && registration.descriptor.handler !== handler ) ) {
return true;
} else {
registration.dispose ();
return false;
}
});
}
};
reset = (): void => {
this.registrations = [];
this.shosho.reset ();
};
trigger = ( shortcut: string ): boolean => {
return this.shosho.trigger ( shortcut );
};
start = (): void => {
this.shosho.start ();
};
stop = (): void => {
this.shosho.stop ();
};
}
/* EXPORT */
export {Shortcut, Shortcuts};
export default Shortcuts;
export type {Descriptor, Disposer, Handler, Options, Registration};
/* MAIN */
/* IMPORT */
type Chord = string;
type ChordID = number;
type Shortcut = string;
type ShortcutID = ChordID[];
import type {Options} from 'shosho';
type Disposer = () => void;
type RecordHandler = ( shortcut: Shortcut ) => void;
type ShouldHandleEventFunction = ( event: KeyboardEvent ) => boolean;
/* MAIN */
type ListenerOptions = {
capture?: boolean,
target?: Node,
handler: ( id: ShortcutID, event: KeyboardEvent ) => 0 | 1 | 2 | ShortcutID,
shouldHandleEvent?: ShouldHandleEventFunction
type Descriptor = {
handler?: Handler,
shortcut: string
};
type ShortcutsOptions = {
capture?: boolean,
target?: Node,
shortcuts?: ShortcutDescriptor[],
shouldHandleEvent?: ShouldHandleEventFunction
type Disposer = {
(): void
};
type ShortcutsTree = {
[id: number]: ShortcutsTree,
parent?: ShortcutsTree,
id?: ChordID,
size: number,
handlers: Function[]
type Handler = {
( event?: Event ): boolean | void
};
type ShortcutDescriptor = {
handler?: ( event: KeyboardEvent ) => boolean | void,
type Registration = {
descriptor: Descriptor,
dispose: Disposer,
shortcut: string

@@ -42,4 +29,2 @@ };

export type {Chord, ChordID, Shortcut, ShortcutID};
export type {Disposer, RecordHandler, ShouldHandleEventFunction};
export type {ListenerOptions, ShortcutsOptions, ShortcutsTree, ShortcutDescriptor};
export type {Descriptor, Disposer, Handler, Options, Registration};
/* MAIN */
const Utils = {
const castArray = <T> ( value: T | T[] ): T[] => {
/* API */
return Array.isArray ( value ) ? value : [value];
isArray: ( value: unknown ): value is unknown[] => {
return Array.isArray ( value );
},
isEqual: ( a: number[], b: number[] ): boolean => {
if ( a.length !== b.length ) return false;
for ( let i = 0, l = a.length; i < l; i++ ) {
if ( a[i] !== b[i] ) return false;
}
return true;
},
isFalsy: ( value: unknown ): boolean => {
return !value;
},
isKeyboardEvent: ( event: Event ): event is KeyboardEvent => {
return event.type.startsWith ( 'key' );
},
isMac: (): boolean => {
if ( typeof navigator !== 'object' ) return false;
return /mac|ipod|iphone|ipad/i.test ( navigator.platform );
},
isTruthy: ( value: unknown ): boolean => {
return !!value;
},
memoize: <T, R> ( fn: (( arg: T ) => R) ): (( arg: T ) => R) => {
const cache = new Map<T, R> ();
return ( arg: T ): R => {
const cached = cache.get ( arg );
if ( cached || cache.has ( arg ) ) return cached as R; //TSC
const result = fn ( arg );
cache.set ( arg, result );
return result;
};
}
};

@@ -76,2 +12,2 @@

export default Utils;
export {castArray};

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc