Skip to content

Commit 189fc5e

Browse files
committed
Add FocusLoop component
1 parent 3e2e885 commit 189fc5e

File tree

1 file changed

+66
-0
lines changed

1 file changed

+66
-0
lines changed

src/FocusLoop.vue

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
<template>
2+
<div class="vue-focus-loop">
3+
<div
4+
class="vue-focus-loop__start"
5+
:tabindex="getTabindex"
6+
@focus="handleFocusStart"
7+
/>
8+
<div ref="focusLoop">
9+
<slot />
10+
</div>
11+
<div
12+
class="vue-focus-loop__end"
13+
:tabindex="getTabindex"
14+
@focus="handleFocusEnd"
15+
/>
16+
</div>
17+
</template>
18+
19+
<script>
20+
const focusableElementsSelector = `[tabindex]:not([tabindex^="-"]), a[href], audio[controls], ${['input', 'select', 'button', 'textarea'].map(field => `${field}:not([disabled])`).join(', ')}`
21+
22+
export default {
23+
name: 'FocusLoop',
24+
25+
props: {
26+
disabled: {
27+
type: Boolean,
28+
default: false
29+
}
30+
},
31+
32+
data () {
33+
return {
34+
alreadyFocused: false
35+
}
36+
},
37+
38+
computed: {
39+
getTabindex () {
40+
return this.disabled ? -1 : 0
41+
}
42+
},
43+
44+
methods: {
45+
getFocusableElements () {
46+
const focusableElements = this.$refs.focusLoop.querySelectorAll(focusableElementsSelector)
47+
if (focusableElements && focusableElements.length) return focusableElements
48+
return []
49+
},
50+
51+
handleFocusStart (e) {
52+
const elements = this.getFocusableElements()
53+
if (elements.length) {
54+
const index = this.alreadyFocused ? elements.length - 1 : 0
55+
this.alreadyFocused = true
56+
elements[index].focus()
57+
}
58+
},
59+
60+
handleFocusEnd () {
61+
const elements = this.getFocusableElements()
62+
elements.length && elements[0].focus()
63+
}
64+
}
65+
}
66+
</script>

0 commit comments

Comments
 (0)