Skip to content

Commit

Permalink
fix(docz-theme-default): active menu link
Browse files Browse the repository at this point in the history
  • Loading branch information
pedronauck committed Mar 14, 2019
1 parent 03d0d58 commit 5e04e0d
Show file tree
Hide file tree
Showing 2 changed files with 90 additions and 107 deletions.
102 changes: 38 additions & 64 deletions core/docz-theme-default/src/components/shared/Sidebar/Menu.tsx
@@ -1,10 +1,10 @@
import * as React from 'react'
import { Component } from 'react'
import { useState } from 'react'
import { MenuItem } from 'docz'
import ChevronDown from 'react-feather/dist/icons/chevron-down'
import styled from 'styled-components'

import { MenuLink, getActiveFromClass } from './MenuLink'
import { MenuLink } from './MenuLink'
import { get } from '@utils/theme'

const Wrapper = styled.div`
Expand Down Expand Up @@ -49,72 +49,46 @@ export interface MenuState {
hasActive: boolean
}

export class Menu extends Component<MenuProps, MenuState> {
public $els: HTMLElement[]
public state: MenuState = {
opened: false,
hasActive: false,
}
export const Menu: React.SFC<MenuProps> = props => {
const [opened, setOpened] = useState(false)
const toggle = () => setOpened(s => !s)

constructor(props: MenuProps) {
super(props)
this.$els = []
}
const { item, sidebarToggle, collapseAll } = props
const show = collapseAll || opened
const hasChildren = !item.href && item.menu && item.menu.length > 0
const hasToggle = !item.href && !item.route

public componentDidMount(): void {
this.checkActiveLink()
const handleToggle = (ev: any) => {
ev.preventDefault()
toggle()
}

public render(): React.ReactNode {
const { item, sidebarToggle, collapseAll } = this.props

const show = collapseAll || this.state.opened
const hasChildren = !item.href && item.menu && item.menu.length > 0
const hasToggle = !item.href && !item.route

const handleToggle = (ev: any) => {
ev.preventDefault()
this.toggle()
}

return (
<Wrapper>
<MenuLink item={item} {...hasToggle && { onClick: handleToggle }}>
{item.name}
{hasChildren && (
<Icon opened={show}>
<ChevronDown size={15} />
</Icon>
)}
</MenuLink>
return (
<Wrapper>
<MenuLink item={item} {...hasToggle && { onClick: handleToggle }}>
{item.name}
{hasChildren && (
<List opened={show}>
{item.menu &&
item.menu.map(item => (
<dt key={item.id}>
<MenuLink
item={item}
onClick={sidebarToggle}
innerRef={(node: any) => {
this.$els = this.$els.concat([node])
}}
>
{item.name}
</MenuLink>
</dt>
))}
</List>
<Icon opened={show}>
<ChevronDown size={15} />
</Icon>
)}
</Wrapper>
)
}

private toggle = (): void => {
this.setState(state => ({ opened: !state.opened }))
}

private checkActiveLink = (): void => {
const hasActive = this.$els.some((el: any) => getActiveFromClass(el))
if (hasActive) this.setState({ hasActive, opened: true })
}
</MenuLink>
{hasChildren && (
<List opened={show}>
{item.menu &&
item.menu.map(item => (
<dt key={item.id}>
<MenuLink
item={item}
onClick={sidebarToggle}
onActiveChange={setOpened}
>
{item.name}
</MenuLink>
</dt>
))}
</List>
)}
</Wrapper>
)
}
95 changes: 52 additions & 43 deletions core/docz-theme-default/src/components/shared/Sidebar/MenuLink.tsx
@@ -1,5 +1,5 @@
import * as React from 'react'
import { useMemo, useEffect, useRef, useState, SFC } from 'react'
import { useMemo, useEffect, useRef, useState } from 'react'
import { MenuItem, useConfig, usePrevious } from 'docz'
import styled, { css } from 'styled-components'

Expand Down Expand Up @@ -70,48 +70,57 @@ interface LinkProps {
onClick?: React.MouseEventHandler<any>
className?: string
innerRef?: (node: any) => void
children?: React.ReactNode
onActiveChange?: (active: boolean) => void
}

export const MenuLink: SFC<LinkProps> = ({
item,
children,
onClick,
innerRef,
}) => {
const { linkComponent } = useConfig()
const [active, setActive] = useState(false)
const prevActive = usePrevious(active)
const $el = useRef(null)
const Link = useMemo(() => createLink(linkComponent!), [linkComponent])

const linkProps = {
children,
onClick,
export const MenuLink = React.forwardRef<any, LinkProps>(
({ item, children, onClick, onActiveChange }, ref) => {
const { linkComponent } = useConfig()
const [active, setActive] = useState(false)
const prevActive = usePrevious(active)
const $el = useRef<any>(ref)
const Link = useMemo(() => createLink(linkComponent!), [linkComponent])

const linkProps = {
children,
onClick,
}

useEffect(() => {
const isActive = getActiveFromClass($el.current)
if (prevActive !== isActive) {
setActive(isActive)
onActiveChange && onActiveChange(isActive)
}
})

return (
<Wrapper active={active}>
{item.route ? (
<Link
{...linkProps}
to={item.route}
innerRef={$el}
activeClassName="active"
partiallyActive={true}
/>
) : (
<LinkAnchor
{...linkProps}
ref={$el}
href={item.href || '#'}
target={item.href ? '_blank' : '_self'}
{...!item.href && {
onClick: (ev: any) => {
ev.preventDefault()
linkProps.onClick && linkProps.onClick(ev)
},
}}
/>
)}
{active && item.route && <MenuHeadings route={item.route} />}
</Wrapper>
)
}

const refFn = (node: any) => {
innerRef && innerRef(node)
$el.current = node
}

useEffect(() => {
const isActive = getActiveFromClass($el.current)
if (prevActive !== isActive) setActive(isActive)
})

return (
<Wrapper active={active}>
{item.route ? (
<Link {...linkProps} innerRef={refFn} to={item.route} />
) : (
<LinkAnchor
{...linkProps}
ref={refFn}
href={item.href || '#'}
target={item.href ? '_blank' : '_self'}
/>
)}
{active && item.route && <MenuHeadings route={item.route} />}
</Wrapper>
)
}
)

0 comments on commit 5e04e0d

Please sign in to comment.