| | |
| | | Plugin.ContentPage({ |
| | | head: Component.Head(), |
| | | header: [Component.PageTitle(), Component.Spacer(), Component.Darkmode()], |
| | | body: [ |
| | | beforeBody: [ |
| | | Component.ArticleTitle(), |
| | | Component.ReadingTime(), |
| | | Component.TagList(), |
| | | Component.TableOfContents(), |
| | | Component.Content() |
| | | ], |
| | | left: [], |
| | | right: [], |
| | | left: [ |
| | | Component.TableOfContents(), |
| | | ], |
| | | right: [ |
| | | ], |
| | | footer: [] |
| | | }), |
| | | Plugin.ContentIndex(), // you can exclude this if you don't plan on using popovers, graph, or backlinks, |
| | |
| | | const tags = fileData.frontmatter?.tags |
| | | const slug = fileData.slug! |
| | | const baseDir = resolveToRoot(slug) |
| | | if (tags) { |
| | | if (tags && tags.length > 0) { |
| | | return <ul class="tags">{tags.map(tag => { |
| | | const display = `#${tag}` |
| | | const linkDest = baseDir + `/tags/${slugAnchor(tag)}` |
| | |
| | | outerBlock.style.maxHeight = height + `px` |
| | | } |
| | | |
| | | function setupCallout(div: HTMLElement) { |
| | | const collapsed = div.classList.contains(`is-collapsed`) |
| | | const title = div.firstElementChild! |
| | | const height = collapsed ? title.scrollHeight : div.scrollHeight |
| | | div.style.maxHeight = height + `px` |
| | | } |
| | | |
| | | document.addEventListener(`nav`, () => { |
| | | function setupCallout() { |
| | | const collapsible = document.getElementsByClassName(`callout is-collapsible`) as HTMLCollectionOf<HTMLElement> |
| | | for (const div of collapsible) { |
| | | const title = div.firstElementChild |
| | | setupCallout(div) |
| | | title?.removeEventListener(`click`, toggleCallout) |
| | | title?.addEventListener(`click`, toggleCallout) |
| | | |
| | | if (title) { |
| | | title.removeEventListener(`click`, toggleCallout) |
| | | title.addEventListener(`click`, toggleCallout) |
| | | |
| | | const collapsed = div.classList.contains(`is-collapsed`) |
| | | const height = collapsed ? title.scrollHeight : div.scrollHeight |
| | | div.style.maxHeight = height + `px` |
| | | } |
| | | } |
| | | }) |
| | | } |
| | | |
| | | document.addEventListener(`nav`, setupCallout) |
| | | window.addEventListener(`resize`, setupCallout) |
| | |
| | | } |
| | | }) |
| | | |
| | | function toggleCollapsible(this: HTMLElement) { |
| | | function toggleToc(this: HTMLElement) { |
| | | this.classList.toggle("collapsed") |
| | | const content = this.nextElementSibling as HTMLElement |
| | | content.classList.toggle("collapsed") |
| | | content.style.maxHeight = content.style.maxHeight === "0px" ? content.scrollHeight + "px" : "0px" |
| | | } |
| | | |
| | | document.addEventListener("nav", () => { |
| | | function setupToc() { |
| | | const toc = document.getElementById("toc")! |
| | | const content = toc.nextElementSibling as HTMLElement |
| | | content.style.maxHeight = content.scrollHeight + "px" |
| | | toc.removeEventListener("click", toggleCollapsible) |
| | | toc.addEventListener("click", toggleCollapsible) |
| | | toc.removeEventListener("click", toggleToc) |
| | | toc.addEventListener("click", toggleToc) |
| | | } |
| | | |
| | | window.addEventListener("resize", setupToc) |
| | | document.addEventListener("nav", () => { |
| | | setupToc() |
| | | |
| | | // update toc entry highlighting |
| | | observer.disconnect() |
| | |
| | | min-width: 30px; |
| | | position: relative; |
| | | |
| | | @media all and (max-width: 450px) { |
| | | padding: 1rem; |
| | | } |
| | | |
| | | & > .toggle { |
| | | display: none; |
| | | box-sizing: border-box; |
| | |
| | | import HeaderConstructor from "../../components/Header" |
| | | import { QuartzComponentProps } from "../../components/types" |
| | | import BodyConstructor from "../../components/Body" |
| | | import ContentConstructor from "../../components/Content" |
| | | |
| | | interface Options { |
| | | head: QuartzComponent |
| | | header: QuartzComponent[], |
| | | body: QuartzComponent[], |
| | | beforeBody: QuartzComponent[], |
| | | left: QuartzComponent[], |
| | | right: QuartzComponent[], |
| | | footer: QuartzComponent[], |
| | |
| | | throw new Error("ContentPage must be initialized with options specifiying the components to use") |
| | | } |
| | | |
| | | const { head: Head, header, body } = opts |
| | | const { head: Head, header, beforeBody, left, right, footer } = opts |
| | | const Header = HeaderConstructor() |
| | | const Body = BodyConstructor() |
| | | const Content = ContentConstructor() |
| | | |
| | | return { |
| | | name: "ContentPage", |
| | | getQuartzComponents() { |
| | | return [opts.head, Header, Body, ...opts.header, ...opts.body, ...opts.left, ...opts.right, ...opts.footer] |
| | | return [opts.head, Header, Body, ...opts.header, ...opts.beforeBody, ...opts.left, ...opts.right, ...opts.footer] |
| | | }, |
| | | async emit(_contentDir, cfg, content, resources, emit): Promise<string[]> { |
| | | const fps: string[] = [] |
| | |
| | | <Header {...componentData} > |
| | | {header.map(HeaderComponent => <HeaderComponent {...componentData} />)} |
| | | </Header> |
| | | {beforeBody.map(BodyComponent => <BodyComponent {...componentData} />)} |
| | | <Body {...componentData}> |
| | | {body.map(BodyComponent => <BodyComponent {...componentData} />)} |
| | | <div class="left"> |
| | | {left.map(BodyComponent => <BodyComponent {...componentData} />)} |
| | | </div> |
| | | <div class="center"> |
| | | <Content {...componentData} /> |
| | | </div> |
| | | <div class="right"> |
| | | {right.map(BodyComponent => <BodyComponent {...componentData} />)} |
| | | </div> |
| | | </Body> |
| | | |
| | | </div> |
| | | </body> |
| | | {pageResources.js.filter(resource => resource.loadTime === "afterDOMReady").map(res => JSResourceToScriptElement(res))} |
| | |
| | | }) |
| | | } |
| | | |
| | | console.log(js) |
| | | |
| | | return { js } |
| | | } |
| | | } |
| | |
| | | padding: 4rem 30vw; |
| | | margin: 0 auto; |
| | | max-width: 1000px; |
| | | position: relative; |
| | | |
| | | & .left, & .right { |
| | | position: fixed; |
| | | padding: 0 4rem 0 6rem; |
| | | max-width: 30vw; |
| | | box-sizing: border-box; |
| | | top: 10rem; |
| | | } |
| | | |
| | | & .left { |
| | | left: 0; |
| | | } |
| | | |
| | | & .right { |
| | | right: 0; |
| | | } |
| | | |
| | | @media all and (max-width: 1200px) { |
| | | padding: 25px 5vw; |
| | | & .left, & .right { |
| | | padding: 0; |
| | | max-width: none; |
| | | position: initial; |
| | | } |
| | | } |
| | | |
| | | & p { |
| | |
| | | overflow-y: hidden; |
| | | transition: max-height 0.3s ease; |
| | | |
| | | & > *:nth-child(2) { |
| | | margin-top: 0; |
| | | } |
| | | |
| | | &[data-callout="note"] { |
| | | --color: #448aff; |
| | | --border: #448aff22; |
| | |
| | | align-items: center; |
| | | gap: 5px; |
| | | padding: 1rem 0; |
| | | margin-bottom: -1rem; |
| | | color: var(--color); |
| | | |
| | | & .fold { |