-
Notifications
You must be signed in to change notification settings - Fork 1.9k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* Experimental feature: shortcut visual hints * Long way around to a custom modifier for keyboard shortcuts * dynamic table and list iterative shortcuts * Progress with regular old tether * Delogging * Table Keynav tether fix, server and client navs, and fix to shiftless on modified arrow keys
- Loading branch information
1 parent
9ddcfa0
commit 6ab02c5
Showing
25 changed files
with
360 additions
and
69 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,14 +1,38 @@ | ||
{{keyboard-commands this.keyCommands}} | ||
{{#if this.keyboard.shortcutsVisible}} | ||
{{keyboard-commands (array this.escapeCommand)}} | ||
<ul class="keyboard-shortcuts"> | ||
<button | ||
class="button is-borderless dismiss" | ||
type="button" | ||
{{on "click" (toggle "keyboard.shortcutsVisible" this)}} | ||
> | ||
{{x-icon "cancel"}} | ||
</button> | ||
{{#each this.commands as |command|}} | ||
<li> | ||
<strong>{{command.label}}</strong> | ||
<span class="keys"> | ||
{{#each command.pattern as |key|}} | ||
<span>{{key}}</span> | ||
{{/each}} | ||
</span> | ||
</li> | ||
{{/each}} | ||
</ul> | ||
{{/if}} | ||
|
||
<ul class="keyboard-shortcuts"> | ||
{{#each this.keyboard.keyCommands as |command|}} | ||
<li> | ||
<strong>{{command.label}}</strong> | ||
<span class="keys"> | ||
{{#each command.pattern as |key|}} | ||
<span>{{key}}</span> | ||
{{/each}} | ||
</span> | ||
</li> | ||
{{#if this.keyboard.displayHints}} | ||
{{#each this.hints as |hint|}} | ||
<span | ||
{{did-insert this.tetherToElement element=hint.element hint=hint}} | ||
{{will-destroy this.untetherFromElement element=hint.element hint=hint}} | ||
data-shortcut={{hint.pattern}} | ||
class="{{if hint.menuLevel "menu-level"}}" | ||
aria-hidden="true" | ||
> | ||
{{#each hint.pattern as |key|}} | ||
<span>{{key}}</span> | ||
{{/each}} | ||
</span> | ||
{{/each}} | ||
</ul> | ||
{{/if}} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,16 +1,53 @@ | ||
import Component from '@glimmer/component'; | ||
import { inject as service } from '@ember/service'; | ||
import { htmlSafe } from '@ember/template'; | ||
import { computed } from '@ember/object'; | ||
import Tether from 'tether'; | ||
|
||
export default class KeyboardShortcutsModalComponent extends Component { | ||
@service keyboard; | ||
|
||
keyCommands = [ | ||
{ | ||
label: 'Hide Keyboard Shortcuts', | ||
pattern: ['Escape'], | ||
action: () => { | ||
this.keyboard.shortcutsVisible = false; | ||
}, | ||
escapeCommand = { | ||
label: 'Hide Keyboard Shortcuts', | ||
pattern: ['Escape'], | ||
action: () => { | ||
this.keyboard.shortcutsVisible = false; | ||
}, | ||
]; | ||
}; | ||
|
||
/** | ||
* commands: filter keyCommands to those that have an action and a label, | ||
* to distinguish between those that are just visual hints of existing commands | ||
*/ | ||
@computed('keyboard.keyCommands.length') | ||
get commands() { | ||
return this.keyboard.keyCommands.filter((c) => c.label && c.action); | ||
} | ||
|
||
/** | ||
* hints: filter keyCommands to those that have an element property, | ||
* and then compute a position on screen to place the hint. | ||
*/ | ||
@computed('keyboard.keyCommands.length', 'keyboard.displayHints') | ||
get hints() { | ||
if (this.keyboard.displayHints) { | ||
return this.keyboard.keyCommands.filter((c) => c.element); | ||
} else { | ||
return []; | ||
} | ||
} | ||
|
||
tetherToElement(self, _, { element, hint }) { | ||
let binder = new Tether({ | ||
element: self, | ||
target: element, | ||
attachment: 'top left', | ||
targetAttachment: 'top left', | ||
targetModifier: 'visible', | ||
}); | ||
hint.binder = binder; | ||
} | ||
untetherFromElement(self, _, { element, hint }) { | ||
hint.binder.destroy(); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
import { inject as service } from '@ember/service'; | ||
import Modifier from 'ember-modifier'; | ||
import { registerDestructor } from '@ember/destroyable'; | ||
import { assert } from '@ember/debug'; | ||
|
||
export default class KeyboardShortcutModifier extends Modifier { | ||
@service keyboard; | ||
|
||
/** | ||
* For Dynamic/iterative keyboard shortcuts, our patterns look like "Shift+0" by default | ||
* Do a couple things to make them more human-friendly: | ||
* 1. Make them 1-based, instead of 0-based | ||
* 2. Prefix numbers 1-9 with "0" to make it so "Shift+10" doesn't trigger "Shift+1" then "0", etc. | ||
* ^--- stops being a good solution with 100+ row lists/tables, but a better UX than waiting for shift key-up otherwise | ||
* | ||
* @param {string[]} pattern | ||
*/ | ||
cleanPattern(pattern) { | ||
let patternNumber = pattern.length === 1 && pattern[0].match(/\d+/g); | ||
if (!patternNumber) { | ||
return pattern; | ||
} else { | ||
patternNumber = +patternNumber[0]; // regex'd string[0] to num | ||
patternNumber = patternNumber + 1; // first item should be Shift+1, not Shift+0 | ||
assert( | ||
'Dynamic keyboard shortcuts only work up to 99 digits', | ||
patternNumber < 100 | ||
); | ||
pattern = [`Shift+${('0' + patternNumber).slice(-2)}`]; // Shift+01, not Shift+1 | ||
} | ||
return pattern; | ||
} | ||
|
||
modify(element, [eventName], { label, pattern, action, menuLevel = false }) { | ||
let commands = [ | ||
{ | ||
label, | ||
action, | ||
pattern: this.cleanPattern(pattern), | ||
element, | ||
menuLevel, | ||
}, | ||
]; | ||
this.keyboard.addCommands(commands); | ||
registerDestructor(this, () => { | ||
this.keyboard.removeCommands(commands); | ||
}); | ||
} | ||
} |
Oops, something went wrong.