Skip to content

Commit

Permalink
refactor(Nav): Use RouterLink instead of custom implementation
Browse files Browse the repository at this point in the history
  • Loading branch information
cballevre committed Aug 24, 2023
1 parent fa21669 commit 5ea33df
Show file tree
Hide file tree
Showing 4 changed files with 127 additions and 104 deletions.
145 changes: 41 additions & 104 deletions src/drive/web/modules/navigation/Nav.jsx
Original file line number Diff line number Diff line change
@@ -1,115 +1,52 @@
/* global __TARGET__ */
import React, { useState } from 'react'
import cx from 'classnames'
import { useLocation } from 'react-router-dom'

import { useI18n } from 'cozy-ui/transpiled/react'
import UINav from 'cozy-ui/transpiled/react/Nav'

import UINav, {
NavItem,
NavIcon,
NavText,
NavLink as UINavLink
} from 'cozy-ui/transpiled/react/Nav'
import { NavItem } from 'drive/web/modules/navigation/NavItem'

/**
* Returns true if `to` and `pathname` match
* Supports `rx` for regex matches.
*/
const navLinkMatch = (rx, to, pathname) => {
return rx ? rx.test(pathname) : pathname.slice(1) === to
}

/**
* Like react-router NavLink but sets the lastClicked state (passed in props)
* to have a faster change of active (not waiting for the route to completely
* change).
*/
export const NavLink = ({
children,
to,
rx,
clickState: [lastClicked, setLastClicked]
}) => {
const location = useLocation()
const pathname = lastClicked ? lastClicked : location.pathname
const isActive = navLinkMatch(rx, to, pathname)
return (
<a
style={{ outline: 'none' }}
onClick={() => setLastClicked(to)}
href={`#${to}`}
className={cx(
UINavLink.className,
isActive ? UINavLink.activeClassName : null
)}
>
{children}
</a>
)
}

const NavItems = ({ items }) => {
const clickState = useState(null)
return (
<>
{items.map((item, i) =>
item ? (
<NavItem key={i} secondary={item.secondary}>
<NavLink to={item.to} rx={item.rx} clickState={clickState}>
{item.icon ? <NavIcon icon={item.icon} /> : null}
<NavText>{item.label}</NavText>
</NavLink>
</NavItem>
) : null
)}
</>
)
}
const folderRoute = /\/folder(\/.*)?/
const settingsRoute = /\/settings(\/.*)?/
const recentRoute = /\/recent(\/.*)?/
const sharingRoute = /\/sharings(\/.*)?/
const trashRoute = /\/trash(\/.*)?/
export const Nav = () => {
const { t } = useI18n()
const routes = [
{
to: '/folder',
icon: 'folder',
label: t('Nav.item_drive'),
rx: folderRoute
},
{
to: '/recent',
icon: 'clock',
label: t('Nav.item_recent'),
rx: recentRoute
},
{
to: '/sharings',
icon: 'share',
label: t('Nav.item_sharings'),
rx: sharingRoute
},
{
to: '/trash',
icon: 'trash',
label: t('Nav.item_trash'),
rx: trashRoute
}
]
if (__TARGET__ === 'mobile') {
routes.push({
to: '/settings',
icon: 'gear',
label: t('Nav.item_settings'),
rx: settingsRoute
})
}
const clickState = useState(null)

return (
<UINav>
<NavItems items={routes} />
<NavItem
to="/folder"
icon="folder"
label="drive"
rx={/\/folder(\/.*)?/}
clickState={clickState}
/>
<NavItem
to="/recent"
icon="clock"
label="recent"
rx={/\/recent(\/.*)?/}
clickState={clickState}
/>
<NavItem
to="/sharings"
icon="share"
label="sharings"
rx={/\/sharings(\/.*)?/}
clickState={clickState}
/>
<NavItem
to="/trash"
icon="trash"
label="trash"
rx={/\/trash(\/.*)?/}
clickState={clickState}
/>
{__TARGET__ === 'mobile' ? (
<NavItem
to="/settings"
icon="gear"
label="settings"
rx={/\/settings(\/.*)?/}
clickState={clickState}
/>
) : null}
</UINav>
)
}
Expand Down
34 changes: 34 additions & 0 deletions src/drive/web/modules/navigation/NavItem.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import React from 'react'
import PropTypes from 'prop-types'

import { useI18n } from 'cozy-ui/transpiled/react'

import {
NavItem as UINavItem,
NavIcon,
NavText
} from 'cozy-ui/transpiled/react/Nav'

import { NavLink } from 'drive/web/modules/navigation/NavLink'

const NavItem = ({ to, icon, label, rx, clickState }) => {
const { t } = useI18n()

return (
<UINavItem>
<NavLink to={to} rx={rx} clickState={clickState}>
<NavIcon icon={icon} />
<NavText>{t(`Nav.item_${label}`)}</NavText>
</NavLink>
</UINavItem>
)
}

NavItem.propTypes = {
to: PropTypes.string.isRequired,
icon: PropTypes.string.isRequired,
label: PropTypes.string.isRequired,
rx: PropTypes.shape(RegExp)
}

export { NavItem }
45 changes: 45 additions & 0 deletions src/drive/web/modules/navigation/NavLink.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import React from 'react'
import cx from 'classnames'
import { useLocation } from 'react-router-dom'
import PropTypes from 'prop-types'

import { NavLink as UINavLink } from 'cozy-ui/transpiled/react/Nav'

import { navLinkMatch } from 'drive/web/modules/navigation/helpers'

/**
* Like react-router NavLink but sets the lastClicked state (passed in props)
* to have a faster change of active (not waiting for the route to completely
* change).
*/
const NavLink = ({
children,
to,
rx,
clickState: [lastClicked, setLastClicked]
}) => {
const location = useLocation()
const pathname = lastClicked ? lastClicked : location.pathname
const isActive = navLinkMatch(rx, to, pathname)
return (
<a
style={{ outline: 'none' }}
onClick={() => setLastClicked(to)}
href={`#${to}`}
className={cx(
UINavLink.className,
isActive ? UINavLink.activeClassName : null
)}
>
{children}
</a>
)
}

NavLink.propTypes = {
children: PropTypes.node.isRequired,
to: PropTypes.string.isRequired,
rx: PropTypes.shape(RegExp)
}

export { NavLink }
7 changes: 7 additions & 0 deletions src/drive/web/modules/navigation/helpers.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
/**
* Returns true if `to` and `pathname` match
* Supports `rx` for regex matches.
*/
export const navLinkMatch = (rx, to, pathname) => {
return rx ? rx.test(pathname) : pathname.slice(1) === to
}

0 comments on commit 5ea33df

Please sign in to comment.