From 8bfee04c8c6948a88114d53769d4bb89b8ec7bf5 Mon Sep 17 00:00:00 2001
From: Jacky Zhao <j.zhao2k19@gmail.com>
Date: Sat, 17 Jun 2023 23:05:46 +0000
Subject: [PATCH] popovers
---
quartz/plugins/transformers/links.ts | 2
quartz/plugins/index.ts | 3
package-lock.json | 14 ++++
package.json | 1
quartz/components/scripts/popover.inline.ts | 41 +++++++++++++
quartz/components/Content.tsx | 30 ++++++++-
quartz/components/styles/popover.scss | 43 ++++++++++++++
quartz/plugins/emitters/contentPage.tsx | 12 ++-
quartz.config.ts | 1
quartz/components/scripts/toc.inline.ts | 12 ++-
10 files changed, 143 insertions(+), 16 deletions(-)
diff --git a/package-lock.json b/package-lock.json
index 6d922f4..eb3a121 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -9,6 +9,7 @@
"version": "4.0.3",
"license": "MIT",
"dependencies": {
+ "@floating-ui/dom": "^1.4.0",
"@inquirer/prompts": "^1.0.3",
"@napi-rs/simple-git": "^0.1.8",
"chalk": "^4.1.2",
@@ -393,6 +394,19 @@
"node": ">=12"
}
},
+ "node_modules/@floating-ui/core": {
+ "version": "1.3.1",
+ "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.3.1.tgz",
+ "integrity": "sha512-Bu+AMaXNjrpjh41znzHqaz3r2Nr8hHuHZT6V2LBKMhyMl0FgKA62PNYbqnfgmzOhoWZj70Zecisbo4H1rotP5g=="
+ },
+ "node_modules/@floating-ui/dom": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.4.0.tgz",
+ "integrity": "sha512-b4F0iWffLiqb/TpP2PWVOixrZqE6ni+6VT64AmFH7sJIF3SFPLbe6/h3jQ5Cwffs+HaC9A8V0TQzCPBwVvziIA==",
+ "dependencies": {
+ "@floating-ui/core": "^1.3.1"
+ }
+ },
"node_modules/@inquirer/checkbox": {
"version": "1.2.8",
"resolved": "https://registry.npmjs.org/@inquirer/checkbox/-/checkbox-1.2.8.tgz",
diff --git a/package.json b/package.json
index 810cf5e..3e42cf7 100644
--- a/package.json
+++ b/package.json
@@ -25,6 +25,7 @@
"quartz": "./quartz/bootstrap-cli.mjs"
},
"dependencies": {
+ "@floating-ui/dom": "^1.4.0",
"@inquirer/prompts": "^1.0.3",
"@napi-rs/simple-git": "^0.1.8",
"chalk": "^4.1.2",
diff --git a/quartz.config.ts b/quartz.config.ts
index e18f8ba..5795672 100644
--- a/quartz.config.ts
+++ b/quartz.config.ts
@@ -64,6 +64,7 @@
Component.ReadingTime(),
Component.TagList(),
],
+ content: Component.Content(),
left: [
Component.TableOfContents(),
],
diff --git a/quartz/components/Content.tsx b/quartz/components/Content.tsx
index cc5d66a..0bcab1e 100644
--- a/quartz/components/Content.tsx
+++ b/quartz/components/Content.tsx
@@ -2,10 +2,30 @@
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
diff --git a/quartz/components/scripts/popover.inline.ts b/quartz/components/scripts/popover.inline.ts
new file mode 100644
index 0000000..24c6aec
--- /dev/null
+++ b/quartz/components/scripts/popover.inline.ts
@@ -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"
+ })
+ }
+})
diff --git a/quartz/components/scripts/toc.inline.ts b/quartz/components/scripts/toc.inline.ts
index 105889d..d6cd50a 100644
--- a/quartz/components/scripts/toc.inline.ts
+++ b/quartz/components/scripts/toc.inline.ts
@@ -22,11 +22,13 @@
}
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)
diff --git a/quartz/components/styles/popover.scss b/quartz/components/styles/popover.scss
new file mode 100644
index 0000000..0d26d7d
--- /dev/null
+++ b/quartz/components/styles/popover.scss
@@ -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;
+}
diff --git a/quartz/plugins/emitters/contentPage.tsx b/quartz/plugins/emitters/contentPage.tsx
index ea626f5..4728920 100644
--- a/quartz/plugins/emitters/contentPage.tsx
+++ b/quartz/plugins/emitters/contentPage.tsx
@@ -6,12 +6,12 @@
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 @@
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 @@
tree
}
+ const Content = opts.content
const doc = <html>
<Head {...componentData} />
<body data-slug={file.data.slug}>
@@ -61,12 +61,14 @@
<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">
diff --git a/quartz/plugins/index.ts b/quartz/plugins/index.ts
index 7e665fc..358c59e 100644
--- a/quartz/plugins/index.ts
+++ b/quartz/plugins/index.ts
@@ -14,7 +14,8 @@
}
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) {
diff --git a/quartz/plugins/transformers/links.ts b/quartz/plugins/transformers/links.ts
index 1619344..4bf0e08 100644
--- a/quartz/plugins/transformers/links.ts
+++ b/quartz/plugins/transformers/links.ts
@@ -48,6 +48,8 @@
// 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
--
Gitblit v1.10.0