ticketfly / ember-keyboard Goto Github PK
View Code? Open in Web Editor NEWAn Ember.js addon for the painless support of keyboard events
License: MIT License
An Ember.js addon for the painless support of keyboard events
License: MIT License
Event triggers will be formatted <eventType>:<modifierKey>+<additionalModifierKey>+<key>
, eg keyUp:ctrl+shift+a
or keyDown:b
:
respondToA: on('keyUp:a', function() {
console.log('a was pressed');
});
Keys will use the mapping of the excellent ember-keyboard-service
. This will allow us to observe events from the arrow keys with listeners like keyUp:ArrowDown
.
Events are only triggered on the currently focused component. If the currently focused component does not have a listener registered for that event, it will optionally bubble up through parent views until it successfully triggers an event or terminates at the top level.
This will allow us to have contextual key responses, such as pressing the down arrow to navigate a ul
, as well as app-wide key responders, such as the s
key taking you to the search bar or Escape
opening the account menu dropdown.
UPDATE:
tldr, this approach is too invasive.
Until we get routable components, the top level view is still that, a view. That means to support this, we'll need to use the deprecated application view. In turn, our consuming app will have to enable legacy views. This can be done by adding _ENABLE_LEGACY_VIEW_SUPPORT: true
to EmberENV
.
Additionally, this will require us to reopen
the View and Component class to inject observers that will alert ember-keyboard
when the component gains and loses focus. This will still be the case, even with routable components.
Let's follow the lead of ember-key-responder
and leave it up to the app developer to decide when to activate and deactivate an ember-keyboard
component. They'll be able to activate a component's key responders with this.get('keyboard').activate();
and correspondingly deactivate it with this.get('keyboard').deactivate();
. When active, the component will try to respond to any keyboard event. If it doesn't have any listeners for that event, then it passes it on the next component on the stack.
Example:
In reports-web we might want to focus on the search-bar whenever the user presses s
. (Invaluable for someone using a screen-reader, though also helpful for power-users in general. Github does this well.) To achieve this, we activate it and add a listener:
// components/search-bar/component.js
export default Ember.Component.extend({
keyboard: Ember.inject.service(),
activateKeyboard: Ember.on('init', function() {
this.get('keyboard').activate();
}),
focusOnKeypress: Ember.on('keyUp:s', function() {
this.$().focus();
})
});
Now let's say we want this to coexist with a dropdown menu that navigates with the UpArrow
and DownArrow
keys. Its responders are activated whenever the dropdown is opened and deactivated whenever the dropdown is closed. While active, this component is the first to respond to key events, giving it a chance to handle UpArrow
and DownArrow
. If it can't handle the event, then it passes the event down the stack to the next active component, which in this case is the search-bar. If the user had pressed s
, then the search-bar triggers its focusOnKeypress
. Otherwise, it passes the event even further down the stack until it finds a responder or terminates at the bottom.
Considerations:
I added in the service and the event listeners as described in the README, but can't seem to get across this error. Any ideas what may be happening?
Cannot read property 'toString' of undefined
in keyboard.js:18
Here is my component file:
import Ember from 'ember';
export default Ember.Component.extend({
keyboard: Ember.inject.service(),
tagName: 'div',
classNames: ['invoice'],
activateKeyboard: Ember.on('didInsertElement', function() {
this.get('keyboard').activate(this);
}),
aFunction: Ember.on('keyDown:a', function() {
console.log('`a` was pressed');
}),
anotherFunction: Ember.on('keyDown:ctrl+shift+a', function() {
console.log('`ctrl+shift+a` was pressed');
}),
});
Trawling the eventStack
after every keyUp
and keyDown
could get expensive. If only we could offset that cost by putting it on another thread. . . .
Web workers to the rescue!
. . . . for IE10+, and everyone else.
In most cases, we will not want our keyboard responders to trigger while in a textarea or input field. However, in other cases a developer might want to opt-in, such as with a text editor that italicizes with keyUp:ctrl+i
. Perhaps we should allow key responders on components with tagName: 'input'
or tagName: 'textarea'
, but these key responders will by default be bubble: false
.
In all other cases, if the focus is in an input field, then the event listeners should not fire.
While creating the test app, I noticed there was a lot of common code for activating/deactivating component keyboard responders. Would be helpful to have mixins for the most common patterns.
Currently, developers define their key listeners directly with Ember.on
, which creates a difficult situation. What if one developer orders their modifier keys like keyUp:ctrl+alt+d
while another does keyUp:alt+ctrl+d
. Since ember-keyboard
doesn't know what order the modifier keys are defined in, it must look for all possible orders. With the above scenario, this could require as many as six iterations for each responder in the eventStack
. If there are dozens of responders, each keystroke could become computationally expensive.
As an alternative, what if we created listener 'macros' that could be imported directly off ember-keyboard
. Developers could declare their listener modifier keys in any order, and it would then be the macro's responsibility to sort them alphabetically and then call Ember.on
on that string. We would then only have to check each responder once to see if it has a listener with the alphabetized modifier keys.
This would lead to markup like this:
import Ember from 'ember';
import { keyUp, keyDown } from 'ember-keyboard';
export default Ember.Componet.extend({
aShift: keyUp('a+shift', function() {
console.log('a was pressed');
}),
shiftB: keyUp('shift+b', function() {
console.log('b was pressed');
})
});
And it would also lead to more performant code.
We might want to give developers the ability to specify a component's priority. For instance, the developer might want their modal-menu
component to always respond before their sidebar-menu
component. Perhaps they could assign priority during activation: this.get('keyboard').activate({ priority: 3 });
.
We might want to give developers the ability to opt-out of bubbling up the event stack. For instance, while the modal-menu
component is active the developer might not want events to get passed on to the lower priority sidebar-menu
. This too could be passed in as an option: this.get('keyboard').activate({ bubble: false });
.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.