-
-
Notifications
You must be signed in to change notification settings - Fork 8.7k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat(v2): highlight items in the table of content #1896
Changes from 3 commits
19efb7c
4f44e36
c23f981
fabaf77
84462e2
01f4d9a
98679b5
f14b6ee
421598e
812a30b
9c69dfd
3172602
3f4c805
b6667a0
7714afb
f635f9a
e6444c0
2e58e83
31d17c9
ad22c9f
a8826b9
f853171
64871b7
6fcee6d
16f10dd
5a2f539
ca4efb6
c7ff07c
255584c
ab15205
472c8fc
b8335fe
0baaad3
c41c19e
3f8cb78
82ece51
33718b6
2d13fe2
34a84a3
146a301
9c34bb7
0d445fc
cd9e2f2
5909b49
32773e5
77e8890
542b7c9
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,89 @@ | ||
/** | ||
* Copyright (c) 2017-present, Facebook, Inc. | ||
* | ||
* This source code is licensed under the MIT license found in the | ||
* LICENSE file in the root directory of this source tree. | ||
*/ | ||
|
||
import {useEffect, useState} from 'react'; | ||
|
||
function useTableOfContentHighlight( | ||
tableOfContentLinkClassName, | ||
tableOfContentLinkActiveClassName, | ||
topOffset, | ||
) { | ||
const [ | ||
lastActiveTableOfContentLink, | ||
setLastActiveTableOfContentLink, | ||
] = useState(undefined); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Use |
||
|
||
useEffect(() => { | ||
let headersAnchor = []; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is not it more correct to call this var that?
|
||
let tableOfContentLinks = []; | ||
|
||
function getActiveHeaderAnchor() { | ||
const TOP_OFFSET = topOffset; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do you really need to create a constant for this when it is used once? I mean, the code is not so big, and there is no point in duplicating the same value in such a way. In my opinion it is better to do without it: - if (top >= 0 && top <= TOP_OFFSET) {}
+ if (top> = 0 && top <= topOffset) {} |
||
let index = 0; | ||
let activeHeaderAnchor; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You must probably explicitly set the default value. let activeHeaderAnchor = null; |
||
headersAnchor = document.querySelectorAll('a.anchor'); | ||
while (index < headersAnchor.length && !activeHeaderAnchor) { | ||
const headerAnchor = headersAnchor[index]; | ||
const {top} = headerAnchor.getBoundingClientRect(); | ||
if (top >= 0 && top <= TOP_OFFSET) { | ||
activeHeaderAnchor = headerAnchor; | ||
} | ||
index += 1; | ||
} | ||
return activeHeaderAnchor; | ||
} | ||
|
||
function highlightTableOfContentLink(tableOfContentLink) { | ||
if (lastActiveTableOfContentLink) { | ||
lastActiveTableOfContentLink.classList.remove( | ||
tableOfContentLinkActiveClassName, | ||
); | ||
} | ||
tableOfContentLink.classList.add(tableOfContentLinkActiveClassName); | ||
setLastActiveTableOfContentLink(tableOfContentLink); | ||
} | ||
|
||
function getAnchorValue(tableOfContentLink) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I would consider this solution, it will be faster. function getAnchorValue(tocLink) {
return decodeURIComponent(
tocLink.href.substring(tocLink.href.indexOf('#') + 1)
);
} |
||
const splittedUrl = tableOfContentLink.href | ||
? decodeURIComponent(tableOfContentLink.href).split('#') | ||
: []; | ||
return splittedUrl.length > 1 ? splittedUrl[1] : ''; | ||
} | ||
|
||
function setActiveTableOfContentLink() { | ||
const activeHeaderAnchor = getActiveHeaderAnchor(); | ||
if (activeHeaderAnchor) { | ||
SantiagoGdaR marked this conversation as resolved.
Show resolved
Hide resolved
|
||
let index = 0; | ||
let itemHighlighted = false; | ||
tableOfContentLinks = document.querySelectorAll( | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hmm, I would prefer to use the old-school |
||
`.${tableOfContentLinkClassName}`, | ||
); | ||
while (index < tableOfContentLinks.length && !itemHighlighted) { | ||
const tableOfContentLink = tableOfContentLinks[index]; | ||
const anchorValue = getAnchorValue(tableOfContentLink); | ||
if (activeHeaderAnchor.id === anchorValue) { | ||
highlightTableOfContentLink(tableOfContentLink); | ||
itemHighlighted = true; | ||
} | ||
index += 1; | ||
} | ||
} | ||
} | ||
|
||
document.addEventListener('scroll', setActiveTableOfContentLink); | ||
document.addEventListener('resize', setActiveTableOfContentLink); | ||
|
||
setActiveTableOfContentLink(); | ||
|
||
return () => { | ||
document.removeEventListener('scroll', setActiveTableOfContentLink); | ||
document.removeEventListener('resize', setActiveTableOfContentLink); | ||
}; | ||
}); | ||
} | ||
|
||
export default useTableOfContentHighlight; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe it makes sense
TableOfContent
shorten to simplytoc
? It will be easier to read and understand.useTableOfContentHighlight
->useTocHighlight
tableOfContentLinkClassName
->tocLinkClassName