@@ -3,8 +3,7 @@ import { $$, $ } from 'select-dom';
33interface TocElements {
44 headings : Element [ ] ;
55 tocLinks : HTMLAnchorElement [ ] ;
6- tocNav : HTMLUListElement | null ;
7- markdownContent : Element | null ;
6+ tocContainer : HTMLUListElement | null ;
87 progressIndicator : HTMLDivElement ;
98}
109
@@ -14,51 +13,23 @@ const HEADING_OFFSET = 28 * 4 + 6 * 4;
1413function initializeTocElements ( ) : TocElements {
1514 const headings = $$ ( 'h2, h3' ) ;
1615 const tocLinks = $$ ( '#toc-nav li>a' ) as HTMLAnchorElement [ ] ;
17- const tocNav = $ ( '#toc-nav ul' ) as HTMLUListElement ;
18- const markdownContent = $ ( '.markdown-content' ) || null ;
19-
20- // Create progress indicator
21- const progressIndicator = document . createElement ( 'div' ) ;
22- progressIndicator . className = 'toc-progress-indicator' ;
23- tocNav ?. appendChild ( progressIndicator ) ;
24-
25- return { headings, tocLinks, tocNav, markdownContent, progressIndicator } ;
26- }
27-
28- // Add required styles for the progress indicator
29- function addProgressIndicatorStyles ( ) {
30- const style = document . createElement ( 'style' ) ;
31- style . textContent = `
32- #toc-nav ul {
33- position: relative;
34- }
35- .toc-progress-indicator {
36- position: absolute;
37- left: calc(var(--spacing) * 2);
38- width: 1px;
39- background: var(--color-blue-elastic);
40- transition: top 250ms ease-out, height 250ms ease-out;
41- }
42- ` ;
43- document . head . appendChild ( style ) ;
16+ const tocContainer = $ ( '#toc-nav ul' ) as HTMLUListElement ;
17+ const progressIndicator = $ ( '.toc-progress-indicator' , tocContainer ) as HTMLDivElement ;
18+ return { headings, tocLinks, tocContainer, progressIndicator } ;
4419}
4520
4621// Find the current TOC links based on visible headings
22+ // It can return multiple links because headings in a tab can have the same position
4723function findCurrentTocLinks ( elements : TocElements ) : HTMLAnchorElement [ ] {
4824 let currentTocLinks : HTMLAnchorElement [ ] = [ ] ;
4925 let currentTop : number | null = null ;
50-
5126 for ( const heading of elements . headings ) {
5227 const rect = heading . getBoundingClientRect ( ) ;
53- const headingText = heading . textContent ?. trim ( ) ?? '' ;
54-
5528 if ( rect . top <= HEADING_OFFSET ) {
56- // If we find a heading at a new height, clear previous links
5729 if ( currentTop !== null && Math . abs ( rect . top - currentTop ) > 1 ) {
5830 currentTocLinks = [ ] ;
5931 }
6032 currentTop = rect . top ;
61-
6233 const foundLink = elements . tocLinks . find ( link =>
6334 link . getAttribute ( 'href' ) === `#${ heading . closest ( 'section' ) ?. id } `
6435 ) ;
@@ -67,7 +38,6 @@ function findCurrentTocLinks(elements: TocElements): HTMLAnchorElement[] {
6738 }
6839 }
6940 }
70-
7141 return currentTocLinks ;
7242}
7343
@@ -82,29 +52,19 @@ function getVisibleHeadings(elements: TocElements) {
8252// Handle bottom of page scroll behavior
8353function handleBottomScroll ( elements : TocElements ) {
8454 const visibleHeadings = getVisibleHeadings ( elements ) ;
85-
86- visibleHeadings . forEach ( heading => {
87- console . log ( heading . textContent . trim ( ) ) ;
88- } )
89-
9055 if ( visibleHeadings . length === 0 ) return ;
91-
9256 const firstHeading = visibleHeadings [ 0 ] ;
9357 const lastHeading = visibleHeadings [ visibleHeadings . length - 1 ] ;
94-
9558 const firstLink = elements . tocLinks . find ( link =>
96- link . getAttribute ( 'href' ) === `#${ firstHeading . closest ( 'section' ) ?. id } `
59+ link . getAttribute ( 'href' ) === `#${ firstHeading . parentElement ?. id } `
9760 ) ?. closest ( 'li' ) ;
98-
9961 const lastLink = elements . tocLinks . find ( link =>
100- link . getAttribute ( 'href' ) === `#${ lastHeading . closest ( 'section' ) ?. id } `
62+ link . getAttribute ( 'href' ) === `#${ lastHeading . parentElement ?. id } `
10163 ) ?. closest ( 'li' ) ;
102-
103- if ( firstLink && lastLink && elements . tocNav ) {
104- const tocRect = elements . tocNav . getBoundingClientRect ( ) ;
64+ if ( firstLink && lastLink && elements . tocContainer ) {
65+ const tocRect = elements . tocContainer . getBoundingClientRect ( ) ;
10566 const firstRect = firstLink . getBoundingClientRect ( ) ;
10667 const lastRect = lastLink . getBoundingClientRect ( ) ;
107-
10868 updateProgressIndicatorPosition (
10969 elements . progressIndicator ,
11070 firstRect . top - tocRect . top ,
@@ -113,7 +73,6 @@ function handleBottomScroll(elements: TocElements) {
11373 }
11474}
11575
116- // Update progress indicator position and height
11776function updateProgressIndicatorPosition (
11877 indicator : HTMLDivElement ,
11978 top : number ,
@@ -123,28 +82,22 @@ function updateProgressIndicatorPosition(
12382 indicator . style . height = `${ height } px` ;
12483}
12584
126- // Main update function for the indicator
12785function updateIndicator ( elements : TocElements ) {
128- if ( ! elements . markdownContent || ! elements . tocNav ) return ;
86+ if ( ! elements . tocContainer ) return ;
12987
13088 const isAtBottom = window . innerHeight + window . scrollY >= document . documentElement . scrollHeight - 10 ;
13189 const currentTocLinks = findCurrentTocLinks ( elements ) ;
13290
13391 if ( isAtBottom ) {
13492 handleBottomScroll ( elements ) ;
13593 } else if ( currentTocLinks . length > 0 ) {
136- const tocRect = elements . tocNav . getBoundingClientRect ( ) ;
137-
138- // Find the topmost and bottommost link positions
94+ const tocRect = elements . tocContainer . getBoundingClientRect ( ) ;
13995 const linkElements = currentTocLinks
14096 . map ( link => link . closest ( 'li' ) )
14197 . filter ( ( li ) : li is HTMLLIElement => li !== null ) ;
142-
14398 if ( linkElements . length === 0 ) return ;
144-
14599 const firstLinkRect = linkElements [ 0 ] . getBoundingClientRect ( ) ;
146100 const lastLinkRect = linkElements [ linkElements . length - 1 ] . getBoundingClientRect ( ) ;
147-
148101 updateProgressIndicatorPosition (
149102 elements . progressIndicator ,
150103 firstLinkRect . top - tocRect . top ,
@@ -153,8 +106,7 @@ function updateIndicator(elements: TocElements) {
153106 }
154107}
155108
156- // Handle smooth scrolling for TOC links
157- function setupTocLinkHandlers ( elements : TocElements ) {
109+ function setupSmoothScrolling ( elements : TocElements ) {
158110 elements . tocLinks . forEach ( link => {
159111 link . addEventListener ( 'click' , ( e ) => {
160112 const href = link . getAttribute ( 'href' ) ;
@@ -172,17 +124,11 @@ function setupTocLinkHandlers(elements: TocElements) {
172124
173125export function initTocNav ( ) {
174126 const elements = initializeTocElements ( ) ;
175- addProgressIndicatorStyles ( ) ;
176-
177- // Initialize indicator position
178127 elements . progressIndicator . style . height = '0' ;
179128 elements . progressIndicator . style . top = '0' ;
180-
181- // Set up event listeners
182- const boundUpdateIndicator = ( ) => updateIndicator ( elements ) ;
183- window . addEventListener ( 'scroll' , boundUpdateIndicator ) ;
184- window . addEventListener ( 'resize' , boundUpdateIndicator ) ;
185-
186- setupTocLinkHandlers ( elements ) ;
187- boundUpdateIndicator ( ) ;
129+ const update = ( ) => updateIndicator ( elements )
130+ window . addEventListener ( 'scroll' , update ) ;
131+ window . addEventListener ( 'resize' , update ) ;
132+ setupSmoothScrolling ( elements ) ;
133+ update ( ) ;
188134}
0 commit comments