diff --git a/package.json b/package.json index a535362..0c87648 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@fwdekker/template", - "version": "3.3.5", + "version": "3.3.6", "description": "The base template for pages on fwdekker.com.", "author": "Florine W. Dekker", "license": "MIT", diff --git a/src/main/css/snippets/nav.css b/src/main/css/snippets/nav.css index 3d59e44..482994c 100644 --- a/src/main/css/snippets/nav.css +++ b/src/main/css/snippets/nav.css @@ -29,10 +29,6 @@ nav.fwd-nav li > :first-child { margin: 0; } -nav.fwd-nav a { - cursor: pointer; -} - nav.fwd-nav ul ul { display: none; @@ -44,7 +40,7 @@ nav.fwd-nav ul ul { box-shadow: var(--fwd-nav-box-shadow); } -nav.fwd-nav li:where(:active, :focus-within, :hover) > ul { +nav.fwd-nav li:where(:active, :focus-within, :hover, .fwd-nav-active) > ul { display: flex; flex-direction: column; align-items: start; @@ -70,15 +66,15 @@ nav.fwd-nav ul { background-color: var(--primary); } -nav.fwd-nav li.border-above { +nav.fwd-nav li.fwd-nav-separator { border-top: 1px solid #ccc; } -nav.fwd-nav li:where(:active, :focus-within, :hover) { +nav.fwd-nav li:where(:active, :focus-within, :hover, .fwd-nav-active) { background-color: var(--primary-focus-dark); } -nav.fwd-nav li.current-page:not(:where(:active, :focus-within, :hover)) { +nav.fwd-nav li.fwd-nav-highlighted:not(:where(:active, :focus-within, :hover, .fwd-nav-active)) { background-color: var(--primary-focus-opaque); } @@ -125,7 +121,7 @@ nav.fwd-nav #fwd-nav-hamburger-label { color: var(--primary-inverse); } -nav.fwd-nav #fwd-nav-hamburger-label:where(:active, :focus-within, :hover) { +nav.fwd-nav #fwd-nav-hamburger-label:where(:active, :focus-within, :hover, .fwd-nav-active) { background-color: var(--primary-focus-dark); } diff --git a/src/main/js/Template.ts b/src/main/js/Template.ts index d28bc84..846acf3 100644 --- a/src/main/js/Template.ts +++ b/src/main/js/Template.ts @@ -93,8 +93,12 @@ function nav(highlightPath?: string, cb?: (json: any) => void): HTMLElement { if (cb !== undefined) cb(json); - json.entries.forEach( - (entry: any) => base.appendChild(stringToHtml(unpackEntry(entry, "/", highlightPath))) + json.entries.forEach((it: any) => base.appendChild(unpackEntry(it, "/", highlightPath))); + + document.body.addEventListener( + "click", + () => $a("li", base).forEach(it => it.classList.remove("fwd-nav-active")), + {capture: true} ); }) .catch(error => console.error("Failed to fetch navigation elements", error)); @@ -113,38 +117,41 @@ function nav(highlightPath?: string, cb?: (json: any) => void): HTMLElement { * @param parentPath the current path traversed, found by joining the names of the entries with `/`s; always starts and * ends with a `/` * @param highlightPath the path to highlight together with its parents, or `undefined` if no path should be highlighted - * @returns the navigation list entry as HTML, described by its children + * @returns the navigation list entry */ -function unpackEntry(entry: any, parentPath: string = "/", highlightPath?: string): string { +function unpackEntry(entry: any, parentPath: string = "/", highlightPath?: string): HTMLLIElement { const path = `${parentPath + entry.name}/`; + const hasChildren = entry.entries.length !== 0; - const classList = []; - if (highlightPath?.startsWith(path) ?? false) classList.push("current-page"); - if (entry.border) classList.push("border-above"); - const classString = classList.length === 0 ? "" : `class="${classList.join(" ")}"`; + const li = document.createElement("li"); + if (highlightPath === path) li.setAttribute("aria-current", "page"); + if (highlightPath?.startsWith(path) ?? false) li.classList.add("fwd-nav-highlighted"); + if (entry.border) li.classList.add("fwd-nav-separator"); + + const a = document.createElement("a"); + a.innerText = entry.name; + if (hasChildren) { + const depth = parentPath.split("/").length - 2; // -1 because count parts, then another -1 because of leading / + a.innerText += ` ${depth === 0 ? "▾" : "▸"}`; + } + if (entry.link != null) { + a.href = entry.link; - let hrefString; - if (entry.link == null) { - hrefString = ""; - } else { - hrefString = `href="${entry.link}"`; if (entry.link !== "#" && !/^https:\/\/.*fwdekker.com/i.test(entry.link)) - hrefString += ` target="_blank"`; + a.target = "_blank"; + } + li.addEventListener("click", () => li.classList.add("fwd-nav-active")); + li.appendChild(a); + + if (hasChildren) { + const ul = document.createElement("ul"); + entry.entries + .map((it: any) => unpackEntry(it, path, highlightPath)) + .forEach((it: HTMLLIElement) => ul.appendChild(it)); + li.appendChild(ul); } - if (entry.entries.length === 0) - return `
  • ${entry.name}
  • `; - - const depth = parentPath.split("/").length - 2; // -1 because count parts, then another -1 because of leading `/` - const arrow = depth === 0 ? "▾" : "▸"; - - return "" + - `
  • ` + - /**/`${entry.name} ${arrow}` + - /**/`` + - `
  • `; + return li; } /**