From 317cce9314ad78d90714dc55aa82a2c3dfa75d1a Mon Sep 17 00:00:00 2001
From: Jacky Zhao <j.zhao2k19@gmail.com>
Date: Thu, 08 Jun 2023 05:27:32 +0000
Subject: [PATCH] generic quartz component for layout

---
 quartz/plugins/types.ts                    |    2 
 quartz/components/Header.tsx               |   18 +----
 quartz/components/Spacer.tsx               |    3 +
 quartz/plugins/index.ts                    |    2 
 quartz/plugins/transformers/description.ts |    6 +
 quartz/components/Body.tsx                 |   15 ++--
 quartz/components/PageTitle.tsx            |    9 +++
 quartz/components/types.ts                 |   14 ++++
 quartz/components/Head.tsx                 |   15 ++---
 quartz/plugins/emitters/contentPage.tsx    |   41 +++++++------
 quartz.config.ts                           |   10 ++-
 11 files changed, 77 insertions(+), 58 deletions(-)

diff --git a/quartz.config.ts b/quartz.config.ts
index 01412f9..65539a8 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 @@
     ],
     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 1d9296b..92e6682 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 <article>
-    {title && <h1>{title}</h1>}
+    <div class="top-section">
+      {displayTitle && <h1>{displayTitle}</h1>}
+    </div>
     {children}
   </article>
 }
diff --git a/quartz/components/Head.tsx b/quartz/components/Head.tsx
index 9e182c7..c56b8cb 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 <head>
     <title>{title}</title>
     <meta charSet="utf-8" />
diff --git a/quartz/components/Header.tsx b/quartz/components/Header.tsx
index 7005e64..8eb2d70 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 <header>
-    <h1><a href={baseDir}>{title}</a></h1>
-    <div class="spacer"></div>
-    <Darkmode />
+    {children}
   </header>
 }
 
-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 0000000..080b74d
--- /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 <h1><a href={baseDir}>{title}</a></h1>
+}
diff --git a/quartz/components/Spacer.tsx b/quartz/components/Spacer.tsx
new file mode 100644
index 0000000..02b1093
--- /dev/null
+++ b/quartz/components/Spacer.tsx
@@ -0,0 +1,3 @@
+export default function() {
+  return <div class="spacer"></div>
+}
diff --git a/quartz/components/types.ts b/quartz/components/types.ts
index 96ead5f..8d7a79c 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<Props> = ComponentType<Props> & {
+export type QuartzComponentProps = {
+  externalResources: StaticResources
+  fileData: QuartzPluginData
+  cfg: GlobalConfiguration
+  children: QuartzComponent[] | JSX.Element[]
+}
+
+export type QuartzComponent = ComponentType<QuartzComponentProps> & {
   css?: string,
   beforeDOMLoaded?: string,
   afterDOMLoaded?: string,
diff --git a/quartz/plugins/emitters/contentPage.tsx b/quartz/plugins/emitters/contentPage.tsx
index c4b357c..2ab914c 100644
--- a/quartz/plugins/emitters/contentPage.tsx
+++ b/quartz/plugins/emitters/contentPage.tsx
@@ -4,17 +4,16 @@
 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<HeadProps>
-  Header: QuartzComponent<HeaderProps>
-  Body: QuartzComponent<BodyProps>
+  head: QuartzComponent
+  header: QuartzComponent[],
+  body: QuartzComponent
 }
 
 export class ContentPage extends QuartzEmitterPlugin {
@@ -26,21 +25,21 @@
     this.opts = opts
   }
 
-  getQuartzComponents(): QuartzComponent<any>[] {
-    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<string[]> {
     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 @@
         ]
       }
 
-      const title = file.data.frontmatter?.title
+      const componentData: QuartzComponentProps = {
+        fileData: file.data,
+        externalResources: pageResources,
+        cfg,
+        children: [content]
+      }
+
       const doc = <html>
-        <Head
-          title={title ?? "Untitled"}
-          description={file.data.description ?? "No description provided"}
-          slug={file.data.slug!}
-          externalResources={pageResources} />
+        <Head {...componentData} />
         <body>
           <div id="quartz-root" class="page">
-            <Header title={cfg.siteTitle} slug={file.data.slug!} />
-            <Body title={file.data.slug === "index" ? undefined : title}>{content}</Body>
+            <Header {...componentData} >
+              {header.map(HeaderComponent => <HeaderComponent {...componentData}/>)}
+            </Header>
+            <Body {...componentData}>
+              {content}
+            </Body>
           </div>
         </body>
         {pageResources.js.filter(resource => resource.loadTime === "afterDOMReady").map(resource => <script key={resource.src} {...resource} />)}
diff --git a/quartz/plugins/index.ts b/quartz/plugins/index.ts
index e904a00..01348c9 100644
--- a/quartz/plugins/index.ts
+++ b/quartz/plugins/index.ts
@@ -17,7 +17,7 @@
 
 export function emitComponentResources(cfg: GlobalConfiguration, resources: StaticResources, plugins: PluginTypes, emit: EmitCallback) {
   const fps: string[] = []
-  const allComponents: Set<QuartzComponent<any>> = new Set()
+  const allComponents: Set<QuartzComponent> = new Set()
   for (const emitter of plugins.emitters) {
     const components = emitter.getQuartzComponents()
     for (const component of components) {
diff --git a/quartz/plugins/transformers/description.ts b/quartz/plugins/transformers/description.ts
index 065d3bf..fa59799 100644
--- a/quartz/plugins/transformers/description.ts
+++ b/quartz/plugins/transformers/description.ts
@@ -29,7 +29,9 @@
       () => {
         return async (tree: HTMLRoot, file) => {
           const frontMatterDescription = file.data.frontmatter?.description
-          const desc = frontMatterDescription ?? toString(tree)
+          const text = toString(tree)
+
+          const desc = frontMatterDescription ?? text
           const sentences = desc.replace(/\s+/g, ' ').split('.')
           let finalDesc = ""
           let sentenceIdx = 0
@@ -40,6 +42,7 @@
           }
 
           file.data.description = finalDesc
+          file.data.text = text
         }
       }
     ]
@@ -49,6 +52,7 @@
 declare module 'vfile' {
   interface DataMap {
     description: string
+    text: string
   }
 }
 
diff --git a/quartz/plugins/types.ts b/quartz/plugins/types.ts
index 80d6cee..11b07db 100644
--- a/quartz/plugins/types.ts
+++ b/quartz/plugins/types.ts
@@ -26,7 +26,7 @@
 export abstract class QuartzEmitterPlugin {
   abstract name: string
   abstract emit(cfg: GlobalConfiguration, content: ProcessedContent[], resources: StaticResources, emitCallback: EmitCallback): Promise<string[]>
-  abstract getQuartzComponents(): QuartzComponent<any>[]
+  abstract getQuartzComponents(): QuartzComponent[]
 }
 
 export interface PluginTypes {

--
Gitblit v1.10.0