generic quartz component for layout
This commit is contained in:
parent
ef54a58cf1
commit
b7fc4ad54c
@ -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 {
|
||||||
|
Loading…
Reference in New Issue
Block a user