mirror of
https://github.com/osmarks/mycorrhiza.git
synced 2025-01-08 02:40:26 +00:00
Read Handlerug's code
I have written some comments in order to understand it by myself. I created the class Shortcut with some static methods for better grouping.
This commit is contained in:
parent
3cefc5038d
commit
d069bd536e
@ -1,10 +1,6 @@
|
|||||||
(() => {
|
class Shortcut {
|
||||||
const $ = document.querySelector.bind(document);
|
// turns the given event into a string representation of it.
|
||||||
const $$ = (...args) => Array.prototype.slice.call(document.querySelectorAll(...args));
|
static fromEvent(event) {
|
||||||
|
|
||||||
const isMac = /Macintosh/.test(window.navigator.userAgent);
|
|
||||||
|
|
||||||
function keyEventToShortcut(event) {
|
|
||||||
let elideShift = event.key.toUpperCase() === event.key && event.shiftKey;
|
let elideShift = event.key.toUpperCase() === event.key && event.shiftKey;
|
||||||
return (event.ctrlKey ? 'Ctrl+' : '') +
|
return (event.ctrlKey ? 'Ctrl+' : '') +
|
||||||
(event.altKey ? 'Alt+' : '') +
|
(event.altKey ? 'Alt+' : '') +
|
||||||
@ -13,9 +9,11 @@
|
|||||||
(event.key === ',' ? 'Comma' : event.key === ' ' ? 'Space' : event.key);
|
(event.key === ',' ? 'Comma' : event.key === ' ' ? 'Space' : event.key);
|
||||||
}
|
}
|
||||||
|
|
||||||
function prettifyShortcut(shortcut) {
|
// Some keys look better with cool symbols instead of their long and boring names.
|
||||||
|
static prettify(shortcut, isMac) {
|
||||||
let keys = shortcut.split('+');
|
let keys = shortcut.split('+');
|
||||||
|
|
||||||
|
// Uh it places the cmd sign before the letter to follow the Mac conventions, I guess.
|
||||||
if (isMac) {
|
if (isMac) {
|
||||||
let cmdIdx = keys.indexOf('Meta');
|
let cmdIdx = keys.indexOf('Meta');
|
||||||
if (cmdIdx !== -1 && keys.length - cmdIdx > 2) {
|
if (cmdIdx !== -1 && keys.length - cmdIdx > 2) {
|
||||||
@ -26,39 +24,52 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
let lastKey = keys[keys.length - 1];
|
let lastKey = keys[keys.length - 1];
|
||||||
|
// Uhh add Shift if the letter is uppercase??
|
||||||
if (!keys.includes('Shift') && lastKey.toUpperCase() === lastKey && lastKey.toLowerCase() !== lastKey) {
|
if (!keys.includes('Shift') && lastKey.toUpperCase() === lastKey && lastKey.toLowerCase() !== lastKey) {
|
||||||
keys.splice(keys.length - 1, 0, 'Shift');
|
keys.splice(keys.length - 1, 0, 'Shift');
|
||||||
}
|
}
|
||||||
|
|
||||||
for (let i = 0; i < keys.length; i++) {
|
return keys.map((key, i) => {
|
||||||
if (isMac) {
|
// If last element and there is more than one element and it's a letter
|
||||||
switch (keys[i]) {
|
if (i === keys.length - 1 && i > 0 && key.length === 1) {
|
||||||
case 'Ctrl': keys[i] = '⌃'; break;
|
// Show in upper case. ⌘K looks better ⌘k, no doubt.
|
||||||
case 'Alt': keys[i] = '⌥'; break;
|
key = key.toUpperCase();
|
||||||
case 'Shift': keys[i] = '⇧'; break;
|
|
||||||
case 'Meta': keys[i] = '⌘'; break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (i === keys.length - 1 && i > 0 && keys[i].length === 1) {
|
return `<kbd>${Shortcut.symbolifyKey(key, isMac)}</kbd>`;
|
||||||
keys[i] = keys[i].toUpperCase();
|
}).join(isMac ? '' : ' + ');
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (keys[i]) {
|
static symbolifyKey(key, isMac) {
|
||||||
case 'ArrowLeft': keys[i] = '←'; break;
|
if (isMac) {
|
||||||
case 'ArrowRight': keys[i] = '→'; break;
|
switch (key) {
|
||||||
case 'ArrowTop': keys[i] = '↑'; break;
|
case 'Ctrl': return '⌃';
|
||||||
case 'ArrowBottom': keys[i] = '↓'; break;
|
case 'Alt': return '⌥';
|
||||||
case 'Comma': keys[i] = ','; break;
|
case 'Shift': return '⇧';
|
||||||
case 'Enter': keys[i] = '↩'; break;
|
case 'Meta': return '⌘';
|
||||||
case ' ': keys[i] = 'Space'; break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
keys[i] = `<kbd>${keys[i]}</kbd>`;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return keys.join(isMac ? '' : ' + ');
|
switch (key) {
|
||||||
|
case 'ArrowLeft': return '←';
|
||||||
|
case 'ArrowRight': return '→';
|
||||||
|
case 'ArrowTop': return '↑';
|
||||||
|
case 'ArrowBottom': return '↓';
|
||||||
|
case 'Comma': return ',';
|
||||||
|
case 'Enter': return '↩';
|
||||||
|
case ' ': return 'Space';
|
||||||
|
}
|
||||||
|
return key
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
(() => {
|
||||||
|
const $ = document.querySelector.bind(document);
|
||||||
|
const $$ = (...args) => Array.prototype.slice.call(document.querySelectorAll(...args));
|
||||||
|
|
||||||
|
// Some things look different on Mac.
|
||||||
|
// Note that the ⌘ command key is called Meta in JS for some reason.
|
||||||
|
const isMac = /Macintosh/.test(window.navigator.userAgent);
|
||||||
|
|
||||||
function isTextField(element) {
|
function isTextField(element) {
|
||||||
let name = element.nodeName.toLowerCase();
|
let name = element.nodeName.toLowerCase();
|
||||||
@ -70,9 +81,12 @@
|
|||||||
|
|
||||||
let notTextField = event => !(event.target instanceof Node && isTextField(event.target));
|
let notTextField = event => !(event.target instanceof Node && isTextField(event.target));
|
||||||
|
|
||||||
|
// The whole shortcut table for current page. It is used for generating the dialog.
|
||||||
let allShortcuts = [];
|
let allShortcuts = [];
|
||||||
|
// Temporary variable for building a shortcut group.
|
||||||
let shortcutsGroup = null;
|
let shortcutsGroup = null;
|
||||||
|
|
||||||
|
// Advanced stuff.
|
||||||
class ShortcutHandler {
|
class ShortcutHandler {
|
||||||
constructor(element, filter = () => true) {
|
constructor(element, filter = () => true) {
|
||||||
this.element = element;
|
this.element = element;
|
||||||
@ -83,10 +97,6 @@
|
|||||||
|
|
||||||
this.handleKeyDown = this.handleKeyDown.bind(this);
|
this.handleKeyDown = this.handleKeyDown.bind(this);
|
||||||
this.resetActive = this.resetActive.bind(this);
|
this.resetActive = this.resetActive.bind(this);
|
||||||
this.addEventListeners();
|
|
||||||
}
|
|
||||||
|
|
||||||
addEventListeners() {
|
|
||||||
this.element.addEventListener('keydown', this.handleKeyDown);
|
this.element.addEventListener('keydown', this.handleKeyDown);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -130,8 +140,11 @@
|
|||||||
shortcutsGroup = null;
|
shortcutsGroup = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// A dirty and shameful hack for inserting non-generated entries into the table.
|
||||||
fakeItem(shortcut, description = null) {
|
fakeItem(shortcut, description = null) {
|
||||||
|
// So it's a boolean, right?
|
||||||
let list = shortcutsGroup || allShortcuts;
|
let list = shortcutsGroup || allShortcuts;
|
||||||
|
// And we push something into a boolean. I give up.
|
||||||
list.push({
|
list.push({
|
||||||
shortcut: description ? shortcut : null,
|
shortcut: description ? shortcut : null,
|
||||||
description: description || shortcut,
|
description: description || shortcut,
|
||||||
@ -143,7 +156,7 @@
|
|||||||
if (['Control', 'Alt', 'Shift', 'Meta'].includes(event.key)) return;
|
if (['Control', 'Alt', 'Shift', 'Meta'].includes(event.key)) return;
|
||||||
if (!this.filter(event)) return;
|
if (!this.filter(event)) return;
|
||||||
|
|
||||||
let shortcut = keyEventToShortcut(event);
|
let shortcut = Shortcut.fromEvent(event);
|
||||||
|
|
||||||
if (!this.active[shortcut]) {
|
if (!this.active[shortcut]) {
|
||||||
this.resetActive();
|
this.resetActive();
|
||||||
@ -192,7 +205,7 @@
|
|||||||
let shortcutsListDialog = null;
|
let shortcutsListDialog = null;
|
||||||
|
|
||||||
function openShortcutsReference() {
|
function openShortcutsReference() {
|
||||||
if (!shortcutsListDialog) {
|
if (!shortcutsListDialog) { // I guess the dialog is reused for second and subsequent invocations.
|
||||||
let wrap = document.createElement('div');
|
let wrap = document.createElement('div');
|
||||||
wrap.className = 'dialog-wrap';
|
wrap.className = 'dialog-wrap';
|
||||||
shortcutsListDialog = wrap;
|
shortcutsListDialog = wrap;
|
||||||
@ -213,7 +226,7 @@
|
|||||||
|
|
||||||
let closeButton = document.createElement('button');
|
let closeButton = document.createElement('button');
|
||||||
closeButton.className = 'dialog__close-button';
|
closeButton.className = 'dialog__close-button';
|
||||||
closeButton.setAttribute('aria-label', 'Close this dialog');
|
closeButton.setAttribute('aria-label', 'Close this dialog'); // a11y gang
|
||||||
dialogHeader.appendChild(closeButton);
|
dialogHeader.appendChild(closeButton);
|
||||||
|
|
||||||
for (let item of allShortcuts) {
|
for (let item of allShortcuts) {
|
||||||
@ -240,7 +253,7 @@
|
|||||||
let shortcutColumn = document.createElement('div');
|
let shortcutColumn = document.createElement('div');
|
||||||
shortcutColumn.className = 'shortcut-row__keys';
|
shortcutColumn.className = 'shortcut-row__keys';
|
||||||
shortcutColumn.innerHTML = shortcut.shortcut.split(',')
|
shortcutColumn.innerHTML = shortcut.shortcut.split(',')
|
||||||
.map(shortcuts => shortcuts.trim().split(' ').map(prettifyShortcut).join(' '))
|
.map(shortcuts => shortcuts.trim().split(' ').map((sc) => Shortcut.prettify(sc, isMac)).join(' '))
|
||||||
.join(' or ');
|
.join(' or ');
|
||||||
listItem.appendChild(shortcutColumn);
|
listItem.appendChild(shortcutColumn);
|
||||||
}
|
}
|
||||||
@ -295,6 +308,7 @@
|
|||||||
// * Common shortcuts
|
// * Common shortcuts
|
||||||
globalShortcuts.fakeItem('Common');
|
globalShortcuts.fakeItem('Common');
|
||||||
|
|
||||||
|
// Nice indentation here
|
||||||
globalShortcuts.groupStart();
|
globalShortcuts.groupStart();
|
||||||
globalShortcuts.fakeItem('g 1 – 9', 'First 9 header links');
|
globalShortcuts.fakeItem('g 1 – 9', 'First 9 header links');
|
||||||
bindLink('g h', '/', 'Home');
|
bindLink('g h', '/', 'Home');
|
||||||
@ -326,7 +340,7 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Hypha editor shortcuts
|
// * Editor shortcuts
|
||||||
if (typeof editTextarea !== 'undefined') {
|
if (typeof editTextarea !== 'undefined') {
|
||||||
let editorShortcuts = new ShortcutHandler(editTextarea);
|
let editorShortcuts = new ShortcutHandler(editTextarea);
|
||||||
let bindElement = bindElementFactory(editorShortcuts);
|
let bindElement = bindElementFactory(editorShortcuts);
|
||||||
|
Loading…
Reference in New Issue
Block a user