-
-
Notifications
You must be signed in to change notification settings - Fork 20
/
FocusTrap.ts
129 lines (118 loc) · 3.46 KB
/
FocusTrap.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
import Vue, { PropType, ComponentOptions } from 'vue'
// import Component from 'vue-class-component'
import { createFocusTrap, FocusTrap as FocusTrapI } from 'focus-trap'
interface FocusTrapComponentProps {
active: boolean
escapeDeactivates: boolean
returnFocusOnDeactivate: boolean
allowOutsideClick: boolean | ((e: MouseEvent) => boolean)
clickOutsideDeactivates: boolean
initialFocus: string | (() => any)
fallbackFocus: string | (() => any)
}
interface FocusTrapComponentsMethods {}
interface FocusTrapComponent
extends Vue,
ComponentOptions<never, {}, FocusTrapComponentsMethods, {}, {}> {
trap: FocusTrapI
active: FocusTrapComponentProps['active']
escapeDeactivates: FocusTrapComponentProps['escapeDeactivates']
returnFocusOnDeactivate: FocusTrapComponentProps['returnFocusOnDeactivate']
allowOutsideClick: FocusTrapComponentProps['allowOutsideClick']
clickOutsideDeactivates: FocusTrapComponentProps['clickOutsideDeactivates']
initialFocus: FocusTrapComponentProps['initialFocus']
fallbackFocus: FocusTrapComponentProps['fallbackFocus']
}
// @ts-ignore
const FocusTrap: FocusTrapComponent = {
// @ts-ignore
props: {
active: {
// TODO: could be options for activate
type: Boolean,
default: true,
},
escapeDeactivates: {
type: Boolean,
default: true,
},
returnFocusOnDeactivate: {
type: Boolean,
default: true,
},
allowOutsideClick: {
type: [Boolean, Function] as PropType<FocusTrapComponentProps['allowOutsideClick']>,
default: true,
},
clickOutsideDeactivates: {
type: Boolean,
default: false,
},
initialFocus: [String, Function],
fallbackFocus: [String, Function],
},
model: {
event: 'update:active',
prop: 'active',
},
mounted() {
this.$watch(
'active',
(active: boolean) => {
if (active) {
// has no effect if already activated
this.trap = createFocusTrap(
// @ts-ignore
this.$el,
{
escapeDeactivates: this.escapeDeactivates,
allowOutsideClick: (e: MouseEvent) => (
typeof this.allowOutsideClick === 'function'
? this.allowOutsideClick(e)
: this.allowOutsideClick
),
clickOutsideDeactivates: this.clickOutsideDeactivates,
returnFocusOnDeactivate: this.returnFocusOnDeactivate,
onActivate: () => {
this.$emit('update:active', true)
this.$emit('activate')
},
onDeactivate: () => {
this.$emit('update:active', false)
this.$emit('deactivate')
},
initialFocus: this.initialFocus || (() => this.$el),
fallbackFocus: this.fallbackFocus,
}
)
this.trap.activate()
} else {
this.trap && this.trap.deactivate()
}
},
{ immediate: true }
)
},
beforeDestroy() {
this.trap && this.trap.deactivate()
// @ts-ignore
this.trap = null
},
methods: {
activate() {
// @ts-ignore
this.trap.activate()
},
deactivate() {
// @ts-ignore
this.trap.deactivate()
},
},
render() {
const content = this.$slots.default
if (!content || !content.length || content.length > 1)
throw new Error('FocusTrap requires exactly one child')
return content[0]
},
}
export default FocusTrap