modern toc tweaks
This commit is contained in:
		
							parent
							
								
									9d2024b11c
								
							
						
					
					
						commit
						917d5791ac
					
				
					 17 changed files with 318 additions and 58 deletions
				
			
		| 
						 | 
				
			
			@ -1,3 +1,4 @@
 | 
			
		|||
// @ts-ignore
 | 
			
		||||
import clipboardScript from './scripts/clipboard.inline'
 | 
			
		||||
import clipboardStyle from './styles/clipboard.scss'
 | 
			
		||||
import { QuartzComponentConstructor, QuartzComponentProps } from "./types"
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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 =
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										27
									
								
								quartz/components/styles/legacyToc.scss
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								quartz/components/styles/legacyToc.scss
									
										
									
									
									
										Normal 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});
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -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});
 | 
			
		||||
    }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue