Skip to content

Commit e0d8464

Browse files
committed
Consolidate useScrollToHash with details implementation
1 parent 54870cb commit e0d8464

File tree

4 files changed

+40
-42
lines changed

4 files changed

+40
-42
lines changed

packages/gitbook/src/components/DocumentView/Expandable/Details.tsx

Lines changed: 15 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,9 @@
11
'use client';
22

3-
import { useParams } from 'next/navigation';
43
import React from 'react';
54

65
import { ClassValue, tcls } from '@/lib/tailwind';
7-
8-
function useHash() {
9-
const params = useParams();
10-
const [hash, setHash] = React.useState<string>(global.location?.hash?.slice(1));
11-
React.useEffect(() => {
12-
function updateHash() {
13-
setHash(global.location?.hash?.slice(1));
14-
}
15-
global.addEventListener('hashchange', updateHash);
16-
updateHash();
17-
return () => global.removeEventListener('hashchange', updateHash);
18-
}, [params]);
19-
return hash;
20-
}
6+
import { useHash } from '@/components/hooks';
217

228
/**
239
* Details component rendered on client so it can expand dependent on url hash changes.
@@ -36,23 +22,24 @@ export function Details(props: {
3622
const [anchorElement, setAnchorElement] = React.useState<Element | null | undefined>();
3723

3824
const hash = useHash();
25+
/**
26+
* Open the details element if the url hash refers to the id of the details element
27+
* or the id of some element contained within the details element.
28+
*/
3929
React.useEffect(() => {
40-
if (!hash) {
30+
if (!hash || !detailsRef.current) {
4131
return;
4232
}
43-
const descendant =
44-
hash === id ? detailsRef.current : detailsRef.current?.querySelector(`#${hash}`);
45-
setAnchorElement(descendant);
46-
}, [hash, id]);
47-
48-
React.useLayoutEffect(() => {
49-
if (anchorElement) {
50-
anchorElement.scrollIntoView({
51-
block: 'start',
52-
behavior: 'smooth',
53-
});
33+
if (hash === id) {
34+
setAnchorElement(detailsRef.current)
5435
}
55-
}, [anchorElement]);
36+
else {
37+
const activeElement = document.getElementById(hash);
38+
if (activeElement && detailsRef.current?.contains(activeElement)) {
39+
setAnchorElement(activeElement);
40+
}
41+
}
42+
}, [hash, id]);
5643

5744
return (
5845
<details
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
export * from './useScrollActiveId';
22
export * from './useScrollToHash';
3+
export * from './useHash';
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import { useParams } from 'next/navigation';
2+
import React from 'react';
3+
4+
export function useHash() {
5+
const params = useParams();
6+
const [hash, setHash] = React.useState<string>(global.location?.hash?.slice(1));
7+
React.useEffect(() => {
8+
function updateHash() {
9+
setHash(global.location?.hash?.slice(1));
10+
}
11+
global.addEventListener('hashchange', updateHash);
12+
updateHash();
13+
return () => global.removeEventListener('hashchange', updateHash);
14+
// With next.js, the hashchange event is not triggered when the hash changes
15+
// Instead a hack is to use the `useParams` hook to listen to changes in the hash
16+
// https://github.com/vercel/next.js/discussions/49465#discussioncomment-5845312
17+
}, [params]);
18+
return hash;
19+
}
Lines changed: 5 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,20 @@
1-
import { useParams } from 'next/navigation';
21
import React from 'react';
2+
import { useHash } from './useHash';
33

44
/**
55
* Scroll to the current URL hash everytime the URL changes.
66
*/
77
export function useScrollToHash() {
8-
const params = useParams();
9-
10-
const scrollToHash = React.useCallback(() => {
11-
const hash = window.location.hash;
8+
const hash = useHash();
9+
React.useLayoutEffect(() => {
1210
if (hash) {
13-
const element = document.getElementById(hash.slice(1));
11+
const element = document.getElementById(hash);
1412
if (element) {
1513
element.scrollIntoView({
1614
block: 'start',
1715
behavior: 'smooth',
1816
});
1917
}
2018
}
21-
}, []);
22-
23-
// With next.js, the hashchange event is not triggered when the hash changes
24-
// Instead a hack is to use the `useParams` hook to listen to changes in the hash
25-
// https://github.com/vercel/next.js/discussions/49465#discussioncomment-5845312
26-
React.useEffect(() => {
27-
scrollToHash();
28-
}, [params, scrollToHash]);
19+
}, [hash]);
2920
}

0 commit comments

Comments
 (0)