Signed-off-by: Avior <github@avior.me>
This commit is contained in:
Florian Bouillon 2021-09-24 17:30:38 +02:00
parent 8b0c2f7ef1
commit 96cd64151f

View File

@ -1,273 +1,274 @@
import Router from 'next/router' import Router from 'next/router'
import Image, { ImageProps } from 'next/image' import Image, { ImageProps } from 'next/image'
import React, { FC } from 'react' import React from 'react'
import { ChevronDown, ChevronsRight, Menu, X } from 'lucide-react' import { ChevronDown, ChevronsRight, Menu, X } from 'lucide-react'
import Text from '../Text' import Text from '../Text'
import Col from '../Col' import Col from '../Col'
import Row from '../Row' import Row from '../Row'
import Link from '../Link' import Link from '../Link'
import { buildClassName } from '../Util' import { buildClassName } from '../Util'
import css from './Navbar.module.styl' import css from './Navbar.module.styl'
import { Icon } from '../interfaces'
interface Props {
/** interface Props {
* Type of Navbar /**
* _note: when in mobile it is not listened_ * Type of Navbar
*/ * _note: when in mobile it is not listened_
type: 'navbar' | 'sidebar' */
type: 'navbar' | 'sidebar'
/**
* Logo to display /**
*/ * Logo to display
logo?: ImageProps & {height: number, width: number} */
/** logo?: ImageProps & {height: number, width: number}
* Login URL /**
*/ * Login URL
loginUrl?: string */
/** loginUrl?: string
* Login URL /**
*/ * Login URL
registerUrl?: string */
/** registerUrl?: string
* User Informations if loggedin /**
*/ * User Informations if loggedin
user?: { */
/** user?: {
* Username /**
*/ * Username
name: string */
/** name: string
* User Short description /**
*/ * User Short description
description?: string */
/** description?: string
* User Menu /**
*/ * User Menu
menu?: { */
/** menu?: {
* Menu links /**
*/ * Menu links
links: Array<{ */
path: string links: Array<{
name: string path: string
}> name: string
/** }>
* Custom informations shown next to the links /**
*/ * Custom informations shown next to the links
informations?: JSX.Element */
} informations?: JSX.Element
} }
/** }
* Links to display /**
*/ * Links to display
items: Array<{ */
path: string items: Array<{
icon?: FC path: string
name: string icon?: Icon
}> name: string
/** }>
* Internal Use don't use it ! /**
*/ * Internal Use don't use it !
mobileMenu?: () => void */
} mobileMenu?: () => void
}
interface State {
path?: string interface State {
short: boolean path?: string
isMobile: boolean short: boolean
menuActive: boolean isMobile: boolean
} menuActive: boolean
}
/**
* Navbar/Sidebar Component /**
* @version 1.0.3 * Navbar/Sidebar Component
*/ * @version 1.0.3
export default class Navbar extends React.Component<Props, State> { */
export default class Navbar extends React.Component<Props, State> {
public state: State = {
short: false, public state: State = {
isMobile: false, short: false,
menuActive: false isMobile: false,
} menuActive: false
}
public componentDidMount() {
this.setState({ public componentDidMount() {
path: Router.asPath, this.setState({
menuActive: !!this.props.mobileMenu path: Router.asPath,
}) menuActive: !!this.props.mobileMenu
Router.events.on('routeChangeComplete', () => { })
this.setState({path: Router.asPath, menuActive: false}) Router.events.on('routeChangeComplete', () => {
}) this.setState({path: Router.asPath, menuActive: false})
Router.events.on('routeChangeError', () => { })
this.setState({path: Router.asPath, menuActive: false}) Router.events.on('routeChangeError', () => {
}) this.setState({path: Router.asPath, menuActive: false})
if (!this.props.mobileMenu) { })
window.addEventListener('resize', this.onResize) if (!this.props.mobileMenu) {
this.onResize() window.addEventListener('resize', this.onResize)
} this.onResize()
} }
}
public onResize = () => {
const isMobile = window.innerWidth <= 768 public onResize = () => {
if (this.state.isMobile !== isMobile) { const isMobile = window.innerWidth <= 768
this.setState({isMobile}) if (this.state.isMobile !== isMobile) {
} this.setState({isMobile})
} }
}
public componentDidUpdate() {
if (!this.props.mobileMenu) { public componentDidUpdate() {
if (this.state.short) { if (!this.props.mobileMenu) {
document.body.classList.add(css.short) if (this.state.short) {
} else { document.body.classList.add(css.short)
document.body.classList.remove(css.short) } else {
} document.body.classList.remove(css.short)
if (this.getType() === 'sidebar') { }
document.body.classList.add(css['body-sidebar']) if (this.getType() === 'sidebar') {
document.body.classList.remove(css['body-navbar']) document.body.classList.add(css['body-sidebar'])
} else { document.body.classList.remove(css['body-navbar'])
document.body.classList.remove(css['body-sidebar']) } else {
document.body.classList.add(css['body-navbar']) document.body.classList.remove(css['body-sidebar'])
} document.body.classList.add(css['body-navbar'])
} }
}
}
}
public componentWillUnmount() {
if (!this.props.mobileMenu) { public componentWillUnmount() {
document.body.classList.remove(css.short, css[`body-${this.getType()}`]) if (!this.props.mobileMenu) {
window.removeEventListener('resize', this.onResize) document.body.classList.remove(css.short, css[`body-${this.getType()}`])
} window.removeEventListener('resize', this.onResize)
} }
}
public onSidebarButton = () => {
if (!this.props.mobileMenu) { public onSidebarButton = () => {
this.setState({short: !this.state.short, menuActive: false}) if (!this.props.mobileMenu) {
} else { this.setState({short: !this.state.short, menuActive: false})
this.props.mobileMenu() } else {
} this.props.mobileMenu()
} }
}
public menuCloseCallback = () => {
this.setState({menuActive: false}) public menuCloseCallback = () => {
} this.setState({menuActive: false})
}
public getType(): 'sidebar' | 'navbar' {
if (this.props.mobileMenu) { public getType(): 'sidebar' | 'navbar' {
return 'sidebar' if (this.props.mobileMenu) {
} return 'sidebar'
return this.state.isMobile ? 'navbar' : this.props.type }
} return this.state.isMobile ? 'navbar' : this.props.type
}
public render = () => (
<> public render = () => (
<nav className={buildClassName(css[this.getType()], [css.short, this.state.short && !this.props.mobileMenu], [css.mobile, this.props.mobileMenu])}> <>
<Row nowrap className={css.header} align="center"> <nav className={buildClassName(css[this.getType()], [css.short, this.state.short && !this.props.mobileMenu], [css.mobile, this.props.mobileMenu])}>
{this.props.logo && ( <Row nowrap className={css.header} align="center">
<Col className={css.imgContainer}> {this.props.logo && (
<Link href="/"> <Col className={css.imgContainer}>
<Image {...this.props.logo} height={34} width={this.props.logo.width*34/this.props.logo.height} /> <Link href="/">
</Link> <Image {...this.props.logo} height={34} width={this.props.logo.width*34/this.props.logo.height} />
</Col> </Link>
)} </Col>
{this.getType() === 'sidebar' && ( )}
<Col nogrow><Text><div onClick={this.onSidebarButton}> {this.getType() === 'sidebar' && (
{this.state.short ? ( <Col nogrow><Text><div onClick={this.onSidebarButton}>
<ChevronsRight size={30} /> {this.state.short ? (
) : ( <ChevronsRight size={30} />
<X size={30} /> ) : (
)} <X size={30} />
</div></Text></Col> )}
)} </div></Text></Col>
</Row> )}
{this.getType() === 'sidebar' && ( </Row>
<hr/> {this.getType() === 'sidebar' && (
)} <hr/>
<ul> )}
{!this.state.isMobile && this.props.items.map((item) => ( <ul>
<li key={item.path}><Link noStyle href={item.path}> {!this.state.isMobile && this.props.items.map((item) => (
<Text className={buildClassName([css.active, this.state.path?.startsWith(item.path)])}> <li key={item.path}><Link noStyle href={item.path}>
{this.getType() === 'sidebar' && item.icon && ( <Text className={buildClassName([css.active, this.state.path?.startsWith(item.path)])}>
<item.icon /> {this.getType() === 'sidebar' && item.icon && (
)} <item.icon />
<span>{item.name}</span> )}
</Text> <span>{item.name}</span>
</Link></li> </Text>
))} </Link></li>
</ul> ))}
<div style={{flex: 1}}></div> </ul>
{/* Spacer */} <div style={{flex: 1}}></div>
{this.state.isMobile && ( {/* Spacer */}
<div className={css.userSpaceParent}> {this.state.isMobile && (
<div onClick={() => this.setState({menuActive: !this.state.menuActive})} className={css.userSpace}> <div className={css.userSpaceParent}>
<Text> <div onClick={() => this.setState({menuActive: !this.state.menuActive})} className={css.userSpace}>
<Menu size={38} className={css.mainGradient} /> <Text>
</Text> <Menu size={38} className={css.mainGradient} />
</div> </Text>
</div> </div>
)} </div>
{!this.state.isMobile && this.props.user ? ( )}
<> {!this.state.isMobile && this.props.user ? (
<div className={css.userSpaceParent}> <>
{this.getType() === 'sidebar' && ( <div className={css.userSpaceParent}>
<hr/> {this.getType() === 'sidebar' && (
)} <hr/>
<div onClick={() => this.setState({menuActive: !this.state.menuActive})} className={css.userSpace}> )}
<Text> <div onClick={() => this.setState({menuActive: !this.state.menuActive})} className={css.userSpace}>
{this.props.user.name} <Text>
<ChevronDown className={buildClassName([css.menuActive, this.state.menuActive])} /> {this.props.user.name}
</Text> <ChevronDown className={buildClassName([css.menuActive, this.state.menuActive])} />
{this.getType() === 'sidebar' && this.props.user.description && ( </Text>
<Text>{this.props.user.description}</Text> {this.getType() === 'sidebar' && this.props.user.description && (
)} <Text>{this.props.user.description}</Text>
</div> )}
</div> </div>
<div className={buildClassName(css.userMenu, [css.menuActive, !this.state.isMobile && this.state.menuActive])}> </div>
<Row nomargin> <div className={buildClassName(css.userMenu, [css.menuActive, !this.state.isMobile && this.state.menuActive])}>
{this.props.user.menu?.informations && ( <Row nomargin>
<Col>{this.props.user.menu?.informations}</Col> {this.props.user.menu?.informations && (
)} <Col>{this.props.user.menu?.informations}</Col>
<Col> )}
<ul> <Col>
{this.props.user.menu?.links.map((l) => ( <ul>
<li key={l.path}><Text><Link noStyle href={l.path}>{l.name}</Link></Text></li> {this.props.user.menu?.links.map((l) => (
))} <li key={l.path}><Text><Link noStyle href={l.path}>{l.name}</Link></Text></li>
</ul> ))}
</Col> </ul>
</Row> </Col>
</div> </Row>
</> </div>
) : !this.state.isMobile ? ( </>
<div className={css.userSpaceParent}> ) : !this.state.isMobile ? (
{this.getType() === 'sidebar' && ( <div className={css.userSpaceParent}>
<hr/> {this.getType() === 'sidebar' && (
)} <hr/>
<ul> )}
{this.props.registerUrl && ( <ul>
<li><Link noStyle href={this.props.registerUrl}><a> {this.props.registerUrl && (
<Text className={buildClassName(css.active)}> <li><Link noStyle href={this.props.registerUrl}><a>
<span>Register</span> <Text className={buildClassName(css.active)}>
</Text> <span>Register</span>
</a></Link></li> </Text>
)} </a></Link></li>
{this.props.loginUrl && ( )}
<li><Link noStyle href={this.props.loginUrl}><a> {this.props.loginUrl && (
<Text> <li><Link noStyle href={this.props.loginUrl}><a>
<span>Login</span> <Text>
</Text> <span>Login</span>
</a></Link></li> </Text>
)} </a></Link></li>
</ul> )}
</div> </ul>
) : undefined} </div>
</nav> ) : undefined}
</nav>
{!this.props.mobileMenu && this.state.isMobile && (
<div className={buildClassName(css.mobileMenu, [css.shown, this.state.menuActive])}> {!this.props.mobileMenu && this.state.isMobile && (
<Navbar {...this.props} type="sidebar" mobileMenu={this.menuCloseCallback} /> <div className={buildClassName(css.mobileMenu, [css.shown, this.state.menuActive])}>
</div> <Navbar {...this.props} type="sidebar" mobileMenu={this.menuCloseCallback} />
)} </div>
</> )}
) </>
} )
}