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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: fc42c14c4aab7798c8153757249be9c4fface8724b422471b15328148947c462
4
- data.tar.gz: 86980573ee2acb25dcb635a107f790b65fed14b13c2703ca5a5931ebf5c52d42
3
+ metadata.gz: 6ae67716c57e2a7d14b83575192dbb948f5aee51a653a3ec07a79fa54ec1bd54
4
+ data.tar.gz: b995d27caef6905dc0b8030d16097438ac1883118097f222d57f7a033d715c85
5
5
  SHA512:
6
- metadata.gz: 26f10ccefbd2422364eea1f1ca80cf81883b20067e3f33a8edae10a824aae1c2fed58a6cb521ceaf77402c50e91f7c2b878394411f8dc4802a8f68e1b80db1f4
7
- data.tar.gz: 883b263a816346b45b7ee7b03b04910aed1cdacaccf9ef14e2830e1627391f1ed9b2afe91b6fc7dd01bd7376d221538b4fc60c71129ebf295dd83ee877c04812
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 (!modalClick && !panelClick) {
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().modal && Event.key.isPressed("tab") && !Event.key.shift) {
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
- if (direction == 'forward') {
99
- // If the previous focused element was in the stack
100
- //if (toolbox.childOf(stack.lastFocus(), stack.root)) {
101
-
102
- // focus on the first input
103
- //var firstItem = el.querySelector('input:not([hidden]), textarea, select, a[tabindex]')
104
- //if (firstItem) firstItem.focus()
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 (el) {
217
+ function showEl(el) {
218
+ el.removeAttribute('hidden')
219
219
  el.setAttribute('aria-hidden', false)
220
220
  }
221
221
 
222
- function isEmpty (el) {
222
+ function isEmpty(el) {
223
223
  return el.childElementCount == 0
224
224
  }
225
225
 
226
- function hideEl (el) {
226
+ function hideEl(el) {
227
227
  el.setAttribute('aria-hidden', true)
228
+ el.setAttribute('hidden', true)
228
229
  }
229
230
 
230
- function isElement (item) {
231
+ function isElement(item) {
231
232
  return item.constructor.toString().search(/HTML.+Element/) > -1
232
233
  }
233
234
 
234
- function navClick (event) {
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)) stack.show(panel)
243
- else return
243
+ else if (stack.findPanel(panel)) {
244
+ stack.show(panel, true)
245
+ }
246
+ }
244
247
 
245
- //event.preventDefault()
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.on(document, 'click', '[data-stack="root"] [data-stack-nav]', navClick)
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
- var target = event.currentTarget,
28
- parent = target.parentElement,
29
- expanded = parent.getAttribute('aria-expanded')
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.setAttribute('aria-expanded', expanded != 'true')
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 && data[node] == 'true') nodeEl.setAttribute('aria-expanded', data[node])
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', '[aria-expanded] > *:first-child', 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