@@ -6,58 +6,134 @@ import cx from 'classnames';
66import { useTocHighlight } from './useTocHighlight' ;
77import type { Toc } from '../MDX/TocContext' ;
88
9+ import { useState , useEffect } from 'react' ;
10+
11+ function ScrollToTop ( ) {
12+ const [ isVisible , setIsVisible ] = useState ( false ) ;
13+
14+ // 控制按钮显示
15+ const toggleVisibility = ( ) => {
16+ if ( window . pageYOffset > 300 ) {
17+ setIsVisible ( true ) ;
18+ } else {
19+ setIsVisible ( false ) ;
20+ }
21+ } ;
22+
23+ // 滚动到顶部
24+ const scrollToTop = ( ) => {
25+ window . scrollTo ( {
26+ top : 0 ,
27+ behavior : 'smooth' ,
28+ } ) ;
29+ } ;
30+
31+ useEffect ( ( ) => {
32+ window . addEventListener ( 'scroll' , toggleVisibility ) ;
33+ return ( ) => {
34+ window . removeEventListener ( 'scroll' , toggleVisibility ) ;
35+ } ;
36+ } , [ ] ) ;
37+
38+ return (
39+ < >
40+ { isVisible && (
41+ < div
42+ onClick = { scrollToTop }
43+ style = { {
44+ position : 'fixed' ,
45+ bottom : '40px' ,
46+ right : '40px' ,
47+ cursor : 'pointer' ,
48+ backgroundColor : '#007bff' ,
49+ borderRadius : '50%' ,
50+ width : '40px' ,
51+ height : '40px' ,
52+ display : 'flex' ,
53+ justifyContent : 'center' ,
54+ alignItems : 'center' ,
55+ boxShadow : '0 2px 10px rgba(0,0,0,0.2)' ,
56+ transition : 'all 0.3s ease' ,
57+ opacity : '0.8' ,
58+ zIndex : 1000 ,
59+ '&:hover' : {
60+ opacity : 1 ,
61+ transform : 'translateY(-2px)' ,
62+ } ,
63+ } } >
64+ < svg
65+ width = "20"
66+ height = "20"
67+ viewBox = "0 0 24 24"
68+ fill = "none"
69+ stroke = "white"
70+ strokeWidth = "2"
71+ strokeLinecap = "round"
72+ strokeLinejoin = "round" >
73+ < path d = "M12 19V5M5 12l7-7 7 7" />
74+ </ svg >
75+ </ div >
76+ ) }
77+ </ >
78+ ) ;
79+ }
80+
981export function Toc ( { headings} : { headings : Toc } ) {
1082 const { currentIndex} = useTocHighlight ( ) ;
1183 // TODO: We currently have a mismatch between the headings in the document
1284 // and the headings we find in MarkdownPage (i.e. we don't find Recap or Challenges).
1385 // Select the max TOC item we have here for now, but remove this after the fix.
1486 const selectedIndex = Math . min ( currentIndex , headings . length - 1 ) ;
87+
1588 return (
16- < nav role = "navigation" className = "pt-20 sticky top-0 end-0" >
17- { headings . length > 0 && (
18- < h2 className = "mb-3 lg:mb-3 uppercase tracking-wide font-bold text-sm text-secondary dark:text-secondary-dark px-4 w-full" >
19- On this page
20- </ h2 >
21- ) }
22- < div
23- className = "h-full overflow-y-auto ps-4 max-h-[calc(100vh-7.5rem)]"
24- style = { {
25- overscrollBehavior : 'contain' ,
26- } } >
27- < ul className = "space-y-2 pb-16" >
28- { headings . length > 0 &&
29- headings . map ( ( h , i ) => {
30- if ( ! h . url && process . env . NODE_ENV === 'development' ) {
31- console . error ( 'Heading does not have URL' ) ;
32- }
33- return (
34- < li
35- key = { `heading-${ h . url } -${ i } ` }
36- className = { cx (
37- 'text-sm px-2 rounded-s-xl' ,
38- selectedIndex === i
39- ? 'bg-highlight dark:bg-highlight-dark'
40- : null ,
41- {
42- 'ps-4' : h ?. depth === 3 ,
43- hidden : h . depth && h . depth > 3 ,
44- }
45- ) } >
46- < a
89+ < >
90+ < nav role = "navigation" className = "pt-20 sticky top-0 end-0" >
91+ { headings . length > 0 && (
92+ < h2 className = "mb-3 lg:mb-3 uppercase tracking-wide font-bold text-sm text-secondary dark:text-secondary-dark px-4 w-full" >
93+ On this page
94+ </ h2 >
95+ ) }
96+ < div
97+ className = "h-full overflow-y-auto ps-4 max-h-[calc(100vh-7.5rem)]"
98+ style = { {
99+ overscrollBehavior : 'contain' ,
100+ } } >
101+ < ul className = "space-y-2 pb-16" >
102+ { headings . length > 0 &&
103+ headings . map ( ( h , i ) => {
104+ if ( ! h . url && process . env . NODE_ENV === 'development' ) {
105+ console . error ( 'Heading does not have URL' ) ;
106+ }
107+ return (
108+ < li
109+ key = { `heading-${ h . url } -${ i } ` }
47110 className = { cx (
111+ 'text-sm px-2 rounded-s-xl' ,
48112 selectedIndex === i
49- ? 'text-link dark:text-link-dark font-bold'
50- : 'text-secondary dark:text-secondary-dark' ,
51- 'block hover:text-link dark:hover:text-link-dark leading-normal py-2'
52- ) }
53- href = { h . url } >
54- { h . text }
55- </ a >
56- </ li >
57- ) ;
58- } ) }
59- </ ul >
60- </ div >
61- </ nav >
113+ ? 'bg-highlight dark:bg-highlight-dark'
114+ : null ,
115+ {
116+ 'ps-4' : h ?. depth === 3 ,
117+ hidden : h . depth && h . depth > 3 ,
118+ }
119+ ) } >
120+ < a
121+ className = { cx (
122+ selectedIndex === i
123+ ? 'text-link dark:text-link-dark font-bold'
124+ : 'text-secondary dark:text-secondary-dark' ,
125+ 'block hover:text-link dark:hover:text-link-dark leading-normal py-2'
126+ ) }
127+ href = { h . url } >
128+ { h . text }
129+ </ a >
130+ </ li >
131+ ) ;
132+ } ) }
133+ </ ul >
134+ </ div >
135+ </ nav >
136+ < ScrollToTop />
137+ </ >
62138 ) ;
63139}
0 commit comments