Jacky Zhao
2023-06-17 b5877824500a19c721c93eedc59704db94487a94
collapsible callout
1 files added
3 files modified
72 ■■■■ changed files
quartz/components/scripts/callout.inline.ts 24 ●●●●● patch | view | raw | blame | history
quartz/plugins/index.ts 2 ●●● patch | view | raw | blame | history
quartz/plugins/transformers/ofm.ts 30 ●●●● patch | view | raw | blame | history
quartz/styles/callouts.scss 16 ●●●●● patch | view | raw | blame | history
quartz/components/scripts/callout.inline.ts
New file
@@ -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<HTMLElement>
  for (const div of collapsible) {
    const title = div.firstElementChild
    setupCallout(div)
    title?.removeEventListener(`click`, toggleCallout)
    title?.addEventListener(`click`, toggleCallout)
  }
})
quartz/plugins/index.ts
@@ -83,7 +83,7 @@
  }
  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)
    }
quartz/plugins/transformers/ofm.ts
@@ -7,6 +7,8 @@
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 @@
                const defaultState = collapseChar === "-" ? "collapsed" : "expanded"
                const title = match.input.slice(calloutDirective.length).trim() || capitalize(calloutType)
                const toggleIcon = `<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="fold">
                  <polyline points="6 9 12 15 18 9"></polyline>
                </svg>`
                const titleNode: HTML = {
                  type: "html",
                  value: `<div 
@@ -217,6 +223,7 @@
                >
                  <div class="callout-icon">${callouts[canonicalizeCallout(calloutType)]}</div>
                  <div class="callout-title-inner">${title}</div>
                  ${collapse ? toggleIcon : ""}
                </div>`
                }
@@ -228,7 +235,6 @@
                      type: 'text',
                      value: remainingText,
                    }]
                  })
                }
@@ -236,7 +242,6 @@
                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,7 +278,18 @@
      return [rehypeRaw]
    },
    externalResources() {
      const mermaidScript: JSResource = {
      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 });
@@ -281,10 +297,12 @@
        loadTime: 'afterDOMReady',
        moduleType: 'module',
        contentType: 'inline'
        })
      }
      return {
        js: opts.mermaid ? [mermaidScript] : []
      }
      console.log(js)
      return { js }
    }
  }
}
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 {