diff --git a/quartz.config.ts b/quartz.config.ts index 25518d5fa..01412f91e 100644 --- a/quartz.config.ts +++ b/quartz.config.ts @@ -1,4 +1,5 @@ import { QuartzConfig } from "./quartz/cfg" +import Body from "./quartz/components/Body" import Head from "./quartz/components/Head" import Header from "./quartz/components/Header" import { @@ -68,7 +69,8 @@ const config: QuartzConfig = { emitters: [ new ContentPage({ Head: Head, - Header: Header + Header: Header, + Body: Body }) ] }, diff --git a/quartz/components/Body.tsx b/quartz/components/Body.tsx new file mode 100644 index 000000000..1d9296b5e --- /dev/null +++ b/quartz/components/Body.tsx @@ -0,0 +1,18 @@ +import { ComponentChildren } from "preact" +import clipboardScript from './scripts/clipboard.inline' +import clipboardStyle from './styles/clipboard.scss' + +export interface BodyProps { + title?: string + children: ComponentChildren +} + +export default function Body({ title, children }: BodyProps) { + return
+ {title &&

{title}

} + {children} +
+} + +Body.afterDOMLoaded = clipboardScript +Body.css = clipboardStyle diff --git a/quartz/components/Darkmode.tsx b/quartz/components/Darkmode.tsx index ae6788cb3..2170253c1 100644 --- a/quartz/components/Darkmode.tsx +++ b/quartz/components/Darkmode.tsx @@ -2,7 +2,7 @@ // modules are automatically deferred and we don't want that to happen for critical beforeDOMLoads // see: https://v8.dev/features/modules#defer import darkmodeScript from "./scripts/darkmode.inline" -import styles from '../styles/darkmode.scss' +import styles from './styles/darkmode.scss' export default function Darkmode() { return
diff --git a/quartz/components/Header.tsx b/quartz/components/Header.tsx index cb2fae8d7..7005e64d0 100644 --- a/quartz/components/Header.tsx +++ b/quartz/components/Header.tsx @@ -1,6 +1,6 @@ import { resolveToRoot } from "../path" import Darkmode from "./Darkmode" -import style from '../styles/header.scss' +import style from './styles/header.scss' export interface HeaderProps { title: string diff --git a/quartz/components/scripts/clipboard.inline.ts b/quartz/components/scripts/clipboard.inline.ts new file mode 100644 index 000000000..efddfa6f3 --- /dev/null +++ b/quartz/components/scripts/clipboard.inline.ts @@ -0,0 +1,32 @@ +const description = "Initialize copy for codeblocks" +export default description + +const svgCopy = + '' +const svgCheck = + '' + +const els = document.getElementsByTagName("pre") +for (let i = 0; i < els.length; i++) { + const codeBlock = els[i].getElementsByTagName("code")[0] + const source = codeBlock.innerText.replace(/\n\n/g, "\n") + const button = document.createElement("button") + button.className = "clipboard-button" + button.type = "button" + button.innerHTML = svgCopy + button.ariaLabel = "Copy source" + button.addEventListener("click", () => { + navigator.clipboard.writeText(source).then( + () => { + button.blur() + button.innerHTML = svgCheck + setTimeout(() => { + button.innerHTML = svgCopy + button.style.borderColor = "" + }, 2000) + }, + (error) => console.error(error), + ) + }) + els[i].prepend(button) +} diff --git a/quartz/components/styles/clipboard.scss b/quartz/components/styles/clipboard.scss new file mode 100644 index 000000000..1702a7bb4 --- /dev/null +++ b/quartz/components/styles/clipboard.scss @@ -0,0 +1,37 @@ +.clipboard-button { + position: absolute; + display: flex; + float: right; + right: 0; + padding: 0.4rem; + margin: -0.2rem 0.3rem; + color: var(--gray); + border-color: var(--dark); + background-color: var(--light); + border: 1px solid; + border-radius: 5px; + z-index: 1; + opacity: 0; + transition: 0.2s; + + & > svg { + fill: var(--light); + filter: contrast(0.3); + } + + &:hover { + cursor: pointer; + border-color: var(--secondary); + } + + &:focus { + outline: 0; + } +} + +pre { + &:hover > .clipboard-button { + opacity: 1; + transition: 0.2s; + } +} diff --git a/quartz/styles/darkmode.scss b/quartz/components/styles/darkmode.scss similarity index 100% rename from quartz/styles/darkmode.scss rename to quartz/components/styles/darkmode.scss diff --git a/quartz/styles/header.scss b/quartz/components/styles/header.scss similarity index 100% rename from quartz/styles/header.scss rename to quartz/components/styles/header.scss diff --git a/quartz/plugins/emitters/contentPage.tsx b/quartz/plugins/emitters/contentPage.tsx index 61ddcfbd9..c4b357c14 100644 --- a/quartz/plugins/emitters/contentPage.tsx +++ b/quartz/plugins/emitters/contentPage.tsx @@ -9,10 +9,12 @@ import { GlobalConfiguration } from "../../cfg" import { HeaderProps } from "../../components/Header" import { QuartzComponent } from "../../components/types" import { resolveToRoot } from "../../path" +import { BodyProps } from "../../components/Body" interface Options { Head: QuartzComponent Header: QuartzComponent + Body: QuartzComponent } export class ContentPage extends QuartzEmitterPlugin { @@ -31,7 +33,7 @@ export class ContentPage extends QuartzEmitterPlugin { async emit(cfg: GlobalConfiguration, content: ProcessedContent[], resources: StaticResources, emit: EmitCallback): Promise { const fps: string[] = [] - const { Head, Header } = this.opts + const { Head, Header, Body } = this.opts for (const [tree, file] of content) { // @ts-ignore (preact makes it angry) const content = toJsxRuntime(tree, { Fragment, jsx, jsxs, elementAttributeNameCase: 'html' }) @@ -42,7 +44,7 @@ export class ContentPage extends QuartzEmitterPlugin { js: [ { src: baseDir + "/prescript.js", loadTime: "beforeDOMReady" }, ...resources.js, - { src: baseDir + "/postscript.js", loadTime: "afterDOMReady" } + { src: baseDir + "/postscript.js", loadTime: "afterDOMReady", type: 'module' } ] } @@ -56,10 +58,7 @@ export class ContentPage extends QuartzEmitterPlugin {
-
- {file.data.slug !== "index" &&

{title}

} - {content} -
+ {content}
{pageResources.js.filter(resource => resource.loadTime === "afterDOMReady").map(resource =>