1
1
import { Controller } from "@hotwired/stimulus" ;
2
2
3
3
export default class extends Controller {
4
- static targets = [ "level1Container" , "level1Link" , "highLevels" ] ;
4
+ static targets = [ "level1Container" , "level1Link" , "highLevels" , "leftNav" ] ;
5
5
6
- // After page update we reset scroll position of nave back to where it was
6
+ // After page update we reset scroll position of nav back to where it
7
+ // was and ensure left nave and window location match.
7
8
connect ( ) {
8
9
let nav = document . getElementsByClassName ( "doc-leftnav" ) ;
9
10
if ( nav . length > 0 ) {
10
11
let position = nav [ 0 ] . getAttribute ( "data-scroll" ) ;
11
12
nav [ 0 ] . scrollTop = position ;
12
13
}
14
+
15
+ this . callback = ( ) => {
16
+ this . setNavToLocation ( ) ;
17
+ } ;
18
+
19
+ document . addEventListener ( "turbo:load" , this . callback ) ;
13
20
}
14
21
15
- // trubo-frame permanent breakes bootstrap data attribute collapse for aria
16
- // so we manually controll collapse
17
- expand ( e ) {
18
- let aria = e . currentTarget . getAttribute ( "aria-expanded" ) ;
19
- let id = e . currentTarget . getAttribute ( "aria-controls" ) ;
22
+ // The active tags should always be set to the current page location
23
+ setNavToLocation ( ) {
24
+ const tag = "a[href='" + window . location . pathname + "']" ;
25
+
26
+ let link = this . element . querySelectorAll ( tag ) ;
27
+ if ( link . length > 0 ) {
28
+ if (
29
+ link [ 0 ] . getAttribute ( "data-navigation-left-nav-docs-target" ) ==
30
+ "highLevels"
31
+ ) {
32
+ this . setHighLevelLeftNav ( link [ 0 ] ) ;
33
+ } else {
34
+ this . setLevel1LeftNav ( link [ 0 ] ) ;
35
+ }
36
+ }
37
+ }
20
38
21
- let bsCollapse = bootstrap . Collapse . getOrCreateInstance (
22
- document . getElementById ( id ) ,
39
+ expandSubmenuIfExists ( containerEl ) {
40
+ const controllerEl = containerEl . querySelector (
41
+ "[data-action='click->navigation-left-nav-docs#toggle']" ,
23
42
) ;
43
+ controllerEl ? this . expand ( controllerEl ) : null ;
44
+ }
45
+
46
+ // Finds all parent submenus this element is in and expands them. Takes
47
+ // the element containing the current level
48
+ expandAllParents ( element ) {
49
+ let level = element . getAttribute ( "data-level" ) ;
50
+
51
+ this . expandSubmenuIfExists ( element ) ;
52
+ if ( level > 1 ) {
53
+ let next = "div[data-level='" + ( parseInt ( level ) - 1 ) + "']" ;
54
+ this . expandAllParents ( element . closest ( next ) ) ;
55
+ }
56
+ }
57
+
58
+ // turbo-frame-permanent breaks bootstrap data attribute collapse for aria
59
+ // so we manually control collapse
60
+ toggle ( event ) {
61
+ let aria = event . currentTarget . getAttribute ( "aria-expanded" ) ;
62
+
24
63
if ( aria === "true" ) {
25
- bsCollapse . hide ( ) ;
26
- e . currentTarget . setAttribute ( "aria-expanded" , "false" ) ;
64
+ this . collapse ( event . currentTarget ) ;
27
65
} else {
66
+ this . expand ( event . currentTarget ) ;
67
+ }
68
+ }
69
+
70
+ // Expands the submenu, takes submenu control element.
71
+ expand ( element ) {
72
+ let id = element . getAttribute ( "aria-controls" ) ;
73
+ let aria = element . getAttribute ( "aria-expanded" ) ;
74
+
75
+ if ( aria === "false" ) {
76
+ let bsCollapse = bootstrap . Collapse . getOrCreateInstance (
77
+ document . getElementById ( id ) ,
78
+ ) ;
28
79
bsCollapse . show ( ) ;
29
- e . currentTarget . setAttribute ( "aria-expanded" , "true" ) ;
80
+ element . setAttribute ( "aria-expanded" , "true" ) ;
30
81
}
31
82
}
32
83
33
- // Activly manage nav state for level 1 links
34
- onNavigateManageLevel1 ( e ) {
84
+ // Collapses the submenu, takes submenu control element.
85
+ collapse ( element ) {
86
+ let id = element . getAttribute ( "aria-controls" ) ;
87
+ let aria = element . getAttribute ( "aria-expanded" ) ;
88
+
89
+ if ( aria === "true" ) {
90
+ let bsCollapse = bootstrap . Collapse . getOrCreateInstance (
91
+ document . getElementById ( id ) ,
92
+ ) ;
93
+ bsCollapse . hide ( ) ;
94
+ element . setAttribute ( "aria-expanded" , "false" ) ;
95
+ }
96
+ }
97
+
98
+ // Actively manage nav state for high level links.
99
+ setHighLevelLeftNav ( element ) {
35
100
this . removeAllActive ( ) ;
36
101
37
- let container = e . currentTarget . closest ( "div" ) ;
38
- container . classList . add ( "active" ) ;
102
+ const parentContainer = element . closest ( 'div[data-level="1"]' ) ;
103
+ const parentMenu = parentContainer . querySelector ( ".menu-item" ) ;
104
+ const parentLink = parentMenu . querySelector (
105
+ ".doc-left-nav-level1-link-container" ,
106
+ ) ;
39
107
40
- e . currentTarget . classList . add ( "active" ) ;
108
+ parentLink . classList . add ( "active" ) ;
109
+ element . classList . add ( "purple" ) ;
110
+
111
+ const container = element . parentElement ;
112
+ this . expandSubmenuIfExists ( container ) ;
113
+
114
+ const levelEl = container . closest ( "div[data-level]" ) ;
115
+ this . expandAllParents ( levelEl ) ;
41
116
42
117
this . preventScrollOnNav ( ) ;
43
118
}
44
119
45
- // Activly manage nav state for high level links
46
- onNavigateManageHighLevels ( e ) {
120
+ // Actively manage nav state for level 1 links
121
+ setLevel1LeftNav ( element ) {
47
122
this . removeAllActive ( ) ;
48
123
49
- let container = e . currentTarget . closest ( 'div[data-level="1"]' ) ;
50
- let menu = container . querySelector ( ".menu-item" ) ;
51
- let link = menu . querySelector ( ".doc-left-nav-level1-link-container" ) ;
124
+ const container = element . closest ( "div" ) ;
125
+ container . classList . add ( "active" ) ;
126
+
127
+ element . classList . add ( "active" ) ;
52
128
53
- link . classList . add ( "active" ) ;
129
+ this . expandSubmenuIfExists ( container ) ;
54
130
55
- e . currentTarget . classList . add ( "purple" ) ;
131
+ this . preventScrollOnNav ( ) ;
132
+ }
133
+
134
+ // Actions to take when nav link is clicked
135
+ // currently just gets the scroll position before state change
136
+ onNavigateManageLevel1 ( ) {
137
+ this . preventScrollOnNav ( ) ;
138
+ }
56
139
140
+ // Actions to take when nav link is clicked
141
+ // currently just gets the scroll position before state change
142
+ onNavigateManageHighLevels ( ) {
57
143
this . preventScrollOnNav ( ) ;
58
144
}
59
145
60
- // trubo -frame permanent scrolles nav to top on navigation so we capture the scrroll position prior
146
+ // turbo -frame permanent scrolls nav to top on navigation so we capture the scroll position prior
61
147
// to updating the page so after we can set the scroll position back to where it was
62
148
preventScrollOnNav ( ) {
63
- let nav = document . getElementsByClassName ( "doc-leftnav" ) ;
64
- if ( nav . length > 0 ) {
65
- let position = nav [ 0 ] . scrollTop ;
66
- nav [ 0 ] . setAttribute ( "data-scroll" , position ) ;
149
+ if ( this . hasLeftNavTarget ) {
150
+ let position = this . leftNavTarget . scrollTop ;
151
+ this . leftNavTarget . setAttribute ( "data-scroll" , position ) ;
67
152
}
68
153
}
69
154
@@ -81,4 +166,8 @@ export default class extends Controller {
81
166
this . level1LinkTargets [ i ] . classList . remove ( "active" ) ;
82
167
}
83
168
}
169
+
170
+ disconnect ( ) {
171
+ document . removeEventListener ( "turbo:load" , this . callback ) ;
172
+ }
84
173
}
0 commit comments