@hashicorp/react-subnav
Version:
Displays a navigation bar, with links and a call-to-action.
132 lines (123 loc) • 4.03 kB
JavaScript
/**
* Copyright (c) HashiCorp, Inc.
* SPDX-License-Identifier: MPL-2.0
*/
import React from 'react'
import LinkWrap from '@hashicorp/react-link-wrap'
import CtaLinks from '../CtaLinks/index.js'
import DropdownTrigger from '../DropdownTrigger/index.js'
import s from './style.module.css'
import classNames from 'classnames'
class MenuItemsOverflow extends React.Component {
constructor(props) {
super(props)
this.state = { isCollapsed: true }
this.toggleCollapsed = this.toggleCollapsed.bind(this)
this.parentRef = React.createRef()
this.handleClick = this.handleClick.bind(this)
}
toggleCollapsed() {
this.setState({ isCollapsed: !this.state.isCollapsed })
}
handleClick(event) {
// If already collapsed, clicks outside the modal don't matter
if (this.state.isCollapsed) return true
// If we're not collapsed, and the click is outside the component,
// we should ensure that the modal closes
const isClickOutside = !this.parentRef.current.contains(event.target)
// If we're not collapsed, and the click is on an anchor element with an
// href attribute, we're likely performing a navigation, and should ensure
// that the modal closes
const isClickNavigation = event.target.tagName === 'A' && event.target.href
if (isClickOutside || isClickNavigation) {
this.setState({ isCollapsed: true })
}
}
componentDidMount() {
document.addEventListener('click', this.handleClick)
}
componentWillUnmount() {
document.removeEventListener('click', this.handleClick)
}
render() {
const {
menuItems,
ctaLinks,
product,
Link,
hideGithubStars,
theme,
menuText,
} = this.props
const { isCollapsed } = this.state
return (
<div className={s.root} ref={this.parentRef}>
<DropdownTrigger
isCollapsed={isCollapsed}
onClick={this.toggleCollapsed}
text={menuText}
product={product}
/>
<div
className={classNames(s.dropdown, { [s.isCollapsed]: isCollapsed })}
>
<ul className={s.ulElem}>
{menuItems.map((menuItem, stableIdx) => {
if (menuItem === 'divider') return null
const { text, url, submenu } = menuItem
if (submenu) {
// If we have a submenu, we need to flatten it
return (
// eslint-disable-next-line react/no-array-index-key
<div key={stableIdx}>
<div className={s.submenuTitle}>{text}</div>
<hr className={s.divider} />
{submenu.map((subItem, subStableIdx) => (
<SubmenuItem
// eslint-disable-next-line react/no-array-index-key
key={subStableIdx}
url={subItem.url}
text={subItem.text}
product={product}
Link={Link}
/>
))}
<hr className={s.divider} />
</div>
)
} else {
return (
<SubmenuItem
// eslint-disable-next-line react/no-array-index-key
key={stableIdx}
url={url}
text={text}
product={product}
Link={Link}
/>
)
}
})}
</ul>
<CtaLinks
links={ctaLinks}
isInDropdown={true}
product={product}
hideGithubStars={hideGithubStars}
theme={theme}
/>
</div>
</div>
)
}
}
function SubmenuItem({ url, text, Link }) {
return (
<li>
<LinkWrap Link={Link} className={s.submenuItem} href={url} title={text}>
{text}
</LinkWrap>
</li>
)
}
export default MenuItemsOverflow