1
1
import target from '../../utils/target'
2
- import { setAttr , addClass , removeClass } from '../../utils/dom'
3
-
4
- // Are we client side?
5
- const inBrowser = typeof window !== 'undefined'
2
+ import { setAttr , removeAttr , addClass , removeClass } from '../../utils/dom'
3
+ import { inBrowser } from '../../utils/env'
6
4
7
5
// target listen types
8
6
const listenTypes = { click : true }
9
7
10
8
// Property key for handler storage
11
9
const BVT = '__BV_toggle__'
10
+ const BVT_STATE = '__BV_toggle_STATE__'
11
+ const BVT_CONTROLS = '__BV_toggle_CONTROLS__'
12
12
13
13
// Emitted Control Event for collapse (emitted to collapse)
14
14
const EVENT_TOGGLE = 'bv::toggle::collapse'
15
15
16
16
// Listen to Event for toggle state update (Emited by collapse)
17
17
const EVENT_STATE = 'bv::collapse::state'
18
18
19
+ /* istanbul ignore next */
20
+ const handleUpdate = ( el , binding , vnode ) => {
21
+ // Ensure the collapse class and aria-* attributes persist
22
+ // after element is updated (eitehr by parent re-rendering
23
+ // or changes to this element or it's contents.
24
+ if ( inBrowser ) {
25
+ if ( el [ BVT_STATE ] === true ) {
26
+ addClass ( el , 'collapsed' )
27
+ setAttr ( el , 'aria-expanded' , 'true' )
28
+ } else if ( el [ BVT_STATE ] === false ) {
29
+ removeClass ( el , 'collapsed' )
30
+ setAttr ( el , 'aria-expanded' , 'false' )
31
+ }
32
+ setAttr ( el , 'aria-controls' , el [ BVT_CONTROLS ] )
33
+ }
34
+ }
35
+
19
36
export default {
20
37
bind ( el , binding , vnode ) {
21
38
const targets = target ( vnode , binding , listenTypes , ( { targets, vnode } ) => {
@@ -26,7 +43,10 @@ export default {
26
43
27
44
if ( inBrowser && vnode . context && targets . length > 0 ) {
28
45
// Add aria attributes to element
29
- setAttr ( el , 'aria-controls' , targets . join ( ' ' ) )
46
+ el [ BVT_CONTROLS ] = targets . join ( ' ' )
47
+ // state is initialy collapsed until we receive a state event
48
+ el [ BVT_STATE ] = false
49
+ setAttr ( el , 'aria-controls' , el [ BVT_CONTROLS ] )
30
50
setAttr ( el , 'aria-expanded' , 'false' )
31
51
if ( el . tagName !== 'BUTTON' ) {
32
52
// If element is not a button, we add `role="button"` for accessibility
@@ -39,6 +59,7 @@ export default {
39
59
// Set aria-expanded state
40
60
setAttr ( el , 'aria-expanded' , state ? 'true' : 'false' )
41
61
// Set/Clear 'collapsed' class state
62
+ el [ BVT_STATE ] = state
42
63
if ( state ) {
43
64
removeClass ( el , 'collapsed' )
44
65
} else {
@@ -51,11 +72,18 @@ export default {
51
72
vnode . context . $root . $on ( EVENT_STATE , el [ BVT ] )
52
73
}
53
74
} ,
75
+ componentUpdated : handleUpdate ,
76
+ updated : handleUpdate ,
54
77
unbind ( el , binding , vnode ) /* istanbul ignore next */ {
55
78
if ( el [ BVT ] ) {
56
79
// Remove our $root listener
57
80
vnode . context . $root . $off ( EVENT_STATE , el [ BVT ] )
58
81
el [ BVT ] = null
82
+ el [ BVT_STATE ] = null
83
+ el [ BVT_CONTROLS ] = null
84
+ removeClass ( el , 'collapsed' )
85
+ removeAttr ( el , 'aria-expanded' )
86
+ removeAttr ( el , 'aria-controls' )
59
87
}
60
88
}
61
89
}
0 commit comments