Skip to content

Commit a7a713b

Browse files
authored
Scroll to active TOC when clicking a link (#3362)
1 parent d6613c7 commit a7a713b

File tree

2 files changed

+21
-23
lines changed

2 files changed

+21
-23
lines changed

.changeset/nasty-donkeys-sell.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"gitbook": patch
3+
---
4+
5+
Scroll to active TOC when clicking a link

packages/gitbook/src/components/TableOfContents/TOCScroller.tsx

Lines changed: 16 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import React, {
1010
import { assert } from 'ts-essentials';
1111

1212
interface TOCScrollContainerContextType {
13-
onContainerMount: (listener: (element: HTMLDivElement) => void) => () => void;
13+
getContainer: (listener: (element: HTMLDivElement) => void) => () => void;
1414
}
1515

1616
const TOCScrollContainerContext = React.createContext<TOCScrollContainerContextType | null>(null);
@@ -27,23 +27,17 @@ function useTOCScrollContainerContext() {
2727
export function TOCScrollContainer(props: ComponentPropsWithoutRef<'div'>) {
2828
const ref = useRef<HTMLDivElement>(null);
2929
const listeners = useRef<((element: HTMLDivElement) => void)[]>([]);
30-
const onContainerMount: TOCScrollContainerContextType['onContainerMount'] = useCallback(
31-
(listener) => {
32-
if (ref.current) {
33-
listener(ref.current);
34-
return () => {};
35-
}
36-
listeners.current.push(listener);
37-
return () => {
38-
listeners.current = listeners.current.filter((l) => l !== listener);
39-
};
40-
},
41-
[]
42-
);
43-
const value: TOCScrollContainerContextType = useMemo(
44-
() => ({ onContainerMount }),
45-
[onContainerMount]
46-
);
30+
const getContainer: TOCScrollContainerContextType['getContainer'] = useCallback((listener) => {
31+
if (ref.current) {
32+
listener(ref.current);
33+
return () => {};
34+
}
35+
listeners.current.push(listener);
36+
return () => {
37+
listeners.current = listeners.current.filter((l) => l !== listener);
38+
};
39+
}, []);
40+
const value: TOCScrollContainerContextType = useMemo(() => ({ getContainer }), [getContainer]);
4741
useEffect(() => {
4842
const element = ref.current;
4943
if (!element) {
@@ -73,18 +67,17 @@ export function useScrollToActiveTOCItem(props: {
7367
isActive: boolean;
7468
}) {
7569
const { isActive, anchorRef } = props;
76-
const isInitialActiveRef = useRef(isActive);
77-
const { onContainerMount } = useTOCScrollContainerContext();
70+
const { getContainer } = useTOCScrollContainerContext();
7871
useEffect(() => {
7972
const anchor = anchorRef.current;
80-
if (isInitialActiveRef.current && anchor) {
81-
return onContainerMount((container) => {
73+
if (isActive && anchor) {
74+
return getContainer((container) => {
8275
if (isOutOfView(anchor, container)) {
8376
container.scrollTo({ top: anchor.offsetTop - TOC_ITEM_OFFSET });
8477
}
8578
});
8679
}
87-
}, [onContainerMount, anchorRef]);
80+
}, [isActive, getContainer, anchorRef]);
8881
}
8982

9083
function isOutOfView(element: HTMLElement, container: HTMLElement) {

0 commit comments

Comments
 (0)