Aaron Pham
2024-02-05 e58c217de11680cb09fa29ccd5f1493bcb77a60a
feat: support checkbox (closes #646) (#799)

* feat: support checkbox

Signed-off-by: Aaron <29749331+aarnphm@users.noreply.github.com>

* chore: apply review from jacky

---------

Signed-off-by: Aaron <29749331+aarnphm@users.noreply.github.com>
1 files added
1 files modified
53 ■■■■■ changed files
quartz/components/scripts/checkbox.inline.ts 23 ●●●●● patch | view | raw | blame | history
quartz/plugins/transformers/ofm.ts 30 ●●●●● patch | view | raw | blame | history
quartz/components/scripts/checkbox.inline.ts
New file
@@ -0,0 +1,23 @@
import { getFullSlug } from "../../util/path"
const checkboxId = (index: number) => `${getFullSlug(window)}-checkbox-${index}`
document.addEventListener("nav", () => {
  const checkboxes = document.querySelectorAll(
    "input.checkbox-toggle",
  ) as NodeListOf<HTMLInputElement>
  checkboxes.forEach((el, index) => {
    const elId = checkboxId(index)
    const switchState = (e: Event) => {
      const newCheckboxState = (e.target as HTMLInputElement)?.checked ? "true" : "false"
      localStorage.setItem(elId, newCheckboxState)
    }
    el.addEventListener("change", switchState)
    window.addCleanup(() => el.removeEventListener("change", switchState))
    if (localStorage.getItem(elId) === "true") {
      el.checked = true
    }
  })
})
quartz/plugins/transformers/ofm.ts
@@ -9,6 +9,8 @@
import { JSResource } from "../../util/resources"
// @ts-ignore
import calloutScript from "../../components/scripts/callout.inline.ts"
// @ts-ignore
import checkboxScript from "../../components/scripts/checkbox.inline.ts"
import { FilePath, pathToRoot, slugTag, slugifyFilePath } from "../../util/path"
import { toHast } from "mdast-util-to-hast"
import { toHtml } from "hast-util-to-html"
@@ -28,6 +30,7 @@
  enableInHtmlEmbed: boolean
  enableYouTubeEmbed: boolean
  enableVideoEmbed: boolean
  enableCheckbox: boolean
}
const defaultOptions: Options = {
@@ -42,6 +45,7 @@
  enableInHtmlEmbed: false,
  enableYouTubeEmbed: true,
  enableVideoEmbed: true,
  enableCheckbox: false,
}
const calloutMapping = {
@@ -554,11 +558,37 @@
        })
      }
      if (opts.enableCheckbox) {
        plugins.push(() => {
          return (tree: HtmlRoot, _file) => {
            visit(tree, "element", (node) => {
              if (node.tagName === "input" && node.properties.type === "checkbox") {
                const isChecked = node.properties?.checked ?? false
                node.properties = {
                  type: "checkbox",
                  disabled: false,
                  checked: isChecked,
                  class: "checkbox-toggle",
                }
              }
            })
          }
        })
      }
      return plugins
    },
    externalResources() {
      const js: JSResource[] = []
      if (opts.enableCheckbox) {
        js.push({
          script: checkboxScript,
          loadTime: "afterDOMReady",
          contentType: "inline",
        })
      }
      if (opts.callouts) {
        js.push({
          script: calloutScript,