forked from ember-a11y/ember-a11y
/
focusing-outlet.js
91 lines (71 loc) · 2.84 KB
/
focusing-outlet.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
import Ember from "ember";
import getOwner from 'ember-getowner-polyfill';
const { get } = Ember;
let scrollLeft = 0;
let scrollTop = 0;
let handler = function(e) {
window.scrollTo(scrollLeft, scrollTop);
window.removeEventListener('scroll', handler);
};
let FocusingOutlet = Ember.Component.extend({
positionalParams: ['inputOutletName'], // needed for Ember 1.13.[0-5] and 2.0.0-beta.[1-3] support
tagName: 'div',
classNames: ['focusing-outlet'],
shouldFocus: false,
didReceiveAttrs() {
this._super(...arguments);
this.set('outletName', this.attrs.inputOutletName || 'main');
},
didInsertElement() {
this._super(...arguments);
this.setFocus();
},
setFocus() {
if (!this.element) { return; }
let shouldFocus = this.get('shouldFocus');
if (shouldFocus) {
// One shouldn't set an attribute when they mean to set a property.
// Except when a property is only settable if the attribute is present.
// We have to make the element interactive prior to focusing it.
this.element.setAttribute('tabindex', '-1');
this.element.setAttribute('role', 'group');
// If we don't do this, the scroll triggered by the focus will be unfortunate.
// This effectively swallows one scroll event.
// TODO: Investigate setting focus to something inside of overflow: auto;
scrollLeft = document.body.scrollLeft;
scrollTop = document.body.scrollTop;
window.addEventListener('scroll', handler);
// Set the focus to the target outlet wrapper.
Ember.run.schedule('afterRender', this, function() { this.element.focus(); });
} else {
this.element.removeAttribute('tabindex');
this.element.removeAttribute('role');
}
},
actions: {
checkFocus(outletState) {
let application = getOwner(this).lookup('application:main');
let pivotHandler = application.get('_stashedHandlerInfos.pivotHandler.handler.routeName');
let outletName = this.get('outletName');
let currentRoute = get(outletState, `${outletName}.render.name`);
if (!currentRoute) {
return;
}
let handled = application.get('_stashedHandlerInfos.pivotHandler.handled');
let isFirstVisit = pivotHandler === undefined;
let isPivot = (pivotHandler === currentRoute);
let isChildState = ~['loading', 'error'].indexOf(currentRoute.split('.').pop());
let isSubstate = ~currentRoute.indexOf('_loading') || ~currentRoute.indexOf('_error');
let shouldFocus = !handled && !isFirstVisit && (isPivot || isChildState || isSubstate);
this.set('shouldFocus', shouldFocus);
if (pivotHandler) {
application.set('_stashedHandlerInfos.pivotHandler.handled', handled || shouldFocus);
}
this.setFocus();
}
}
});
FocusingOutlet.reopenClass({
positionalParams: ['inputOutletName']
});
export default FocusingOutlet;