Switch scripts to redesign

This commit is contained in:
Zack Rauen 2023-09-27 23:18:03 -04:00
parent a7edc5e03e
commit 4147f2b8d8
10 changed files with 204 additions and 28 deletions

View File

@ -87,5 +87,8 @@
"wrap-iife": ["error", "inside"], "wrap-iife": ["error", "inside"],
"yield-star-spacing": "error", "yield-star-spacing": "error",
"yoda": "error" "yoda": "error"
},
"globals": {
"NodeJS": "readonly"
} }
} }

View File

@ -0,0 +1,11 @@
export default function debounce<T extends (...args: unknown[]) => unknown>(executor: T, delay: number) {
let timeout: NodeJS.Timeout | null;
return function(...args: Parameters<T>): void {
const callback = () => {
timeout = null;
Reflect.apply(executor, null, args);
};
if (timeout) clearTimeout(timeout);
timeout = setTimeout(callback, delay);
};
}

View File

@ -0,0 +1,7 @@
export default function parents<T extends HTMLElement>(el: T, selector: string) {
const result = [];
for (let p = el && el.parentElement; p; p = p.parentElement) {
if (p.matches(selector)) result.push(p);
}
return result;
}

View File

@ -0,0 +1,7 @@
export default function parseHTML(html: string, fragment = false) {
const template = document.createElement("template");
template.innerHTML = html;
const node = template.content.cloneNode(true);
if (fragment) return node;
return node.childNodes.length > 1 ? node.childNodes : node.childNodes[0];
}

23
src/scripts/expanders.ts Normal file
View File

@ -0,0 +1,23 @@
export default function setupExpanders() {
const expanders = Array.from(document.querySelectorAll("#menu .collapse-button"));
for (const ex of expanders) {
ex.addEventListener("click", e => {
e.preventDefault();
e.stopPropagation();
// ex.parentElement.parentElement.classList.toggle("expanded");
ex.closest(".submenu-item")?.classList.toggle("expanded");
});
}
const activeLink = document.querySelector("#menu a.active");
if (activeLink) {
let parent = activeLink.parentElement;
const mainMenu = document.getElementById("#menu");
while (parent && parent !== mainMenu) {
if (parent.matches(".submenu-item")) {
parent.classList.add("expanded");
}
parent = parent.parentElement;
}
}
}

View File

@ -1,25 +1,29 @@
import fixActiveLink from "./fixes/activelink"; // import fixActiveLink from "./fixes/activelink";
import fixTableHeaders from "./fixes/tableheaders"; // import fixTableHeaders from "./fixes/tableheaders";
import highlight from "./other/highlight"; import highlight from "./other/highlight";
import buildSidenav from "./navigation/sidenav"; // import buildSidenav from "./navigation/sidenav";
import buildBreadcrumbs from "./navigation/breadcrumbs"; // import buildBreadcrumbs from "./navigation/breadcrumbs";
import fixSubMenus from "./fixes/submenu"; // import fixSubMenus from "./fixes/submenu";
import generateTOC from "./navigation/toc"; import generateTOC from "./navigation/toc";
import addExternalLinks from "./fixes/externallinks"; // import addExternalLinks from "./fixes/externallinks";
import injectSwagger from "./other/swagger"; // import injectSwagger from "./other/swagger";
import makeMobileMenu from "./other/mobile"; // import makeMobileMenu from "./other/mobile";
import setupExpanders from "./expanders";
import setupMobileMenu from "./mobile";
import setupSearch from "./search";
import setupThemeSelector from "./theme";
const ETAPI_REF_NOTE_ID = "pPIXi0uwF5GX"; // const ETAPI_REF_NOTE_ID = "pPIXi0uwF5GX";
const HIDDEN_SUBMENUS = ["blog"]; // const HIDDEN_SUBMENUS = ["blog"];
const EXTERNAL_LINKS = { // const EXTERNAL_LINKS = {
EGFtX8Uw96FQ: "https://github.com/zadam/trilium", // EGFtX8Uw96FQ: "https://github.com/zadam/trilium",
dXAKFE0fJtom: "https://discord.gg/eTaTXUgcBr" // dXAKFE0fJtom: "https://discord.gg/eTaTXUgcBr"
}; // };
const ALIASES = { // const ALIASES = {
WqBnya4Ye8rS: "", // WqBnya4Ye8rS: "",
ZapIU17QNEyU: "blog" // ZapIU17QNEyU: "blog"
}; // };
function $try<T extends (...a: unknown[]) => unknown>(func: T, ...args: Parameters<T>) { function $try<T extends (...a: unknown[]) => unknown>(func: T, ...args: Parameters<T>) {
try { try {
@ -44,20 +48,25 @@ function $try<T extends (...a: unknown[]) => unknown>(func: T, ...args: Paramete
*/ */
// Perform fixes first // Perform fixes first
$try(fixActiveLink, ALIASES); // $try(fixActiveLink, ALIASES);
$try(fixTableHeaders); // $try(fixTableHeaders);
$try(fixSubMenus, HIDDEN_SUBMENUS); // $try(fixSubMenus, HIDDEN_SUBMENUS);
$try(addExternalLinks, EXTERNAL_LINKS); // $try(addExternalLinks, EXTERNAL_LINKS);
// Now layout changes // Now layout changes
$try(buildBreadcrumbs); // $try(buildBreadcrumbs);
$try(buildSidenav); // $try(buildSidenav);
$try(generateTOC); $try(generateTOC);
// Finally, other features // Finally, other features
$try(highlight); $try(highlight);
$try(injectSwagger, ETAPI_REF_NOTE_ID); // $try(injectSwagger, ETAPI_REF_NOTE_ID);
$try(makeMobileMenu);
$try(setupExpanders);
$try(setupMobileMenu);
$try(setupSearch);
$try(setupThemeSelector);
// $try(makeMobileMenu);
/** /**
* This was removed because both the title change and the opengraph * This was removed because both the title change and the opengraph

25
src/scripts/mobile.ts Normal file
View File

@ -0,0 +1,25 @@
import parents from "./common/parents";
export default function setupMobileMenu() {
function toggleMobileMenu(event: MouseEvent) {
event.stopPropagation(); // Don't prevent default for links
const isOpen = document.body.classList.contains("menu-open");
if (isOpen) return document.body.classList.remove("menu-open");
return document.body.classList.add("menu-open");
}
const showMenuButton = document.getElementById("show-menu-button");
showMenuButton?.addEventListener("click", toggleMobileMenu);
window.addEventListener("click", e => {
const isOpen = document.body.classList.contains("menu-open");
if (!isOpen) return; // This listener is only to close
// If the click was anywhere in the mobile nav, don't close
if (parents(e.target as HTMLElement, "#left-pane").length) return;
return toggleMobileMenu(e);
});
}

View File

@ -6,6 +6,7 @@ const buildItem = (heading: Element) => {
const slug = slugify(heading.textContent ?? ""); const slug = slugify(heading.textContent ?? "");
const anchor = document.createElement("a"); const anchor = document.createElement("a");
anchor.className = "toc-anchor";
anchor.setAttribute("href", `#${slug}`); anchor.setAttribute("href", `#${slug}`);
anchor.setAttribute("name", slug); anchor.setAttribute("name", slug);
anchor.setAttribute("id", slug); anchor.setAttribute("id", slug);
@ -108,8 +109,18 @@ export default function generateTOC() {
changeLinkState(); changeLinkState();
window.addEventListener("scroll", changeLinkState); window.addEventListener("scroll", changeLinkState);
// Create the toc wrapper
const pane = document.createElement("div");
pane.id = "toc-pane";
// Create the header
const header = document.createElement("h3");
header.textContent = "On This Page";
pane.append(header);
pane.append(toc);
// Finally, add the ToC to the end of layout. Give the layout a class for adjusting widths. // Finally, add the ToC to the end of layout. Give the layout a class for adjusting widths.
const layout = document.querySelector("#layout"); const layout = document.querySelector("#right-pane");
layout?.classList.add("toc"); layout?.classList.add("toc");
layout?.append(toc); layout?.append(pane);
} }

53
src/scripts/search.ts Normal file
View File

@ -0,0 +1,53 @@
import debounce from "./common/debounce";
import parents from "./common/parents";
import parseHTML from "./common/parsehtml";
interface SearchResults {
results: SearchResult[];
}
interface SearchResult {
id: string;
title: string;
score: number;
path: string;
}
export default function setupSearch() {
const searchInput: HTMLInputElement = document.querySelector(".search-input")!;
searchInput.addEventListener("keyup", debounce(async () => {
// console.log("CHANGE EVENT");
const current = document.body.dataset.noteId;
const query = searchInput.value;
if (query.length < 3) return;
const resp = await fetch(`api/search/${current}?query=${query}`);
const json = await resp.json() as SearchResults;
const results = json.results.slice(0, 5);
const lines = [`<div class="search-results">`];
for (const result of results) {
lines.push(`<a class="search-result-item" href="./${result.id}"><div class="search-result-title">${result.title}</div><div class="search-result-note">${result.path || "Home"}</div></a>`);
}
lines.push("</div>");
const container = parseHTML(lines.join("")) as HTMLDivElement;
// console.log(container, lines);
const rect = searchInput.getBoundingClientRect();
container.style.top = `${rect.bottom}px`;
container.style.left = `${rect.left}px`;
container.style.minWidth = `${rect.width}px`;
const existing = document.querySelector(".search-results");
if (existing) existing.replaceWith(container);
else document.body.append(container);
}, 500));
window.addEventListener("click", e => {
const existing = document.querySelector(".search-results");
if (!existing) return;
// If the click was anywhere search components ignore it
if (parents(e.target as HTMLElement, ".search-results,.search-item").length) return;
if (existing) existing.remove();
});
}

27
src/scripts/theme.ts Normal file
View File

@ -0,0 +1,27 @@
export default function setupThemeSelector() {
const themeSwitch: HTMLInputElement = document.querySelector(".theme-selection input")!;
themeSwitch?.addEventListener("change", () => {
if (themeSwitch.checked) {
document.body.classList.add("theme-dark");
document.body.classList.remove("theme-light");
localStorage.setItem("theme", "dark");
}
else {
document.body.classList.remove("theme-dark");
document.body.classList.add("theme-light");
localStorage.setItem("theme", "light");
}
});
const preference = localStorage.getItem("theme");
if (preference) {
if (preference === "dark") {
document.body.classList.add("theme-dark");
document.body.classList.remove("theme-light");
}
else {
document.body.classList.remove("theme-dark");
document.body.classList.add("theme-light");
}
}
}