diff --git a/package.json b/package.json index dba7bce..2a7809e 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@fwdekker/template", - "version": "0.0.10", + "version": "0.0.11", "description": "The base template for pages on fwdekker.com.", "author": "Felix W. Dekker (https://fwdekker.com)", "license": "MIT", diff --git a/src/main/js/Template.js b/src/main/js/Template.js index 4a6246d..4bf3b0b 100644 --- a/src/main/js/Template.js +++ b/src/main/js/Template.js @@ -34,40 +34,44 @@ export const doAfterLoad = function (fun) { /** * Creates a navigation element for navigating through the website. * - * @returns {HTMLElement} a navigation element + * Fetches entries asynchronously from the website's API. + * + * @returns {Promise} a navigation element, eventually */ -export const nav = function () { - return h("nav.nav", - h("ul", - h("li", h("a", {href: "https://fwdekker.com/"}, - h("div.logo", h("img.logo", {src: "https://fwdekker.com/favicon.png"})), - h("b", "FWDekker") - )), - h("li", h("a", {href: "#", innerHTML: "Tools ▾"}), - h("ul", - h("li", - h("a", "Converter", {href: "https://fwdekker.com/tools/converter/"}) - ), - h("li", - h("a", "Dice", {href: "https://fwdekker.com/tools/dice/"}) - ), - h("li", - h("a", "Doomsday", {href: "https://fwdekker.com/tools/doomsday/"}) - ), - h("li", - h("a", "Interlanguage Checker", {href: "https://fwdekker.com/tools/interlanguage-checker/"}) - ), - h("li", - h("a", "Random FO76", {href: "https://fwdekker.com/tools/random-fo76/"}) - ), - ) - ), - h("li", h("a", "Gitea", {href: "https://git.fwdekker.com/"})), - h("li", h("a", "Blog", {href: "https://blog.fwdekker.com/"})) - ) +export const nav = async function () { + const entries = await fetch("https://fwdekker.com/api/nav/") + .then(it => it.json()) + .then(it => it.entries) + .then(it => it.map(entry => unpackEntry(entry))) + .catch(e => { + console.error("Failed to fetch navigation elements", e); + return []; + }); + const prefix = h("li", h("a", {href: "https://fwdekker.com/"}, + h("div.logo", h("img.logo", {src: "https://fwdekker.com/favicon.png"})), + h("b", "FWDekker") + )); + + return h("nav.nav", h("ul", prefix, ...entries)); +}; + +/** + * Unpacks a navigation entry returned from the navigation API into an HTML element. + * + * @param entry {Object} the entry to unpack + * @returns {HTMLElement} the navigation list entry as HTML, described by its children + */ +const unpackEntry = function(entry) { + if (entry.entries.length === 0) + return h("li", h("a", {href: entry.link, innerHTML: entry.name})); + + return h("li", + h("a", {href: entry.link, innerHTML: `${entry.name} ▾`}), + h("ul", entry.entries.map(it => unpackEntry(it))) ); }; + /** * Creates a header element with the given title and description. * @@ -84,6 +88,7 @@ export const header = function ({title, description}) { ); }; + /** * Creates a footer element with the given data. * @@ -107,7 +112,6 @@ export const footer = function ({author, authorURL, license, licenseURL, vcs, vc ); }; - /** * Constructs a link that is used in footers. *