import React , { useEffect , useState } from 'react' ; import styles from './Navigation.module.scss' ; import classNames from '../../utils/classNames' ; import { Logo , LogoLanguage } from '../logo' ; import { NavigationContext , NavigationContextProps } from './NavigationContext' ; import { NavigationRow } from './navigationRow/NavigationRow' ; import { NavigationItem } from './navigationItem/NavigationItem' ; import { NavigationActions } from './navigationActions/NavigationActions' ; import { NavigationUser } from './navigationUser/NavigationUser' ; import { NavigationSearch } from './navigationSearch/NavigationSearch' ; import { NavigationLanguageSelector } from './navigationLanguageSelector/NavigationLanguageSelector' ; import { NavigationDropdown } from './navigationDropdown/NavigationDropdown' ; import { useMobile } from '../../hooks/useMobile' ; import { IconCross , IconMenuHamburger } from '../../icons' ; import { NavigationTheme , NavigationVariant } from './Navigation.interface' ; import { getChildrenAsArray , getComponentFromChildren } from '../../utils/getChildren' ; import { FCWithName } from '../../common/types' ; import { useTheme } from '../../hooks/useTheme' ; import { Visible } from '../../internal/visible/Visible' ; export type NavigationProps = React . PropsWithChildren < { /**
     * Additional class names to apply to the navigation
     */ className ? : string ; default fixed ? : boolean ; /**
     * ID of the header element
     */ id ? : string ; default logoLanguage ? : LogoLanguage ; default menuOpen ? : boolean ; /**
     * aria-label for the mobile menu toggle button
     */ menuToggleAriaLabel : string ; /**
     * Callback fired when the mobile menu is toggled. Can be used together with the `menuOpen` prop to override the internal state handling
     */ onMenuToggle ? : ( ) => void ; /**
     * Callback fired when the title or logo is clicked
     */ onTitleClick ? : ( e ? : Event ) => void ; /**
     * ID of the element to jump to when the "skip to content" accessibility shortcut is clicked
     */ skipTo : string ; /**
     * aria-label for the "skip to content" accessibility shortcut
     */ skipToContentAriaLabel ? : string ; /**
     * Text for the "skip to content" accessibility shortcut
     */ skipToContentLabel : React . ReactNode ; default theme ? : NavigationTheme ; /**
     * The title of the service shown next to the logo
     */ title ? : React . ReactNode ; /**
     * The aria-label for the title describing the logo and service to screen reader users
     */ titleAriaLabel ? : React . ReactNode ; /**
     * URL to navigate to when the logo or title is clicked
     */ titleUrl ? : string ; } > ; param { React . ReactElement [ ] } children param { boolean } authenticated const rearrangeChildrenForMobile = ( children , authenticated ) : React . ReactElement [ ] => { const rearrangedChildren = [ ... children ] ; const moveComponentToTop = ( name ) => { const index = rearrangedChildren . findIndex ( ( item ) => ( item ?. type as FCWithName ) ?. componentName === name ) ; if ( index > - 1 ) { const component = rearrangedChildren . splice ( index , 1 ) [ 0 ] ; rearrangedChildren . splice ( 0 , 0 , component ) ; } } ; moveComponentToTop ( 'NavigationSearch' ) ; if ( ! authenticated ) moveComponentToTop ( 'NavigationUser' ) ; return rearrangedChildren ; } ; const getNavigationVariantFromChild = ( children ) : NavigationVariant => { const navigationRow = getChildrenAsArray ( children ) . find ( ( child ) => ( child . type as FCWithName ) . componentName === 'NavigationRow' ) ; return navigationRow ?. props ?. variant || 'default' ; } ; param children param logoLanguage param onTitleClick param title param titleAriaLabel param titleUrl param mobileMenuOpen const HeaderWrapper = ( { children , logoLanguage , onTitleClick , title , titleAriaLabel , titleUrl } ) => ( < div className = { styles . headerBackgroundWrapper } > < div className = { styles . headerContainer } > < a className = { styles . title } href = { titleUrl } aria - label = { titleAriaLabel } onKeyPress = { ( e ) => { if ( ( e . key === 'Enter' || e . key === ' ' ) && onTitleClick ) onTitleClick ( e ) ; } } onClick = { ( event ) => onTitleClick && onTitleClick ( event ) } { ... ( ! titleUrl && onTitleClick && { tabIndex : 0 } ) } > < Logo className = { styles . logo } language = { logoLanguage } aria - hidden / > { title && < span > { title } < /span>} < /a> { children } < /div> < /div>); export const Navigation = ( { children , className , fixed = false , id , logoLanguage = 'fi' , menuOpen = false , menuToggleAriaLabel , onMenuToggle , onTitleClick , skipTo , skipToContentAriaLabel , skipToContentLabel , theme = 'light' , title , titleAriaLabel , titleUrl , } ) => { const isMobile = useMobile ( ) ; const customThemeClass = useTheme < NavigationTheme > ( styles . header , theme ) ; const [ mobileMenuOpen , setMobileMenuOpen ] = useState ( menuOpen ) ; const [ authenticated , setAuthenticated ] = useState ( false ) ; const [ navigationVariant , setNavigationVariant ] = useState ( getNavigationVariantFromChild ( children ) ) ; useEffect ( ( ) => setMobileMenuOpen ( false ) , [ isMobile ] ) ; useEffect ( ( ) => setMobileMenuOpen ( menuOpen ) , [ menuOpen ] ) ; const context = { setAuthenticated , setNavigationVariant , isMobile } ; const [ navigation , childrenWithoutNavigation ] = getComponentFromChildren ( children , 'NavigationRow' ) ; const mobileActions = getChildrenAsArray ( children ) . find ( ( child ) => ( child . type as FCWithName ) . componentName === 'NavigationActions' ) ?. props ?. children ; const [ mobileLanguageSelector , mobileActionsWithoutLanguageSelector ] = getComponentFromChildren ( mobileActions , 'NavigationLanguageSelector' ) ; const mobileMenuItems = getChildrenAsArray ( [ navigation , mobileActionsWithoutLanguageSelector ] ) ; const mobileMenuChildren = rearrangeChildrenForMobile ( mobileMenuItems , authenticated ) ; const headerWrapperProps = { logoLanguage , onTitleClick , title , titleAriaLabel , titleUrl } ; return ( < NavigationContext . Provider value = { context } > < header id = { id } className = { classNames ( styles . header , fixed && styles . fixed , mobileMenuOpen && styles . menuOpen , ! title && styles . noTitle , styles [ `theme-${ theme }` ] , customThemeClass , className ) } > < a className = { styles . skipToContent } href = { skipTo } aria - label = { skipToContentAriaLabel } > { skipToContentLabel } < /a> < Visible below = "m" > < HeaderWrapper { ... headerWrapperProps } > { mobileLanguageSelector } < button aria - label = { menuToggleAriaLabel } aria - haspopup = "true" aria - expanded = { mobileMenuOpen } { ... ( mobileMenuOpen && { 'aria-controls' : 'hds-mobile-menu' } ) } type = "button" className = { styles . mobileMenuToggle } onClick = { ( ) => ( typeof onMenuToggle === 'function' ? onMenuToggle ( ) : setMobileMenuOpen ( ! mobileMenuOpen ) ) } > { mobileMenuOpen ? < IconCross aria - hidden / > : < IconMenuHamburger aria - hidden / > } < /button> < /HeaderWrapper> { mobileMenuOpen && ( < div id = "hds-mobile-menu" className = { styles . mobileMenu } > { mobileMenuChildren } < /div>)} < /Visible> < Visible above = "m" > < HeaderWrapper { ... headerWrapperProps } > { navigationVariant === 'inline' && < div className = { styles . headerContent } > { children } < /div>} { navigationVariant === 'default' && < div className = { styles . headerContent } > { childrenWithoutNavigation } < /div>} < /HeaderWrapper> { navigationVariant === 'default' && navigation } < /Visible> < /header> < /NavigationContext.Provider>); } ; Navigation . Actions = NavigationActions ; Navigation . Dropdown = NavigationDropdown ; Navigation . Item = NavigationItem ; Navigation . LanguageSelector = NavigationLanguageSelector ; Navigation . Row = NavigationRow ; Navigation . Search = NavigationSearch ; Navigation . User = NavigationUser ;