feat: support non-singleton explorer
This commit is contained in:
		@@ -3,7 +3,7 @@ import style from "./styles/backlinks.scss"
 | 
			
		||||
import { resolveRelative, simplifySlug } from "../util/path"
 | 
			
		||||
import { i18n } from "../i18n"
 | 
			
		||||
import { classNames } from "../util/lang"
 | 
			
		||||
import OverflowList from "./OverflowList"
 | 
			
		||||
import OverflowListFactory from "./OverflowList"
 | 
			
		||||
 | 
			
		||||
interface BacklinksOptions {
 | 
			
		||||
  hideWhenEmpty: boolean
 | 
			
		||||
@@ -15,6 +15,7 @@ const defaultOptions: BacklinksOptions = {
 | 
			
		||||
 | 
			
		||||
export default ((opts?: Partial<BacklinksOptions>) => {
 | 
			
		||||
  const options: BacklinksOptions = { ...defaultOptions, ...opts }
 | 
			
		||||
  const { OverflowList, overflowListAfterDOMLoaded } = OverflowListFactory()
 | 
			
		||||
 | 
			
		||||
  const Backlinks: QuartzComponent = ({
 | 
			
		||||
    fileData,
 | 
			
		||||
@@ -30,7 +31,7 @@ export default ((opts?: Partial<BacklinksOptions>) => {
 | 
			
		||||
    return (
 | 
			
		||||
      <div class={classNames(displayClass, "backlinks")}>
 | 
			
		||||
        <h3>{i18n(cfg.locale).components.backlinks.title}</h3>
 | 
			
		||||
        <OverflowList id="backlinks-ul">
 | 
			
		||||
        <OverflowList>
 | 
			
		||||
          {backlinkFiles.length > 0 ? (
 | 
			
		||||
            backlinkFiles.map((f) => (
 | 
			
		||||
              <li>
 | 
			
		||||
@@ -48,7 +49,7 @@ export default ((opts?: Partial<BacklinksOptions>) => {
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  Backlinks.css = style
 | 
			
		||||
  Backlinks.afterDOMLoaded = OverflowList.afterDOMLoaded("backlinks-ul")
 | 
			
		||||
  Backlinks.afterDOMLoaded = overflowListAfterDOMLoaded
 | 
			
		||||
 | 
			
		||||
  return Backlinks
 | 
			
		||||
}) satisfies QuartzComponentConstructor
 | 
			
		||||
 
 | 
			
		||||
@@ -6,7 +6,8 @@ import script from "./scripts/explorer.inline"
 | 
			
		||||
import { classNames } from "../util/lang"
 | 
			
		||||
import { i18n } from "../i18n"
 | 
			
		||||
import { FileTrieNode } from "../util/fileTrie"
 | 
			
		||||
import OverflowList from "./OverflowList"
 | 
			
		||||
import OverflowListFactory from "./OverflowList"
 | 
			
		||||
import { concatenateResources } from "../util/resources"
 | 
			
		||||
 | 
			
		||||
type OrderEntries = "sort" | "filter" | "map"
 | 
			
		||||
 | 
			
		||||
@@ -56,6 +57,7 @@ export type FolderState = {
 | 
			
		||||
 | 
			
		||||
export default ((userOpts?: Partial<Options>) => {
 | 
			
		||||
  const opts: Options = { ...defaultOptions, ...userOpts }
 | 
			
		||||
  const { OverflowList, overflowListAfterDOMLoaded } = OverflowListFactory()
 | 
			
		||||
 | 
			
		||||
  const Explorer: QuartzComponent = ({ cfg, displayClass }: QuartzComponentProps) => {
 | 
			
		||||
    return (
 | 
			
		||||
@@ -73,8 +75,7 @@ export default ((userOpts?: Partial<Options>) => {
 | 
			
		||||
      >
 | 
			
		||||
        <button
 | 
			
		||||
          type="button"
 | 
			
		||||
          id="mobile-explorer"
 | 
			
		||||
          class="explorer-toggle hide-until-loaded"
 | 
			
		||||
          class="explorer-toggle mobile-explorer hide-until-loaded"
 | 
			
		||||
          data-mobile={true}
 | 
			
		||||
          aria-controls="explorer-content"
 | 
			
		||||
        >
 | 
			
		||||
@@ -95,8 +96,7 @@ export default ((userOpts?: Partial<Options>) => {
 | 
			
		||||
        </button>
 | 
			
		||||
        <button
 | 
			
		||||
          type="button"
 | 
			
		||||
          id="desktop-explorer"
 | 
			
		||||
          class="title-button explorer-toggle"
 | 
			
		||||
          class="title-button explorer-toggle desktop-explorer"
 | 
			
		||||
          data-mobile={false}
 | 
			
		||||
          aria-expanded={true}
 | 
			
		||||
        >
 | 
			
		||||
@@ -116,8 +116,8 @@ export default ((userOpts?: Partial<Options>) => {
 | 
			
		||||
            <polyline points="6 9 12 15 18 9"></polyline>
 | 
			
		||||
          </svg>
 | 
			
		||||
        </button>
 | 
			
		||||
        <div id="explorer-content" aria-expanded={false}>
 | 
			
		||||
          <OverflowList id="explorer-ul" />
 | 
			
		||||
        <div class="explorer-content" aria-expanded={false}>
 | 
			
		||||
          <OverflowList class="explorer-ul" />
 | 
			
		||||
        </div>
 | 
			
		||||
        <template id="template-file">
 | 
			
		||||
          <li>
 | 
			
		||||
@@ -157,6 +157,6 @@ export default ((userOpts?: Partial<Options>) => {
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  Explorer.css = style
 | 
			
		||||
  Explorer.afterDOMLoaded = script + OverflowList.afterDOMLoaded("explorer-ul")
 | 
			
		||||
  Explorer.afterDOMLoaded = concatenateResources(script, overflowListAfterDOMLoaded)
 | 
			
		||||
  return Explorer
 | 
			
		||||
}) satisfies QuartzComponentConstructor
 | 
			
		||||
 
 | 
			
		||||
@@ -1,22 +1,31 @@
 | 
			
		||||
import { JSX } from "preact"
 | 
			
		||||
import { randomIdNonSecure } from "../util/random"
 | 
			
		||||
 | 
			
		||||
const OverflowList = ({
 | 
			
		||||
  children,
 | 
			
		||||
  ...props
 | 
			
		||||
}: JSX.HTMLAttributes<HTMLUListElement> & { id: string }) => {
 | 
			
		||||
  return (
 | 
			
		||||
    <ul class="overflow" {...props}>
 | 
			
		||||
    <ul {...props} class={[props.class, "overflow"].filter(Boolean).join(" ")} id={props.id}>
 | 
			
		||||
      {children}
 | 
			
		||||
      <li class="overflow-end" />
 | 
			
		||||
    </ul>
 | 
			
		||||
  )
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
OverflowList.afterDOMLoaded = (id: string) => `
 | 
			
		||||
export default () => {
 | 
			
		||||
  const id = randomIdNonSecure()
 | 
			
		||||
 | 
			
		||||
  return {
 | 
			
		||||
    OverflowList: (props: JSX.HTMLAttributes<HTMLUListElement>) => (
 | 
			
		||||
      <OverflowList {...props} id={id} />
 | 
			
		||||
    ),
 | 
			
		||||
    overflowListAfterDOMLoaded: `
 | 
			
		||||
document.addEventListener("nav", (e) => {
 | 
			
		||||
  const observer = new IntersectionObserver((entries) => {
 | 
			
		||||
    for (const entry of entries) {
 | 
			
		||||
      const parentUl = entry.target.parentElement
 | 
			
		||||
      if (!parentUl) return
 | 
			
		||||
      if (entry.isIntersecting) {
 | 
			
		||||
        parentUl.classList.remove("gradient-active")
 | 
			
		||||
      } else {
 | 
			
		||||
@@ -34,6 +43,6 @@ document.addEventListener("nav", (e) => {
 | 
			
		||||
  observer.observe(end)
 | 
			
		||||
  window.addCleanup(() => observer.disconnect())
 | 
			
		||||
})
 | 
			
		||||
`
 | 
			
		||||
 | 
			
		||||
export default OverflowList
 | 
			
		||||
`,
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -6,7 +6,8 @@ import { classNames } from "../util/lang"
 | 
			
		||||
// @ts-ignore
 | 
			
		||||
import script from "./scripts/toc.inline"
 | 
			
		||||
import { i18n } from "../i18n"
 | 
			
		||||
import OverflowList from "./OverflowList"
 | 
			
		||||
import OverflowListFactory from "./OverflowList"
 | 
			
		||||
import { concatenateResources } from "../util/resources"
 | 
			
		||||
 | 
			
		||||
interface Options {
 | 
			
		||||
  layout: "modern" | "legacy"
 | 
			
		||||
@@ -16,41 +17,70 @@ const defaultOptions: Options = {
 | 
			
		||||
  layout: "modern",
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const TableOfContents: QuartzComponent = ({
 | 
			
		||||
  fileData,
 | 
			
		||||
  displayClass,
 | 
			
		||||
  cfg,
 | 
			
		||||
}: QuartzComponentProps) => {
 | 
			
		||||
  if (!fileData.toc) {
 | 
			
		||||
    return null
 | 
			
		||||
export default ((opts?: Partial<Options>) => {
 | 
			
		||||
  const layout = opts?.layout ?? defaultOptions.layout
 | 
			
		||||
  const { OverflowList, overflowListAfterDOMLoaded } = OverflowListFactory()
 | 
			
		||||
  const TableOfContents: QuartzComponent = ({
 | 
			
		||||
    fileData,
 | 
			
		||||
    displayClass,
 | 
			
		||||
    cfg,
 | 
			
		||||
  }: QuartzComponentProps) => {
 | 
			
		||||
    if (!fileData.toc) {
 | 
			
		||||
      return null
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return (
 | 
			
		||||
      <div class={classNames(displayClass, "toc")}>
 | 
			
		||||
        <button
 | 
			
		||||
          type="button"
 | 
			
		||||
          class={fileData.collapseToc ? "collapsed toc-header" : "toc-header"}
 | 
			
		||||
          aria-controls="toc-content"
 | 
			
		||||
          aria-expanded={!fileData.collapseToc}
 | 
			
		||||
        >
 | 
			
		||||
          <h3>{i18n(cfg.locale).components.tableOfContents.title}</h3>
 | 
			
		||||
          <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>
 | 
			
		||||
        </button>
 | 
			
		||||
        <div class={fileData.collapseToc ? "collapsed toc-content" : "toc-content"}>
 | 
			
		||||
          <OverflowList>
 | 
			
		||||
            {fileData.toc.map((tocEntry) => (
 | 
			
		||||
              <li key={tocEntry.slug} class={`depth-${tocEntry.depth}`}>
 | 
			
		||||
                <a href={`#${tocEntry.slug}`} data-for={tocEntry.slug}>
 | 
			
		||||
                  {tocEntry.text}
 | 
			
		||||
                </a>
 | 
			
		||||
              </li>
 | 
			
		||||
            ))}
 | 
			
		||||
          </OverflowList>
 | 
			
		||||
        </div>
 | 
			
		||||
      </div>
 | 
			
		||||
    )
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return (
 | 
			
		||||
    <div class={classNames(displayClass, "toc")}>
 | 
			
		||||
      <button
 | 
			
		||||
        type="button"
 | 
			
		||||
        class={fileData.collapseToc ? "collapsed toc-header" : "toc-header"}
 | 
			
		||||
        aria-controls="toc-content"
 | 
			
		||||
        aria-expanded={!fileData.collapseToc}
 | 
			
		||||
      >
 | 
			
		||||
        <h3>{i18n(cfg.locale).components.tableOfContents.title}</h3>
 | 
			
		||||
        <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>
 | 
			
		||||
      </button>
 | 
			
		||||
      <div class={fileData.collapseToc ? "collapsed toc-content" : "toc-content"}>
 | 
			
		||||
        <OverflowList id="toc-ul">
 | 
			
		||||
  TableOfContents.css = modernStyle
 | 
			
		||||
  TableOfContents.afterDOMLoaded = concatenateResources(script, overflowListAfterDOMLoaded)
 | 
			
		||||
 | 
			
		||||
  const LegacyTableOfContents: QuartzComponent = ({ fileData, cfg }: QuartzComponentProps) => {
 | 
			
		||||
    if (!fileData.toc) {
 | 
			
		||||
      return null
 | 
			
		||||
    }
 | 
			
		||||
    return (
 | 
			
		||||
      <details class="toc" open={!fileData.collapseToc}>
 | 
			
		||||
        <summary>
 | 
			
		||||
          <h3>{i18n(cfg.locale).components.tableOfContents.title}</h3>
 | 
			
		||||
        </summary>
 | 
			
		||||
        <ul>
 | 
			
		||||
          {fileData.toc.map((tocEntry) => (
 | 
			
		||||
            <li key={tocEntry.slug} class={`depth-${tocEntry.depth}`}>
 | 
			
		||||
              <a href={`#${tocEntry.slug}`} data-for={tocEntry.slug}>
 | 
			
		||||
@@ -58,38 +88,11 @@ const TableOfContents: QuartzComponent = ({
 | 
			
		||||
              </a>
 | 
			
		||||
            </li>
 | 
			
		||||
          ))}
 | 
			
		||||
        </OverflowList>
 | 
			
		||||
      </div>
 | 
			
		||||
    </div>
 | 
			
		||||
  )
 | 
			
		||||
}
 | 
			
		||||
TableOfContents.css = modernStyle
 | 
			
		||||
TableOfContents.afterDOMLoaded = script + OverflowList.afterDOMLoaded("toc-ul")
 | 
			
		||||
 | 
			
		||||
const LegacyTableOfContents: QuartzComponent = ({ fileData, cfg }: QuartzComponentProps) => {
 | 
			
		||||
  if (!fileData.toc) {
 | 
			
		||||
    return null
 | 
			
		||||
        </ul>
 | 
			
		||||
      </details>
 | 
			
		||||
    )
 | 
			
		||||
  }
 | 
			
		||||
  return (
 | 
			
		||||
    <details class="toc" open={!fileData.collapseToc}>
 | 
			
		||||
      <summary>
 | 
			
		||||
        <h3>{i18n(cfg.locale).components.tableOfContents.title}</h3>
 | 
			
		||||
      </summary>
 | 
			
		||||
      <ul>
 | 
			
		||||
        {fileData.toc.map((tocEntry) => (
 | 
			
		||||
          <li key={tocEntry.slug} class={`depth-${tocEntry.depth}`}>
 | 
			
		||||
            <a href={`#${tocEntry.slug}`} data-for={tocEntry.slug}>
 | 
			
		||||
              {tocEntry.text}
 | 
			
		||||
            </a>
 | 
			
		||||
          </li>
 | 
			
		||||
        ))}
 | 
			
		||||
      </ul>
 | 
			
		||||
    </details>
 | 
			
		||||
  )
 | 
			
		||||
}
 | 
			
		||||
LegacyTableOfContents.css = legacyStyle
 | 
			
		||||
  LegacyTableOfContents.css = legacyStyle
 | 
			
		||||
 | 
			
		||||
export default ((opts?: Partial<Options>) => {
 | 
			
		||||
  const layout = opts?.layout ?? defaultOptions.layout
 | 
			
		||||
  return layout === "modern" ? TableOfContents : LegacyTableOfContents
 | 
			
		||||
}) satisfies QuartzComponentConstructor
 | 
			
		||||
 
 | 
			
		||||
@@ -9,6 +9,7 @@ import { htmlToJsx } from "../../util/jsx"
 | 
			
		||||
import { i18n } from "../../i18n"
 | 
			
		||||
import { QuartzPluginData } from "../../plugins/vfile"
 | 
			
		||||
import { ComponentChildren } from "preact"
 | 
			
		||||
import { concatenateResources } from "../../util/resources"
 | 
			
		||||
 | 
			
		||||
interface FolderContentOptions {
 | 
			
		||||
  /**
 | 
			
		||||
@@ -104,6 +105,6 @@ export default ((opts?: Partial<FolderContentOptions>) => {
 | 
			
		||||
    )
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  FolderContent.css = style + PageList.css
 | 
			
		||||
  FolderContent.css = concatenateResources(style, PageList.css)
 | 
			
		||||
  return FolderContent
 | 
			
		||||
}) satisfies QuartzComponentConstructor
 | 
			
		||||
 
 | 
			
		||||
@@ -7,6 +7,7 @@ import { Root } from "hast"
 | 
			
		||||
import { htmlToJsx } from "../../util/jsx"
 | 
			
		||||
import { i18n } from "../../i18n"
 | 
			
		||||
import { ComponentChildren } from "preact"
 | 
			
		||||
import { concatenateResources } from "../../util/resources"
 | 
			
		||||
 | 
			
		||||
interface TagContentOptions {
 | 
			
		||||
  sort?: SortFn
 | 
			
		||||
@@ -124,6 +125,6 @@ export default ((opts?: Partial<TagContentOptions>) => {
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  TagContent.css = style + PageList.css
 | 
			
		||||
  TagContent.css = concatenateResources(style, PageList.css)
 | 
			
		||||
  return TagContent
 | 
			
		||||
}) satisfies QuartzComponentConstructor
 | 
			
		||||
 
 | 
			
		||||
@@ -21,14 +21,13 @@ type FolderState = {
 | 
			
		||||
 | 
			
		||||
let currentExplorerState: Array<FolderState>
 | 
			
		||||
function toggleExplorer(this: HTMLElement) {
 | 
			
		||||
  const explorers = document.querySelectorAll(".explorer")
 | 
			
		||||
  for (const explorer of explorers) {
 | 
			
		||||
    explorer.classList.toggle("collapsed")
 | 
			
		||||
    explorer.setAttribute(
 | 
			
		||||
      "aria-expanded",
 | 
			
		||||
      explorer.getAttribute("aria-expanded") === "true" ? "false" : "true",
 | 
			
		||||
    )
 | 
			
		||||
  }
 | 
			
		||||
  const nearestExplorer = this.closest(".explorer") as HTMLElement
 | 
			
		||||
  if (!nearestExplorer) return
 | 
			
		||||
  nearestExplorer.classList.toggle("collapsed")
 | 
			
		||||
  nearestExplorer.setAttribute(
 | 
			
		||||
    "aria-expanded",
 | 
			
		||||
    nearestExplorer.getAttribute("aria-expanded") === "true" ? "false" : "true",
 | 
			
		||||
  )
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function toggleFolder(evt: MouseEvent) {
 | 
			
		||||
@@ -145,7 +144,7 @@ function createFolderNode(
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
async function setupExplorer(currentSlug: FullSlug) {
 | 
			
		||||
  const allExplorers = document.querySelectorAll(".explorer") as NodeListOf<HTMLElement>
 | 
			
		||||
  const allExplorers = document.querySelectorAll("div.explorer") as NodeListOf<HTMLElement>
 | 
			
		||||
 | 
			
		||||
  for (const explorer of allExplorers) {
 | 
			
		||||
    const dataFns = JSON.parse(explorer.dataset.dataFns || "{}")
 | 
			
		||||
@@ -192,7 +191,7 @@ async function setupExplorer(currentSlug: FullSlug) {
 | 
			
		||||
      collapsed: oldIndex.get(path) === true,
 | 
			
		||||
    }))
 | 
			
		||||
 | 
			
		||||
    const explorerUl = document.getElementById("explorer-ul")
 | 
			
		||||
    const explorerUl = explorer.querySelector(".explorer-ul")
 | 
			
		||||
    if (!explorerUl) continue
 | 
			
		||||
 | 
			
		||||
    // Create and insert new content
 | 
			
		||||
@@ -219,14 +218,12 @@ async function setupExplorer(currentSlug: FullSlug) {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Set up event handlers
 | 
			
		||||
    const explorerButtons = explorer.querySelectorAll(
 | 
			
		||||
      "button.explorer-toggle",
 | 
			
		||||
    ) as NodeListOf<HTMLElement>
 | 
			
		||||
    if (explorerButtons) {
 | 
			
		||||
      window.addCleanup(() =>
 | 
			
		||||
        explorerButtons.forEach((button) => button.removeEventListener("click", toggleExplorer)),
 | 
			
		||||
      )
 | 
			
		||||
      explorerButtons.forEach((button) => button.addEventListener("click", toggleExplorer))
 | 
			
		||||
    const explorerButtons = explorer.getElementsByClassName(
 | 
			
		||||
      "explorer-toggle",
 | 
			
		||||
    ) as HTMLCollectionOf<HTMLElement>
 | 
			
		||||
    for (const button of explorerButtons) {
 | 
			
		||||
      button.addEventListener("click", toggleExplorer)
 | 
			
		||||
      window.addCleanup(() => button.removeEventListener("click", toggleExplorer))
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Set up folder click handlers
 | 
			
		||||
@@ -235,8 +232,8 @@ async function setupExplorer(currentSlug: FullSlug) {
 | 
			
		||||
        "folder-button",
 | 
			
		||||
      ) as HTMLCollectionOf<HTMLElement>
 | 
			
		||||
      for (const button of folderButtons) {
 | 
			
		||||
        window.addCleanup(() => button.removeEventListener("click", toggleFolder))
 | 
			
		||||
        button.addEventListener("click", toggleFolder)
 | 
			
		||||
        window.addCleanup(() => button.removeEventListener("click", toggleFolder))
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -244,15 +241,15 @@ async function setupExplorer(currentSlug: FullSlug) {
 | 
			
		||||
      "folder-icon",
 | 
			
		||||
    ) as HTMLCollectionOf<HTMLElement>
 | 
			
		||||
    for (const icon of folderIcons) {
 | 
			
		||||
      window.addCleanup(() => icon.removeEventListener("click", toggleFolder))
 | 
			
		||||
      icon.addEventListener("click", toggleFolder)
 | 
			
		||||
      window.addCleanup(() => icon.removeEventListener("click", toggleFolder))
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
document.addEventListener("prenav", async (e: CustomEventMap["prenav"]) => {
 | 
			
		||||
document.addEventListener("prenav", async () => {
 | 
			
		||||
  // save explorer scrollTop position
 | 
			
		||||
  const explorer = document.getElementById("explorer-ul")
 | 
			
		||||
  const explorer = document.querySelector(".explorer-ul")
 | 
			
		||||
  if (!explorer) return
 | 
			
		||||
  sessionStorage.setItem("explorerScrollTop", explorer.scrollTop.toString())
 | 
			
		||||
})
 | 
			
		||||
@@ -262,9 +259,8 @@ document.addEventListener("nav", async (e: CustomEventMap["nav"]) => {
 | 
			
		||||
  await setupExplorer(currentSlug)
 | 
			
		||||
 | 
			
		||||
  // if mobile hamburger is visible, collapse by default
 | 
			
		||||
  const mobileExplorer = document.getElementById("mobile-explorer")
 | 
			
		||||
  if (mobileExplorer && mobileExplorer.checkVisibility()) {
 | 
			
		||||
    for (const explorer of document.querySelectorAll(".explorer")) {
 | 
			
		||||
  for (const explorer of document.getElementsByClassName("mobile-explorer")) {
 | 
			
		||||
    if (explorer.checkVisibility()) {
 | 
			
		||||
      explorer.classList.add("collapsed")
 | 
			
		||||
      explorer.setAttribute("aria-expanded", "false")
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
@@ -20,7 +20,7 @@
 | 
			
		||||
      margin: 0;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    .hide-until-loaded ~ #explorer-content {
 | 
			
		||||
    .hide-until-loaded ~ .explorer-content {
 | 
			
		||||
      display: none;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
@@ -30,6 +30,8 @@
 | 
			
		||||
  display: flex;
 | 
			
		||||
  flex-direction: column;
 | 
			
		||||
  overflow-y: hidden;
 | 
			
		||||
 | 
			
		||||
  min-height: 1.2rem;
 | 
			
		||||
  flex: 0 1 auto;
 | 
			
		||||
  &.collapsed {
 | 
			
		||||
    flex: 0 1 1.2rem;
 | 
			
		||||
@@ -52,20 +54,20 @@
 | 
			
		||||
    align-self: flex-start;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  button#mobile-explorer {
 | 
			
		||||
  button.mobile-explorer {
 | 
			
		||||
    display: none;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  button#desktop-explorer {
 | 
			
		||||
  button.desktop-explorer {
 | 
			
		||||
    display: flex;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  @media all and ($mobile) {
 | 
			
		||||
    button#mobile-explorer {
 | 
			
		||||
    button.mobile-explorer {
 | 
			
		||||
      display: flex;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    button#desktop-explorer {
 | 
			
		||||
    button.desktop-explorer {
 | 
			
		||||
      display: none;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
@@ -86,8 +88,8 @@
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
button#mobile-explorer,
 | 
			
		||||
button#desktop-explorer {
 | 
			
		||||
button.mobile-explorer,
 | 
			
		||||
button.desktop-explorer {
 | 
			
		||||
  background-color: transparent;
 | 
			
		||||
  border: none;
 | 
			
		||||
  text-align: left;
 | 
			
		||||
@@ -104,7 +106,7 @@ button#desktop-explorer {
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#explorer-content {
 | 
			
		||||
.explorer-content {
 | 
			
		||||
  list-style: none;
 | 
			
		||||
  overflow: hidden;
 | 
			
		||||
  overflow-y: auto;
 | 
			
		||||
@@ -209,7 +211,7 @@ li:has(> .folder-outer:not(.open)) > .folder-container > svg {
 | 
			
		||||
    &.collapsed {
 | 
			
		||||
      flex: 0 0 34px;
 | 
			
		||||
 | 
			
		||||
      & > #explorer-content {
 | 
			
		||||
      & > .explorer-content {
 | 
			
		||||
        transform: translateX(-100vw);
 | 
			
		||||
        visibility: hidden;
 | 
			
		||||
      }
 | 
			
		||||
@@ -218,13 +220,13 @@ li:has(> .folder-outer:not(.open)) > .folder-container > svg {
 | 
			
		||||
    &:not(.collapsed) {
 | 
			
		||||
      flex: 0 0 34px;
 | 
			
		||||
 | 
			
		||||
      & > #explorer-content {
 | 
			
		||||
      & > .explorer-content {
 | 
			
		||||
        transform: translateX(0);
 | 
			
		||||
        visibility: visible;
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #explorer-content {
 | 
			
		||||
    .explorer-content {
 | 
			
		||||
      box-sizing: border-box;
 | 
			
		||||
      z-index: 100;
 | 
			
		||||
      position: absolute;
 | 
			
		||||
@@ -245,7 +247,7 @@ li:has(> .folder-outer:not(.open)) > .folder-container > svg {
 | 
			
		||||
      visibility: hidden;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #mobile-explorer {
 | 
			
		||||
    .mobile-explorer {
 | 
			
		||||
      margin: 0;
 | 
			
		||||
      padding: 5px;
 | 
			
		||||
      z-index: 101;
 | 
			
		||||
 
 | 
			
		||||
@@ -5,6 +5,7 @@
 | 
			
		||||
  flex-direction: column;
 | 
			
		||||
 | 
			
		||||
  overflow-y: hidden;
 | 
			
		||||
  min-height: 4rem;
 | 
			
		||||
  flex: 0 1 auto;
 | 
			
		||||
  &:has(button.toc-header.collapsed) {
 | 
			
		||||
    flex: 0 1 1.2rem;
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,5 @@
 | 
			
		||||
import { ComponentType, JSX } from "preact"
 | 
			
		||||
import { StaticResources } from "../util/resources"
 | 
			
		||||
import { StaticResources, StringResource } from "../util/resources"
 | 
			
		||||
import { QuartzPluginData } from "../plugins/vfile"
 | 
			
		||||
import { GlobalConfiguration } from "../cfg"
 | 
			
		||||
import { Node } from "hast"
 | 
			
		||||
@@ -19,9 +19,9 @@ export type QuartzComponentProps = {
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
export type QuartzComponent = ComponentType<QuartzComponentProps> & {
 | 
			
		||||
  css?: string
 | 
			
		||||
  beforeDOMLoaded?: string
 | 
			
		||||
  afterDOMLoaded?: string
 | 
			
		||||
  css?: StringResource
 | 
			
		||||
  beforeDOMLoaded?: StringResource
 | 
			
		||||
  afterDOMLoaded?: StringResource
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export type QuartzComponentConstructor<Options extends object | undefined = undefined> = (
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user