generic quartz component for layout
This commit is contained in:
		@@ -1,7 +1,9 @@
 | 
				
			|||||||
import { QuartzConfig } from "./quartz/cfg"
 | 
					import { QuartzConfig } from "./quartz/cfg"
 | 
				
			||||||
import Body from "./quartz/components/Body"
 | 
					import Body from "./quartz/components/Body"
 | 
				
			||||||
 | 
					import Darkmode from "./quartz/components/Darkmode"
 | 
				
			||||||
import Head from "./quartz/components/Head"
 | 
					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 {
 | 
					import {
 | 
				
			||||||
  ContentPage,
 | 
					  ContentPage,
 | 
				
			||||||
  CreatedModifiedDate,
 | 
					  CreatedModifiedDate,
 | 
				
			||||||
@@ -68,9 +70,9 @@ const config: QuartzConfig = {
 | 
				
			|||||||
    ],
 | 
					    ],
 | 
				
			||||||
    emitters: [
 | 
					    emitters: [
 | 
				
			||||||
      new ContentPage({
 | 
					      new ContentPage({
 | 
				
			||||||
        Head: Head,
 | 
					        head: Head,
 | 
				
			||||||
        Header: Header,
 | 
					        header: [PageTitle, Spacer, Darkmode],
 | 
				
			||||||
        Body: Body
 | 
					        body: Body
 | 
				
			||||||
      })
 | 
					      })
 | 
				
			||||||
    ]
 | 
					    ]
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,15 +1,14 @@
 | 
				
			|||||||
import { ComponentChildren } from "preact"
 | 
					 | 
				
			||||||
import clipboardScript from './scripts/clipboard.inline'
 | 
					import clipboardScript from './scripts/clipboard.inline'
 | 
				
			||||||
import clipboardStyle from './styles/clipboard.scss'
 | 
					import clipboardStyle from './styles/clipboard.scss'
 | 
				
			||||||
 | 
					import { QuartzComponentProps } from "./types"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export interface BodyProps {
 | 
					export default function Body({ fileData, children }: QuartzComponentProps) {
 | 
				
			||||||
  title?: string
 | 
					  const title = fileData.frontmatter?.title
 | 
				
			||||||
  children: ComponentChildren
 | 
					  const displayTitle = fileData.slug === "index" ? undefined : title
 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export default function Body({ title, children }: BodyProps) {
 | 
					 | 
				
			||||||
  return <article>
 | 
					  return <article>
 | 
				
			||||||
    {title && <h1>{title}</h1>}
 | 
					    <div class="top-section">
 | 
				
			||||||
 | 
					      {displayTitle && <h1>{displayTitle}</h1>}
 | 
				
			||||||
 | 
					    </div>
 | 
				
			||||||
    {children}
 | 
					    {children}
 | 
				
			||||||
  </article>
 | 
					  </article>
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,18 +1,15 @@
 | 
				
			|||||||
import { resolveToRoot } from "../path"
 | 
					import { resolveToRoot } from "../path"
 | 
				
			||||||
import { StaticResources } from "../resources"
 | 
					import { QuartzComponentProps } from "./types"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export interface HeadProps {
 | 
					export default function Head({ fileData, externalResources }: QuartzComponentProps) {
 | 
				
			||||||
  title: string
 | 
					  const slug = fileData.slug!
 | 
				
			||||||
  description: string
 | 
					  const title = fileData.frontmatter?.title ?? "Untitled"
 | 
				
			||||||
  slug: string
 | 
					  const description = fileData.description ?? "No description provided"
 | 
				
			||||||
  externalResources: StaticResources
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export default function Head({ title, description, slug, externalResources }: HeadProps) {
 | 
					 | 
				
			||||||
  const { css, js } = externalResources
 | 
					  const { css, js } = externalResources
 | 
				
			||||||
  const baseDir = resolveToRoot(slug)
 | 
					  const baseDir = resolveToRoot(slug)
 | 
				
			||||||
  const iconPath = baseDir + "/static/icon.png"
 | 
					  const iconPath = baseDir + "/static/icon.png"
 | 
				
			||||||
  const ogImagePath = baseDir + "/static/og-image.png"
 | 
					  const ogImagePath = baseDir + "/static/og-image.png"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  return <head>
 | 
					  return <head>
 | 
				
			||||||
    <title>{title}</title>
 | 
					    <title>{title}</title>
 | 
				
			||||||
    <meta charSet="utf-8" />
 | 
					    <meta charSet="utf-8" />
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,20 +1,10 @@
 | 
				
			|||||||
import { resolveToRoot } from "../path"
 | 
					 | 
				
			||||||
import Darkmode from "./Darkmode"
 | 
					 | 
				
			||||||
import style from './styles/header.scss'
 | 
					import style from './styles/header.scss'
 | 
				
			||||||
 | 
					import { QuartzComponentProps } from "./types"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export interface HeaderProps {
 | 
					export default function Header({ children }: QuartzComponentProps) {
 | 
				
			||||||
  title: string
 | 
					 | 
				
			||||||
  slug: string
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export default function Header({ title, slug }: HeaderProps) {
 | 
					 | 
				
			||||||
  const baseDir = resolveToRoot(slug)
 | 
					 | 
				
			||||||
  return <header>
 | 
					  return <header>
 | 
				
			||||||
    <h1><a href={baseDir}>{title}</a></h1>
 | 
					    {children}
 | 
				
			||||||
    <div class="spacer"></div>
 | 
					 | 
				
			||||||
    <Darkmode />
 | 
					 | 
				
			||||||
  </header>
 | 
					  </header>
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Header.beforeDOMLoaded = Darkmode.beforeDOMLoaded
 | 
					Header.css = style
 | 
				
			||||||
Header.css = style + Darkmode.css
 | 
					 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										9
									
								
								quartz/components/PageTitle.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								quartz/components/PageTitle.tsx
									
									
									
									
									
										Normal file
									
								
							@@ -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>
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										3
									
								
								quartz/components/Spacer.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								quartz/components/Spacer.tsx
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,3 @@
 | 
				
			|||||||
 | 
					export default function() {
 | 
				
			||||||
 | 
					  return <div class="spacer"></div>
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -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,
 | 
					  css?: string,
 | 
				
			||||||
  beforeDOMLoaded?: string,
 | 
					  beforeDOMLoaded?: string,
 | 
				
			||||||
  afterDOMLoaded?: string,
 | 
					  afterDOMLoaded?: string,
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -4,17 +4,16 @@ import { EmitCallback, QuartzEmitterPlugin } from "../types"
 | 
				
			|||||||
import { ProcessedContent } from "../vfile"
 | 
					import { ProcessedContent } from "../vfile"
 | 
				
			||||||
import { Fragment, jsx, jsxs } from 'preact/jsx-runtime'
 | 
					import { Fragment, jsx, jsxs } from 'preact/jsx-runtime'
 | 
				
			||||||
import { render } from "preact-render-to-string"
 | 
					import { render } from "preact-render-to-string"
 | 
				
			||||||
import { HeadProps } from "../../components/Head"
 | 
					 | 
				
			||||||
import { GlobalConfiguration } from "../../cfg"
 | 
					import { GlobalConfiguration } from "../../cfg"
 | 
				
			||||||
import { HeaderProps } from "../../components/Header"
 | 
					 | 
				
			||||||
import { QuartzComponent } from "../../components/types"
 | 
					import { QuartzComponent } from "../../components/types"
 | 
				
			||||||
import { resolveToRoot } from "../../path"
 | 
					import { resolveToRoot } from "../../path"
 | 
				
			||||||
import { BodyProps } from "../../components/Body"
 | 
					import Header from "../../components/Header"
 | 
				
			||||||
 | 
					import { QuartzComponentProps } from "../../components/types"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
interface Options {
 | 
					interface Options {
 | 
				
			||||||
  Head: QuartzComponent<HeadProps>
 | 
					  head: QuartzComponent
 | 
				
			||||||
  Header: QuartzComponent<HeaderProps>
 | 
					  header: QuartzComponent[],
 | 
				
			||||||
  Body: QuartzComponent<BodyProps>
 | 
					  body: QuartzComponent
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export class ContentPage extends QuartzEmitterPlugin {
 | 
					export class ContentPage extends QuartzEmitterPlugin {
 | 
				
			||||||
@@ -26,21 +25,21 @@ export class ContentPage extends QuartzEmitterPlugin {
 | 
				
			|||||||
    this.opts = opts
 | 
					    this.opts = opts
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  getQuartzComponents(): QuartzComponent<any>[] {
 | 
					  getQuartzComponents(): QuartzComponent[] {
 | 
				
			||||||
    return [...Object.values(this.opts)]
 | 
					    return [this.opts.head, Header, ...this.opts.header, this.opts.body]
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  async emit(cfg: GlobalConfiguration, content: ProcessedContent[], resources: StaticResources, emit: EmitCallback): Promise<string[]> {
 | 
					  async emit(cfg: GlobalConfiguration, content: ProcessedContent[], resources: StaticResources, emit: EmitCallback): Promise<string[]> {
 | 
				
			||||||
    const fps: string[] = []
 | 
					    const fps: string[] = []
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const { Head, Header, Body } = this.opts
 | 
					    const { head: Head, header, body: Body } = this.opts
 | 
				
			||||||
    for (const [tree, file] of content) {
 | 
					    for (const [tree, file] of content) {
 | 
				
			||||||
      // @ts-ignore (preact makes it angry)
 | 
					      // @ts-ignore (preact makes it angry)
 | 
				
			||||||
      const content = toJsxRuntime(tree, { Fragment, jsx, jsxs, elementAttributeNameCase: 'html' })
 | 
					      const content = toJsxRuntime(tree, { Fragment, jsx, jsxs, elementAttributeNameCase: 'html' })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      const baseDir = resolveToRoot(file.data.slug!)
 | 
					      const baseDir = resolveToRoot(file.data.slug!)
 | 
				
			||||||
      const pageResources: StaticResources = {
 | 
					      const pageResources: StaticResources = {
 | 
				
			||||||
        css: [baseDir + "/index.css", ...resources.css,],
 | 
					        css: [baseDir + "/index.css", ...resources.css],
 | 
				
			||||||
        js: [
 | 
					        js: [
 | 
				
			||||||
          { src: baseDir + "/prescript.js", loadTime: "beforeDOMReady" },
 | 
					          { src: baseDir + "/prescript.js", loadTime: "beforeDOMReady" },
 | 
				
			||||||
          ...resources.js,
 | 
					          ...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 = <html>
 | 
					      const doc = <html>
 | 
				
			||||||
        <Head
 | 
					        <Head {...componentData} />
 | 
				
			||||||
          title={title ?? "Untitled"}
 | 
					 | 
				
			||||||
          description={file.data.description ?? "No description provided"}
 | 
					 | 
				
			||||||
          slug={file.data.slug!}
 | 
					 | 
				
			||||||
          externalResources={pageResources} />
 | 
					 | 
				
			||||||
        <body>
 | 
					        <body>
 | 
				
			||||||
          <div id="quartz-root" class="page">
 | 
					          <div id="quartz-root" class="page">
 | 
				
			||||||
            <Header title={cfg.siteTitle} slug={file.data.slug!} />
 | 
					            <Header {...componentData} >
 | 
				
			||||||
            <Body title={file.data.slug === "index" ? undefined : title}>{content}</Body>
 | 
					              {header.map(HeaderComponent => <HeaderComponent {...componentData}/>)}
 | 
				
			||||||
 | 
					            </Header>
 | 
				
			||||||
 | 
					            <Body {...componentData}>
 | 
				
			||||||
 | 
					              {content}
 | 
				
			||||||
 | 
					            </Body>
 | 
				
			||||||
          </div>
 | 
					          </div>
 | 
				
			||||||
        </body>
 | 
					        </body>
 | 
				
			||||||
        {pageResources.js.filter(resource => resource.loadTime === "afterDOMReady").map(resource => <script key={resource.src} {...resource} />)}
 | 
					        {pageResources.js.filter(resource => resource.loadTime === "afterDOMReady").map(resource => <script key={resource.src} {...resource} />)}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -17,7 +17,7 @@ function joinScripts(scripts: string[]): string {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
export function emitComponentResources(cfg: GlobalConfiguration, resources: StaticResources, plugins: PluginTypes, emit: EmitCallback) {
 | 
					export function emitComponentResources(cfg: GlobalConfiguration, resources: StaticResources, plugins: PluginTypes, emit: EmitCallback) {
 | 
				
			||||||
  const fps: string[] = []
 | 
					  const fps: string[] = []
 | 
				
			||||||
  const allComponents: Set<QuartzComponent<any>> = new Set()
 | 
					  const allComponents: Set<QuartzComponent> = new Set()
 | 
				
			||||||
  for (const emitter of plugins.emitters) {
 | 
					  for (const emitter of plugins.emitters) {
 | 
				
			||||||
    const components = emitter.getQuartzComponents()
 | 
					    const components = emitter.getQuartzComponents()
 | 
				
			||||||
    for (const component of components) {
 | 
					    for (const component of components) {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -29,7 +29,9 @@ export class Description extends QuartzTransformerPlugin {
 | 
				
			|||||||
      () => {
 | 
					      () => {
 | 
				
			||||||
        return async (tree: HTMLRoot, file) => {
 | 
					        return async (tree: HTMLRoot, file) => {
 | 
				
			||||||
          const frontMatterDescription = file.data.frontmatter?.description
 | 
					          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('.')
 | 
					          const sentences = desc.replace(/\s+/g, ' ').split('.')
 | 
				
			||||||
          let finalDesc = ""
 | 
					          let finalDesc = ""
 | 
				
			||||||
          let sentenceIdx = 0
 | 
					          let sentenceIdx = 0
 | 
				
			||||||
@@ -40,6 +42,7 @@ export class Description extends QuartzTransformerPlugin {
 | 
				
			|||||||
          }
 | 
					          }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
          file.data.description = finalDesc
 | 
					          file.data.description = finalDesc
 | 
				
			||||||
 | 
					          file.data.text = text
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    ]
 | 
					    ]
 | 
				
			||||||
@@ -49,6 +52,7 @@ export class Description extends QuartzTransformerPlugin {
 | 
				
			|||||||
declare module 'vfile' {
 | 
					declare module 'vfile' {
 | 
				
			||||||
  interface DataMap {
 | 
					  interface DataMap {
 | 
				
			||||||
    description: string
 | 
					    description: string
 | 
				
			||||||
 | 
					    text: string
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -26,7 +26,7 @@ export type EmitCallback = (data: EmitOptions) => Promise<string>
 | 
				
			|||||||
export abstract class QuartzEmitterPlugin {
 | 
					export abstract class QuartzEmitterPlugin {
 | 
				
			||||||
  abstract name: string
 | 
					  abstract name: string
 | 
				
			||||||
  abstract emit(cfg: GlobalConfiguration, content: ProcessedContent[], resources: StaticResources, emitCallback: EmitCallback): Promise<string[]>
 | 
					  abstract emit(cfg: GlobalConfiguration, content: ProcessedContent[], resources: StaticResources, emitCallback: EmitCallback): Promise<string[]>
 | 
				
			||||||
  abstract getQuartzComponents(): QuartzComponent<any>[]
 | 
					  abstract getQuartzComponents(): QuartzComponent[]
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export interface PluginTypes {
 | 
					export interface PluginTypes {
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user