<commit_before>import * as React from "react"
import { useState, useEffect, useRef } from "react"
import * as ReactDOM from "react-dom"
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"
import { faListAlt } from "@fortawesome/free-solid-svg-icons/faListAlt"
import { faChevronLeft } from "@fortawesome/free-solid-svg-icons/faChevronLeft"

const TOC_CLASS_NAME = "entry-sidebar"

interface TableOfContentsData {
    headings: { isSubheading: boolean; slug: string; text: string }[]
    pageTitle: string
}

export const TableOfContents = ({
    headings,
    pageTitle
}: TableOfContentsData) => {
    const [isToggled, setIsToggled] = useState(false)
    const [isSticky, setIsSticky] = useState(false)
    const tocRef = useRef<HTMLElement>(null)

    const toggle = () => {
        setIsToggled(!isToggled)
    }

    useEffect(() => {
        const handleClick = (e: MouseEvent) => {
            if (!tocRef.current?.contains(e.target as Node)) {
                setIsToggled(false)
            }
        }
        document.addEventListener("mousedown", handleClick)

        return () => {
            document.removeEventListener("mousedown", handleClick)
        }
    }, [])

    useEffect(() => {
        // Sets up an intersection observer to notify when the element with the class
        // `.sticky-sentinel` becomes visible/invisible at the top of the viewport.
        // Inspired by https://developers.google.com/web/updates/2017/09/sticky-headers
        const observer = new IntersectionObserver((records, observer) => {
            for (const record of records) {
                const targetInfo = record.boundingClientRect
                // Started sticking
                if (targetInfo.top < 0) {
                    setIsSticky(true)
                }
                // Stopped sticking
                if (targetInfo.bottom > 0) {
                    setIsSticky(false)
                }
            }
        })
        const sentinel = document.querySelector(".sticky-sentinel")
        if (sentinel) observer.observe(sentinel)
    }, [])

    return (
        <aside
            ref={tocRef}
            className={`${TOC_CLASS_NAME}${isToggled ? " toggled" : ""}${
                isSticky ? " sticky" : ""
            }`}
        >
            <div className="sticky-sentinel" />
            <nav className="entry-toc">
                <button
                    aria-label={`${
                        isToggled ? "Close" : "Open"
                    } table of contents`}
                    className="toggle-toc"
                    onClick={toggle}
                ><commit_msg>refactor(toc): use ref for sticky sentinel<commit_after>import * as React from "react"
import { useState, useEffect, useRef } from "react"
import * as ReactDOM from "react-dom"
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"
import { faListAlt } from "@fortawesome/free-solid-svg-icons/faListAlt"
import { faChevronLeft } from "@fortawesome/free-solid-svg-icons/faChevronLeft"

const TOC_CLASS_NAME = "entry-sidebar"

interface TableOfContentsData {
    headings: { isSubheading: boolean; slug: string; text: string }[]
    pageTitle: string
}

export const TableOfContents = ({
    headings,
    pageTitle
}: TableOfContentsData) => {
    const [isToggled, setIsToggled] = useState(false)
    const [isSticky, setIsSticky] = useState(false)
    const tocRef = useRef<HTMLElement>(null)
    const stickySentinelRef = useRef<HTMLDivElement>(null)

    const toggle = () => {
        setIsToggled(!isToggled)
    }

    useEffect(() => {
        const handleClick = (e: MouseEvent) => {
            if (!tocRef.current?.contains(e.target as Node)) {
                setIsToggled(false)
            }
        }
        document.addEventListener("mousedown", handleClick)

        return () => {
            document.removeEventListener("mousedown", handleClick)
        }
    }, [])

    useEffect(() => {
        // Sets up an intersection observer to notify when the element with the class
        // `.sticky-sentinel` becomes visible/invisible at the top of the viewport.
        // Inspired by https://developers.google.com/web/updates/2017/09/sticky-headers
        const observer = new IntersectionObserver((records, observer) => {
            for (const record of records) {
                const targetInfo = record.boundingClientRect
                // Started sticking
                if (targetInfo.top < 0) {
                    setIsSticky(true)
                }
                // Stopped sticking
                if (targetInfo.bottom > 0) {
                    setIsSticky(false)
                }
            }
        })
        if (stickySentinelRef.current) {
            observer.observe(stickySentinelRef.current)
        }
    }, [])

    return (
        <aside
            ref={tocRef}
            className={`${TOC_CLASS_NAME}${isToggled ? " toggled" : ""}${
                isSticky ? " sticky" : ""
            }`}
        >
            <div className="sticky-sentinel" ref={stickySentinelRef} />
            <nav className="entry-toc">
                <button
                    aria-label={`${
                        isToggled ? "Close" : "Open"
                    } table of contents`}
                    className="toggle-toc"
                    onClick={toggle}
                >
{'fraction_non_alphanumeric': 0.1127889634601044, 'fraction_numerical': 0.002982848620432513, 'mean_word_length': 1.8968682505399568, 'pattern_counts': {'":': 0, '<': 16, '<?xml version=': 0, '>': 30, 'https://': 2, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3844574', 'n_tokens_mistral': 1570, 'n_tokens_neox': 1487, 'n_words': 316}