From ad8ac0b479a36ce75699511c82f5faf71db6d205 Mon Sep 17 00:00:00 2001 From: Jacky Zhao Date: Sat, 17 Jun 2023 13:08:06 -0700 Subject: [PATCH] collapsible callout --- quartz/components/scripts/callout.inline.ts | 24 +++++++++++++ quartz/plugins/index.ts | 2 +- quartz/plugins/transformers/ofm.ts | 38 +++++++++++++++------ quartz/styles/callouts.scss | 16 ++++++++- 4 files changed, 68 insertions(+), 12 deletions(-) create mode 100644 quartz/components/scripts/callout.inline.ts diff --git a/quartz/components/scripts/callout.inline.ts b/quartz/components/scripts/callout.inline.ts new file mode 100644 index 00000000..5f358731 --- /dev/null +++ b/quartz/components/scripts/callout.inline.ts @@ -0,0 +1,24 @@ +function toggleCallout(this: HTMLElement) { + const outerBlock = this.parentElement! + this.classList.toggle(`is-collapsed`) + const collapsed = this.classList.contains(`is-collapsed`) + const height = collapsed ? this.scrollHeight : outerBlock.scrollHeight + 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`, () => { + const collapsible = document.getElementsByClassName(`callout is-collapsible`) as HTMLCollectionOf + for (const div of collapsible) { + const title = div.firstElementChild + setupCallout(div) + title?.removeEventListener(`click`, toggleCallout) + title?.addEventListener(`click`, toggleCallout) + } +}) diff --git a/quartz/plugins/index.ts b/quartz/plugins/index.ts index 04de0d4c..7e665fc7 100644 --- a/quartz/plugins/index.ts +++ b/quartz/plugins/index.ts @@ -83,7 +83,7 @@ export function getStaticResourcesFromPlugins(plugins: PluginTypes) { } for (const transformer of plugins.transformers) { - const res = transformer.externalResources + const res = transformer.externalResources ? transformer.externalResources() : {} if (res?.js) { staticResources.js = staticResources.js.concat(res.js) } diff --git a/quartz/plugins/transformers/ofm.ts b/quartz/plugins/transformers/ofm.ts index aa839536..4ade4765 100644 --- a/quartz/plugins/transformers/ofm.ts +++ b/quartz/plugins/transformers/ofm.ts @@ -7,6 +7,8 @@ import rehypeRaw from "rehype-raw" import { visit } from "unist-util-visit" import path from "path" import { JSResource } from "../../resources" +// @ts-ignore +import calloutScript from "../../components/scripts/callout.inline.ts" export interface Options { highlight: boolean @@ -210,6 +212,10 @@ export const ObsidianFlavoredMarkdown: QuartzTransformerPlugin const defaultState = collapseChar === "-" ? "collapsed" : "expanded" const title = match.input.slice(calloutDirective.length).trim() || capitalize(calloutType) + const toggleIcon = ` + + ` + const titleNode: HTML = { type: "html", value: `
>
${callouts[canonicalizeCallout(calloutType)]}
${title}
+ ${collapse ? toggleIcon : ""}
` } @@ -228,7 +235,6 @@ export const ObsidianFlavoredMarkdown: QuartzTransformerPlugin type: 'text', value: remainingText, }] - }) } @@ -236,7 +242,6 @@ export const ObsidianFlavoredMarkdown: QuartzTransformerPlugin node.children.splice(0, 1, ...blockquoteContent) // add properties to base blockquote - // TODO: add the js to actually support collapsing callout node.data = { hProperties: { ...(node.data?.hProperties ?? {}), @@ -273,18 +278,31 @@ export const ObsidianFlavoredMarkdown: QuartzTransformerPlugin return [rehypeRaw] }, externalResources() { - const mermaidScript: JSResource = { - script: ` + const js: JSResource[] = [] + + if (opts.callouts) { + js.push({ + script: calloutScript, + loadTime: 'afterDOMReady', + contentType: 'inline' + }) + } + + if (opts.mermaid) { + js.push({ + script: ` import mermaid from 'https://cdn.jsdelivr.net/npm/mermaid/dist/mermaid.esm.min.mjs'; mermaid.initialize({ startOnLoad: true }); `, - loadTime: 'afterDOMReady', - moduleType: 'module', - contentType: 'inline' - } - return { - js: opts.mermaid ? [mermaidScript] : [] + loadTime: 'afterDOMReady', + moduleType: 'module', + contentType: 'inline' + }) } + + console.log(js) + + return { js } } } } diff --git a/quartz/styles/callouts.scss b/quartz/styles/callouts.scss index 26cb0ba4..84d70b4f 100644 --- a/quartz/styles/callouts.scss +++ b/quartz/styles/callouts.scss @@ -5,6 +5,8 @@ background-color: var(--bg); border-radius: 5px; padding: 0 1rem; + overflow-y: hidden; + transition: max-height 0.3s ease; &[data-callout="note"] { --color: #448aff; @@ -71,8 +73,20 @@ display: flex; align-items: center; gap: 5px; - margin: 1rem 0; + padding: 1rem 0; + margin-bottom: -1rem; color: var(--color); + + & .fold { + margin-left: 0.5rem; + transition: transform 0.3s ease; + opacity: 0.8; + cursor: pointer; + } + + &.is-collapsed .fold { + transform: rotateZ(-90deg) + } } .callout-icon {