modern toc tweaks

This commit is contained in:
Jacky Zhao 2023-06-16 19:41:59 -07:00
parent 9d2024b11c
commit 917d5791ac
17 changed files with 318 additions and 58 deletions

View file

@ -1,3 +1,4 @@
// @ts-ignore
import clipboardScript from './scripts/clipboard.inline'
import clipboardStyle from './styles/clipboard.scss'
import { QuartzComponentConstructor, QuartzComponentProps } from "./types"

View file

@ -1,38 +1,65 @@
import { QuartzComponentConstructor, QuartzComponentProps } from "./types"
import style from "./styles/toc.scss"
import legacyStyle from "./styles/legacyToc.scss"
import modernStyle from "./styles/toc.scss"
interface Options {
layout: 'modern' | 'quartz-3'
layout: 'modern' | 'legacy'
}
const defaultOptions: Options = {
layout: 'quartz-3'
layout: 'modern'
}
export default ((opts?: Partial<Options>) => {
const layout = opts?.layout ?? defaultOptions.layout
if (layout === "modern") {
return function() {
return null // TODO (make this look like nextra)
}
} else {
function TableOfContents({ fileData }: QuartzComponentProps) {
if (!fileData.toc) {
return null
}
return <details class="toc" open>
<summary><h3>Table of Contents</h3></summary>
<ul>
{fileData.toc.map(tocEntry => <li key={tocEntry.slug} class={`depth-${tocEntry.depth}`}>
<a href={`#${tocEntry.slug}`}>{tocEntry.text}</a>
</li>)}
</ul>
</details>
function TableOfContents({ fileData }: QuartzComponentProps) {
if (!fileData.toc) {
return null
}
TableOfContents.css = style
return TableOfContents
return <details class="toc" open>
<summary><h3>Table of Contents</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>
}
TableOfContents.css = layout === "modern" ? modernStyle : legacyStyle
if (layout === "modern") {
TableOfContents.afterDOMLoaded = `
const bufferPx = 150
const observer = new IntersectionObserver(entries => {
for (const entry of entries) {
const slug = entry.target.id
const tocEntryElement = document.querySelector(\`a[data-for="$\{slug\}"]\`)
const windowHeight = entry.rootBounds?.height
if (windowHeight && tocEntryElement) {
if (entry.boundingClientRect.y < windowHeight) {
tocEntryElement.classList.add("in-view")
} else {
tocEntryElement.classList.remove("in-view")
}
}
}
})
function init() {
const headers = document.querySelectorAll("h1[id], h2[id], h3[id], h4[id], h5[id], h6[id]")
headers.forEach(header => observer.observe(header))
}
init()
document.addEventListener("spa_nav", (e) => {
observer.disconnect()
init()
})
`
}
return TableOfContents
}) satisfies QuartzComponentConstructor

View file

@ -1,6 +1,3 @@
const description = "Initialize copy for codeblocks"
export default description
const svgCopy =
'<svg aria-hidden="true" height="16" viewBox="0 0 16 16" version="1.1" width="16" data-view-component="true"><path fill-rule="evenodd" d="M0 6.75C0 5.784.784 5 1.75 5h1.5a.75.75 0 010 1.5h-1.5a.25.25 0 00-.25.25v7.5c0 .138.112.25.25.25h7.5a.25.25 0 00.25-.25v-1.5a.75.75 0 011.5 0v1.5A1.75 1.75 0 019.25 16h-7.5A1.75 1.75 0 010 14.25v-7.5z"></path><path fill-rule="evenodd" d="M5 1.75C5 .784 5.784 0 6.75 0h7.5C15.216 0 16 .784 16 1.75v7.5A1.75 1.75 0 0114.25 11h-7.5A1.75 1.75 0 015 9.25v-7.5zm1.75-.25a.25.25 0 00-.25.25v7.5c0 .138.112.25.25.25h7.5a.25.25 0 00.25-.25v-7.5a.25.25 0 00-.25-.25h-7.5z"></path></svg>'
const svgCheck =

View file

@ -29,6 +29,11 @@ const getOpts = ({ target }: Event): { url: URL, scroll?: boolean } | undefined
return { url: new URL(href), scroll: 'routerNoscroll' in a.dataset ? false : undefined }
}
function notifyNav(slug: string) {
const event = new CustomEvent("spa_nav", { detail: { slug } })
document.dispatchEvent(event)
}
let p: DOMParser
async function navigate(url: URL, isBack: boolean = false) {
p = p || new DOMParser()
@ -64,9 +69,7 @@ async function navigate(url: URL, isBack: boolean = false) {
const elementsToAdd = html.head.querySelectorAll(':not([spa-preserve])')
elementsToAdd.forEach(el => document.head.appendChild(el))
if (!document.activeElement?.closest('[data-persist]')) {
document.body.focus()
}
notifyNav(document.body.dataset.slug!)
delete announcer.dataset.persist
}

View file

@ -0,0 +1,27 @@
details.toc {
& summary {
cursor: pointer;
&::marker {
color: var(--dark);
}
& > * {
padding-left: 0.25rem;
display: inline-block;
margin: 0;
}
}
& ul {
list-style: none;
margin: 0.5rem 1.25rem;
padding: 0;
}
@for $i from 1 through 6 {
& .depth-#{$i} {
padding-left: calc(1rem * #{$i});
}
}
}

View file

@ -2,24 +2,36 @@ details.toc {
& summary {
cursor: pointer;
&::marker {
color: var(--dark);
list-style: none;
&::marker, &::-webkit-details-marker {
display: none;
}
& > * {
padding-left: 0.25rem;
display: inline-block;
margin: 0;
}
& > h3 {
font-size: 1rem;
}
}
& ul {
list-style: none;
margin: 0.5rem 1.25rem;
margin: 0.5rem 0;
padding: 0;
& > li > a {
color: var(--dark);
opacity: 0.35;
transition: 0.5s ease opacity;
&.in-view {
opacity: 0.75;
}
}
}
@for $i from 1 through 6 {
@for $i from 0 through 6 {
& .depth-#{$i} {
padding-left: calc(1rem * #{$i});
}