Result goes here…
# Usage
Browser
<!DOCTYPE html>
<html dir="ltr">
<head>
<meta charset="utf-8">
</head>
<body>
<p>
<textarea></textarea>
</p>
<script src="./index.min.js"></script>
<script>
const map = new Key;
const self = document.querySelector('textarea');
self.addEventListener('blur', e => map.pull()); // Reset!
self.addEventListener('focus', e => map.pull()); // Reset!
let wait;
self.addEventListener('keydown', e => {
// Make the `Alt`, `Control`, `Meta`, and `Shift` keys sticky (does not require the user to release all keys first to repeat or change the current key combination).
map[e.altKey ? 'push' : 'pull']('Alt');
map[e.ctrlKey ? 'push' : 'pull']('Control');
map[e.metaKey ? 'push' : 'pull']('Meta');
map[e.shiftKey ? 'push' : 'pull']('Shift');
// Add the actual key to the queue. Don’t worry, this will not mistakenly add a key that already exists in the queue.
e.key && map.push(e.key);
console.log(map.toString());
wait && clearTimeout(wait);
wait = setTimeout(() => map.pull(), 1000); // Reset after 1 second idle!
});
self.addEventListener('keyup', e => e.key && map.pull(e.key));
</script>
</body>
</html>
Note: For better compatibility with mobile devices, it is also a good practice to listen to the beforeinput event to verify the character being typed. Unlike the keydown event, which has the KeyboardEvent.key property that returns the character of the key pressed before it is inserted into the text area, the beforeinput (and input) event has the InputEvent.data property that also returns the character of the key pressed when the InputEvent.inputType value is 'insertText'. For more details on this tweak, please refer to the source code of this page.
Node.js
Functions and methods in this application are mostly native JavaScript and are intended for use by the browser. Node.js doesn’t know about the DOM, so this kind of practice will probably be used more often to build new browser packages than to be used directly in the Node.js server.
CommonJS
const Key = require('@taufik-nurrohman/key').default;
const map = new Key;
ECMAScript
import Key from '@taufik-nurrohman/key';
const map = new Key;
# Tests
# Tweaks
# Constructor
const map = new Key(self);
# Parameters
self
The object that will becomes the this context in the command.
# Methods
Instance Methods
Instance methods are methods available through the results of a Key construct.
map.command()
Checks if current key combination exists.
self.addEventListener('keydown', e => {
e.key && map.push(e.key);
let command = map.command();
if (command) {
console.log(command);
}
});
Checks if current key combination is Control-b.
self.addEventListener('keydown', e => {
e.key && map.push(e.key);
if (map.command('Control-b')) {
console.log('Bold!');
}
});
Example context usage, using @taufik-nurrohman/text-editor application:
import Key from '@taufik-nurrohman/key';
import TextEditor from '@taufik-nurrohman/text-editor';
const editor = new TextEditor(document.querySelector('textarea'));
const map = new Key(editor);
map.commands.bold = function () {
return this.wrap('<b>', '</b>'), false; // `this` refers to the `editor`
};
map.fire(command, data)
Executes command data returned by map.command() method.
// …
let command = map.command();
if (command) {
let value = map.fire(command, [e]);
if (false === value) {
e.preventDefault();
} else if (null === value) {
console.error('Unknown command:', command);
}
}
map.pull(key)
Removes key from the queue.
self.addEventListener('blur', e => map.pull());
self.addEventListener('focus', e => map.pull());
self.addEventListener('keyup', e => e.key && map.pull(e.key));
map.push(key)
Adds key to the queue.
self.addEventListener('keydown', e => e.key && map.push(e.key));
map.toArray()
Returns the current key combination as array.
self.addEventListener('keydown', e => {
e.key && map.push(e.key);
console.info(map.toArray());
});
map.toString()
Returns the current key combination as string.
self.addEventListener('keydown', e => {
e.key && map.push(e.key);
console.info(map + "");
console.info(map.toString());
});
# Properties
Instance Properties
Instance properties are properties available through the results of a Key construct.
map.commands
Returns list of commands to be executed on key combination match.
// Simple function
map.commands.bold = function () {
// Your `bold` function goes here…
return false; // Return `false` to tell the event that it has to prevent the default effect
};
// Function with argument(s)
map.commands.insertLink = function (href = 'http://') {
// Your `insertLink` function goes here…
// Return `void` will be normalized into `true` by `map.fire()` method.
};
map.keys
Returns list of key combination with its command that will be returned by map.command(). Key names follow the KeyboardEvent.key specification.
// Anonymous function
map.keys['Control-b'] = function () {
return this.wrap('<b>', '</b>'), false;
};
// Named function without argument(s)
map.keys['Control-b'] = 'bold'; // This will execute `map.commands.bold` command if available
// Named function with argument(s)
map.keys['Control-k'] = ['insertLink', ['http://']]; // This will execute `map.commands.insertLink` command if available
Note: You can also use your own key names, since in most cases they are specified by the KeyboardEvent.key value generated by the keydown event. Here is an example of custom key names, with normalized order for consistency:
// Should work for both `Control-Shift-E` and `Shift-Control-E`, even when `CapsLock` is active.
map.keys['C-S-e'] = function (e) {
console.log('Focus to the file/folder tree.');
};
self.addEventListener('keydown', function (e) {
// This sequence ensures that the combination of keys will form `C-A-S-`, or `C-A-`, or `C-S-`, or `A-S-` no matter which key is pressed first.
map[e.ctrlKey ? 'push' : 'pull']('C').pull('Control'); // Normalize `Control` key to `C`
map[e.altKey ? 'push' : 'pull']('A').pull('Alt'); // Normalize `Alt` key to `A`
map[e.shiftKey ? 'push' : 'pull']('S').pull('Shift'); // Normalize `Shift` key to `S`
// Add only the lower case version of the key, so that the `CapsLock` and `Shift` key states will not affect the results of the key combination.
e.key && !['Alt', 'Control', 'Shift'].includes(e.key) && map.push(e.key.toLowerCase());
});
self.addEventListener('keyup', function (e) {
map[e.ctrlKey ? 'push' : 'pull']('C').pull('Control');
map[e.altKey ? 'push' : 'pull']('A').pull('Alt');
map[e.shiftKey ? 'push' : 'pull']('S').pull('Shift');
e.key && map.pull(e.key.toLowerCase());
});
# License
Use it for free, pay if you get paid. So, you’ve just benefited financially after using this project? It’s a good idea to share a little financial support with this open source project too. Your support will motivate me to do any further development, as well as to provide voluntary support to overcome problems related to this project.
Thank you! ❤️