1 | 'use strict';
|
2 |
|
3 |
|
4 | import $ from 'jquery';
|
5 | import { Keyboard } from './foundation.util.keyboard';
|
6 | import { Nest } from './foundation.util.nest';
|
7 | import { GetYoDigits } from './foundation.util.core';
|
8 | import { Plugin } from './foundation.plugin';
|
9 |
|
10 |
|
11 |
|
12 |
|
13 |
|
14 |
|
15 |
|
16 |
|
17 | class AccordionMenu extends Plugin {
|
18 | |
19 |
|
20 |
|
21 |
|
22 |
|
23 |
|
24 |
|
25 |
|
26 | _setup(element, options) {
|
27 | this.$element = element;
|
28 | this.options = $.extend({}, AccordionMenu.defaults, this.$element.data(), options);
|
29 | this.className = 'AccordionMenu';
|
30 |
|
31 | Nest.Feather(this.$element, 'accordion');
|
32 |
|
33 | this._init();
|
34 |
|
35 | Keyboard.register('AccordionMenu', {
|
36 | 'ENTER': 'toggle',
|
37 | 'SPACE': 'toggle',
|
38 | 'ARROW_RIGHT': 'open',
|
39 | 'ARROW_UP': 'up',
|
40 | 'ARROW_DOWN': 'down',
|
41 | 'ARROW_LEFT': 'close',
|
42 | 'ESCAPE': 'closeAll'
|
43 | });
|
44 | }
|
45 |
|
46 |
|
47 |
|
48 | |
49 |
|
50 |
|
51 |
|
52 | _init() {
|
53 | var _this = this;
|
54 |
|
55 | this.$element.find('[data-submenu]').not('.is-active').slideUp(0);
|
56 | this.$element.attr({
|
57 | 'role': 'tree',
|
58 | 'aria-multiselectable': this.options.multiOpen
|
59 | });
|
60 |
|
61 | this.$menuLinks = this.$element.find('.is-accordion-submenu-parent');
|
62 | this.$menuLinks.each(function(){
|
63 | var linkId = this.id || GetYoDigits(6, 'acc-menu-link'),
|
64 | $elem = $(this),
|
65 | $sub = $elem.children('[data-submenu]'),
|
66 | subId = $sub[0].id || GetYoDigits(6, 'acc-menu'),
|
67 | isActive = $sub.hasClass('is-active');
|
68 |
|
69 |
|
70 | if(_this.options.submenuToggle) {
|
71 | $elem.addClass('has-submenu-toggle');
|
72 | $elem.children('a').after('<button id="' + linkId + '" class="submenu-toggle" aria-controls="' + subId + '" aria-expanded="' + isActive + '" title="' + _this.options.submenuToggleText + '"><span class="submenu-toggle-text">' + _this.options.submenuToggleText + '</span></button>');
|
73 | } else {
|
74 | $elem.attr({
|
75 | 'aria-controls': subId,
|
76 | 'aria-expanded': isActive,
|
77 | 'id': linkId
|
78 | });
|
79 | }
|
80 | $sub.attr({
|
81 | 'aria-labelledby': linkId,
|
82 | 'aria-hidden': !isActive,
|
83 | 'role': 'group',
|
84 | 'id': subId
|
85 | });
|
86 | });
|
87 | this.$element.find('li').attr({
|
88 | 'role': 'treeitem'
|
89 | });
|
90 | var initPanes = this.$element.find('.is-active');
|
91 | if(initPanes.length){
|
92 | var _this = this;
|
93 | initPanes.each(function(){
|
94 | _this.down($(this));
|
95 | });
|
96 | }
|
97 | this._events();
|
98 | }
|
99 |
|
100 | |
101 |
|
102 |
|
103 |
|
104 | _events() {
|
105 | var _this = this;
|
106 |
|
107 | this.$element.find('li').each(function() {
|
108 | var $submenu = $(this).children('[data-submenu]');
|
109 |
|
110 | if ($submenu.length) {
|
111 | if(_this.options.submenuToggle) {
|
112 | $(this).children('.submenu-toggle').off('click.zf.accordionMenu').on('click.zf.accordionMenu', function(e) {
|
113 | _this.toggle($submenu);
|
114 | });
|
115 | } else {
|
116 | $(this).children('a').off('click.zf.accordionMenu').on('click.zf.accordionMenu', function(e) {
|
117 | e.preventDefault();
|
118 | _this.toggle($submenu);
|
119 | });
|
120 | }
|
121 | }
|
122 | }).on('keydown.zf.accordionmenu', function(e){
|
123 | var $element = $(this),
|
124 | $elements = $element.parent('ul').children('li'),
|
125 | $prevElement,
|
126 | $nextElement,
|
127 | $target = $element.children('[data-submenu]');
|
128 |
|
129 | $elements.each(function(i) {
|
130 | if ($(this).is($element)) {
|
131 | $prevElement = $elements.eq(Math.max(0, i-1)).find('a').first();
|
132 | $nextElement = $elements.eq(Math.min(i+1, $elements.length-1)).find('a').first();
|
133 |
|
134 | if ($(this).children('[data-submenu]:visible').length) {
|
135 | $nextElement = $element.find('li:first-child').find('a').first();
|
136 | }
|
137 | if ($(this).is(':first-child')) {
|
138 | $prevElement = $element.parents('li').first().find('a').first();
|
139 | } else if ($prevElement.parents('li').first().children('[data-submenu]:visible').length) {
|
140 | $prevElement = $prevElement.parents('li').find('li:last-child').find('a').first();
|
141 | }
|
142 | if ($(this).is(':last-child')) {
|
143 | $nextElement = $element.parents('li').first().next('li').find('a').first();
|
144 | }
|
145 |
|
146 | return;
|
147 | }
|
148 | });
|
149 |
|
150 | Keyboard.handleKey(e, 'AccordionMenu', {
|
151 | open: function() {
|
152 | if ($target.is(':hidden')) {
|
153 | _this.down($target);
|
154 | $target.find('li').first().find('a').first().focus();
|
155 | }
|
156 | },
|
157 | close: function() {
|
158 | if ($target.length && !$target.is(':hidden')) {
|
159 | _this.up($target);
|
160 | } else if ($element.parent('[data-submenu]').length) {
|
161 | _this.up($element.parent('[data-submenu]'));
|
162 | $element.parents('li').first().find('a').first().focus();
|
163 | }
|
164 | },
|
165 | up: function() {
|
166 | $prevElement.focus();
|
167 | return true;
|
168 | },
|
169 | down: function() {
|
170 | $nextElement.focus();
|
171 | return true;
|
172 | },
|
173 | toggle: function() {
|
174 | if (_this.options.submenuToggle) {
|
175 | return false;
|
176 | }
|
177 | if ($element.children('[data-submenu]').length) {
|
178 | _this.toggle($element.children('[data-submenu]'));
|
179 | return true;
|
180 | }
|
181 | },
|
182 | closeAll: function() {
|
183 | _this.hideAll();
|
184 | },
|
185 | handled: function(preventDefault) {
|
186 | if (preventDefault) {
|
187 | e.preventDefault();
|
188 | }
|
189 | e.stopImmediatePropagation();
|
190 | }
|
191 | });
|
192 | });
|
193 | }
|
194 |
|
195 | |
196 |
|
197 |
|
198 |
|
199 | hideAll() {
|
200 | this.up(this.$element.find('[data-submenu]'));
|
201 | }
|
202 |
|
203 | |
204 |
|
205 |
|
206 |
|
207 | showAll() {
|
208 | this.down(this.$element.find('[data-submenu]'));
|
209 | }
|
210 |
|
211 | |
212 |
|
213 |
|
214 |
|
215 |
|
216 | toggle($target){
|
217 | if(!$target.is(':animated')) {
|
218 | if (!$target.is(':hidden')) {
|
219 | this.up($target);
|
220 | }
|
221 | else {
|
222 | this.down($target);
|
223 | }
|
224 | }
|
225 | }
|
226 |
|
227 | |
228 |
|
229 |
|
230 |
|
231 |
|
232 | down($target) {
|
233 | var _this = this;
|
234 |
|
235 | if(!this.options.multiOpen) {
|
236 | this.up(this.$element.find('.is-active').not($target.parentsUntil(this.$element).add($target)));
|
237 | }
|
238 |
|
239 | $target.addClass('is-active').attr({'aria-hidden': false});
|
240 |
|
241 | if(this.options.submenuToggle) {
|
242 | $target.prev('.submenu-toggle').attr({'aria-expanded': true});
|
243 | }
|
244 | else {
|
245 | $target.parent('.is-accordion-submenu-parent').attr({'aria-expanded': true});
|
246 | }
|
247 |
|
248 | $target.slideDown(_this.options.slideSpeed, function () {
|
249 | |
250 |
|
251 |
|
252 |
|
253 | _this.$element.trigger('down.zf.accordionMenu', [$target]);
|
254 | });
|
255 | }
|
256 |
|
257 | |
258 |
|
259 |
|
260 |
|
261 |
|
262 | up($target) {
|
263 | var _this = this;
|
264 | $target.slideUp(_this.options.slideSpeed, function () {
|
265 | |
266 |
|
267 |
|
268 |
|
269 | _this.$element.trigger('up.zf.accordionMenu', [$target]);
|
270 | });
|
271 |
|
272 | var $menus = $target.find('[data-submenu]').slideUp(0).addBack().attr('aria-hidden', true);
|
273 |
|
274 | if(this.options.submenuToggle) {
|
275 | $menus.prev('.submenu-toggle').attr('aria-expanded', false);
|
276 | }
|
277 | else {
|
278 | $menus.parent('.is-accordion-submenu-parent').attr('aria-expanded', false);
|
279 | }
|
280 | }
|
281 |
|
282 | |
283 |
|
284 |
|
285 |
|
286 | _destroy() {
|
287 | this.$element.find('[data-submenu]').slideDown(0).css('display', '');
|
288 | this.$element.find('a').off('click.zf.accordionMenu');
|
289 |
|
290 | if(this.options.submenuToggle) {
|
291 | this.$element.find('.has-submenu-toggle').removeClass('has-submenu-toggle');
|
292 | this.$element.find('.submenu-toggle').remove();
|
293 | }
|
294 |
|
295 | Nest.Burn(this.$element, 'accordion');
|
296 | }
|
297 | }
|
298 |
|
299 | AccordionMenu.defaults = {
|
300 | |
301 |
|
302 |
|
303 |
|
304 |
|
305 |
|
306 | slideSpeed: 250,
|
307 | |
308 |
|
309 |
|
310 |
|
311 |
|
312 | submenuToggle: false,
|
313 | |
314 |
|
315 |
|
316 |
|
317 |
|
318 | submenuToggleText: 'Toggle menu',
|
319 | |
320 |
|
321 |
|
322 |
|
323 |
|
324 |
|
325 | multiOpen: true
|
326 | };
|
327 |
|
328 | export {AccordionMenu};
|