Join our webinar on Wednesday, June 26, at 1pm EDTHow Chia Mitigates Risk in the Crypto Industry.Register
Socket
Socket
Sign inDemoInstall

@github/hotkey

Package Overview
Dependencies
0
Maintainers
15
Versions
40
Alerts
File Explorer

Advanced tools

Install Socket

Detect and block malicious and high-risk dependencies

Install

Comparing version 2.0.1 to 2.1.0

dist/sequence.d.ts

8

dist/hotkey.d.ts

@@ -1,1 +0,7 @@

export default function hotkey(event: KeyboardEvent): string;
declare const normalizedHotkeyBrand: unique symbol;
export declare type NormalizedHotkeyString = string & {
[normalizedHotkeyBrand]: true;
};
export declare function eventToHotkeyString(event: KeyboardEvent): NormalizedHotkeyString;
export declare function normalizeHotkey(hotkey: string, platform?: string | undefined): NormalizedHotkeyString;
export {};

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

export default function hotkey(event) {
const normalizedHotkeyBrand = Symbol('normalizedHotkey');
export function eventToHotkeyString(event) {
const { ctrlKey, altKey, metaKey, key } = event;

@@ -14,3 +15,3 @@ const hotkeyString = [];

}
const modifierKeyNames = [`Control`, 'Alt', 'Meta', 'Shift'];
const modifierKeyNames = ['Control', 'Alt', 'Meta', 'Shift'];
function showShift(event) {

@@ -20,1 +21,23 @@ const { shiftKey, code, key } = event;

}
export function normalizeHotkey(hotkey, platform) {
let result;
result = localizeMod(hotkey, platform);
result = sortModifiers(result);
return result;
}
const matchApplePlatform = /Mac|iPod|iPhone|iPad/i;
function localizeMod(hotkey, platform = navigator.platform) {
const localModifier = matchApplePlatform.test(platform) ? 'Meta' : 'Control';
return hotkey.replace('Mod', localModifier);
}
function sortModifiers(hotkey) {
const key = hotkey.split('+').pop();
const modifiers = [];
for (const modifier of ['Control', 'Alt', 'Meta', 'Shift']) {
if (hotkey.includes(modifier)) {
modifiers.push(modifier);
}
}
modifiers.push(key);
return modifiers.join('+');
}

6

dist/index.d.ts

@@ -1,5 +0,5 @@

import { Leaf, RadixTrie } from './radix-trie';
import eventToHotkeyString from './hotkey';
export { RadixTrie, Leaf, eventToHotkeyString };
export { eventToHotkeyString, normalizeHotkey, NormalizedHotkeyString } from './hotkey';
export { SequenceTracker, normalizeSequence, NormalizedSequenceString } from './sequence';
export { RadixTrie, Leaf } from './radix-trie';
export declare function install(element: HTMLElement, hotkey?: string): void;
export declare function uninstall(element: HTMLElement): void;

@@ -73,2 +73,85 @@ class Leaf {

function eventToHotkeyString(event) {
const { ctrlKey, altKey, metaKey, key } = event;
const hotkeyString = [];
const modifiers = [ctrlKey, altKey, metaKey, showShift(event)];
for (const [i, mod] of modifiers.entries()) {
if (mod)
hotkeyString.push(modifierKeyNames[i]);
}
if (!modifierKeyNames.includes(key)) {
hotkeyString.push(key);
}
return hotkeyString.join('+');
}
const modifierKeyNames = ['Control', 'Alt', 'Meta', 'Shift'];
function showShift(event) {
const { shiftKey, code, key } = event;
return shiftKey && !(code.startsWith('Key') && key.toUpperCase() === key);
}
function normalizeHotkey(hotkey, platform) {
let result;
result = localizeMod(hotkey, platform);
result = sortModifiers(result);
return result;
}
const matchApplePlatform = /Mac|iPod|iPhone|iPad/i;
function localizeMod(hotkey, platform = navigator.platform) {
const localModifier = matchApplePlatform.test(platform) ? 'Meta' : 'Control';
return hotkey.replace('Mod', localModifier);
}
function sortModifiers(hotkey) {
const key = hotkey.split('+').pop();
const modifiers = [];
for (const modifier of ['Control', 'Alt', 'Meta', 'Shift']) {
if (hotkey.includes(modifier)) {
modifiers.push(modifier);
}
}
modifiers.push(key);
return modifiers.join('+');
}
const SEQUENCE_DELIMITER = ' ';
class SequenceTracker {
constructor({ onReset } = {}) {
this._path = [];
this.timer = null;
this.onReset = onReset;
}
get path() {
return this._path;
}
get sequence() {
return this._path.join(SEQUENCE_DELIMITER);
}
registerKeypress(event) {
this._path = [...this._path, eventToHotkeyString(event)];
this.startTimer();
}
reset() {
var _a;
this.killTimer();
this._path = [];
(_a = this.onReset) === null || _a === void 0 ? void 0 : _a.call(this);
}
killTimer() {
if (this.timer != null) {
window.clearTimeout(this.timer);
}
this.timer = null;
}
startTimer() {
this.killTimer();
this.timer = window.setTimeout(() => this.reset(), SequenceTracker.CHORD_TIMEOUT);
}
}
SequenceTracker.CHORD_TIMEOUT = 1500;
function normalizeSequence(sequence) {
return sequence
.split(SEQUENCE_DELIMITER)
.map(h => normalizeHotkey(h))
.join(SEQUENCE_DELIMITER);
}
function isFormField(element) {

@@ -113,3 +196,3 @@ if (!(element instanceof HTMLElement)) {

}
if (hotkey[i] === ' ') {
if (hotkey[i] === SEQUENCE_DELIMITER) {
acc.push('');

@@ -128,34 +211,13 @@ commaIsSeparator = false;

output.push(acc);
return output.map(h => h.filter(k => k !== '')).filter(h => h.length > 0);
return output.map(h => h.map(k => normalizeHotkey(k)).filter(k => k !== '')).filter(h => h.length > 0);
}
function hotkey(event) {
const { ctrlKey, altKey, metaKey, key } = event;
const hotkeyString = [];
const modifiers = [ctrlKey, altKey, metaKey, showShift(event)];
for (const [i, mod] of modifiers.entries()) {
if (mod)
hotkeyString.push(modifierKeyNames[i]);
}
if (!modifierKeyNames.includes(key)) {
hotkeyString.push(key);
}
return hotkeyString.join('+');
}
const modifierKeyNames = [`Control`, 'Alt', 'Meta', 'Shift'];
function showShift(event) {
const { shiftKey, code, key } = event;
return shiftKey && !(code.startsWith('Key') && key.toUpperCase() === key);
}
const hotkeyRadixTrie = new RadixTrie();
const elementsLeaves = new WeakMap();
let currentTriePosition = hotkeyRadixTrie;
let resetTriePositionTimer = null;
let path = [];
function resetTriePosition() {
path = [];
resetTriePositionTimer = null;
currentTriePosition = hotkeyRadixTrie;
}
const sequenceTracker = new SequenceTracker({
onReset() {
currentTriePosition = hotkeyRadixTrie;
}
});
function keyDownHandler(event) {

@@ -173,12 +235,8 @@ if (event.defaultPrevented)

}
if (resetTriePositionTimer != null) {
window.clearTimeout(resetTriePositionTimer);
}
resetTriePositionTimer = window.setTimeout(resetTriePosition, 1500);
const newTriePosition = currentTriePosition.get(hotkey(event));
const newTriePosition = currentTriePosition.get(eventToHotkeyString(event));
if (!newTriePosition) {
resetTriePosition();
sequenceTracker.reset();
return;
}
path.push(hotkey(event));
sequenceTracker.registerKeypress(event);
currentTriePosition = newTriePosition;

@@ -199,6 +257,6 @@ if (newTriePosition instanceof Leaf) {

if (elementToFire && shouldFire) {
fireDeterminedAction(elementToFire, path);
fireDeterminedAction(elementToFire, sequenceTracker.path);
event.preventDefault();
}
resetTriePosition();
sequenceTracker.reset();
}

@@ -226,2 +284,2 @@ }

export { Leaf, RadixTrie, hotkey as eventToHotkeyString, install, uninstall };
export { Leaf, RadixTrie, SequenceTracker, eventToHotkeyString, install, normalizeHotkey, normalizeSequence, uninstall };

@@ -0,3 +1,4 @@

import { NormalizedHotkeyString } from './hotkey';
export declare function isFormField(element: Node): boolean;
export declare function fireDeterminedAction(el: HTMLElement, path: string[]): void;
export declare function expandHotkeyToEdges(hotkey: string): string[][];
export declare function fireDeterminedAction(el: HTMLElement, path: readonly NormalizedHotkeyString[]): void;
export declare function expandHotkeyToEdges(hotkey: string): NormalizedHotkeyString[][];

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

import { normalizeHotkey } from './hotkey';
import { SEQUENCE_DELIMITER } from './sequence';
export function isFormField(element) {

@@ -40,3 +42,3 @@ if (!(element instanceof HTMLElement)) {

}
if (hotkey[i] === ' ') {
if (hotkey[i] === SEQUENCE_DELIMITER) {
acc.push('');

@@ -55,3 +57,3 @@ commaIsSeparator = false;

output.push(acc);
return output.map(h => h.filter(k => k !== '')).filter(h => h.length > 0);
return output.map(h => h.map(k => normalizeHotkey(k)).filter(k => k !== '')).filter(h => h.length > 0);
}
{
"name": "@github/hotkey",
"version": "2.0.1",
"version": "2.1.0",
"description": "",

@@ -17,3 +17,4 @@ "main": "dist/index.js",

"pretest": "npm run build",
"prepublishOnly": "npm run build"
"prepublishOnly": "npm run build",
"buildSite": "npm run build && mkdir -p pages/hotkey && cp -r dist/* pages/hotkey"
},

@@ -20,0 +21,0 @@ "files": [

@@ -36,9 +36,16 @@ # Hotkey Behavior

## Usage
### HTML
``` html
```html
<!-- Single character hotkey: triggers when "j" is pressed-->
<a href="/page/2" data-hotkey="j">Next</a>
<a href="/help" data-hotkey="Control+h">Help</a>
<!-- Multiple hotkey aliases: triggers on both "s" and "/" -->
<a href="/search" data-hotkey="s,/">Search</a>
<!-- Key-sequence hotkey: triggers when "g" is pressed followed by "c"-->
<a href="/rails/rails" data-hotkey="g c">Code</a>
<a href="/search" data-hotkey="s,/">Search</a>
<!-- Hotkey with modifiers: triggers when "Control", "Alt", and "h" are pressed at the same time -->
<a href="/help" data-hotkey="Control+Alt+h">Help</a>
<!-- Special "Mod" modifier localizes to "Meta" on mac, "Control" on Windows or Linux-->
<a href="/settings" data-hotkey="Mod+s">Search</a>
```

@@ -77,2 +84,20 @@

By default form elements (such as `input`,`textarea`,`select`) or elements with `contenteditable` will call `focus()` when the hotkey is triggered. All other elements trigger a `click()`. All elements, regardless of type, will emit a cancellable `hotkey-fire` event, so you can customize the behaviour, if you so choose:
```js
for (const el of document.querySelectorAll('[data-shortcut]')) {
install(el, el.dataset.shortcut)
if (el.matches('.frobber')) {
el.addEventListener('hotkey-fire', event => {
// ensure the default `focus()`/`click()` is prevented:
event.preventDefault()
// Use a custom behaviour instead
frobulateFrobber(event.target)
})
}
}
```
## Hotkey string format

@@ -84,4 +109,8 @@

4. Multiple keys separated by a blank space represent a key sequence. For example the hotkey `g n` would activate when a user types the `g` key followed by the `n` key.
5. Modifier key combos are separated with a `+` and are prepended to a key in a consistent order as follows: `Control+Alt+Meta+Shift+KEY`.
6. You can use the comma key `,` as a hotkey, e.g. `a,,` would activate if the user typed `a` or `,`. `Control+,,x` would activate for `Control+,` or `x`.
5. Modifier key combos are separated with a `+` and are prepended to a key in a consistent order as follows: `"Control+Alt+Meta+Shift+KEY"`.
6. `"Mod"` is a special modifier that localizes to `Meta` on MacOS/iOS, and `Control` on Windows/Linux.
1. `"Mod+"` can appear in any order in a hotkey string. For example: `"Mod+Alt+Shift+KEY"`
2. Neither the `Control` or `Meta` modifiers should appear in a hotkey string with `Mod`.
3. Due to the inconsistent lowercasing of `event.key` on Mac and iOS when `Meta` is pressed along with `Shift`, it is recommended to avoid hotkey strings containing both `Mod` and `Shift`.
7. You can use the comma key `,` as a hotkey, e.g. `a,,` would activate if the user typed `a` or `,`. `Control+,,x` would activate for `Control+,` or `x`.

@@ -93,3 +122,3 @@ ### Example

```js
"a b,Control+Alt+/"
'a b,Control+Alt+/'
```

@@ -96,0 +125,0 @@

SocketSocket SOC 2 Logo

Product

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

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc