This commit is contained in:
Jacky Zhao 2023-06-17 16:05:46 -07:00
parent cb89cce183
commit 8bfee04c8c
10 changed files with 143 additions and 16 deletions

View file

@ -2,10 +2,30 @@ import { QuartzComponentConstructor, QuartzComponentProps } from "./types"
import { Fragment, jsx, jsxs } from 'preact/jsx-runtime'
import { toJsxRuntime } from "hast-util-to-jsx-runtime"
function Content({ tree }: QuartzComponentProps) {
// @ts-ignore (preact makes it angry)
const content = toJsxRuntime(tree, { Fragment, jsx, jsxs, elementAttributeNameCase: 'html' })
return <article>{content}</article>
// @ts-ignore
import popoverScript from './scripts/popover.inline'
import popoverStyle from './styles/popover.scss'
interface Options {
enablePopover: boolean
}
export default (() => Content) satisfies QuartzComponentConstructor
const defaultOptions: Options = {
enablePopover: true
}
export default ((opts?: Partial<Options>) => {
function Content({ tree }: QuartzComponentProps) {
// @ts-ignore (preact makes it angry)
const content = toJsxRuntime(tree, { Fragment, jsx, jsxs, elementAttributeNameCase: 'html' })
return <article>{content}</article>
}
const enablePopover = opts?.enablePopover ?? defaultOptions.enablePopover
if (enablePopover) {
Content.afterDOMLoaded = popoverScript
Content.css = popoverStyle
}
return Content
}) satisfies QuartzComponentConstructor

View file

@ -0,0 +1,41 @@
import { computePosition, inline, shift, autoPlacement } from "@floating-ui/dom"
document.addEventListener("nav", () => {
const links = [...document.getElementsByClassName("internal")] as HTMLLinkElement[]
const p = new DOMParser()
for (const link of links) {
link.addEventListener("mouseenter", async ({ clientX, clientY }) => {
if (link.dataset.fetchedPopover === "true") return
const url = link.href
const contents = await fetch(`${url}`)
.then((res) => res.text())
.catch((err) => {
console.error(err)
})
if (!contents) return
const html = p.parseFromString(contents, "text/html")
const elts = [...html.getElementsByClassName("popover-hint")]
if (elts.length === 0) return
const popoverElement = document.createElement("div")
popoverElement.classList.add("popover")
elts.forEach(elt => popoverElement.appendChild(elt))
const { x, y } = await computePosition(link, popoverElement, {
middleware: [inline({
x: clientX,
y: clientY
}), shift(), autoPlacement()]
})
Object.assign(popoverElement.style, {
left: `${x}px`,
top: `${y}px`,
})
link.appendChild(popoverElement)
link.dataset.fetchedPopover = "true"
})
}
})

View file

@ -22,11 +22,13 @@ function toggleToc(this: HTMLElement) {
}
function setupToc() {
const toc = document.getElementById("toc")!
const content = toc.nextElementSibling as HTMLElement
content.style.maxHeight = content.scrollHeight + "px"
toc.removeEventListener("click", toggleToc)
toc.addEventListener("click", toggleToc)
const toc = document.getElementById("toc")
if (toc) {
const content = toc.nextElementSibling as HTMLElement
content.style.maxHeight = content.scrollHeight + "px"
toc.removeEventListener("click", toggleToc)
toc.addEventListener("click", toggleToc)
}
}
window.addEventListener("resize", setupToc)

View file

@ -0,0 +1,43 @@
@keyframes dropin {
0% {
opacity: 0;
visibility: hidden;
}
50% {
opacity: 0;
}
100% {
opacity: 1;
visibility: visible;
}
}
.popover {
z-index: 999;
position: absolute;
overflow: scroll;
width: 30rem;
height: 20rem;
padding: 0 1rem;
margin-top: -1rem;
border: 1px solid var(--lightgray);
background-color: var(--light);
border-radius: 5px;
box-shadow: 6px 6px 36px 0 rgba(0,0,0,0.25);
font-weight: initial;
visibility: hidden;
opacity: 0;
transition: opacity 0.2s ease, visibility 0.2s ease;
@media all and (max-width: 600px) {
display: none !important;
}
}
a:hover .popover, .popover:hover {
animation: dropin 0.5s ease;
opacity: 1;
visibility: visible;
}

View file

@ -6,12 +6,12 @@ import { resolveToRoot } from "../../path"
import HeaderConstructor from "../../components/Header"
import { QuartzComponentProps } from "../../components/types"
import BodyConstructor from "../../components/Body"
import ContentConstructor from "../../components/Content"
interface Options {
head: QuartzComponent
header: QuartzComponent[],
beforeBody: QuartzComponent[],
content: QuartzComponent,
left: QuartzComponent[],
right: QuartzComponent[],
footer: QuartzComponent[],
@ -25,12 +25,11 @@ export const ContentPage: QuartzEmitterPlugin<Options> = (opts) => {
const { head: Head, header, beforeBody, left, right, footer } = opts
const Header = HeaderConstructor()
const Body = BodyConstructor()
const Content = ContentConstructor()
return {
name: "ContentPage",
getQuartzComponents() {
return [opts.head, Header, Body, ...opts.header, ...opts.beforeBody, ...opts.left, ...opts.right, ...opts.footer]
return [opts.head, Header, Body, ...opts.header, ...opts.beforeBody, opts.content, ...opts.left, ...opts.right, ...opts.footer]
},
async emit(_contentDir, cfg, content, resources, emit): Promise<string[]> {
const fps: string[] = []
@ -54,6 +53,7 @@ export const ContentPage: QuartzEmitterPlugin<Options> = (opts) => {
tree
}
const Content = opts.content
const doc = <html>
<Head {...componentData} />
<body data-slug={file.data.slug}>
@ -61,12 +61,14 @@ export const ContentPage: QuartzEmitterPlugin<Options> = (opts) => {
<Header {...componentData} >
{header.map(HeaderComponent => <HeaderComponent {...componentData} />)}
</Header>
{beforeBody.map(BodyComponent => <BodyComponent {...componentData} />)}
<div class="popover-hint">
{beforeBody.map(BodyComponent => <BodyComponent {...componentData} />)}
</div>
<Body {...componentData}>
<div class="left">
{left.map(BodyComponent => <BodyComponent {...componentData} />)}
</div>
<div class="center">
<div class="center popover-hint">
<Content {...componentData} />
</div>
<div class="right">

View file

@ -14,7 +14,8 @@ export type ComponentResources = {
}
function joinScripts(scripts: string[]): string {
return scripts.join("\n")
// wrap with iife to prevent scope collision
return scripts.map(script => `(function () {${script}})();`).join("\n")
}
export function emitComponentResources(cfg: GlobalConfiguration, resources: StaticResources, plugins: PluginTypes, emit: EmitCallback) {

View file

@ -48,6 +48,8 @@ export const ResolveLinks: QuartzTransformerPlugin<Partial<Options> | undefined>
// don't process external links or intra-document anchors
if (!(isAbsoluteUrl(node.properties.href) || node.properties.href.startsWith("#"))) {
node.properties.href = transformLink(node.properties.href)
} else {
}
// rewrite link internals if prettylinks is on