/* * SPDX-FileCopyrightText: 2023 Kristóf Marussy * * SPDX-License-Identifier: MIT */ import { useLocation } from '@docusaurus/router'; import { type ReactNode, createContext, useCallback, useContext, useRef, useState, } from 'react'; export interface ActiveSection { hash: string | undefined; setHash: (hash: string | undefined) => void; } const ActiveSectionContext = createContext( undefined, ); export function useActiveSection(): ActiveSection { const value = useContext(ActiveSectionContext); if (value === undefined) { throw new Error( 'useActiveSection is only valid inside ActiveSectionProvider', ); } return value; } export default function ActiveSectionProvider({ children, }: { children: ReactNode; }) { const location = useLocation(); const [hash, setHashState] = useState(); const lastHashRef = useRef(); const setHash = useCallback( (hash: string | undefined) => { if (hash === lastHashRef.current) { return; } // Passing `undefined` to `hash` will give contraol pack to react-router. const newURL = `${location.pathname}${location.search}${ hash ?? location.hash }`; // Update the history entry without informing react-router so Docusaurus // doesn't scroll to the top of the element. See // https://github.com/facebook/docusaurus/blob/ca3dba5e5eab0f8b8a9b3388e2b2e637fe3a19a7/packages/docusaurus/src/client/ClientLifecyclesDispatcher.tsx#L30-L39 window.history.replaceState(null, null, newURL); setHashState(hash); lastHashRef.current = hash; }, [location, setHashState], ); return ( {children} ); }