mirror of
https://github.com/alrayyes/wiki.git
synced 2025-05-02 23:08:14 +00:00
run prettier
This commit is contained in:
parent
2034b970b6
commit
7db2eda76c
101 changed files with 1810 additions and 1405 deletions
|
@ -7,7 +7,9 @@ function toggleCallout(this: HTMLElement) {
|
|||
}
|
||||
|
||||
function setupCallout() {
|
||||
const collapsible = document.getElementsByClassName(`callout is-collapsible`) as HTMLCollectionOf<HTMLElement>
|
||||
const collapsible = document.getElementsByClassName(
|
||||
`callout is-collapsible`,
|
||||
) as HTMLCollectionOf<HTMLElement>
|
||||
for (const div of collapsible) {
|
||||
const title = div.firstElementChild
|
||||
|
||||
|
|
|
@ -1,24 +1,23 @@
|
|||
const userPref = window.matchMedia('(prefers-color-scheme: light)').matches ? 'light' : 'dark'
|
||||
const currentTheme = localStorage.getItem('theme') ?? userPref
|
||||
document.documentElement.setAttribute('saved-theme', currentTheme)
|
||||
const userPref = window.matchMedia("(prefers-color-scheme: light)").matches ? "light" : "dark"
|
||||
const currentTheme = localStorage.getItem("theme") ?? userPref
|
||||
document.documentElement.setAttribute("saved-theme", currentTheme)
|
||||
|
||||
document.addEventListener("nav", () => {
|
||||
const switchTheme = (e: any) => {
|
||||
if (e.target.checked) {
|
||||
document.documentElement.setAttribute('saved-theme', 'dark')
|
||||
localStorage.setItem('theme', 'dark')
|
||||
}
|
||||
else {
|
||||
document.documentElement.setAttribute('saved-theme', 'light')
|
||||
localStorage.setItem('theme', 'light')
|
||||
document.documentElement.setAttribute("saved-theme", "dark")
|
||||
localStorage.setItem("theme", "dark")
|
||||
} else {
|
||||
document.documentElement.setAttribute("saved-theme", "light")
|
||||
localStorage.setItem("theme", "light")
|
||||
}
|
||||
}
|
||||
|
||||
// Darkmode toggle
|
||||
const toggleSwitch = document.querySelector('#darkmode-toggle') as HTMLInputElement
|
||||
toggleSwitch.removeEventListener('change', switchTheme)
|
||||
toggleSwitch.addEventListener('change', switchTheme)
|
||||
if (currentTheme === 'dark') {
|
||||
const toggleSwitch = document.querySelector("#darkmode-toggle") as HTMLInputElement
|
||||
toggleSwitch.removeEventListener("change", switchTheme)
|
||||
toggleSwitch.addEventListener("change", switchTheme)
|
||||
if (currentTheme === "dark") {
|
||||
toggleSwitch.checked = true
|
||||
}
|
||||
})
|
||||
|
|
|
@ -1,16 +1,16 @@
|
|||
import { ContentDetails } from "../../plugins/emitters/contentIndex"
|
||||
import * as d3 from 'd3'
|
||||
import * as d3 from "d3"
|
||||
import { registerEscapeHandler, removeAllChildren } from "./util"
|
||||
import { CanonicalSlug, getCanonicalSlug, getClientSlug, resolveRelative } from "../../path"
|
||||
|
||||
type NodeData = {
|
||||
id: CanonicalSlug,
|
||||
text: string,
|
||||
id: CanonicalSlug
|
||||
text: string
|
||||
tags: string[]
|
||||
} & d3.SimulationNodeDatum
|
||||
|
||||
type LinkData = {
|
||||
source: CanonicalSlug,
|
||||
source: CanonicalSlug
|
||||
target: CanonicalSlug
|
||||
}
|
||||
|
||||
|
@ -40,7 +40,7 @@ async function renderGraph(container: string, slug: CanonicalSlug) {
|
|||
centerForce,
|
||||
linkDistance,
|
||||
fontSize,
|
||||
opacityScale
|
||||
opacityScale,
|
||||
} = JSON.parse(graph.dataset["cfg"]!)
|
||||
|
||||
const data = await fetchData
|
||||
|
@ -66,18 +66,22 @@ async function renderGraph(container: string, slug: CanonicalSlug) {
|
|||
wl.push("__SENTINEL")
|
||||
} else {
|
||||
neighbourhood.add(cur)
|
||||
const outgoing = links.filter(l => l.source === cur)
|
||||
const incoming = links.filter(l => l.target === cur)
|
||||
const outgoing = links.filter((l) => l.source === cur)
|
||||
const incoming = links.filter((l) => l.target === cur)
|
||||
wl.push(...outgoing.map((l) => l.target), ...incoming.map((l) => l.source))
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Object.keys(data).forEach(id => neighbourhood.add(id as CanonicalSlug))
|
||||
Object.keys(data).forEach((id) => neighbourhood.add(id as CanonicalSlug))
|
||||
}
|
||||
|
||||
const graphData: { nodes: NodeData[], links: LinkData[] } = {
|
||||
nodes: [...neighbourhood].map(url => ({ id: url, text: data[url]?.title ?? url, tags: data[url]?.tags ?? [] })),
|
||||
links: links.filter((l) => neighbourhood.has(l.source) && neighbourhood.has(l.target))
|
||||
const graphData: { nodes: NodeData[]; links: LinkData[] } = {
|
||||
nodes: [...neighbourhood].map((url) => ({
|
||||
id: url,
|
||||
text: data[url]?.title ?? url,
|
||||
tags: data[url]?.tags ?? [],
|
||||
})),
|
||||
links: links.filter((l) => neighbourhood.has(l.source) && neighbourhood.has(l.target)),
|
||||
}
|
||||
|
||||
const simulation: d3.Simulation<NodeData, LinkData> = d3
|
||||
|
@ -96,11 +100,11 @@ async function renderGraph(container: string, slug: CanonicalSlug) {
|
|||
const width = graph.offsetWidth
|
||||
|
||||
const svg = d3
|
||||
.select<HTMLElement, NodeData>('#' + container)
|
||||
.select<HTMLElement, NodeData>("#" + container)
|
||||
.append("svg")
|
||||
.attr("width", width)
|
||||
.attr("height", height)
|
||||
.attr('viewBox', [-width / 2 / scale, -height / 2 / scale, width / scale, height / scale])
|
||||
.attr("viewBox", [-width / 2 / scale, -height / 2 / scale, width / scale, height / scale])
|
||||
|
||||
// draw links between nodes
|
||||
const link = svg
|
||||
|
@ -145,7 +149,7 @@ async function renderGraph(container: string, slug: CanonicalSlug) {
|
|||
d.fy = null
|
||||
}
|
||||
|
||||
const noop = () => { }
|
||||
const noop = () => {}
|
||||
return d3
|
||||
.drag<Element, NodeData>()
|
||||
.on("start", enableDrag ? dragstarted : noop)
|
||||
|
@ -170,9 +174,11 @@ async function renderGraph(container: string, slug: CanonicalSlug) {
|
|||
const targ = resolveRelative(slug, d.id)
|
||||
window.spaNavigate(new URL(targ, getClientSlug(window)))
|
||||
})
|
||||
.on("mouseover", function(_, d) {
|
||||
.on("mouseover", function (_, d) {
|
||||
const neighbours: CanonicalSlug[] = data[slug].links ?? []
|
||||
const neighbourNodes = d3.selectAll<HTMLElement, NodeData>(".node").filter((d) => neighbours.includes(d.id))
|
||||
const neighbourNodes = d3
|
||||
.selectAll<HTMLElement, NodeData>(".node")
|
||||
.filter((d) => neighbours.includes(d.id))
|
||||
console.log(neighbourNodes)
|
||||
const currentId = d.id
|
||||
const linkNodes = d3
|
||||
|
@ -183,12 +189,7 @@ async function renderGraph(container: string, slug: CanonicalSlug) {
|
|||
neighbourNodes.transition().duration(200).attr("fill", color)
|
||||
|
||||
// highlight links
|
||||
linkNodes
|
||||
.transition()
|
||||
.duration(200)
|
||||
.attr("stroke", "var(--gray)")
|
||||
.attr("stroke-width", 1)
|
||||
|
||||
linkNodes.transition().duration(200).attr("stroke", "var(--gray)").attr("stroke-width", 1)
|
||||
|
||||
const bigFont = fontSize * 1.5
|
||||
|
||||
|
@ -199,11 +200,11 @@ async function renderGraph(container: string, slug: CanonicalSlug) {
|
|||
.select("text")
|
||||
.transition()
|
||||
.duration(200)
|
||||
.attr('opacityOld', d3.select(parent).select('text').style("opacity"))
|
||||
.style('opacity', 1)
|
||||
.style('font-size', bigFont + 'em')
|
||||
.attr("opacityOld", d3.select(parent).select("text").style("opacity"))
|
||||
.style("opacity", 1)
|
||||
.style("font-size", bigFont + "em")
|
||||
})
|
||||
.on("mouseleave", function(_, d) {
|
||||
.on("mouseleave", function (_, d) {
|
||||
const currentId = d.id
|
||||
const linkNodes = d3
|
||||
.selectAll(".link")
|
||||
|
@ -216,8 +217,8 @@ async function renderGraph(container: string, slug: CanonicalSlug) {
|
|||
.select("text")
|
||||
.transition()
|
||||
.duration(200)
|
||||
.style('opacity', d3.select(parent).select('text').attr("opacityOld"))
|
||||
.style('font-size', fontSize + 'em')
|
||||
.style("opacity", d3.select(parent).select("text").attr("opacityOld"))
|
||||
.style("font-size", fontSize + "em")
|
||||
})
|
||||
// @ts-ignore
|
||||
.call(drag(simulation))
|
||||
|
@ -228,10 +229,12 @@ async function renderGraph(container: string, slug: CanonicalSlug) {
|
|||
.attr("dx", 0)
|
||||
.attr("dy", (d) => -nodeRadius(d) + "px")
|
||||
.attr("text-anchor", "middle")
|
||||
.text((d) => data[d.id]?.title || (d.id.charAt(1).toUpperCase() + d.id.slice(2)).replace("-", " "))
|
||||
.style('opacity', (opacityScale - 1) / 3.75)
|
||||
.text(
|
||||
(d) => data[d.id]?.title || (d.id.charAt(1).toUpperCase() + d.id.slice(2)).replace("-", " "),
|
||||
)
|
||||
.style("opacity", (opacityScale - 1) / 3.75)
|
||||
.style("pointer-events", "none")
|
||||
.style('font-size', fontSize + 'em')
|
||||
.style("font-size", fontSize + "em")
|
||||
.raise()
|
||||
// @ts-ignore
|
||||
.call(drag(simulation))
|
||||
|
@ -249,7 +252,7 @@ async function renderGraph(container: string, slug: CanonicalSlug) {
|
|||
.on("zoom", ({ transform }) => {
|
||||
link.attr("transform", transform)
|
||||
node.attr("transform", transform)
|
||||
const scale = transform.k * opacityScale;
|
||||
const scale = transform.k * opacityScale
|
||||
const scaledOpacity = Math.max((scale - 1) / 3.75, 0)
|
||||
labels.attr("transform", transform).style("opacity", scaledOpacity)
|
||||
}),
|
||||
|
@ -263,17 +266,13 @@ async function renderGraph(container: string, slug: CanonicalSlug) {
|
|||
.attr("y1", (d: any) => d.source.y)
|
||||
.attr("x2", (d: any) => d.target.x)
|
||||
.attr("y2", (d: any) => d.target.y)
|
||||
node
|
||||
.attr("cx", (d: any) => d.x)
|
||||
.attr("cy", (d: any) => d.y)
|
||||
labels
|
||||
.attr("x", (d: any) => d.x)
|
||||
.attr("y", (d: any) => d.y)
|
||||
node.attr("cx", (d: any) => d.x).attr("cy", (d: any) => d.y)
|
||||
labels.attr("x", (d: any) => d.x).attr("y", (d: any) => d.y)
|
||||
})
|
||||
}
|
||||
|
||||
function renderGlobalGraph() {
|
||||
const slug = getCanonicalSlug(window)
|
||||
const slug = getCanonicalSlug(window)
|
||||
const container = document.getElementById("global-graph-outer")
|
||||
const sidebar = container?.closest(".sidebar") as HTMLElement
|
||||
container?.classList.add("active")
|
||||
|
@ -305,4 +304,3 @@ document.addEventListener("nav", async (e: unknown) => {
|
|||
containerIcon?.removeEventListener("click", renderGlobalGraph)
|
||||
containerIcon?.addEventListener("click", renderGlobalGraph)
|
||||
})
|
||||
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
import Plausible from 'plausible-tracker'
|
||||
import Plausible from "plausible-tracker"
|
||||
const { trackPageview } = Plausible()
|
||||
document.addEventListener("nav", () => trackPageview())
|
||||
|
|
|
@ -2,33 +2,25 @@ import { computePosition, flip, inline, shift } from "@floating-ui/dom"
|
|||
|
||||
// from micromorph/src/utils.ts
|
||||
// https://github.com/natemoo-re/micromorph/blob/main/src/utils.ts#L5
|
||||
export function normalizeRelativeURLs(
|
||||
el: Element | Document,
|
||||
base: string | URL
|
||||
) {
|
||||
export function normalizeRelativeURLs(el: Element | Document, base: string | URL) {
|
||||
const update = (el: Element, attr: string, base: string | URL) => {
|
||||
el.setAttribute(attr, new URL(el.getAttribute(attr)!, base).pathname)
|
||||
}
|
||||
|
||||
el.querySelectorAll('[href^="./"], [href^="../"]').forEach((item) =>
|
||||
update(item, 'href', base)
|
||||
)
|
||||
el.querySelectorAll('[href^="./"], [href^="../"]').forEach((item) => update(item, "href", base))
|
||||
|
||||
el.querySelectorAll('[src^="./"], [src^="../"]').forEach((item) =>
|
||||
update(item, 'src', base)
|
||||
)
|
||||
el.querySelectorAll('[src^="./"], [src^="../"]').forEach((item) => update(item, "src", base))
|
||||
}
|
||||
|
||||
const p = new DOMParser()
|
||||
async function mouseEnterHandler(this: HTMLLinkElement, { clientX, clientY }: { clientX: number, clientY: number }) {
|
||||
async function mouseEnterHandler(
|
||||
this: HTMLLinkElement,
|
||||
{ clientX, clientY }: { clientX: number; clientY: number },
|
||||
) {
|
||||
const link = this
|
||||
async function setPosition(popoverElement: HTMLElement) {
|
||||
const { x, y } = await computePosition(link, popoverElement, {
|
||||
middleware: [
|
||||
inline({ x: clientX, y: clientY }),
|
||||
shift(),
|
||||
flip()
|
||||
]
|
||||
middleware: [inline({ x: clientX, y: clientY }), shift(), flip()],
|
||||
})
|
||||
Object.assign(popoverElement.style, {
|
||||
left: `${x}px`,
|
||||
|
@ -37,7 +29,7 @@ async function mouseEnterHandler(this: HTMLLinkElement, { clientX, clientY }: {
|
|||
}
|
||||
|
||||
// dont refetch if there's already a popover
|
||||
if ([...link.children].some(child => child.classList.contains("popover"))) {
|
||||
if ([...link.children].some((child) => child.classList.contains("popover"))) {
|
||||
return setPosition(link.lastChild as HTMLElement)
|
||||
}
|
||||
|
||||
|
@ -68,7 +60,7 @@ async function mouseEnterHandler(this: HTMLLinkElement, { clientX, clientY }: {
|
|||
const popoverInner = document.createElement("div")
|
||||
popoverInner.classList.add("popover-inner")
|
||||
popoverElement.appendChild(popoverInner)
|
||||
elts.forEach(elt => popoverInner.appendChild(elt))
|
||||
elts.forEach((elt) => popoverInner.appendChild(elt))
|
||||
|
||||
setPosition(popoverElement)
|
||||
link.appendChild(popoverElement)
|
||||
|
@ -77,7 +69,7 @@ async function mouseEnterHandler(this: HTMLLinkElement, { clientX, clientY }: {
|
|||
const heading = popoverInner.querySelector(hash) as HTMLElement | null
|
||||
if (heading) {
|
||||
// leave ~12px of buffer when scrolling to a heading
|
||||
popoverInner.scroll({ top: heading.offsetTop - 12, behavior: 'instant' })
|
||||
popoverInner.scroll({ top: heading.offsetTop - 12, behavior: "instant" })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,9 +4,9 @@ import { registerEscapeHandler, removeAllChildren } from "./util"
|
|||
import { CanonicalSlug, getClientSlug, resolveRelative } from "../../path"
|
||||
|
||||
interface Item {
|
||||
slug: CanonicalSlug,
|
||||
title: string,
|
||||
content: string,
|
||||
slug: CanonicalSlug
|
||||
title: string
|
||||
content: string
|
||||
}
|
||||
|
||||
let index: Document<Item> | undefined = undefined
|
||||
|
@ -15,15 +15,17 @@ const contextWindowWords = 30
|
|||
const numSearchResults = 5
|
||||
function highlight(searchTerm: string, text: string, trim?: boolean) {
|
||||
// try to highlight longest tokens first
|
||||
const tokenizedTerms = searchTerm.split(/\s+/).filter(t => t !== "").sort((a, b) => b.length - a.length)
|
||||
let tokenizedText = text
|
||||
const tokenizedTerms = searchTerm
|
||||
.split(/\s+/)
|
||||
.filter(t => t !== "")
|
||||
.filter((t) => t !== "")
|
||||
.sort((a, b) => b.length - a.length)
|
||||
let tokenizedText = text.split(/\s+/).filter((t) => t !== "")
|
||||
|
||||
let startIndex = 0
|
||||
let endIndex = tokenizedText.length - 1
|
||||
if (trim) {
|
||||
const includesCheck = (tok: string) => tokenizedTerms.some((term) => tok.toLowerCase().startsWith(term.toLowerCase()))
|
||||
const includesCheck = (tok: string) =>
|
||||
tokenizedTerms.some((term) => tok.toLowerCase().startsWith(term.toLowerCase()))
|
||||
const occurencesIndices = tokenizedText.map(includesCheck)
|
||||
|
||||
let bestSum = 0
|
||||
|
@ -42,19 +44,22 @@ function highlight(searchTerm: string, text: string, trim?: boolean) {
|
|||
tokenizedText = tokenizedText.slice(startIndex, endIndex)
|
||||
}
|
||||
|
||||
const slice = tokenizedText.map(tok => {
|
||||
// see if this tok is prefixed by any search terms
|
||||
for (const searchTok of tokenizedTerms) {
|
||||
if (tok.toLowerCase().includes(searchTok.toLowerCase())) {
|
||||
const regex = new RegExp(searchTok.toLowerCase(), "gi")
|
||||
return tok.replace(regex, `<span class="highlight">$&</span>`)
|
||||
const slice = tokenizedText
|
||||
.map((tok) => {
|
||||
// see if this tok is prefixed by any search terms
|
||||
for (const searchTok of tokenizedTerms) {
|
||||
if (tok.toLowerCase().includes(searchTok.toLowerCase())) {
|
||||
const regex = new RegExp(searchTok.toLowerCase(), "gi")
|
||||
return tok.replace(regex, `<span class="highlight">$&</span>`)
|
||||
}
|
||||
}
|
||||
}
|
||||
return tok
|
||||
})
|
||||
return tok
|
||||
})
|
||||
.join(" ")
|
||||
|
||||
return `${startIndex === 0 ? "" : "..."}${slice}${endIndex === tokenizedText.length - 1 ? "" : "..."}`
|
||||
return `${startIndex === 0 ? "" : "..."}${slice}${
|
||||
endIndex === tokenizedText.length - 1 ? "" : "..."
|
||||
}`
|
||||
}
|
||||
|
||||
const encoder = (str: string) => str.toLowerCase().split(/([^a-z]|[^\x00-\x7F])/)
|
||||
|
@ -113,7 +118,7 @@ document.addEventListener("nav", async (e: unknown) => {
|
|||
button.classList.add("result-card")
|
||||
button.id = slug
|
||||
button.innerHTML = `<h3>${title}</h3><p>${content}</p>`
|
||||
button.addEventListener('click', () => {
|
||||
button.addEventListener("click", () => {
|
||||
const targ = resolveRelative(currentSlug, slug)
|
||||
window.spaNavigate(new URL(targ, getClientSlug(window)))
|
||||
})
|
||||
|
@ -132,7 +137,6 @@ document.addEventListener("nav", async (e: unknown) => {
|
|||
} else {
|
||||
results.append(...finalResults.map(resultToHTML))
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function onType(e: HTMLElementEventMap["input"]) {
|
||||
|
@ -140,12 +144,12 @@ document.addEventListener("nav", async (e: unknown) => {
|
|||
const searchResults = index?.search(term, numSearchResults) ?? []
|
||||
const getByField = (field: string): CanonicalSlug[] => {
|
||||
const results = searchResults.filter((x) => x.field === field)
|
||||
return results.length === 0 ? [] : [...results[0].result] as CanonicalSlug[]
|
||||
return results.length === 0 ? [] : ([...results[0].result] as CanonicalSlug[])
|
||||
}
|
||||
|
||||
// order titles ahead of content
|
||||
const allIds: Set<CanonicalSlug> = new Set([...getByField("title"), ...getByField("content")])
|
||||
const finalResults = [...allIds].map(id => formatForDisplay(term, id))
|
||||
const finalResults = [...allIds].map((id) => formatForDisplay(term, id))
|
||||
displayResults(finalResults)
|
||||
}
|
||||
|
||||
|
@ -160,7 +164,7 @@ document.addEventListener("nav", async (e: unknown) => {
|
|||
if (!index) {
|
||||
index = new Document({
|
||||
cache: true,
|
||||
charset: 'latin:extra',
|
||||
charset: "latin:extra",
|
||||
optimize: true,
|
||||
encode: encoder,
|
||||
document: {
|
||||
|
@ -174,7 +178,7 @@ document.addEventListener("nav", async (e: unknown) => {
|
|||
field: "content",
|
||||
tokenize: "reverse",
|
||||
},
|
||||
]
|
||||
],
|
||||
},
|
||||
})
|
||||
|
||||
|
@ -182,7 +186,7 @@ document.addEventListener("nav", async (e: unknown) => {
|
|||
await index.addAsync(slug, {
|
||||
slug: slug as CanonicalSlug,
|
||||
title: fileData.title,
|
||||
content: fileData.content
|
||||
content: fileData.content,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,8 +5,9 @@ import { CanonicalSlug, RelativeURL, getCanonicalSlug } from "../../path"
|
|||
// https://github.com/natemoo-re/micromorph
|
||||
|
||||
const NODE_TYPE_ELEMENT = 1
|
||||
let announcer = document.createElement('route-announcer')
|
||||
const isElement = (target: EventTarget | null): target is Element => (target as Node)?.nodeType === NODE_TYPE_ELEMENT
|
||||
let announcer = document.createElement("route-announcer")
|
||||
const isElement = (target: EventTarget | null): target is Element =>
|
||||
(target as Node)?.nodeType === NODE_TYPE_ELEMENT
|
||||
const isLocalUrl = (href: string) => {
|
||||
try {
|
||||
const url = new URL(href)
|
||||
|
@ -16,18 +17,18 @@ const isLocalUrl = (href: string) => {
|
|||
}
|
||||
return true
|
||||
}
|
||||
} catch (e) { }
|
||||
} catch (e) {}
|
||||
return false
|
||||
}
|
||||
|
||||
const getOpts = ({ target }: Event): { url: URL, scroll?: boolean } | undefined => {
|
||||
const getOpts = ({ target }: Event): { url: URL; scroll?: boolean } | undefined => {
|
||||
if (!isElement(target)) return
|
||||
const a = target.closest("a")
|
||||
if (!a) return
|
||||
if ('routerIgnore' in a.dataset) return
|
||||
if ("routerIgnore" in a.dataset) return
|
||||
const { href } = a
|
||||
if (!isLocalUrl(href)) return
|
||||
return { url: new URL(href), scroll: 'routerNoscroll' in a.dataset ? false : undefined }
|
||||
return { url: new URL(href), scroll: "routerNoscroll" in a.dataset ? false : undefined }
|
||||
}
|
||||
|
||||
function notifyNav(url: CanonicalSlug) {
|
||||
|
@ -44,7 +45,7 @@ async function navigate(url: URL, isBack: boolean = false) {
|
|||
window.location.assign(url)
|
||||
})
|
||||
|
||||
if (!contents) return;
|
||||
if (!contents) return
|
||||
if (!isBack) {
|
||||
history.pushState({}, "", url)
|
||||
window.scrollTo({ top: 0 })
|
||||
|
@ -54,22 +55,22 @@ async function navigate(url: URL, isBack: boolean = false) {
|
|||
if (title) {
|
||||
document.title = title
|
||||
} else {
|
||||
const h1 = document.querySelector('h1')
|
||||
const h1 = document.querySelector("h1")
|
||||
title = h1?.innerText ?? h1?.textContent ?? url.pathname
|
||||
}
|
||||
if (announcer.textContent !== title) {
|
||||
announcer.textContent = title
|
||||
}
|
||||
announcer.dataset.persist = ''
|
||||
announcer.dataset.persist = ""
|
||||
html.body.appendChild(announcer)
|
||||
|
||||
micromorph(document.body, html.body)
|
||||
|
||||
// now, patch head
|
||||
const elementsToRemove = document.head.querySelectorAll(':not([spa-preserve])')
|
||||
elementsToRemove.forEach(el => el.remove())
|
||||
const elementsToAdd = html.head.querySelectorAll(':not([spa-preserve])')
|
||||
elementsToAdd.forEach(el => document.head.appendChild(el))
|
||||
// now, patch head
|
||||
const elementsToRemove = document.head.querySelectorAll(":not([spa-preserve])")
|
||||
elementsToRemove.forEach((el) => el.remove())
|
||||
const elementsToAdd = html.head.querySelectorAll(":not([spa-preserve])")
|
||||
elementsToAdd.forEach((el) => document.head.appendChild(el))
|
||||
|
||||
notifyNav(getCanonicalSlug(window))
|
||||
delete announcer.dataset.persist
|
||||
|
@ -101,7 +102,7 @@ function createRouter() {
|
|||
})
|
||||
}
|
||||
|
||||
return new class Router {
|
||||
return new (class Router {
|
||||
go(pathname: RelativeURL) {
|
||||
const url = new URL(pathname, window.location.toString())
|
||||
return navigate(url, false)
|
||||
|
@ -114,26 +115,30 @@ function createRouter() {
|
|||
forward() {
|
||||
return window.history.forward()
|
||||
}
|
||||
}
|
||||
})()
|
||||
}
|
||||
|
||||
createRouter()
|
||||
notifyNav(getCanonicalSlug(window))
|
||||
|
||||
if (!customElements.get('route-announcer')) {
|
||||
if (!customElements.get("route-announcer")) {
|
||||
const attrs = {
|
||||
'aria-live': 'assertive',
|
||||
'aria-atomic': 'true',
|
||||
'style': 'position: absolute; left: 0; top: 0; clip: rect(0 0 0 0); clip-path: inset(50%); overflow: hidden; white-space: nowrap; width: 1px; height: 1px'
|
||||
"aria-live": "assertive",
|
||||
"aria-atomic": "true",
|
||||
style:
|
||||
"position: absolute; left: 0; top: 0; clip: rect(0 0 0 0); clip-path: inset(50%); overflow: hidden; white-space: nowrap; width: 1px; height: 1px",
|
||||
}
|
||||
customElements.define('route-announcer', class RouteAnnouncer extends HTMLElement {
|
||||
constructor() {
|
||||
super()
|
||||
}
|
||||
connectedCallback() {
|
||||
for (const [key, value] of Object.entries(attrs)) {
|
||||
this.setAttribute(key, value)
|
||||
customElements.define(
|
||||
"route-announcer",
|
||||
class RouteAnnouncer extends HTMLElement {
|
||||
constructor() {
|
||||
super()
|
||||
}
|
||||
}
|
||||
})
|
||||
connectedCallback() {
|
||||
for (const [key, value] of Object.entries(attrs)) {
|
||||
this.setAttribute(key, value)
|
||||
}
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
const bufferPx = 150
|
||||
const observer = new IntersectionObserver(entries => {
|
||||
const observer = new IntersectionObserver((entries) => {
|
||||
for (const entry of entries) {
|
||||
const slug = entry.target.id
|
||||
const tocEntryElement = document.querySelector(`a[data-for="${slug}"]`)
|
||||
|
@ -38,5 +38,5 @@ document.addEventListener("nav", () => {
|
|||
// update toc entry highlighting
|
||||
observer.disconnect()
|
||||
const headers = document.querySelectorAll("h1[id], h2[id], h3[id], h4[id], h5[id], h6[id]")
|
||||
headers.forEach(header => observer.observe(header))
|
||||
headers.forEach((header) => observer.observe(header))
|
||||
})
|
||||
|
|
|
@ -15,7 +15,7 @@ export function registerEscapeHandler(outsideContainer: HTMLElement | null, cb:
|
|||
outsideContainer?.removeEventListener("click", click)
|
||||
outsideContainer?.addEventListener("click", click)
|
||||
document.removeEventListener("keydown", esc)
|
||||
document.addEventListener('keydown', esc)
|
||||
document.addEventListener("keydown", esc)
|
||||
}
|
||||
|
||||
export function removeAllChildren(node: HTMLElement) {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue