Rewrite without hyperscript

Fixes #5. Reduces bundle by 6kB.
This commit is contained in:
Florine W. Dekker 2021-03-26 02:58:50 +01:00
parent 55cce0c433
commit 0e4f973553
Signed by: FWDekker
GPG Key ID: B1B567AF58D6EE0F
3 changed files with 68 additions and 51 deletions

BIN
package-lock.json generated

Binary file not shown.

View File

@ -24,7 +24,6 @@
"prepare": "grunt clean deploy"
},
"dependencies": {
"hyperscript": "^2.0.2",
"milligram": "^1.4.1"
},
"devDependencies": {

View File

@ -1,4 +1,3 @@
import h from "hyperscript";
import "../css/normalize.css";
import "milligram/dist/milligram.min.css";
import "../css/common.css";
@ -6,6 +5,17 @@ import "../css/nav.css";
import "../css/overrides.css";
/**
* Converts the given string to an HTML element.
*
* @param string the string to convert to an HTML element
* @param query the type of element to return
* @returns {HTMLElement} the HTML element described by the given string
*/
const stringToHtml = function (string, query) {
return new DOMParser().parseFromString(string, "text/html").body.querySelector(query);
}
/**
* Alias for `document.querySelector`.
*
@ -41,22 +51,28 @@ export const doAfterLoad = function (fun) {
* @returns {HTMLElement} a base navigation element that will eventually be filled with contents
*/
export const nav = function (highlightPath = "") {
const base = 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")
))
);
const base = stringToHtml(`
<ul>
<li><a href="https://fwdekker.com/">
<div class="logo"><img class="logo" src="https://fwdekker.com/favicon.png" alt="FWDekker" /></div>
<b>FWDekker</b>
</a></li>
</ul>
`, "ul");
fetch("https://fwdekker.com/api/nav/")
.then(it => it.json())
.then(json => json.entries.forEach(entry => base.appendChild(unpackEntry(entry, "/", highlightPath))))
.then(json => {
json.entries.forEach(entry => base.appendChild(stringToHtml(unpackEntry(entry, "/", highlightPath), "li")))
})
.catch(e => {
console.error("Failed to fetch navigation elements", e);
return [];
});
return h("nav.nav", base);
const nav = stringToHtml(`<nav class="nav"></nav>`, "nav");
nav.appendChild(base);
return nav;
};
/**
@ -66,25 +82,23 @@ export const nav = function (highlightPath = "") {
* @param [path] {number} the current path traversed, found by joining the names of the entries with `/`s; always starts
* and ends with a `/`
* @param [highlightPath] {String} the path to highlight together with its parents
* @returns {HTMLElement} the navigation list entry as HTML, described by its children
* @returns {string} the navigation list entry as HTML, described by its children
*/
const unpackEntry = function (entry, path = "/", highlightPath = "") {
const shouldHighlight = highlightPath.startsWith(`${path + entry.name}/`);
if (entry.entries.length === 0)
return h("li",
h("a", {href: entry.link, innerHTML: entry.name}),
{className: shouldHighlight ? "currentPage" : ""}
);
return `<li><a href="${entry.link}" class="${shouldHighlight ? "currentPage" : ""}">${entry.name}</a></li>`;
const depth = path.split("/").length - 2; // -1 because count parts, then another -1 because of leading `/`
const depth = path.split("/").length - 2; // -1 because count parts, then another -1 because of leading `/`
const arrow = depth === 0 ? "&#9662;" : "&#9656;";
return h("li",
h("a", {href: entry.link, innerHTML: `${entry.name} ${arrow}`}),
h("ul", entry.entries.map(it => unpackEntry(it, `${path + entry.name}/`, highlightPath))),
{className: shouldHighlight ? "currentPage" : ""}
);
return `
<li class="${shouldHighlight ? "currentPage" : ""}">
<a href="${entry.link}">${entry.name} ${arrow}</a>
<ul>${entry.entries.map(it => unpackEntry(it, `${path + entry.name}/`, highlightPath)).join("")}</ul>
</li>
`;
};
@ -97,14 +111,16 @@ const unpackEntry = function (entry, path = "/", highlightPath = "") {
*/
export const header = function ({title, description}) {
if (title === undefined && description === undefined)
return h("header.header");
return stringToHtml(`<header class="header"></header>`, "header");
return h("header.header",
h("section.container",
title !== undefined ? h("h1", {innerHTML: title}) : undefined,
description !== undefined ? h("p", h("em", {innerHTML: description})) : undefined
)
);
return stringToHtml(`
<header class="header">
<section class="container">
${(title !== undefined ? `<h1>${title}</h1>` : "")}
${(description !== undefined ? `<p><em>${description}</em></p>` : "")}
</section>
</header>
`, "header");
};
@ -127,20 +143,22 @@ export const footer = function (
author, authorURL, license, licenseURL, vcs, vcsURL, version,
privacyPolicyURL = undefined
}) {
return h("footer.footer",
h("section.container",
footerLink("Made by ", author, authorURL, ". "),
footerLink("Licensed under the ", license, licenseURL, ". "),
footerLink("Source code available on ", vcs, vcsURL, ". "),
footerLink(
"Consider reading the ",
privacyPolicyURL === null ? undefined : "privacy policy",
privacyPolicyURL === undefined ? "https://fwdekker.com/privacy/" : privacyPolicyURL,
". "
),
h("div", version || "", {style: {"float": "right"}})
)
);
return stringToHtml(`
<footer class="footer">
<section class="container">
${footerLink("Made by ", author, authorURL, ". ")}
${footerLink("Licensed under the ", license, licenseURL, ". ")}
${footerLink("Source code available on ", vcs, vcsURL, ". ")}
${footerLink(
"Consider reading the ",
privacyPolicyURL === null ? undefined : "privacy policy",
privacyPolicyURL === undefined ? "https://fwdekker.com/privacy/" : privacyPolicyURL,
". "
)}
<div style="float: right;">${version || ""}</div>
</section>
</footer>
`, "footer");
};
/**
@ -150,18 +168,18 @@ export const footer = function (
* @param text {string|undefined} the text to display, or `undefined` if the returned element should be empty
* @param url {string|undefined} the URL to link the text to, or `undefined` if the text should not be a link
* @param suffix {string} the text to display after the text if the text is not undefined
* @returns {HTMLElement} a footer link element
* @returns {string} a footer link element
*/
const footerLink = function (prefix, text, url, suffix) {
if (text === undefined) return h("span");
if (text === undefined) return "";
return h("span",
h("span", prefix),
url !== undefined
? h("a", text, {href: url})
: h("span", text),
h("span", suffix)
);
return `
<span>
${prefix}
${url !== undefined ? `<a href="${url}">${text}</a>` : text}
${suffix}
</span>
`;
};