From b7fc4ad54c4771a58f53d6e88c55f2f3c6d7a6ee Mon Sep 17 00:00:00 2001 From: Jacky Zhao Date: Wed, 7 Jun 2023 22:27:32 -0700 Subject: [PATCH] generic quartz component for layout --- quartz.config.ts | 10 +++--- quartz/components/Body.tsx | 15 ++++---- quartz/components/Head.tsx | 15 ++++---- quartz/components/Header.tsx | 18 +++------- quartz/components/PageTitle.tsx | 9 +++++ quartz/components/Spacer.tsx | 3 ++ quartz/components/types.ts | 14 ++++++-- quartz/plugins/emitters/contentPage.tsx | 41 ++++++++++++---------- quartz/plugins/index.ts | 2 +- quartz/plugins/transformers/description.ts | 6 +++- quartz/plugins/types.ts | 2 +- 11 files changed, 77 insertions(+), 58 deletions(-) create mode 100644 quartz/components/PageTitle.tsx create mode 100644 quartz/components/Spacer.tsx diff --git a/quartz.config.ts b/quartz.config.ts index 01412f91..65539a84 100644 --- a/quartz.config.ts +++ b/quartz.config.ts @@ -1,7 +1,9 @@ import { QuartzConfig } from "./quartz/cfg" import Body from "./quartz/components/Body" +import Darkmode from "./quartz/components/Darkmode" import Head from "./quartz/components/Head" -import Header from "./quartz/components/Header" +import PageTitle from "./quartz/components/PageTitle" +import Spacer from "./quartz/components/Spacer" import { ContentPage, CreatedModifiedDate, @@ -68,9 +70,9 @@ const config: QuartzConfig = { ], emitters: [ new ContentPage({ - Head: Head, - Header: Header, - Body: Body + head: Head, + header: [PageTitle, Spacer, Darkmode], + body: Body }) ] }, diff --git a/quartz/components/Body.tsx b/quartz/components/Body.tsx index 1d9296b5..92e66828 100644 --- a/quartz/components/Body.tsx +++ b/quartz/components/Body.tsx @@ -1,15 +1,14 @@ -import { ComponentChildren } from "preact" import clipboardScript from './scripts/clipboard.inline' import clipboardStyle from './styles/clipboard.scss' +import { QuartzComponentProps } from "./types" -export interface BodyProps { - title?: string - children: ComponentChildren -} - -export default function Body({ title, children }: BodyProps) { +export default function Body({ fileData, children }: QuartzComponentProps) { + const title = fileData.frontmatter?.title + const displayTitle = fileData.slug === "index" ? undefined : title return
- {title &&

{title}

} +
+ {displayTitle &&

{displayTitle}

} +
{children}
} diff --git a/quartz/components/Head.tsx b/quartz/components/Head.tsx index 9e182c7f..c56b8cbc 100644 --- a/quartz/components/Head.tsx +++ b/quartz/components/Head.tsx @@ -1,18 +1,15 @@ import { resolveToRoot } from "../path" -import { StaticResources } from "../resources" +import { QuartzComponentProps } from "./types" -export interface HeadProps { - title: string - description: string - slug: string - externalResources: StaticResources -} - -export default function Head({ title, description, slug, externalResources }: HeadProps) { +export default function Head({ fileData, externalResources }: QuartzComponentProps) { + const slug = fileData.slug! + const title = fileData.frontmatter?.title ?? "Untitled" + const description = fileData.description ?? "No description provided" const { css, js } = externalResources const baseDir = resolveToRoot(slug) const iconPath = baseDir + "/static/icon.png" const ogImagePath = baseDir + "/static/og-image.png" + return {title} diff --git a/quartz/components/Header.tsx b/quartz/components/Header.tsx index 7005e64d..8eb2d70d 100644 --- a/quartz/components/Header.tsx +++ b/quartz/components/Header.tsx @@ -1,20 +1,10 @@ -import { resolveToRoot } from "../path" -import Darkmode from "./Darkmode" import style from './styles/header.scss' +import { QuartzComponentProps } from "./types" -export interface HeaderProps { - title: string - slug: string -} - -export default function Header({ title, slug }: HeaderProps) { - const baseDir = resolveToRoot(slug) +export default function Header({ children }: QuartzComponentProps) { return
-

{title}

-
- + {children}
} -Header.beforeDOMLoaded = Darkmode.beforeDOMLoaded -Header.css = style + Darkmode.css +Header.css = style diff --git a/quartz/components/PageTitle.tsx b/quartz/components/PageTitle.tsx new file mode 100644 index 00000000..080b74d1 --- /dev/null +++ b/quartz/components/PageTitle.tsx @@ -0,0 +1,9 @@ +import { resolveToRoot } from "../path" +import { QuartzComponentProps } from "./types" + +export default function({ cfg, fileData }: QuartzComponentProps) { + const title = cfg.siteTitle + const slug = fileData.slug! + const baseDir = resolveToRoot(slug) + return

{title}

+} diff --git a/quartz/components/Spacer.tsx b/quartz/components/Spacer.tsx new file mode 100644 index 00000000..02b10935 --- /dev/null +++ b/quartz/components/Spacer.tsx @@ -0,0 +1,3 @@ +export default function() { + return
+} diff --git a/quartz/components/types.ts b/quartz/components/types.ts index 96ead5f9..8d7a79c1 100644 --- a/quartz/components/types.ts +++ b/quartz/components/types.ts @@ -1,6 +1,16 @@ -import { ComponentType } from "preact" +import { ComponentType, JSX } from "preact" +import { StaticResources } from "../resources" +import { QuartzPluginData } from "../plugins/vfile" +import { GlobalConfiguration } from "../cfg" -export type QuartzComponent = ComponentType & { +export type QuartzComponentProps = { + externalResources: StaticResources + fileData: QuartzPluginData + cfg: GlobalConfiguration + children: QuartzComponent[] | JSX.Element[] +} + +export type QuartzComponent = ComponentType & { css?: string, beforeDOMLoaded?: string, afterDOMLoaded?: string, diff --git a/quartz/plugins/emitters/contentPage.tsx b/quartz/plugins/emitters/contentPage.tsx index c4b357c1..2ab914cd 100644 --- a/quartz/plugins/emitters/contentPage.tsx +++ b/quartz/plugins/emitters/contentPage.tsx @@ -4,17 +4,16 @@ import { EmitCallback, QuartzEmitterPlugin } from "../types" import { ProcessedContent } from "../vfile" import { Fragment, jsx, jsxs } from 'preact/jsx-runtime' import { render } from "preact-render-to-string" -import { HeadProps } from "../../components/Head" import { GlobalConfiguration } from "../../cfg" -import { HeaderProps } from "../../components/Header" import { QuartzComponent } from "../../components/types" import { resolveToRoot } from "../../path" -import { BodyProps } from "../../components/Body" +import Header from "../../components/Header" +import { QuartzComponentProps } from "../../components/types" interface Options { - Head: QuartzComponent - Header: QuartzComponent - Body: QuartzComponent + head: QuartzComponent + header: QuartzComponent[], + body: QuartzComponent } export class ContentPage extends QuartzEmitterPlugin { @@ -26,21 +25,21 @@ export class ContentPage extends QuartzEmitterPlugin { this.opts = opts } - getQuartzComponents(): QuartzComponent[] { - return [...Object.values(this.opts)] + getQuartzComponents(): QuartzComponent[] { + return [this.opts.head, Header, ...this.opts.header, this.opts.body] } async emit(cfg: GlobalConfiguration, content: ProcessedContent[], resources: StaticResources, emit: EmitCallback): Promise { const fps: string[] = [] - const { Head, Header, Body } = this.opts + const { head: Head, header, body: Body } = this.opts for (const [tree, file] of content) { // @ts-ignore (preact makes it angry) const content = toJsxRuntime(tree, { Fragment, jsx, jsxs, elementAttributeNameCase: 'html' }) const baseDir = resolveToRoot(file.data.slug!) const pageResources: StaticResources = { - css: [baseDir + "/index.css", ...resources.css,], + css: [baseDir + "/index.css", ...resources.css], js: [ { src: baseDir + "/prescript.js", loadTime: "beforeDOMReady" }, ...resources.js, @@ -48,17 +47,23 @@ export class ContentPage extends QuartzEmitterPlugin { ] } - const title = file.data.frontmatter?.title + const componentData: QuartzComponentProps = { + fileData: file.data, + externalResources: pageResources, + cfg, + children: [content] + } + const doc = - +
-
- {content} +
+ {header.map(HeaderComponent => )} +
+ + {content} +
{pageResources.js.filter(resource => resource.loadTime === "afterDOMReady").map(resource =>