2 files added
8 files modified
| | |
| | | "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", |
| | |
| | | "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", |
| | |
| | | "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", |
| | |
| | | Component.ReadingTime(), |
| | | Component.TagList(), |
| | | ], |
| | | content: Component.Content(), |
| | | left: [ |
| | | Component.TableOfContents(), |
| | | ], |
| | |
| | | import { Fragment, jsx, jsxs } from 'preact/jsx-runtime' |
| | | import { toJsxRuntime } from "hast-util-to-jsx-runtime" |
| | | |
| | | // @ts-ignore |
| | | import popoverScript from './scripts/popover.inline' |
| | | import popoverStyle from './styles/popover.scss' |
| | | |
| | | interface Options { |
| | | enablePopover: boolean |
| | | } |
| | | |
| | | 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> |
| | | } |
| | | |
| | | export default (() => Content) satisfies QuartzComponentConstructor |
| | | const enablePopover = opts?.enablePopover ?? defaultOptions.enablePopover |
| | | if (enablePopover) { |
| | | Content.afterDOMLoaded = popoverScript |
| | | Content.css = popoverStyle |
| | | } |
| | | |
| | | return Content |
| | | }) satisfies QuartzComponentConstructor |
| New file |
| | |
| | | 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" |
| | | }) |
| | | } |
| | | }) |
| | |
| | | } |
| | | |
| | | function setupToc() { |
| | | const toc = document.getElementById("toc")! |
| | | 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) |
| | | document.addEventListener("nav", () => { |
| New file |
| | |
| | | @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; |
| | | } |
| | |
| | | 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[], |
| | |
| | | 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[] = [] |
| | |
| | | tree |
| | | } |
| | | |
| | | const Content = opts.content |
| | | const doc = <html> |
| | | <Head {...componentData} /> |
| | | <body data-slug={file.data.slug}> |
| | |
| | | <Header {...componentData} > |
| | | {header.map(HeaderComponent => <HeaderComponent {...componentData} />)} |
| | | </Header> |
| | | <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"> |
| | |
| | | } |
| | | |
| | | 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) { |
| | |
| | | // 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 |