1- import { ReactElement , ReactNode , useMemo , useState } from "react" ;
1+ import { ReactElement , ReactNode , useEffect , useMemo , useState } from "react" ;
22import type { NextPage } from "next" ;
33import type { AppProps } from "next/app" ;
44import Head from "next/head" ;
55import { DxcApplicationLayout , DxcTextInput , DxcToastsQueue } from "@dxc-technology/halstack-react" ;
6- import SidenavLogo from "@/common/sidenav/SidenavLogo" ;
76import MainContent from "@/common/MainContent" ;
87import { useRouter } from "next/router" ;
98import { LinksSectionDetails , LinksSections } from "@/common/pagesList" ;
10- import Link from "next/link" ;
119import StatusBadge from "@/common/StatusBadge" ;
1210import "../global-styles.css" ;
1311import createCache , { EmotionCache } from "@emotion/cache" ;
1412import { CacheProvider } from "@emotion/react" ;
13+ import { usePathname } from "next/navigation" ;
14+ import Link from "next/link" ;
15+ import { GroupItem , Item , Section } from "../../../packages/lib/src/base-menu/types" ;
16+ import { isGroupItem } from "../../../packages/lib/src/base-menu/utils" ;
17+ import SidenavLogo from "@/common/sidenav/SidenavLogo" ;
1518
1619type NextPageWithLayout = NextPage & {
1720 getLayout ?: ( _page : ReactElement ) => ReactNode ;
@@ -26,73 +29,110 @@ const clientSideEmotionCache = createCache({ key: "css", prepend: true });
2629export default function App ( { Component, pageProps, emotionCache = clientSideEmotionCache } : AppPropsWithLayout ) {
2730 const getLayout = Component . getLayout || ( ( page ) => page ) ;
2831 const componentWithLayout = getLayout ( < Component { ...pageProps } /> ) ;
32+ const router = useRouter ( ) ;
33+ const pathname = usePathname ( ) ;
2934 const [ filter , setFilter ] = useState ( "" ) ;
30- const { asPath : currentPath } = useRouter ( ) ;
31- const filteredLinks = useMemo ( ( ) => {
32- const filtered : LinksSectionDetails [ ] = [ ] ;
33- LinksSections . map ( ( section ) => {
34- const sectionFilteredLinks = section ?. links . filter ( ( link ) =>
35- link . label . toLowerCase ( ) . includes ( filter . toLowerCase ( ) )
36- ) ;
37- if ( sectionFilteredLinks . length ) {
38- filtered . push ( { label : section . label , links : sectionFilteredLinks } ) ;
39- }
40- } ) ;
41- return filtered ;
42- } , [ filter ] ) ;
35+ const [ isExpanded , setIsExpanded ] = useState ( true ) ;
36+
37+ const filterSections = ( sections : Section [ ] , query : string ) : Section [ ] => {
38+ const q = query . trim ( ) . toLowerCase ( ) ;
39+ if ( ! q ) return sections ;
40+
41+ const filterItem = ( item : Item | GroupItem ) : Item | GroupItem | null => {
42+ const labelMatches = item . label . toLowerCase ( ) . includes ( q ) ;
43+
44+ if ( ! isGroupItem ( item ) ) return labelMatches ? item : null ;
45+
46+ const items = item . items . reduce < ( Item | GroupItem ) [ ] > ( ( acc , child ) => {
47+ const filtered = filterItem ( child ) ;
48+ if ( filtered ) acc . push ( filtered ) ;
49+ return acc ;
50+ } , [ ] ) ;
4351
44- const matchPaths = ( linkPath : string ) => {
45- const desiredPaths = [ linkPath , `${ linkPath } /code` ] ;
46- const pathToBeMatched = currentPath ?. split ( "#" ) [ 0 ] ?. slice ( 0 , - 1 ) ;
47- return pathToBeMatched ? desiredPaths . includes ( pathToBeMatched ) : false ;
52+ return labelMatches || items . length ? { ...item , items } : null ;
53+ } ;
54+
55+ return sections . reduce < Section [ ] > ( ( acc , section ) => {
56+ const items = section . items . reduce < ( Item | GroupItem ) [ ] > ( ( acc , item ) => {
57+ const filtered = filterItem ( item ) ;
58+ if ( filtered ) acc . push ( filtered ) ;
59+ return acc ;
60+ } , [ ] ) ;
61+ if ( items . length ) acc . push ( { ...section , items } ) ;
62+ return acc ;
63+ } , [ ] ) ;
4864 } ;
4965
66+ const mapLinksToGroupItems = ( sections : LinksSectionDetails [ ] ) : Section [ ] => {
67+ const matchPaths = ( linkPath : string ) => {
68+ const desiredPaths = [ linkPath , `${ linkPath } /code` ] ;
69+ const pathToBeMatched = pathname ?. split ( "#" ) [ 0 ] ?. slice ( 0 , - 1 ) ;
70+ return pathToBeMatched ? desiredPaths . includes ( pathToBeMatched ) : false ;
71+ } ;
72+
73+ return sections . map ( ( section ) => ( {
74+ title : section . label ,
75+ items : section . links . map ( ( link ) => ( {
76+ label : link . label ,
77+ href : link . path ,
78+ selected : matchPaths ( link . path ) ,
79+ ...( link . status && {
80+ badge : link . status !== "stable" ? < StatusBadge hasTitle status = { link . status } /> : undefined ,
81+ } ) ,
82+ renderItem : ( { children } : { children : ReactNode } ) => (
83+ < Link key = { link . path } href = { link . path } passHref legacyBehavior >
84+ { children }
85+ </ Link >
86+ ) ,
87+ } ) ) ,
88+ } ) ) ;
89+ } ;
90+
91+ useEffect ( ( ) => {
92+ const paths = [ ...new Set ( LinksSections . flatMap ( ( s ) => s . links . map ( ( l ) => l . path ) ) ) ] ;
93+ const prefetchPaths = async ( ) => {
94+ for ( const path of paths ) {
95+ await router . prefetch ( path ) ;
96+ }
97+ } ;
98+ void prefetchPaths ( ) ;
99+ } , [ ] ) ;
100+
101+ // TODO: ADD NEW CATEGORIZATION
102+
103+ const filteredSections = useMemo ( ( ) => {
104+ const sections = mapLinksToGroupItems ( LinksSections ) ;
105+ return filterSections ( sections , filter ) ;
106+ } , [ filter ] ) ;
107+
50108 return (
51109 < CacheProvider value = { emotionCache } >
52110 < Head >
53111 < link rel = "icon" type = "image/png" sizes = "32x32" href = "/favicon.png" />
54112 </ Head >
55113 < DxcApplicationLayout
56- visibilityToggleLabel = "Menu"
57114 sidenav = {
58- < DxcApplicationLayout . SideNav title = { < SidenavLogo /> } >
59- < DxcApplicationLayout . SideNav . Section >
60- < DxcTextInput
61- placeholder = "Search docs"
62- value = { filter }
63- onChange = { ( { value } : { value : string } ) => {
64- setFilter ( value ) ;
65- } }
66- size = "fillParent"
67- clearable
68- margin = { {
69- top : "large" ,
70- bottom : "large" ,
71- right : "medium" ,
72- left : "medium" ,
73- } }
74- />
75- </ DxcApplicationLayout . SideNav . Section >
76- { filteredLinks ?. map ( ( { label, links } ) => (
77- < DxcApplicationLayout . SideNav . Section key = { label } >
78- < DxcApplicationLayout . SideNav . Group title = { label } >
79- { links . map ( ( { label, path, status } ) => (
80- < Link key = { `${ label } -${ path } ` } href = { path } passHref legacyBehavior >
81- < DxcApplicationLayout . SideNav . Link selected = { matchPaths ( path ) } >
82- { label }
83- { status && status !== "stable" && < StatusBadge hasTitle status = { status } /> }
84- </ DxcApplicationLayout . SideNav . Link >
85- </ Link >
86- ) ) }
87- </ DxcApplicationLayout . SideNav . Group >
88- </ DxcApplicationLayout . SideNav . Section >
89- ) ) }
90- < DxcApplicationLayout . SideNav . Section >
91- < DxcApplicationLayout . SideNav . Link href = "https://github.com/dxc-technology/halstack-react" newWindow >
92- GitHub
93- </ DxcApplicationLayout . SideNav . Link >
94- </ DxcApplicationLayout . SideNav . Section >
95- </ DxcApplicationLayout . SideNav >
115+ < DxcApplicationLayout . Sidenav
116+ navItems = { filteredSections }
117+ branding = { < SidenavLogo expanded = { isExpanded } /> }
118+ topContent = {
119+ isExpanded && (
120+ < DxcTextInput
121+ placeholder = "Search docs"
122+ value = { filter }
123+ onChange = { ( { value } : { value : string } ) => {
124+ setFilter ( value ) ;
125+ } }
126+ size = "fillParent"
127+ clearable
128+ />
129+ )
130+ }
131+ expanded = { isExpanded }
132+ onExpandedChange = { ( ) => {
133+ setIsExpanded ( ( currentlyExpanded ) => ! currentlyExpanded ) ;
134+ } }
135+ />
96136 }
97137 >
98138 < DxcApplicationLayout . Main >
0 commit comments