aboutsummaryrefslogtreecommitdiffstats
path: root/src/components/ActiveSectionProvider.tsx
diff options
context:
space:
mode:
Diffstat (limited to 'src/components/ActiveSectionProvider.tsx')
-rw-r--r--src/components/ActiveSectionProvider.tsx67
1 files changed, 67 insertions, 0 deletions
diff --git a/src/components/ActiveSectionProvider.tsx b/src/components/ActiveSectionProvider.tsx
new file mode 100644
index 0000000..022dbad
--- /dev/null
+++ b/src/components/ActiveSectionProvider.tsx
@@ -0,0 +1,67 @@
1/*
2 * SPDX-FileCopyrightText: 2023 Kristóf Marussy
3 *
4 * SPDX-License-Identifier: MIT
5 */
6
7import { useLocation } from '@docusaurus/router';
8import {
9 type ReactNode,
10 createContext,
11 useCallback,
12 useContext,
13 useRef,
14 useState,
15} from 'react';
16
17export interface ActiveSection {
18 hash: string | undefined;
19 setHash: (hash: string | undefined) => void;
20}
21
22const ActiveSectionContext = createContext<ActiveSection | undefined>(
23 undefined,
24);
25
26export function useActiveSection(): ActiveSection {
27 const value = useContext(ActiveSectionContext);
28 if (value === undefined) {
29 throw new Error(
30 'useActiveSection is only valid inside ActiveSectionProvider',
31 );
32 }
33 return value;
34}
35
36export default function ActiveSectionProvider({
37 children,
38}: {
39 children: ReactNode;
40}) {
41 const location = useLocation();
42 const [hash, setHashState] = useState<string | undefined>();
43 const lastHashRef = useRef<string | undefined>();
44 const setHash = useCallback(
45 (hash: string | undefined) => {
46 if (hash === lastHashRef.current) {
47 return;
48 }
49 // Passing `undefined` to `hash` will give contraol pack to react-router.
50 const newURL = `${location.pathname}${location.search}${
51 hash ?? location.hash
52 }`;
53 // Update the history entry without informing react-router so Docusaurus
54 // doesn't scroll to the top of the element. See
55 // https://github.com/facebook/docusaurus/blob/ca3dba5e5eab0f8b8a9b3388e2b2e637fe3a19a7/packages/docusaurus/src/client/ClientLifecyclesDispatcher.tsx#L30-L39
56 window.history.replaceState(null, null, newURL);
57 setHashState(hash);
58 lastHashRef.current = hash;
59 },
60 [location, setHashState],
61 );
62 return (
63 <ActiveSectionContext.Provider value={{ hash, setHash }}>
64 {children}
65 </ActiveSectionContext.Provider>
66 );
67}