@github/hotkey
Advanced tools
Comparing version 1.4.0 to 1.4.1
@@ -1,162 +0,98 @@ | ||
function _defineProperty(obj, key, value) { | ||
if (key in obj) { | ||
Object.defineProperty(obj, key, { | ||
value: value, | ||
enumerable: true, | ||
configurable: true, | ||
writable: true | ||
}); | ||
} else { | ||
obj[key] = value; | ||
} | ||
return obj; | ||
} | ||
class Leaf { | ||
constructor(trie) { | ||
_defineProperty(this, "parent", void 0); | ||
_defineProperty(this, "children", []); | ||
this.parent = trie; | ||
} | ||
delete(value) { | ||
const index = this.children.indexOf(value); | ||
if (index === -1) return false; | ||
this.children = this.children.slice(0, index).concat(this.children.slice(index + 1)); | ||
if (this.children.length === 0) { | ||
this.parent.delete(this); | ||
constructor(trie) { | ||
this.children = []; | ||
this.parent = trie; | ||
} | ||
return true; | ||
} | ||
add(value) { | ||
this.children.push(value); | ||
return this; | ||
} | ||
delete(value) { | ||
const index = this.children.indexOf(value); | ||
if (index === -1) | ||
return false; | ||
this.children = this.children.slice(0, index).concat(this.children.slice(index + 1)); | ||
if (this.children.length === 0) { | ||
this.parent.delete(this); | ||
} | ||
return true; | ||
} | ||
add(value) { | ||
this.children.push(value); | ||
return this; | ||
} | ||
} | ||
class RadixTrie { | ||
constructor(trie) { | ||
_defineProperty(this, "parent", null); | ||
_defineProperty(this, "children", {}); | ||
this.parent = trie || null; | ||
} | ||
get(edge) { | ||
return this.children[edge]; | ||
} | ||
insert(edges) { | ||
let currentNode = this; | ||
for (let i = 0; i < edges.length; i += 1) { | ||
const edge = edges[i]; | ||
let nextNode = currentNode.get(edge); // If we're at the end of this set of edges: | ||
if (i === edges.length - 1) { | ||
// If this end already exists as a RadixTrie, then hose it and replace with a Leaf: | ||
if (nextNode instanceof RadixTrie) { | ||
currentNode.delete(nextNode); | ||
nextNode = null; | ||
} // If nextNode doesn't exist (or used to be a RadixTrie) then make a Leaf: | ||
if (!nextNode) { | ||
nextNode = new Leaf(currentNode); | ||
currentNode.children[edge] = nextNode; | ||
constructor(trie) { | ||
this.parent = null; | ||
this.children = {}; | ||
this.parent = trie || null; | ||
} | ||
get(edge) { | ||
return this.children[edge]; | ||
} | ||
insert(edges) { | ||
let currentNode = this; | ||
for (let i = 0; i < edges.length; i += 1) { | ||
const edge = edges[i]; | ||
let nextNode = currentNode.get(edge); | ||
if (i === edges.length - 1) { | ||
if (nextNode instanceof RadixTrie) { | ||
currentNode.delete(nextNode); | ||
nextNode = null; | ||
} | ||
if (!nextNode) { | ||
nextNode = new Leaf(currentNode); | ||
currentNode.children[edge] = nextNode; | ||
} | ||
return nextNode; | ||
} | ||
else { | ||
if (nextNode instanceof Leaf) | ||
nextNode = null; | ||
if (!nextNode) { | ||
nextNode = new RadixTrie(currentNode); | ||
currentNode.children[edge] = nextNode; | ||
} | ||
} | ||
currentNode = nextNode; | ||
} | ||
return nextNode; // We're not at the end of this set of edges: | ||
} else { | ||
// If we're not at the end, but we've hit a Leaf, replace with a RadixTrie | ||
if (nextNode instanceof Leaf) nextNode = null; | ||
if (!nextNode) { | ||
nextNode = new RadixTrie(currentNode); | ||
currentNode.children[edge] = nextNode; | ||
} | ||
} | ||
currentNode = nextNode; | ||
return currentNode; | ||
} | ||
return currentNode; | ||
} // eslint-disable-next-line flowtype/no-weak-types | ||
delete(node) { | ||
for (const edge in this.children) { | ||
const currentNode = this.children[edge]; | ||
if (currentNode === node) { | ||
const success = delete this.children[edge]; | ||
if (Object.keys(this.children).length === 0 && this.parent) { | ||
this.parent.delete(this); | ||
delete(node) { | ||
for (const edge in this.children) { | ||
const currentNode = this.children[edge]; | ||
if (currentNode === node) { | ||
const success = delete this.children[edge]; | ||
if (Object.keys(this.children).length === 0 && this.parent) { | ||
this.parent.delete(this); | ||
} | ||
return success; | ||
} | ||
} | ||
return success; | ||
} | ||
return false; | ||
} | ||
return false; | ||
} | ||
} | ||
function isFormField(element) { | ||
if (!(element instanceof HTMLElement)) { | ||
return false; | ||
} | ||
const name = element.nodeName.toLowerCase(); | ||
const type = (element.getAttribute('type') || '').toLowerCase(); | ||
return name === 'select' || name === 'textarea' || name === 'input' && type !== 'submit' && type !== 'reset' && type !== 'checkbox' && type !== 'radio' || element.isContentEditable; | ||
if (!(element instanceof HTMLElement)) { | ||
return false; | ||
} | ||
const name = element.nodeName.toLowerCase(); | ||
const type = (element.getAttribute('type') || '').toLowerCase(); | ||
return (name === 'select' || | ||
name === 'textarea' || | ||
(name === 'input' && type !== 'submit' && type !== 'reset' && type !== 'checkbox' && type !== 'radio') || | ||
element.isContentEditable); | ||
} | ||
function fireDeterminedAction(el) { | ||
if (isFormField(el)) { | ||
el.focus(); | ||
} else { | ||
el.click(); | ||
} | ||
if (isFormField(el)) { | ||
el.focus(); | ||
} | ||
else { | ||
el.click(); | ||
} | ||
} | ||
function expandHotkeyToEdges(hotkey) { | ||
return hotkey.split(',').map(edge => edge.split(' ')); | ||
return hotkey.split(',').map(edge => edge.split(' ')); | ||
} | ||
// # Returns a hotkey character string for keydown and keyup events. | ||
// | ||
// A full list of key names can be found here: | ||
// https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/key/Key_Values | ||
// | ||
// ## Code Example | ||
// | ||
// ``` | ||
// document.addEventListener('keydown', function(event) { | ||
// if (hotkey(event) === 'h') ... | ||
// }) | ||
// ``` | ||
// ## Hotkey examples | ||
// | ||
// "s" // Lowercase character for single letters | ||
// "S" // Uppercase character for shift plus a letter | ||
// "1" // Number character | ||
// "?" // Shift plus "/" symbol | ||
// | ||
// "Enter" // Enter key | ||
// "ArrowUp" // Up arrow | ||
// | ||
// "Control+s" // Control modifier plus letter | ||
// "Control+Alt+Delete" // Multiple modifiers | ||
// | ||
// Returns key character String or null. | ||
function hotkey(event) { | ||
return `${event.ctrlKey ? 'Control+' : ''}${event.altKey ? 'Alt+' : ''}${event.metaKey ? 'Meta+' : ''}${event.shiftKey && event.key.toUpperCase() !== event.key ? 'Shift+' : ''}${event.key}`; | ||
return `${event.ctrlKey ? 'Control+' : ''}${event.altKey ? 'Alt+' : ''}${event.metaKey ? 'Meta+' : ''}${event.shiftKey && event.key.toUpperCase() !== event.key ? 'Shift+' : ''}${event.key}`; | ||
} | ||
@@ -168,58 +104,46 @@ | ||
let resetTriePositionTimer = null; | ||
function resetTriePosition() { | ||
resetTriePositionTimer = null; | ||
currentTriePosition = hotkeyRadixTrie; | ||
resetTriePositionTimer = null; | ||
currentTriePosition = hotkeyRadixTrie; | ||
} | ||
function keyDownHandler(event) { | ||
if (event.target instanceof Node && isFormField(event.target)) return; | ||
if (resetTriePositionTimer != null) { | ||
clearTimeout(resetTriePositionTimer); | ||
} | ||
resetTriePositionTimer = setTimeout(resetTriePosition, 1500); // If the user presses a hotkey that doesn't exist in the Trie, | ||
// they've pressed a wrong key-combo and we should reset the flow | ||
const newTriePosition = currentTriePosition.get(hotkey(event)); | ||
if (!newTriePosition) { | ||
resetTriePosition(); | ||
return; | ||
} | ||
currentTriePosition = newTriePosition; | ||
if (newTriePosition instanceof Leaf) { | ||
fireDeterminedAction(newTriePosition.children[newTriePosition.children.length - 1]); | ||
event.preventDefault(); | ||
resetTriePosition(); | ||
return; | ||
} | ||
if (event.target instanceof Node && isFormField(event.target)) | ||
return; | ||
if (resetTriePositionTimer != null) { | ||
window.clearTimeout(resetTriePositionTimer); | ||
} | ||
resetTriePositionTimer = window.setTimeout(resetTriePosition, 1500); | ||
const newTriePosition = currentTriePosition.get(hotkey(event)); | ||
if (!newTriePosition) { | ||
resetTriePosition(); | ||
return; | ||
} | ||
currentTriePosition = newTriePosition; | ||
if (newTriePosition instanceof Leaf) { | ||
fireDeterminedAction(newTriePosition.children[newTriePosition.children.length - 1]); | ||
event.preventDefault(); | ||
resetTriePosition(); | ||
return; | ||
} | ||
} | ||
function install(element, hotkey) { | ||
// Install the keydown handler if this is the first install | ||
if (Object.keys(hotkeyRadixTrie.children).length === 0) { | ||
document.addEventListener('keydown', keyDownHandler); | ||
} | ||
const hotkeys = expandHotkeyToEdges(hotkey || element.getAttribute('data-hotkey') || ''); | ||
const leaves = hotkeys.map(h => hotkeyRadixTrie.insert(h).add(element)); | ||
elementsLeaves.set(element, leaves); | ||
if (Object.keys(hotkeyRadixTrie.children).length === 0) { | ||
document.addEventListener('keydown', keyDownHandler); | ||
} | ||
const hotkeys = expandHotkeyToEdges(hotkey || element.getAttribute('data-hotkey') || ''); | ||
const leaves = hotkeys.map(h => hotkeyRadixTrie.insert(h).add(element)); | ||
elementsLeaves.set(element, leaves); | ||
} | ||
function uninstall(element) { | ||
const leaves = elementsLeaves.get(element); | ||
if (leaves && leaves.length) { | ||
for (const leaf of leaves) { | ||
leaf && leaf.delete(element); | ||
const leaves = elementsLeaves.get(element); | ||
if (leaves && leaves.length) { | ||
for (const leaf of leaves) { | ||
leaf && leaf.delete(element); | ||
} | ||
} | ||
} | ||
if (Object.keys(hotkeyRadixTrie.children).length === 0) { | ||
document.removeEventListener('keydown', keyDownHandler); | ||
} | ||
if (Object.keys(hotkeyRadixTrie.children).length === 0) { | ||
document.removeEventListener('keydown', keyDownHandler); | ||
} | ||
} | ||
export { Leaf, RadixTrie, hotkey as eventToHotkeyString, install, uninstall }; |
{ | ||
"name": "@github/hotkey", | ||
"version": "1.4.0", | ||
"version": "1.4.1", | ||
"description": "", | ||
"main": "dist/index.umd.js", | ||
"main": "dist/index.js", | ||
"type": "module", | ||
"module": "dist/index.js", | ||
"types": "index.d.ts", | ||
"types": "dist/index.d.ts", | ||
"repository": "github/hotkey", | ||
"scripts": { | ||
"build": "rollup -c && cp src/index.js.flow dist/index.js.flow && cp src/index.js.flow dist/index.umd.js.flow", | ||
"lint": "github-lint", | ||
"test": "karma start test/karma.config.js", | ||
"build": "tsc && rollup -c", | ||
"lint": "eslint . --ext .js,.ts && tsc --noEmit", | ||
"test": "karma start karma.config.cjs", | ||
"clean": "rm -rf dist", | ||
@@ -27,11 +28,6 @@ "prebuild": "npm run clean && npm run lint && mkdir dist", | ||
"devDependencies": { | ||
"@babel/core": "^7.9.0", | ||
"@babel/plugin-proposal-class-properties": "^7.8.3", | ||
"@babel/preset-env": "^7.9.5", | ||
"@babel/preset-flow": "^7.9.0", | ||
"@github/prettier-config": "0.0.4", | ||
"chai": "^4.2.0", | ||
"eslint": "^6.8.0", | ||
"eslint-plugin-github": "^3.4.1", | ||
"flow-bin": "^0.122.0", | ||
"eslint-plugin-github": "^4.0.0", | ||
"karma": "^5.0.0", | ||
@@ -44,3 +40,3 @@ "karma-chai": "^0.1.0", | ||
"rollup": "^2.4.0", | ||
"rollup-plugin-babel": "^4.4.0" | ||
"typescript": "^3.9.3" | ||
}, | ||
@@ -47,0 +43,0 @@ "eslintIgnore": [ |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
New author
Supply chain riskA new npm collaborator published a version of the package for the first time. New collaborators are usually benign additions to a project, but do indicate a change to the security surface area of a package.
Found 1 instance in 1 package
12
11
1
Yes
14211
266