fleetio_spark 0.2.27 → 0.2.28
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/app/assets/javascripts/spark/_menu.js +74 -0
- data/app/assets/javascripts/spark/_modal.js +37 -65
- data/app/assets/javascripts/spark/_search.js +12 -0
- data/app/assets/javascripts/spark/_stack.js +36 -19
- data/app/assets/javascripts/spark/components/nav/_tree.js +112 -8
- data/app/assets/javascripts/spark/spark.js +2 -0
- data/app/assets/stylesheets/spark/components/app/_header.scss +11 -2
- data/app/assets/stylesheets/spark/components/nav/_toggle.scss +1 -1
- data/app/helpers/spark/nav_menu_helper.rb +1 -1
- data/lib/fleetio_spark/version.rb +1 -1
- data/public/code-0.2.27.js +17648 -2
- data/public/code-0.2.28.js +2 -0
- data/public/{code-0.2.27.js.gz → code-0.2.28.js.gz} +0 -0
- data/public/{code-0.2.27.js.map → code-0.2.28.js.map} +1 -1
- data/public/spark-0.2.27.js +4591 -2
- data/public/{spark-0.2.27.css → spark-0.2.28.css} +1 -1
- data/public/{spark-0.2.27.css.gz → spark-0.2.28.css.gz} +0 -0
- data/public/spark-0.2.28.js +2 -0
- data/public/spark-0.2.28.js.gz +0 -0
- data/public/spark-0.2.28.js.map +1 -0
- metadata +11 -8
- data/public/spark-0.2.27.js.gz +0 -0
- data/public/spark-0.2.27.js.map +0 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 6ae67716c57e2a7d14b83575192dbb948f5aee51a653a3ec07a79fa54ec1bd54
|
4
|
+
data.tar.gz: b995d27caef6905dc0b8030d16097438ac1883118097f222d57f7a033d715c85
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b593cd1e85c41b3ee0f4fa67154036c93419cc5e2c4d59fcdbbef22da2220c68738e665c94dd1b261b713b0186e6cd792d5f2970b5c57f3376464c356488cc2d
|
7
|
+
data.tar.gz: 655489cb5b0716f7fb5debc574cb6a0efb816972dbc7e5a3b0b76621d08611c2284a9289fb505594b6200194ecb5d5b7657f1ce02ef3c5520ddc26130e6a1b6a
|
@@ -0,0 +1,74 @@
|
|
1
|
+
var toolbox = require('@spark-engine/toolbox'),
|
2
|
+
Event = require('@spark-engine/event')
|
3
|
+
|
4
|
+
function navigateMenu(event) {
|
5
|
+
var link,
|
6
|
+
activeEl = document.activeElement
|
7
|
+
|
8
|
+
if(Event.key.isPressed('down')) {
|
9
|
+
if (activeEl.tagName.match(/select|button/i) || activeEl.closest('[aria-hidden="true"]')) { return }
|
10
|
+
// Prevent page scrolling as a result of keypress
|
11
|
+
event.preventDefault()
|
12
|
+
event.stopImmediatePropagation()
|
13
|
+
link = getNextLink()
|
14
|
+
|
15
|
+
} else if(Event.key.isPressed('up')) {
|
16
|
+
// Prevent page scrolling as a result of keypress
|
17
|
+
if (activeEl.tagName.match(/select|button/i)) { return }
|
18
|
+
event.preventDefault()
|
19
|
+
event.stopImmediatePropagation()
|
20
|
+
link = getPreviousLink()
|
21
|
+
} else {
|
22
|
+
link = false
|
23
|
+
}
|
24
|
+
|
25
|
+
// Only focus if there is a next or previous link to move to
|
26
|
+
// At top or bottom of a list there is no "next" or "previous"
|
27
|
+
if (link) link.focus()
|
28
|
+
|
29
|
+
}
|
30
|
+
|
31
|
+
// Find the next focusable link
|
32
|
+
function getNextLink() {
|
33
|
+
var items = links(),
|
34
|
+
pos = items.indexOf(document.activeElement)
|
35
|
+
|
36
|
+
// No active element: return first element
|
37
|
+
if (pos == -1) return items[ 0 ]
|
38
|
+
|
39
|
+
// Last element focused, return nothing (no next element)
|
40
|
+
else if (pos == items.length) return
|
41
|
+
|
42
|
+
// return next focusable link
|
43
|
+
else return items[ pos + 1 ]
|
44
|
+
}
|
45
|
+
|
46
|
+
// Find the previous focusable link
|
47
|
+
function getPreviousLink (el) {
|
48
|
+
var items = links(),
|
49
|
+
pos = items.indexOf(document.activeElement)
|
50
|
+
|
51
|
+
// No previous element to select
|
52
|
+
if (pos == -1 || pos == 0) return
|
53
|
+
|
54
|
+
// return previous focusable link
|
55
|
+
else return items[ pos - 1 ]
|
56
|
+
}
|
57
|
+
|
58
|
+
// Return all links in current modal
|
59
|
+
function links() {
|
60
|
+
var menu = document.activeElement.closest('[role="menu"]')
|
61
|
+
return Array.prototype.filter.call(menu.querySelectorAll('a'), function(a) {
|
62
|
+
return a.offsetParent != null
|
63
|
+
})
|
64
|
+
}
|
65
|
+
|
66
|
+
function setup () {
|
67
|
+
Event.ready(function() {
|
68
|
+
Event.on(document, 'keydown', 'nav[role="menu"]', navigateMenu)
|
69
|
+
})
|
70
|
+
}
|
71
|
+
|
72
|
+
module.exports = {
|
73
|
+
setup: setup
|
74
|
+
}
|
@@ -12,7 +12,7 @@ var style = '<style id="modal-toggle-style">body[data-active-modal] * { p
|
|
12
12
|
|
13
13
|
// types of modals
|
14
14
|
// Menu - trigger toggles, clicking outside closes
|
15
|
-
// Panel - trigger opens, but does not close, clicking outside closes
|
15
|
+
// Panel - trigger opens, but does not close, clicking outside (or esc) closes
|
16
16
|
// Dialog - trigger opens, clicking outside is ignored, must be closed by clicking a close button or the escape key
|
17
17
|
|
18
18
|
|
@@ -50,6 +50,7 @@ var Modal = {
|
|
50
50
|
el.dataset.modalId = modal.id
|
51
51
|
// Hide modal by default
|
52
52
|
el.classList.add('hidden')
|
53
|
+
el.setAttribute('hidden', true)
|
53
54
|
el.setAttribute('aria-hidden', true)
|
54
55
|
el.setAttribute('aria-modal', true)
|
55
56
|
|
@@ -78,6 +79,7 @@ var Modal = {
|
|
78
79
|
document.body.classList.add('modal-active')
|
79
80
|
document.body.dataset.modal = el.id || ''
|
80
81
|
|
82
|
+
el.removeAttribute('hidden')
|
81
83
|
el.classList.remove('hidden')
|
82
84
|
el.setAttribute('aria-hidden', false)
|
83
85
|
|
@@ -118,6 +120,7 @@ var Modal = {
|
|
118
120
|
|
119
121
|
el.classList.remove('modal-is-closing')
|
120
122
|
el.classList.add('hidden')
|
123
|
+
el.setAttribute('hidden', true)
|
121
124
|
el.setAttribute('aria-hidden', true)
|
122
125
|
|
123
126
|
// Set state on modal triggers
|
@@ -184,12 +187,8 @@ function open (modal) {
|
|
184
187
|
function activeClick (event) {
|
185
188
|
var modal = activeModal()
|
186
189
|
|
187
|
-
// Did the click happen in side the modal, or in a modal safe control?
|
188
|
-
var modalClick = event.target.closest('[data-modal-id="'+modal.id+'"]')
|
189
|
-
var panelClick = modal.panel && event.target.closest('[data-for-modal="'+modal.id+'"]')
|
190
|
-
|
191
190
|
// Stop the click if it happened outside the modal or panel control
|
192
|
-
if (!
|
191
|
+
if (!isWithinModal(event.target)) {
|
193
192
|
event.stopImmediatePropagation()
|
194
193
|
event.preventDefault()
|
195
194
|
|
@@ -197,6 +196,27 @@ function activeClick (event) {
|
|
197
196
|
}
|
198
197
|
}
|
199
198
|
|
199
|
+
function focused(event) {
|
200
|
+
var modal = activeModal()
|
201
|
+
|
202
|
+
// If a modal panel, close when focus occurs outside.
|
203
|
+
if (document.activeElement && !isWithinModal(document.activeElement)) {
|
204
|
+
event.stopImmediatePropagation()
|
205
|
+
if (modal.panel) modal.close()
|
206
|
+
else event.preventDefault()
|
207
|
+
}
|
208
|
+
}
|
209
|
+
|
210
|
+
function isWithinModal(el) {
|
211
|
+
var modal = activeModal(),
|
212
|
+
// Did the click happen in side the modal
|
213
|
+
modalClick = el.closest('[data-modal-id="'+modal.id+'"]'),
|
214
|
+
// Or in a modal safe control?
|
215
|
+
panelClick = modal.panel && !!el.closest('[data-for-modal="'+modal.id+'"]')
|
216
|
+
|
217
|
+
return modalClick || panelClick
|
218
|
+
}
|
219
|
+
|
200
220
|
// Use the stack position to determine the current modal
|
201
221
|
function activeModal () {
|
202
222
|
return Stack[ Stack.length - 1 ]
|
@@ -276,7 +296,7 @@ function triggerEvent (event) {
|
|
276
296
|
if (el.tagName == 'INPUT') {
|
277
297
|
|
278
298
|
// Don't open when input is focused
|
279
|
-
//if (event.type == 'focusin' && el.value == '') return
|
299
|
+
// if (event.type == 'focusin' && el.value == '') return
|
280
300
|
|
281
301
|
// Do not trigger open if input event clears input value
|
282
302
|
if (event.type == 'input' && el.value == '') return
|
@@ -304,11 +324,11 @@ function keyPress (event) {
|
|
304
324
|
return close()
|
305
325
|
}
|
306
326
|
|
307
|
-
if (activeModal()
|
327
|
+
if (activeModal() && Event.key.isPressed("tab") && !Event.key.shift) {
|
308
328
|
focusNext(event)
|
309
329
|
}
|
310
330
|
|
311
|
-
|
331
|
+
/*
|
312
332
|
if (activeModal().panel || activeModal().menu) {
|
313
333
|
var link
|
314
334
|
|
@@ -331,70 +351,20 @@ function keyPress (event) {
|
|
331
351
|
// At top or bottom of a list there is no "next" or "previous"
|
332
352
|
if (link) link.focus()
|
333
353
|
|
334
|
-
}
|
354
|
+
}*/
|
335
355
|
}
|
336
356
|
|
337
357
|
function focusNext(event) {
|
338
358
|
var modalEl = activeModal().el,
|
339
|
-
focusable = modalEl.querySelectorAll('input:not([type=hidden]), textarea, select, button'),
|
359
|
+
focusable = modalEl.querySelectorAll('input:not([type=hidden]), textarea, select, button, [tabindex]:not([tabindex="-1"])'),
|
360
|
+
focusable = Array.prototype.filter.call(focusable, function(el) {
|
361
|
+
return !el.closest('[hidden], [aria-hidden="true"]')
|
362
|
+
}),
|
340
363
|
first = focusable[0],
|
341
364
|
last = focusable[focusable.length - 1],
|
342
365
|
focused = document.activeElement
|
343
366
|
|
344
367
|
// If focused on the last focusable element, tab to the first element.
|
345
|
-
if (focused == last && !Event.key.shift) {
|
346
|
-
if (event){ event.preventDefault() }
|
347
|
-
first.focus()
|
348
|
-
}
|
349
|
-
|
350
|
-
if (focused == first && Event.key.shift) {
|
351
|
-
if (event){ event.preventDefault() }
|
352
|
-
last.focus()
|
353
|
-
}
|
354
|
-
|
355
|
-
// Focus on the first element if the focused element is not a child of the modal
|
356
|
-
if (!focused || !toolbox.childOf(focused, modalEl)) {
|
357
|
-
first.focus()
|
358
|
-
}
|
359
|
-
}
|
360
|
-
|
361
|
-
// Find the next focusable link
|
362
|
-
function getNextLink (el) {
|
363
|
-
var items = links()
|
364
|
-
var pos = activePosition()
|
365
|
-
|
366
|
-
// No active element: return first element
|
367
|
-
if (pos == -1) return items[ 0 ]
|
368
|
-
|
369
|
-
// Last element focused, return nothing (no next element)
|
370
|
-
else if (pos == items.length) return
|
371
|
-
|
372
|
-
// return next focusable link
|
373
|
-
else return items[ pos + 1 ]
|
374
|
-
}
|
375
|
-
|
376
|
-
// Find the previous focusable link
|
377
|
-
function getPreviousLink (el) {
|
378
|
-
var items = links()
|
379
|
-
var pos = activePosition()
|
380
|
-
|
381
|
-
// No previous element to select
|
382
|
-
if (pos == -1 || pos == 0) return
|
383
|
-
|
384
|
-
// return previous focusable link
|
385
|
-
else return items[ pos - 1 ]
|
386
|
-
}
|
387
|
-
|
388
|
-
// Search links in the current modal for an active (focused) element
|
389
|
-
function activePosition () {
|
390
|
-
return links().indexOf(document.activeElement)
|
391
|
-
}
|
392
|
-
|
393
|
-
// Return all links in current modal
|
394
|
-
function links () {
|
395
|
-
return toolbox.slice(activeModal().el.querySelectorAll('a')).filter(function(a) {
|
396
|
-
return !a.closest('[aria-hidden="true"]')
|
397
|
-
})
|
398
368
|
}
|
399
369
|
|
400
370
|
function setup () {
|
@@ -403,6 +373,9 @@ function setup () {
|
|
403
373
|
|
404
374
|
// Check all body clicks to see if they should be permitted
|
405
375
|
Event.on(document, 'click', 'body.modal-active', activeClick, { useCapture: true })
|
376
|
+
|
377
|
+
// Focus outisde of modal (if a panel) should close modal
|
378
|
+
Event.on(document, 'focus', '.modal-active', focused, { useCapture: true })
|
406
379
|
|
407
380
|
// Trigger modals on input events (like a search results panel that waits until typing to show)
|
408
381
|
Event.on(document, 'input', 'input[data-for-modal], [data-for-modal] input', Event.debounce(triggerEvent, 50, { leading: true }))
|
@@ -419,7 +392,6 @@ function setup () {
|
|
419
392
|
|
420
393
|
module.exports = {
|
421
394
|
new: Modal.new,
|
422
|
-
links: links,
|
423
395
|
setup: setup,
|
424
396
|
modals: Modals,
|
425
397
|
init: init,
|
@@ -12,6 +12,12 @@ Event.keyOn('/', function(event){
|
|
12
12
|
}
|
13
13
|
})
|
14
14
|
|
15
|
+
function autoClose(event) {
|
16
|
+
if (!document.activeElement.closest('#search_modal')) {
|
17
|
+
modal.get('#search-results').close()
|
18
|
+
}
|
19
|
+
}
|
20
|
+
|
15
21
|
function setup() {
|
16
22
|
var q = document.querySelector('[name=q]'),
|
17
23
|
filter = document.querySelector('#search_model')
|
@@ -35,6 +41,10 @@ function setup() {
|
|
35
41
|
} else if (focusEl == q && Event.key.isPressed("esc")) {
|
36
42
|
q.value = ''
|
37
43
|
Event.fire(q, 'input')
|
44
|
+
} else if (Event.key.isPressed("down")) {
|
45
|
+
if (document.activeElement && document.activeElement.tagName.match(/select|button/i)) { return }
|
46
|
+
var result = searchStack.currentPanel().querySelector('[role=menu] a')
|
47
|
+
if (result) result.focus()
|
38
48
|
}
|
39
49
|
}
|
40
50
|
|
@@ -56,6 +66,7 @@ function setup() {
|
|
56
66
|
|
57
67
|
searchModal.on('open', function() {
|
58
68
|
Event.on(document, 'keydown', keyWatcher)
|
69
|
+
Event.on(document, 'blur', autoClose)
|
59
70
|
})
|
60
71
|
|
61
72
|
// Reset stack when modal is closed and query is clear
|
@@ -69,6 +80,7 @@ function setup() {
|
|
69
80
|
searchPanel.classList.remove('search-in-progress')
|
70
81
|
|
71
82
|
Event.off(document, 'keydown', keyWatcher)
|
83
|
+
Event.off(document, 'blur', autoClose)
|
72
84
|
})
|
73
85
|
}
|
74
86
|
|
@@ -95,17 +95,16 @@ var Stack = {
|
|
95
95
|
|
96
96
|
stack.updateWatchers()
|
97
97
|
|
98
|
-
|
99
|
-
|
100
|
-
//if (toolbox.childOf(
|
101
|
-
|
102
|
-
//
|
103
|
-
|
104
|
-
|
105
|
-
|
98
|
+
// When going forward remove focus from elements in the previous panel
|
99
|
+
//if (direction == 'forward') {
|
100
|
+
//if (toolbox.childOf(document.activeElement, stack.root)) {
|
101
|
+
//document.activeElement.blur()
|
102
|
+
//if (navigated) {
|
103
|
+
//var navEl = el.querySelector('[data-stack-nav]')
|
104
|
+
//if (navEl) navEl.focus()
|
105
|
+
//}
|
106
106
|
//}
|
107
|
-
|
108
|
-
}
|
107
|
+
//}
|
109
108
|
|
110
109
|
// When reversing, focus on the previously focused element
|
111
110
|
if (direction == 'reverse' && stack.focus.length > 0) {
|
@@ -215,23 +214,25 @@ var Stack = {
|
|
215
214
|
}
|
216
215
|
}
|
217
216
|
|
218
|
-
function showEl
|
217
|
+
function showEl(el) {
|
218
|
+
el.removeAttribute('hidden')
|
219
219
|
el.setAttribute('aria-hidden', false)
|
220
220
|
}
|
221
221
|
|
222
|
-
function isEmpty
|
222
|
+
function isEmpty(el) {
|
223
223
|
return el.childElementCount == 0
|
224
224
|
}
|
225
225
|
|
226
|
-
function hideEl
|
226
|
+
function hideEl(el) {
|
227
227
|
el.setAttribute('aria-hidden', true)
|
228
|
+
el.setAttribute('hidden', true)
|
228
229
|
}
|
229
230
|
|
230
|
-
function isElement
|
231
|
+
function isElement(item) {
|
231
232
|
return item.constructor.toString().search(/HTML.+Element/) > -1
|
232
233
|
}
|
233
234
|
|
234
|
-
function navClick
|
235
|
+
function navClick(event) {
|
235
236
|
var el = event.currentTarget
|
236
237
|
var panel = el.dataset.stackNav
|
237
238
|
var stack = getStack(el.closest('[data-stack="root"]'))
|
@@ -239,10 +240,23 @@ function navClick (event) {
|
|
239
240
|
|
240
241
|
if (panel == 'next') stack.next()
|
241
242
|
else if (panel == 'back') stack.back()
|
242
|
-
else if (stack.findPanel(panel))
|
243
|
-
|
243
|
+
else if (stack.findPanel(panel)) {
|
244
|
+
stack.show(panel, true)
|
245
|
+
}
|
246
|
+
}
|
244
247
|
|
245
|
-
|
248
|
+
function navKey(event) {
|
249
|
+
var panel = event.currentTarget.closest('[data-stack="root"]').querySelector('[data-stack-index][aria-hidden="false"]')
|
250
|
+
if (!panel) { return }
|
251
|
+
|
252
|
+
if (Event.key.isPressed("left")) {
|
253
|
+
var back = panel.querySelector('[data-stack-nav].nav-back')
|
254
|
+
if (back) Event.fire(back, 'click')
|
255
|
+
}
|
256
|
+
if (Event.key.isPressed("right")) {
|
257
|
+
var next = panel.querySelector('[data-stack-nav].nav-next')
|
258
|
+
if (next) Event.fire(next, 'click')
|
259
|
+
}
|
246
260
|
}
|
247
261
|
|
248
262
|
function prevPanel (event) {
|
@@ -258,7 +272,10 @@ function setup () {
|
|
258
272
|
|
259
273
|
// Localise clicks to a stack root
|
260
274
|
// Future: consider expanding this to allow controls to live anywhere and point to a stack
|
261
|
-
Event.
|
275
|
+
Event.ready(function() {
|
276
|
+
Event.on(document, 'click', '[data-stack="root"] [data-stack-nav]', navClick)
|
277
|
+
Event.on(document, 'keydown', '[data-stack-index]', navKey)
|
278
|
+
})
|
262
279
|
}
|
263
280
|
|
264
281
|
function getStack (search) {
|
@@ -24,18 +24,27 @@ function setCookie (node) {
|
|
24
24
|
}
|
25
25
|
|
26
26
|
function click (event) {
|
27
|
-
|
28
|
-
|
29
|
-
|
27
|
+
toggleNode(event.currentTarget)
|
28
|
+
}
|
29
|
+
|
30
|
+
function toggleNode(target) {
|
31
|
+
parent = target.parentElement,
|
32
|
+
expanded = parent.getAttribute('aria-expanded')
|
30
33
|
|
31
34
|
// Add a classname to indicate that it was manually triggered
|
32
35
|
// This allows for animating interactions, but not other attribute changes
|
33
36
|
parent.classList.add('triggered')
|
34
|
-
parent
|
37
|
+
toggleExpansion(parent, expanded != 'true')
|
38
|
+
select(target)
|
35
39
|
|
36
40
|
setCookie(parent)
|
37
41
|
}
|
38
42
|
|
43
|
+
function toggleExpansion(el, expanded) {
|
44
|
+
el.setAttribute('aria-expanded', expanded)
|
45
|
+
setupItemTabIndex(el)
|
46
|
+
}
|
47
|
+
|
39
48
|
// Sets tree nav state based off of cookies
|
40
49
|
function restoreNav () {
|
41
50
|
toolbox.each(document.querySelectorAll('[data-tree]'), function(tree) {
|
@@ -44,22 +53,117 @@ function restoreNav () {
|
|
44
53
|
var data = cookie.get(key)
|
45
54
|
|
46
55
|
Object.keys(data).forEach(function(node) {
|
47
|
-
var nodeEl = tree.querySelector('[data-node='+node+']')
|
56
|
+
var nodeEl = tree.querySelector('[data-node='+node+']'),
|
57
|
+
expanded = data[node] == 'true'
|
48
58
|
// Use cookies to expand.
|
49
|
-
if (nodeEl
|
59
|
+
if (nodeEl) {
|
60
|
+
nodeEl.setAttribute('aria-expanded', expanded)
|
61
|
+
toggleExpansion(nodeEl, expanded)
|
62
|
+
}
|
50
63
|
})
|
51
64
|
}
|
52
65
|
})
|
53
66
|
}
|
54
67
|
|
68
|
+
function navigateTree(event) {
|
69
|
+
var item = document.activeElement
|
70
|
+
if (!item) return
|
71
|
+
|
72
|
+
var tree = item.closest('[role="tree"]')
|
73
|
+
if (!tree) return
|
74
|
+
|
75
|
+
var trigger = item.matches('.tree-trigger')
|
76
|
+
|
77
|
+
if (trigger) {
|
78
|
+
var expanded = item.parentElement.getAttribute('aria-expanded') != 'false'
|
79
|
+
|
80
|
+
if (Event.key.isPressed('space')) {
|
81
|
+
toggleNode(item)
|
82
|
+
event.preventDefault()
|
83
|
+
}
|
84
|
+
|
85
|
+
if (Event.key.isPressed('right')) {
|
86
|
+
event.preventDefault()
|
87
|
+
if (expanded) return select(getNextItem(tree, item))
|
88
|
+
else return toggleNode(item)
|
89
|
+
}
|
90
|
+
|
91
|
+
if (Event.key.isPressed('left')) {
|
92
|
+
event.preventDefault()
|
93
|
+
if (expanded) return toggleNode(item)
|
94
|
+
else return select(getUpwardItem(tree, item))
|
95
|
+
}
|
96
|
+
}
|
97
|
+
|
98
|
+
if (Event.key.isPressed('down')) {
|
99
|
+
event.preventDefault()
|
100
|
+
select(getNextItem(tree, item))
|
101
|
+
}
|
102
|
+
|
103
|
+
if (Event.key.isPressed('left')) {
|
104
|
+
event.preventDefault()
|
105
|
+
select(getUpwardItem(tree, item))
|
106
|
+
}
|
107
|
+
|
108
|
+
else if (Event.key.isPressed('up')) {
|
109
|
+
event.preventDefault()
|
110
|
+
select(getPreviousItem(tree, item))
|
111
|
+
}
|
112
|
+
}
|
113
|
+
|
114
|
+
function select(el) {
|
115
|
+
if (!el) return
|
116
|
+
el.focus()
|
117
|
+
}
|
118
|
+
|
119
|
+
function getNextItem(tree, item) {
|
120
|
+
items = getSelectableItems(tree)
|
121
|
+
return items[ items.indexOf(item) + 1]
|
122
|
+
}
|
123
|
+
|
124
|
+
function getPreviousItem(tree, item) {
|
125
|
+
items = getSelectableItems(tree)
|
126
|
+
return items[ items.indexOf(item) - 1]
|
127
|
+
}
|
128
|
+
|
129
|
+
function getUpwardItem(tree, item) {
|
130
|
+
var upward = item.parentElement.closest('[aria-expanded]')
|
131
|
+
if (upward) return upward.querySelector('.tree-trigger')
|
132
|
+
}
|
133
|
+
|
134
|
+
function getSelectableItems(tree) {
|
135
|
+
return Array.prototype.filter.call(tree.querySelectorAll('[tabindex]'), function(el) {
|
136
|
+
return !el.closest('[aria-expanded="false"] [role="group"]')
|
137
|
+
})
|
138
|
+
}
|
139
|
+
|
140
|
+
// Remove hidden items from the tabindex
|
141
|
+
function setupItemTabIndex(tree) {
|
142
|
+
toolbox.each(tree.querySelectorAll('a'), function(el) {
|
143
|
+
if (el.closest('[aria-expanded="false"] [role="group"]')) {
|
144
|
+
el.setAttribute('tabindex', -1)
|
145
|
+
} else {
|
146
|
+
el.setAttribute('tabindex', 0)
|
147
|
+
}
|
148
|
+
})
|
149
|
+
}
|
150
|
+
|
151
|
+
|
55
152
|
function setup() {
|
56
153
|
Event.ready(function() {
|
57
154
|
|
58
155
|
// The first child of a node is the trigger
|
59
|
-
Event.on(document, 'click', '
|
156
|
+
Event.on(document, 'click', '.tree-trigger', click)
|
157
|
+
Event.on(document, 'keydown', '[role="tree"]', navigateTree)
|
60
158
|
|
61
159
|
// Set states according to cookies
|
62
|
-
//restoreNav()
|
160
|
+
// restoreNav()
|
161
|
+
})
|
162
|
+
|
163
|
+
Event.change(function() {
|
164
|
+
toolbox.each(document.querySelectorAll('[role="tree"]'), function(el) {
|
165
|
+
setupItemTabIndex(el)
|
166
|
+
})
|
63
167
|
})
|
64
168
|
}
|
65
169
|
|