11/* eslint-disable react/prop-types */
22import React , { useState , useEffect , useMemo } from "react" ;
33import {
4- Link ,
54 Routes ,
65 Route ,
76 useLocation ,
@@ -27,58 +26,22 @@ import memberNames from "../components/docs/member-names.md?raw";
2726
2827// Map of all docs with their content
2928const DOCS_MAP = {
30- "about-sast" : {
31- title : "About SAST" ,
32- content : aboutSast ,
33- category : "Introduction" ,
34- } ,
35- guidelines : {
36- title : "Guidelines" ,
37- content : guidelines ,
38- category : "Getting Started" ,
39- } ,
40- contribution : {
41- title : "Contribution Guide" ,
42- content : contribution ,
43- category : "Getting Started" ,
44- } ,
45- "code-of-conduct" : {
46- title : "Code of Conduct" ,
47- content : codeOfConduct ,
48- category : "Getting Started" ,
49- } ,
50- "github-process" : {
51- title : "GitHub Process" ,
52- content : githubProcess ,
53- category : "Development" ,
54- } ,
55- "community-roles" : {
56- title : "Community Roles" ,
57- content : communityRoles ,
58- category : "Community" ,
59- } ,
29+ "about-sast" : { title : "About SAST" , content : aboutSast , category : "Introduction" } ,
30+ guidelines : { title : "Guidelines" , content : guidelines , category : "Getting Started" } ,
31+ contribution : { title : "Contribution Guide" , content : contribution , category : "Getting Started" } ,
32+ "code-of-conduct" : { title : "Code of Conduct" , content : codeOfConduct , category : "Getting Started" } ,
33+ "github-process" : { title : "GitHub Process" , content : githubProcess , category : "Development" } ,
34+ "community-roles" : { title : "Community Roles" , content : communityRoles , category : "Community" } ,
6035 faqs : { title : "FAQs" , content : faqs , category : "Help" } ,
61- learning : {
62- title : "Learning Resources" ,
63- content : learning ,
64- category : "Resources" ,
65- } ,
66- "contributors-name" : {
67- title : "Contributors" ,
68- content : contributorsName ,
69- category : "Community" ,
70- } ,
71- "member-names" : {
72- title : "Members" ,
73- content : memberNames ,
74- category : "Community" ,
75- } ,
36+ learning : { title : "Learning Resources" , content : learning , category : "Resources" } ,
37+ "contributors-name" : { title : "Contributors" , content : contributorsName , category : "Community" } ,
38+ "member-names" : { title : "Members" , content : memberNames , category : "Community" } ,
7639} ;
7740
7841// Header Component
7942function DocsHeader ( ) {
8043 return (
81- < header className = "docs-header" >
44+ < header className = "docs-header" id = "docs-header" >
8245 < div className = "docs-header-badge" >
8346 < Book size = { 20 } />
8447 < span > Documentation</ span >
@@ -107,14 +70,32 @@ function DocsSidebar({ docs, query, setQuery, activeId }) {
10770 const groupedDocs = useMemo ( ( ) => {
10871 const groups = { } ;
10972 docs . forEach ( ( doc ) => {
110- if ( ! groups [ doc . category ] ) {
111- groups [ doc . category ] = [ ] ;
112- }
73+ if ( ! groups [ doc . category ] ) groups [ doc . category ] = [ ] ;
11374 groups [ doc . category ] . push ( doc ) ;
11475 } ) ;
11576 return groups ;
11677 } , [ docs ] ) ;
11778
79+ const handleNavClick = ( e , id ) => {
80+ e . preventDefault ( ) ; // prevent default Link scroll
81+ const header = document . getElementById ( "docs-header" ) ;
82+ const headerHeight = header ? header . offsetHeight : 300 ;
83+
84+ // manually navigate to the page without reloading
85+ window . history . pushState ( { } , "" , `/docs/${ id } ` ) ;
86+
87+ // scroll smoothly below the header
88+ setTimeout ( ( ) => {
89+ window . scrollTo ( {
90+ top : headerHeight + 40 ,
91+ behavior : "smooth" ,
92+ } ) ;
93+ } , 100 ) ;
94+
95+ // trigger router update
96+ window . dispatchEvent ( new PopStateEvent ( "popstate" ) ) ;
97+ } ;
98+
11899 return (
119100 < aside className = "docs-sidebar" >
120101 < div className = "docs-search" >
@@ -135,18 +116,17 @@ function DocsSidebar({ docs, query, setQuery, activeId }) {
135116 { categoryDocs . map ( ( doc ) => {
136117 const isActive = activeId === doc . id ;
137118 return (
138- < Link
119+ < a
139120 key = { doc . id }
140- to = { `/docs/${ doc . id } ` }
121+ href = { `/docs/${ doc . id } ` }
141122 className = { `docs-nav-item ${ isActive ? "active" : "" } ` }
123+ onClick = { ( e ) => handleNavClick ( e , doc . id ) }
142124 >
143125 < span className = "docs-nav-indicator" > </ span >
144126 < FileText size = { 16 } className = "docs-nav-icon" />
145127 < span className = "docs-nav-title" > { doc . title } </ span >
146- { isActive && (
147- < CheckCircle size = { 14 } className = "docs-nav-check" />
148- ) }
149- </ Link >
128+ { isActive && < CheckCircle size = { 14 } className = "docs-nav-check" /> }
129+ </ a >
150130 ) ;
151131 } ) }
152132 </ div >
@@ -163,8 +143,26 @@ function DocsSidebar({ docs, query, setQuery, activeId }) {
163143
164144// Content Renderer Component
165145function DocRenderer ( { doc } ) {
146+ const docRef = React . useRef ( null ) ;
147+
148+ useEffect ( ( ) => {
149+ // scroll below header when document changes
150+ const header = document . getElementById ( "docs-header" ) ;
151+ const headerHeight = header ? header . offsetHeight : 300 ;
152+
153+ window . scrollTo ( {
154+ top : headerHeight + 40 ,
155+ behavior : "smooth" ,
156+ } ) ;
157+
158+ // reset internal scroll of markdown content
159+ if ( docRef . current ) {
160+ docRef . current . scrollTo ( { top : 0 , behavior : "smooth" } ) ;
161+ }
162+ } , [ doc . id ] ) ;
163+
166164 return (
167- < article className = "docs-content" >
165+ < article className = "docs-content" ref = { docRef } >
168166 < div className = "docs-content-header" >
169167 < span className = "docs-content-category" > { doc . category } </ span >
170168 < h1 className = "docs-content-title" > { doc . title } </ h1 >
@@ -189,10 +187,10 @@ function DocPage({ docs }) {
189187 < FileText size = { 48 } />
190188 < h2 > Document not found</ h2 >
191189 < p > The requested documentation page could not be found.</ p >
192- < Link to = "/docs" className = "docs-back-button" >
190+ < a href = "/docs" className = "docs-back-button" >
193191 < ArrowLeft size = { 16 } />
194192 Back to Documentation
195- </ Link >
193+ </ a >
196194 </ div >
197195 ) ;
198196 }
@@ -207,7 +205,6 @@ export default function DocsHub() {
207205 const [ allDocs , setAllDocs ] = useState ( [ ] ) ;
208206
209207 useEffect ( ( ) => {
210- // Convert DOCS_MAP to array format
211208 const docsArray = Object . entries ( DOCS_MAP ) . map ( ( [ id , data ] ) => ( {
212209 id,
213210 title : data . title ,
@@ -237,15 +234,13 @@ export default function DocsHub() {
237234 < section className = "docs-page" >
238235 < div className = "docs-container" >
239236 < DocsHeader />
240-
241237 < div className = "docs-layout" >
242238 < DocsSidebar
243239 docs = { filtered }
244240 query = { query }
245241 setQuery = { setQuery }
246242 activeId = { activeId }
247243 />
248-
249244 < main className = "docs-main" >
250245 < Routes >
251246 < Route
@@ -269,4 +264,4 @@ export default function DocsHub() {
269264 </ div >
270265 </ section >
271266 ) ;
272- }
267+ }
0 commit comments