11import { Controller } from "@hotwired/stimulus" ;
22
33export default class extends Controller {
4- static targets = [ "level1Container" , "level1Link" , "highLevels" ] ;
4+ static targets = [ "level1Container" , "level1Link" , "highLevels" , "leftNav" ] ;
55
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.
78 connect ( ) {
89 let nav = document . getElementsByClassName ( "doc-leftnav" ) ;
910 if ( nav . length > 0 ) {
1011 let position = nav [ 0 ] . getAttribute ( "data-scroll" ) ;
1112 nav [ 0 ] . scrollTop = position ;
1213 }
14+
15+ this . callback = ( ) => {
16+ this . setNavToLocation ( ) ;
17+ } ;
18+
19+ document . addEventListener ( "turbo:load" , this . callback ) ;
1320 }
1421
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+ }
2038
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']" ,
2342 ) ;
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+
2463 if ( aria === "true" ) {
25- bsCollapse . hide ( ) ;
26- e . currentTarget . setAttribute ( "aria-expanded" , "false" ) ;
64+ this . collapse ( event . currentTarget ) ;
2765 } 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+ ) ;
2879 bsCollapse . show ( ) ;
29- e . currentTarget . setAttribute ( "aria-expanded" , "true" ) ;
80+ element . setAttribute ( "aria-expanded" , "true" ) ;
3081 }
3182 }
3283
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 ) {
35100 this . removeAllActive ( ) ;
36101
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+ ) ;
39107
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 ) ;
41116
42117 this . preventScrollOnNav ( ) ;
43118 }
44119
45- // Activly manage nav state for high level links
46- onNavigateManageHighLevels ( e ) {
120+ // Actively manage nav state for level 1 links
121+ setLevel1LeftNav ( element ) {
47122 this . removeAllActive ( ) ;
48123
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" ) ;
52128
53- link . classList . add ( "active" ) ;
129+ this . expandSubmenuIfExists ( container ) ;
54130
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+ }
56139
140+ // Actions to take when nav link is clicked
141+ // currently just gets the scroll position before state change
142+ onNavigateManageHighLevels ( ) {
57143 this . preventScrollOnNav ( ) ;
58144 }
59145
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
61147 // to updating the page so after we can set the scroll position back to where it was
62148 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 ) ;
67152 }
68153 }
69154
@@ -81,4 +166,8 @@ export default class extends Controller {
81166 this . level1LinkTargets [ i ] . classList . remove ( "active" ) ;
82167 }
83168 }
169+
170+ disconnect ( ) {
171+ document . removeEventListener ( "turbo:load" , this . callback ) ;
172+ }
84173}
0 commit comments