-
Notifications
You must be signed in to change notification settings - Fork 5
/
menu.js
75 lines (65 loc) · 2 KB
/
menu.js
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
customElements.define(
"tree-menu",
class extends HTMLElement {
constructor() {
super();
const ul = document.createElement("ul");
this.prepend(ul);
this.loadMenu(ul);
}
render(menu, ul, url) {
const fragment = document.createDocumentFragment();
const baseUrl = new URL(
this.dataset.base ?? "/",
document.location.origin,
);
for (const { data, children, slug } of menu) {
let li = document.createElement("li");
fragment.appendChild(li);
if (children) {
li.innerHTML = `<details><summary></summary></details>`;
li = li.querySelector("summary");
}
if (data.url) {
const a = document.createElement("a");
const href = new URL(`.${data.url}`, baseUrl).pathname;
a.href = href;
a.textContent = data.title || slug;
if (href === url) {
a.setAttribute("aria-current", "page");
}
li.appendChild(a);
} else {
li.innerHTML = `<strong>${data.title || slug}</strong>`;
}
if (children) {
const ul = document.createElement("ul");
li.parentElement.appendChild(ul);
this.render(children, ul, url);
}
}
ul.appendChild(fragment);
}
async loadMenu(ul) {
const url = this.dataset.url;
const response = await fetch(url);
const data = await response.json();
this.render(data, ul, decodeURIComponentSafe(location.pathname));
const current = ul.querySelector("[aria-current]");
if (current) {
let parent = current.parentElement;
while (parent) {
if (parent.tagName === "DETAILS") {
parent.open = true;
parent = parent.parentElement.parentElement;
} else {
parent = parent.parentElement;
}
}
}
}
},
);
function decodeURIComponentSafe(path) {
return decodeURIComponent(path.replace(/%(?![0-9a-fA-F]+)/g, "%25"));
}