Skip to content

Commit 65b898d

Browse files
Dan docs left nav manage state w js update (#1417)
1 parent 2b83a11 commit 65b898d

File tree

4 files changed

+138
-37
lines changed

4 files changed

+138
-37
lines changed

pgml-dashboard/src/components/cms/index_link/mod.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ pub struct IndexLink {
1212
pub open: bool,
1313
pub active: bool,
1414
pub level: i32,
15+
pub id_suffix: String,
1516
}
1617

1718
impl IndexLink {
@@ -25,6 +26,7 @@ impl IndexLink {
2526
open: false,
2627
active: false,
2728
level,
29+
id_suffix: "".to_owned(),
2830
}
2931
}
3032

@@ -70,4 +72,12 @@ impl IndexLink {
7072
}
7173
self
7274
}
75+
76+
// Adds a suffix to this and all children ids.
77+
// this prevents id collision with multiple naves on one screen
78+
// like d-none for mobile nav
79+
pub fn id_suffix(mut self, id_suffix: &str) -> IndexLink {
80+
self.id_suffix = id_suffix.to_owned();
81+
self
82+
}
7383
}

pgml-dashboard/src/components/cms/index_link/template.html

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@
4646
<span class="text-wrap"><%- title %></span>
4747
</a>
4848
<div class="pt-2">
49-
<span class="material-symbols-outlined rotate-on-aria-expanded text-white" href="#doc-<%= id %>" role="button" aria-expanded="<%- aria %>" aria-controls="doc-<%= id %>" data-action="click->navigation-left-nav-docs#expand">expand_more</span>
49+
<span class="material-symbols-outlined rotate-on-aria-expanded text-white" href="#doc-<%= id %><%- id_suffix %>" role="button" aria-expanded="<%- aria %>" aria-controls="doc-<%= id %><%- id_suffix %>" data-action="click->navigation-left-nav-docs#toggle">expand_more</span>
5050
</div>
5151
</div>
5252
</div>
@@ -56,15 +56,16 @@
5656
<span class="text-wrap"><%- title %></span>
5757
</a>
5858
<div class="pt-2">
59-
<span class="material-symbols-outlined rotate-on-aria-expanded" href="#doc-<%= id %>" role="button" aria-expanded="<%- aria %>" aria-controls="doc-<%= id %>" data-action="click->navigation-left-nav-docs#expand">expand_more</span>
59+
<span class="material-symbols-outlined rotate-on-aria-expanded" href="#doc-<%= id %><%- id_suffix %>" role="button" aria-expanded="<%- aria %>" aria-controls="doc-<%= id %><%- id_suffix %>" data-action="click->navigation-left-nav-docs#toggle">expand_more</span>
6060
</div>
6161
</span>
6262
<% } %>
6363

64-
<div class="collapse <%- show %>" id="doc-<%= id %>">
64+
<div class="collapse <%- show %>" id="doc-<%= id %><%- id_suffix %>">
6565
<div class='nav flex-column level-<%- level %>-list' role="tablist" aria-orentation="vertical">
6666
<% for child in children.into_iter() { %>
67-
<%- child.render_once().unwrap() %>
67+
<% let child = child.id_suffix(&id_suffix); %>
68+
<%- child.render_once().unwrap() %>
6869
<% } %>
6970
</div>
7071
</div>
Lines changed: 118 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,69 +1,154 @@
11
import { Controller } from "@hotwired/stimulus";
22

33
export 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
}

pgml-dashboard/src/components/navigation/left_nav/docs/template.html

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,8 @@
2424
%>
2525

2626
<% if !mobile { %>
27-
<div class="doc-leftnav-container" id="cart-counter" data-controller="navigation-left-nav-docs" data-turbo-permanent>
28-
<nav class="doc-leftnav" data-scroll="0">
27+
<div class="doc-leftnav-container" id="doc-leftnav-container" data-controller="navigation-left-nav-docs" data-turbo-permanent>
28+
<nav class="doc-leftnav" data-scroll="0" data-navigation-left-nav-docs-target="leftNav">
2929
<div class="d-flex flex-column justify-content-between">
3030
<div class="d-xl-flex flex-column py-4">
3131
<div class="pt-2 ps-2 d-flex flex-column gap-4_5">
@@ -52,7 +52,7 @@
5252
<nav class="navbar px-0">
5353
<div class="card nav guides rounded-0 w-100">
5454
<div class="card-body py-2 py-xl-4">
55-
<a class="my-1 d-flex justify-content-between align-items-center text-white" role="button" data-bs-toggle="collapse" href="#guides" aria-expanded="false" aria-congrols="guides">
55+
<a class="my-1 d-flex justify-content-between align-items-center text-white" role="button" data-bs-toggle="collapse" href="#guides" aria-expanded="false" aria-controls="guides">
5656
<span>Docs</span><span class="material-symbols-outlined rotate-on-aria-expanded">expand_more</span>
5757
</a>
5858
<div class="collapse border-top pt-2" id="guides">
@@ -61,9 +61,10 @@
6161
<%+ doc_link %>
6262
<% } else { %>
6363
<div class="d-flex flex-column pt-2">
64-
<%- title(doc_link.title) %>
64+
<%- title(doc_link.title.to_uppercase()) %>
6565

6666
<% for item in doc_link.children {%>
67+
<% let item = item.id_suffix("mobile"); %>
6768
<%+ item %>
6869
<% } %>
6970
</div>

0 commit comments

Comments
 (0)