mirror of
https://github.com/zadam/trilium.git
synced 2025-10-26 07:46:30 +01:00
Switch scripts to redesign
This commit is contained in:
@@ -87,5 +87,8 @@
|
||||
"wrap-iife": ["error", "inside"],
|
||||
"yield-star-spacing": "error",
|
||||
"yoda": "error"
|
||||
},
|
||||
"globals": {
|
||||
"NodeJS": "readonly"
|
||||
}
|
||||
}
|
||||
11
src/scripts/common/debounce.ts
Normal file
11
src/scripts/common/debounce.ts
Normal 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);
|
||||
};
|
||||
}
|
||||
7
src/scripts/common/parents.ts
Normal file
7
src/scripts/common/parents.ts
Normal 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;
|
||||
}
|
||||
7
src/scripts/common/parsehtml.ts
Normal file
7
src/scripts/common/parsehtml.ts
Normal 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
23
src/scripts/expanders.ts
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,25 +1,29 @@
|
||||
import fixActiveLink from "./fixes/activelink";
|
||||
import fixTableHeaders from "./fixes/tableheaders";
|
||||
// import fixActiveLink from "./fixes/activelink";
|
||||
// import fixTableHeaders from "./fixes/tableheaders";
|
||||
import highlight from "./other/highlight";
|
||||
import buildSidenav from "./navigation/sidenav";
|
||||
import buildBreadcrumbs from "./navigation/breadcrumbs";
|
||||
import fixSubMenus from "./fixes/submenu";
|
||||
// import buildSidenav from "./navigation/sidenav";
|
||||
// import buildBreadcrumbs from "./navigation/breadcrumbs";
|
||||
// import fixSubMenus from "./fixes/submenu";
|
||||
import generateTOC from "./navigation/toc";
|
||||
import addExternalLinks from "./fixes/externallinks";
|
||||
import injectSwagger from "./other/swagger";
|
||||
import makeMobileMenu from "./other/mobile";
|
||||
// import addExternalLinks from "./fixes/externallinks";
|
||||
// import injectSwagger from "./other/swagger";
|
||||
// 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 HIDDEN_SUBMENUS = ["blog"];
|
||||
const EXTERNAL_LINKS = {
|
||||
EGFtX8Uw96FQ: "https://github.com/zadam/trilium",
|
||||
dXAKFE0fJtom: "https://discord.gg/eTaTXUgcBr"
|
||||
};
|
||||
const ALIASES = {
|
||||
WqBnya4Ye8rS: "",
|
||||
ZapIU17QNEyU: "blog"
|
||||
};
|
||||
// const ETAPI_REF_NOTE_ID = "pPIXi0uwF5GX";
|
||||
// const HIDDEN_SUBMENUS = ["blog"];
|
||||
// const EXTERNAL_LINKS = {
|
||||
// EGFtX8Uw96FQ: "https://github.com/zadam/trilium",
|
||||
// dXAKFE0fJtom: "https://discord.gg/eTaTXUgcBr"
|
||||
// };
|
||||
// const ALIASES = {
|
||||
// WqBnya4Ye8rS: "",
|
||||
// ZapIU17QNEyU: "blog"
|
||||
// };
|
||||
|
||||
function $try<T extends (...a: unknown[]) => unknown>(func: T, ...args: Parameters<T>) {
|
||||
try {
|
||||
@@ -44,20 +48,25 @@ function $try<T extends (...a: unknown[]) => unknown>(func: T, ...args: Paramete
|
||||
*/
|
||||
|
||||
// Perform fixes first
|
||||
$try(fixActiveLink, ALIASES);
|
||||
$try(fixTableHeaders);
|
||||
$try(fixSubMenus, HIDDEN_SUBMENUS);
|
||||
$try(addExternalLinks, EXTERNAL_LINKS);
|
||||
// $try(fixActiveLink, ALIASES);
|
||||
// $try(fixTableHeaders);
|
||||
// $try(fixSubMenus, HIDDEN_SUBMENUS);
|
||||
// $try(addExternalLinks, EXTERNAL_LINKS);
|
||||
|
||||
// Now layout changes
|
||||
$try(buildBreadcrumbs);
|
||||
$try(buildSidenav);
|
||||
// $try(buildBreadcrumbs);
|
||||
// $try(buildSidenav);
|
||||
$try(generateTOC);
|
||||
|
||||
// Finally, other features
|
||||
$try(highlight);
|
||||
$try(injectSwagger, ETAPI_REF_NOTE_ID);
|
||||
$try(makeMobileMenu);
|
||||
// $try(injectSwagger, ETAPI_REF_NOTE_ID);
|
||||
|
||||
$try(setupExpanders);
|
||||
$try(setupMobileMenu);
|
||||
$try(setupSearch);
|
||||
$try(setupThemeSelector);
|
||||
// $try(makeMobileMenu);
|
||||
|
||||
/**
|
||||
* This was removed because both the title change and the opengraph
|
||||
|
||||
25
src/scripts/mobile.ts
Normal file
25
src/scripts/mobile.ts
Normal 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);
|
||||
});
|
||||
|
||||
}
|
||||
@@ -6,6 +6,7 @@ const buildItem = (heading: Element) => {
|
||||
const slug = slugify(heading.textContent ?? "");
|
||||
|
||||
const anchor = document.createElement("a");
|
||||
anchor.className = "toc-anchor";
|
||||
anchor.setAttribute("href", `#${slug}`);
|
||||
anchor.setAttribute("name", slug);
|
||||
anchor.setAttribute("id", slug);
|
||||
@@ -108,8 +109,18 @@ export default function generateTOC() {
|
||||
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.
|
||||
const layout = document.querySelector("#layout");
|
||||
const layout = document.querySelector("#right-pane");
|
||||
layout?.classList.add("toc");
|
||||
layout?.append(toc);
|
||||
layout?.append(pane);
|
||||
}
|
||||
53
src/scripts/search.ts
Normal file
53
src/scripts/search.ts
Normal 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
27
src/scripts/theme.ts
Normal 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");
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user