Skip to content

Commit 88da9f8

Browse files
authored
Merge branch 'master' into es-blog-heading-angle-bracket
2 parents 3c14420 + 69009fb commit 88da9f8

40 files changed

+761
-386
lines changed

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,3 +22,5 @@ package-lock.json
2222
/.node_modules.ember-try/
2323
/bower.json.ember-try
2424
/package.json.ember-try
25+
26+
.vscode

CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,10 @@
11

2+
v2.5.0 / 2018-12-06
3+
==================
4+
5+
* Using ember-fontawesome #104 from @prasannavijayan
6+
* Add CLI Guides to the default navbar links #115 from @jenweber
7+
28
v2.4.0 / 2018-11-08
39
==================
410

addon/components/es-navbar/component.js

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,28 @@ import defaultLinks from '../../constants/links';
66

77
export default Component.extend({
88
layout,
9+
tagName: 'nav',
10+
classNames: ['es-navbar'],
11+
ariaLabel: 'Ember',
12+
navHome: computed('home', function() {
13+
if (this.home) {
14+
return this.home;
15+
}
16+
17+
return 'https://www.emberjs.com';
18+
}),
919
navLinks: computed('links.[]', function() {
1020
if(this.links) {
1121
return this.links;
1222
}
1323

1424
return defaultLinks;
15-
})
25+
}),
26+
actions: {
27+
toggleMenu() {
28+
let menu = this.element.querySelector('ul[role="menubar"]');
29+
30+
menu.setAttribute('aria-expanded', menu.getAttribute('aria-expanded') !== 'true');
31+
}
32+
}
1633
});
Lines changed: 241 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,241 @@
1+
import Component from '@ember/component';
2+
import layout from './template';
3+
import { computed } from '@ember/object';
4+
import { equal } from '@ember/object/computed';
5+
import { inject as service } from '@ember/service';
6+
import { next } from '@ember/runloop';
7+
8+
export default Component.extend({
9+
layout,
10+
tagName: 'li',
11+
tabIndex: 0,
12+
13+
classNameBindings: ['isDropdown:dropdown'],
14+
isDropdown: equal('link.type', 'dropdown'),
15+
16+
keyCode: Object.freeze({
17+
'TAB': 9,
18+
'RETURN': 13,
19+
'ESC': 27,
20+
'SPACE': 32,
21+
'PAGEUP': 33,
22+
'PAGEDOWN': 34,
23+
'END': 35,
24+
'HOME': 36,
25+
'LEFT': 37,
26+
'UP': 38,
27+
'RIGHT': 39,
28+
'DOWN': 40
29+
}),
30+
31+
navbar: service(),
32+
33+
didInsertElement() {
34+
this.element.tabIndex = -1;
35+
36+
this.get('navbar').register(this);
37+
this.domNode = this.element.querySelector('ul[role="menu"]');
38+
39+
if(this.domNode) {
40+
this.element.querySelector('a').onmousedown = () => this.expand();
41+
let links = Array.from(this.domNode.querySelectorAll('a'))
42+
43+
links.forEach((ancor) => {
44+
ancor.addEventListener('blur', () => this.handleBlur());
45+
});
46+
}
47+
},
48+
49+
handleBlur() {
50+
next(this, function() {
51+
let subItems = Array.from(this.element.querySelectorAll('ul[role="menu"] li'));
52+
let focused = subItems.find(item => document.activeElement === item.querySelector('a'));
53+
54+
// debugger
55+
if(!focused) {
56+
this.closePopupMenu();
57+
}
58+
})
59+
},
60+
61+
openPopupMenu() {
62+
// Get position and bounding rectangle of controller object's DOM node
63+
var rect = this.element.getBoundingClientRect();
64+
65+
// Set CSS properties
66+
if(this.domNode) {
67+
this.domNode.style.display = 'block';
68+
this.domNode.style.top = rect.height + 'px';
69+
this.domNode.style.zIndex = 1000;
70+
}
71+
72+
this.set('expanded', true);
73+
},
74+
75+
closePopupMenu(force) {
76+
var controllerHasHover = this.hasHover;
77+
78+
var hasFocus = this.hasFocus;
79+
80+
if (!this.isMenubarItem) {
81+
controllerHasHover = false;
82+
}
83+
84+
if (force || (!hasFocus && !this.hasHover && !controllerHasHover)) {
85+
if(this.domNode) {
86+
this.domNode.style.display = 'none';
87+
this.domNode.style.zIndex = 0;
88+
}
89+
this.set('expanded', false);
90+
}
91+
},
92+
93+
expanded: computed({
94+
get() {
95+
return this.element.getAttribute('aria-expanded') === 'true';
96+
},
97+
set(key, value) {
98+
this.element.setAttribute('aria-expanded', value);
99+
}
100+
}).volatile(),
101+
102+
setFocusToFirstItem() {
103+
let element = this.element.querySelector('ul[role="menu"] li a')
104+
if (element) {
105+
element.focus();
106+
}
107+
},
108+
109+
setFocusToLastItem() {
110+
this.element.querySelector('ul[role="menu"] li a:last-of-type').focus();
111+
},
112+
113+
setFocusToNextItem() {
114+
let subItems = Array.from(this.element.querySelectorAll('ul[role="menu"] li'));
115+
116+
let focused = subItems.find(item => document.activeElement === item.querySelector('a'));
117+
let focusedIndex = subItems.indexOf(focused);
118+
119+
let nextItem = subItems[(focusedIndex + 1) % subItems.length];
120+
121+
if (!nextItem) {
122+
return;
123+
}
124+
125+
nextItem.querySelector('a').focus();
126+
},
127+
128+
setFocusToPreviousItem() {
129+
let subItems = Array.from(this.element.querySelectorAll('ul[role="menu"] li'));
130+
131+
let focused = subItems.find(item => document.activeElement === item.querySelector('a'));
132+
let focusedIndex = subItems.indexOf(focused);
133+
134+
let nextIndex = focusedIndex - 1;
135+
136+
if (nextIndex < 0) {
137+
nextIndex = subItems.length - 1;
138+
}
139+
140+
let nextItem = subItems[nextIndex];
141+
142+
if (!nextItem) {
143+
return;
144+
}
145+
146+
nextItem.querySelector('a').focus();
147+
},
148+
149+
keyDown(event) {
150+
let flag = false;
151+
let clickEvent;
152+
let mousedownEvent;
153+
154+
switch (event.keyCode) {
155+
case this.keyCode.RETURN:
156+
case this.keyCode.SPACE:
157+
// Create simulated mouse event to mimic the behavior of ATs
158+
// and let the event handler handleClick do the housekeeping.
159+
mousedownEvent = new MouseEvent('mousedown', {
160+
'view': window,
161+
'bubbles': true,
162+
'cancelable': true
163+
});
164+
clickEvent = new MouseEvent('click', {
165+
'view': window,
166+
'bubbles': true,
167+
'cancelable': true
168+
});
169+
170+
document.activeElement.dispatchEvent(mousedownEvent);
171+
document.activeElement.dispatchEvent(clickEvent);
172+
173+
flag = true;
174+
break;
175+
case this.keyCode.DOWN:
176+
if(this.get('expanded')) {
177+
this.setFocusToNextItem();
178+
} else {
179+
this.openPopupMenu();
180+
this.setFocusToFirstItem();
181+
}
182+
flag = true;
183+
break;
184+
185+
case this.keyCode.LEFT:
186+
this.get('navbar').setFocusToPreviousItem(this);
187+
flag = true;
188+
break;
189+
190+
case this.keyCode.RIGHT:
191+
this.get('navbar').setFocusToNextItem(this);
192+
flag = true;
193+
break;
194+
195+
case this.keyCode.UP:
196+
if(this.get('expanded')) {
197+
this.setFocusToPreviousItem();
198+
} else {
199+
this.openPopupMenu();
200+
this.setFocusToLastItem();
201+
}
202+
break;
203+
204+
case this.keyCode.HOME:
205+
case this.keyCode.PAGEUP:
206+
this.setFocusToFirstItem();
207+
flag = true;
208+
break;
209+
210+
case this.keyCode.END:
211+
case this.keyCode.PAGEDOWN:
212+
this.setFocusToLastItem();
213+
flag = true;
214+
break;
215+
216+
case this.keyCode.TAB:
217+
this.closePopupMenu(true);
218+
break;
219+
220+
case this.keyCode.ESC:
221+
this.closePopupMenu(true);
222+
break;
223+
}
224+
225+
if (flag) {
226+
event.stopPropagation();
227+
event.preventDefault();
228+
}
229+
},
230+
231+
expand() {
232+
next(this, () => {
233+
if(this.get('expanded')) {
234+
this.closePopupMenu();
235+
} else {
236+
this.openPopupMenu();
237+
this.setFocusToFirstItem();
238+
}
239+
})
240+
}
241+
});
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
{{#if (eq link.type "link")}}
2+
<a
3+
role="menuitem"
4+
aria-haspopup="true"
5+
aria-expanded="false"
6+
href={{link.href}}
7+
tabindex={{if (eq index 0) 0 -1}}
8+
>
9+
{{link.name}}
10+
</a>
11+
{{/if}}
12+
{{#if (eq link.type "dropdown")}}
13+
<a
14+
role="menuitem"
15+
aria-haspopup="true"
16+
aria-expanded="false"
17+
href="javascript:void(0)"
18+
tabindex={{if (eq index 0) 0 -1}}
19+
>
20+
{{link.name}}
21+
</a>
22+
<ul class="dropdown" role="menu" aria-label={{link.name}}>
23+
{{#each link.items as |item|}}
24+
{{#if (eq item.type "link")}}
25+
<li role="none">
26+
<a
27+
role="menuitem"
28+
href={{item.href}}
29+
tabindex="-1"
30+
>
31+
{{item.name}}
32+
</a>
33+
</li>
34+
{{/if}}
35+
{{#if (eq item.type "divider")}}
36+
<hr>
37+
{{/if}}
38+
{{/each}}
39+
</ul>
40+
{{/if}}

0 commit comments

Comments
 (0)