import { For, Show, createSignal, createEffect, on, onMount, onCleanup } from 'solid-js' import { clsx } from 'clsx' import { DEFAULT_HEADER_OFFSET } from '../../stores/router' import { useLocalize } from '../../context/localize' import debounce from 'debounce' import { Icon } from '../_shared/Icon' import styles from './TableOfContents.module.scss' import { isDesktop } from '../../utils/media-query' import throttle from 'just-throttle' interface Props { variant: 'article' | 'editor' parentSelector: string body: string } const isInViewport = (el: Element): boolean => { const rect = el.getBoundingClientRect() return ( rect.top >= 0 && rect.left >= 0 && rect.bottom <= (window.innerHeight || document.documentElement.clientHeight) && rect.right <= (window.innerWidth || document.documentElement.clientWidth) ) } const scrollToHeader = (element) => { window.scrollTo({ behavior: 'smooth', top: element.getBoundingClientRect().top - document.body.getBoundingClientRect().top - DEFAULT_HEADER_OFFSET }) } export const TableOfContents = (props: Props) => { const { t } = useLocalize() const [headings, setHeadings] = createSignal([]) const [areHeadingsLoaded, setAreHeadingsLoaded] = createSignal(false) const [activeHeaderIndex, setActiveHeaderIndex] = createSignal(-1) const [isVisible, setIsVisible] = createSignal(props.variant === 'article') const toggleIsVisible = () => { setIsVisible((visible) => !visible) } setIsVisible(isDesktop()) const updateHeadings = () => { setHeadings( // eslint-disable-next-line unicorn/prefer-spread Array.from(document.querySelector(props.parentSelector).querySelectorAll('h2, h3, h4')) ) setAreHeadingsLoaded(true) } const debouncedUpdateHeadings = debounce(updateHeadings, 500) const updateActiveHeader = throttle(() => { const newActiveIndex = headings().findIndex((heading) => isInViewport(heading)) setActiveHeaderIndex(newActiveIndex) }, 50) createEffect( on( () => props.body, () => debouncedUpdateHeadings() ) ) onMount(() => { window.addEventListener('scroll', updateActiveHeader) onCleanup(() => window.removeEventListener('scroll', updateActiveHeader)) }) return ( 2 : headings().length > 1) } >

{t('contents')}

    {(h, index) => (
  • )}
) }