22
33import Link from 'next/link' ;
44import { usePathname } from 'next/navigation' ;
5- import type { ReferencePage } from '../services/referenceService' ;
65import { capitalCase } from 'change-case' ;
76import pluralize from 'pluralize' ;
7+ import React from 'react' ;
8+
9+ type ReferencePage = {
10+ name : string ;
11+ description : string ;
12+ reference : string ;
13+ type : string ;
14+ category ?: string ;
15+ } ;
16+
17+ type NavigationStructure = {
18+ type : string ;
19+ displayName : string ;
20+ categories : {
21+ category : string ;
22+ displayName : string ;
23+ items : ReferencePage [ ] ;
24+ } [ ] ;
25+ } [ ] ;
26+
27+ const icons = {
28+ chevronRight : (
29+ < path
30+ strokeLinecap = "round"
31+ strokeLinejoin = "round"
32+ strokeWidth = { 2 }
33+ d = "M9 5l7 7-7 7"
34+ />
35+ ) ,
36+ } ;
837
938export default function Index ( {
1039 groupedReferences
@@ -13,81 +42,125 @@ export default function Index({
1342} ) {
1443 const pathname = usePathname ( ) ;
1544
16- // Get all types and sort them
17- const types = Object . keys ( groupedReferences ) . sort ( ) ;
45+ // Convert old structure to new structure
46+ const navigationStructure : NavigationStructure = Object . entries ( groupedReferences )
47+ . sort ( ( [ a ] , [ b ] ) => a . localeCompare ( b ) )
48+ . map ( ( [ type , categories ] ) => ( {
49+ type,
50+ displayName : type ,
51+ categories : Object . entries ( categories )
52+ . sort ( ( [ a ] , [ b ] ) => a . localeCompare ( b ) )
53+ . map ( ( [ category , items ] ) => ( {
54+ category,
55+ displayName : category ,
56+ items : items . sort ( ( a , b ) => a . name . localeCompare ( b . name ) )
57+ } ) )
58+ } ) ) ;
59+
60+ // Parse pathname to determine current location
61+ const pathParts = pathname . split ( '/' ) . filter ( Boolean ) ;
62+ const currentType = pathParts [ 2 ] ; // /docs/reference/[type]
63+ const currentCategory = pathParts [ 3 ] ; // /docs/reference/[type]/[category]
64+ const currentName = pathParts [ 4 ] ? decodeURIComponent ( pathParts [ 4 ] ) : undefined ; // /docs/reference/[type]/[category]/[name]
1865
1966 return (
20- < section className = "flex-1 p-4 overflow-y-auto" >
67+ < section className = "flex-1 overflow-y-auto p-4 " >
2168 < nav className = "space-y-1" >
22- { types . map ( ( type , index ) => {
23- const categories = Object . keys ( groupedReferences [ type ] ) . sort ( ) ;
69+ { navigationStructure . map ( ( typeSection , typeIndex ) => {
70+ // Types are always expanded
71+ const isTypeExpanded = true ;
72+ const isTypeActive = pathname === `/docs/reference/${ typeSection . type } ` ;
2473
2574 return (
26- < div key = { type } >
27- { /* Add horizontal rule between sections (not before the first one ) */ }
28- { index > 0 && (
75+ < div key = { typeSection . type } >
76+ { /* Separator between type sections (not before first) */ }
77+ { typeIndex > 0 && (
2978 < div className = "my-4 border-t border-neutral-700/50" > </ div >
3079 ) }
3180
32- { /* Type header */ }
33- { categories . length > 0 && (
34- < >
35- < Option
36- key = { type }
37- target = { `/docs/reference/${ type } ` }
38- display = { pluralize ( capitalCase ( type ) ) }
39- className = "uppercase"
40- currentPath = { pathname }
41- />
81+ { /* Type row */ }
82+ < div className = "flex items-center gap-1" >
83+ < div className = "p-1 flex-shrink-0" >
84+ < svg
85+ className = "w-4 h-4 text-gray-500 rotate-90"
86+ fill = "none"
87+ stroke = "currentColor"
88+ viewBox = "0 0 24 24"
89+ >
90+ { icons . chevronRight }
91+ </ svg >
92+ </ div >
93+ < Link
94+ href = { `/docs/reference/${ typeSection . type } ` }
95+ className = { `flex-1 px-3 py-3 rounded-lg text-sm font-semibold uppercase transition-all duration-200 ${
96+ isTypeActive
97+ ? "bg-blue-500/20 text-blue-300 border border-blue-500/30"
98+ : "text-gray-300 hover:text-white hover:bg-neutral-700/50"
99+ } `}
100+ >
101+ { pluralize ( capitalCase ( typeSection . displayName ) ) }
102+ </ Link >
103+ </ div >
42104
43- { /* Category links */ }
44- { categories . map ( ( category ) => (
45- < Option
46- key = { `${ type } -${ category } ` }
47- target = { `/docs/reference/${ type } /${ category } ` }
48- display = { capitalCase ( category ) }
49- currentPath = { pathname }
50- />
51- ) ) }
52- </ >
53- ) }
105+ { /* Categories */ }
106+ { isTypeExpanded && typeSection . categories . map ( ( categorySection ) => {
107+ // Category is expanded if we're on that category page or on one of its item pages
108+ const isCategoryExpanded = currentType === typeSection . type && currentCategory === categorySection . category ;
109+ const isCategoryActive = pathname === `/docs/reference/${ typeSection . type } /${ categorySection . category } ` ;
110+
111+ return (
112+ < div key = { `${ typeSection . type } -${ categorySection . category } ` } >
113+ { /* Category row */ }
114+ < div className = "flex items-center gap-1 pl-4" >
115+ < div className = "p-1 flex-shrink-0" >
116+ < svg
117+ className = { `w-3 h-3 text-gray-500 transition-transform ${ isCategoryExpanded ? 'rotate-90' : '' } ` }
118+ fill = "none"
119+ stroke = "currentColor"
120+ viewBox = "0 0 24 24"
121+ >
122+ { icons . chevronRight }
123+ </ svg >
124+ </ div >
125+ < Link
126+ href = { `/docs/reference/${ typeSection . type } /${ categorySection . category } ` }
127+ className = { `flex-1 px-3 py-3 rounded-lg text-sm transition-all duration-200 ${
128+ isCategoryActive
129+ ? "bg-blue-500/20 text-blue-300 border border-blue-500/30"
130+ : "text-gray-300 hover:text-white hover:bg-neutral-700/50"
131+ } `}
132+ >
133+ { capitalCase ( categorySection . displayName ) }
134+ </ Link >
135+ </ div >
136+
137+ { /* Individual reference items */ }
138+ { isCategoryExpanded && categorySection . items . map ( ( item ) => {
139+ const itemPath = `/docs/reference/${ typeSection . type } /${ categorySection . category } /${ item . reference . split ( '/' ) . pop ( ) } ` ;
140+ const isItemActive = pathname === itemPath ;
141+
142+ return (
143+ < div key = { item . reference } className = "flex items-center pl-10 ml-4" >
144+ < Link
145+ href = { itemPath }
146+ className = { `flex-1 px-3 py-3 rounded-lg text-sm transition-all duration-200 ${
147+ isItemActive
148+ ? "bg-blue-500/20 text-blue-300 border border-blue-500/30"
149+ : "text-gray-400 hover:text-white hover:bg-neutral-700/50"
150+ } `}
151+ >
152+ { item . name }
153+ </ Link >
154+ </ div >
155+ ) ;
156+ } ) }
157+ </ div >
158+ ) ;
159+ } ) }
54160 </ div >
55161 ) ;
56162 } ) }
57163 </ nav >
58164 </ section >
59165 ) ;
60166}
61-
62- function Option ( {
63- target,
64- display,
65- className,
66- currentPath
67- } : {
68- target : string ;
69- display : string ;
70- className ?: string ;
71- currentPath : string ;
72- } ) {
73- // Determine if this option should be highlighted
74- // Highlight if:
75- // 1. Exact match (e.g., on /docs/reference/operator, highlight "Operators")
76- // 2. Category match (e.g., on /docs/reference/operator/accumulator or /docs/reference/operator/accumulator/avg, highlight "Operators" and "Accumulator")
77- const isExactMatch = currentPath === target ;
78- const isCategoryMatch = currentPath . startsWith ( target + '/' ) ;
79- const isActive = isExactMatch || isCategoryMatch ;
80-
81- return (
82- < Link
83- href = { target }
84- className = { `block w-full text-left px-4 py-3 rounded-lg text-sm transition-all duration-200 ${
85- isActive
86- ? "bg-blue-500/20 text-blue-300 border border-blue-500/30"
87- : "text-gray-300 hover:text-white hover:bg-neutral-700/50"
88- } ${ className ? ' ' + className : '' } `}
89- >
90- { display }
91- </ Link >
92- ) ;
93- }
0 commit comments