1
0
Fork 0
freesewing/packages/freesewing.shared/components/navigation/primary.js

189 lines
6.4 KiB
JavaScript
Raw Normal View History

2021-12-16 19:01:37 +01:00
import get from 'lodash.get'
2021-12-12 18:58:24 +01:00
import Link from 'next/link'
2021-12-16 19:01:37 +01:00
import orderBy from 'lodash.orderby'
import Logo from 'shared/components/logos/freesewing.js'
import ThemePicker from 'shared/components/theme-picker.js'
import RssIcon from 'shared/components/icons/rss.js'
import ThemeIcon from 'shared/components/icons/theme.js'
import TutorialIcon from 'shared/components/icons/tutorial.js'
import GuideIcon from 'shared/components/icons/guide.js'
import HelpIcon from 'shared/components/icons/help.js'
import DocsIcon from 'shared/components/icons/docs.js'
2021-12-11 14:04:05 +01:00
2021-12-17 17:51:20 +01:00
// Don't show children for blog and showcase posts
2021-12-12 18:58:24 +01:00
const keepClosed = ['blog', 'showcase', ]
// List of icons matched to top-level slug
const icons = {
blog: <RssIcon />,
tutorials: <TutorialIcon />,
guides: <GuideIcon />,
howtos: <HelpIcon />,
reference: <DocsIcon />
}
2021-12-25 13:43:41 +01:00
/* helper method to order nav entries */
const order = obj => orderBy(obj, ['__order', '__title'], ['asc', 'asc'])
2021-12-17 17:51:20 +01:00
// Component for the collapse toggle
const Chevron = ({w=8, m=2}) => <svg className={`
fill-current opacity-75 w-${w} h-${w} mr-${m}
details-toggle hover:text-secondary sm:hover:text-secondary-focus
`}
2021-12-17 17:51:20 +01:00
xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20">
<path d="M12.95 10.707l.707-.707L8 4.343 6.586 5.757 10.828 10l-4.242 4.243L8 15.657l4.95-4.95z"/>
</svg>
// Helper method to filter out the real children
2021-12-25 13:43:41 +01:00
const currentChildren = current => Object.values(order(current))
2021-12-17 17:51:20 +01:00
.filter(entry => (typeof entry === 'object'))
2021-12-16 19:01:37 +01:00
2021-12-21 20:47:13 +01:00
// Shared classes for links
const linkClasses = `text-lg lg:text-xl
py-1 hover:cursor-pointer
text-base-content sm:text-neutral-content
hover:text-secondary
sm:hover:text-secondary-focus
`
// Figure out whether a page is on the path to the active page
const isActive = (slug, active) => {
if (slug === active) return true
let result = true
const slugParts = slug.split('/')
const activeParts = active.split('/')
for (const i in slugParts) {
if (slugParts[i] !== activeParts[i]) result = false
}
return result
}
2021-12-17 17:51:20 +01:00
// Component that renders a sublevel of navigation
const SubLevel = ({ nodes={}, active }) => (
2021-12-21 20:47:13 +01:00
<ul className="pl-5 list-inside">
2021-12-17 17:51:20 +01:00
{currentChildren(nodes).map(child => (Object.keys(child).length > 4)
? (
2021-12-21 20:47:13 +01:00
<li key={child.__slug} className="flex flex-row">
<details className="grow" open={isActive(child.__slug, active)}>
2021-12-17 17:51:20 +01:00
<summary className={`
2021-12-21 20:47:13 +01:00
flex flex-row
px-2
text-base-content
sm:text-neutral-content
2021-12-17 17:51:20 +01:00
hover:cursor-row-resize
2021-12-21 20:47:13 +01:00
items-center
2021-12-17 17:51:20 +01:00
`}>
2021-12-28 16:29:06 +01:00
<Link href={`/${child.__slug}`}>
<a title={child.__title} className={`
grow pl-2 border-l-2
${linkClasses}
hover:border-secondary
sm:hover:border-secondary-focus
${child.__slug === active
? 'text-secondary border-secondary sm:text-secondary-focus sm:border-secondary-focus'
: 'text-base-content sm:text-neutral-content'
}
`}>
<span className={`
text-3xl mr-2 inline-block p-0 leading-3
${child.__slug === active
? 'text-secondary sm:text-secondary-focus translate-y-1'
: 'translate-y-3'
}
`}>
{child.__slug === active ? <>&bull;</> : <>&deg;</>}
</span>
<span className={child.__slug === active ? 'font-bold' : ''}>
{ child?.__linktitle || child.__title }
</span>
2021-12-21 20:47:13 +01:00
</a>
</Link>
2021-12-17 17:51:20 +01:00
<Chevron w={6} m={3}/>
</summary>
<SubLevel nodes={child} active={active} />
2021-12-17 17:51:20 +01:00
</details>
</li>
) : (
2021-12-21 20:47:13 +01:00
<li className='pl-2 flex flex-row items-center'>
2021-12-28 16:29:06 +01:00
<Link href={`/${child.__slug}`} title={child.__title}>
<a className={`
pl-2 border-l-2
grow
${linkClasses}
hover:border-secondary
sm:hover:border-secondary-focus
${child.__slug === active
? 'text-secondary border-secondary sm:text-secondary-focus sm:border-secondary-focus'
: 'text-base-content sm:text-neutral-content'
}`}>
<span className={`
text-3xl mr-2 inline-block p-0 leading-3
${child.__slug === active
? 'text-secondary sm:text-secondary-focus translate-y-1'
: 'translate-y-3'
}
`}>
{child.__slug === active ? <>&bull;</> : <>&deg;</>}
</span>
<span className={child.__slug === active ? 'font-bold' : ''}>
{child.__linktitle} here
</span>
</a>
2021-12-17 17:51:20 +01:00
</Link>
</li>
)
)}
</ul>
)
2021-12-16 19:01:37 +01:00
2021-12-17 17:51:20 +01:00
// Component that renders a toplevel of navigation
2021-12-21 20:47:13 +01:00
const TopLevel = ({ icon, title, nav, current, slug, hasChildren=false, active }) => (
<details className='py-1' open={((keepClosed.indexOf(current.__slug) === -1) ? 1 : 0)}>
2021-12-12 18:58:24 +01:00
<summary className={`
flex flex-row uppercase gap-4 font-bold text-lg
hover:cursor-row-resize
p-2
text-base-content
sm:text-neutral-content
items-center
2021-12-12 18:58:24 +01:00
`}>
<span className="text-secondary-focus">{icon}</span>
<Link href={`/${slug}`}>
<a className={`grow ${linkClasses} ${slug === active ? 'text-secondary sm:text-secondary-focus' : ''}`}>
{title}
</a>
</Link>
2021-12-21 20:47:13 +01:00
{hasChildren && <Chevron />}
</summary>
{hasChildren && <SubLevel nodes={current} active={active} />}
</details>
)
2021-12-21 20:47:13 +01:00
const Navigation = ({ app, active }) => {
if (!app.navigation) return null
2021-12-12 18:58:24 +01:00
const output = []
2021-12-28 16:29:06 +01:00
for (const key of Object.keys(app.navigation).sort()) output.push(<TopLevel
icon={icons[key] || <span className="text-3xl mr-2 translate-y-3 inline-block p-0 leading-3">&deg;</span>}
title={key}
slug={key}
key={key}
hasChildren={keepClosed.indexOf(key) === -1}
nav={app.navigation}
current={order(app.navigation[key])}
active={active}
/>)
2021-12-12 18:58:24 +01:00
return <div className='pb-20'>{output}</div>
2021-12-12 18:58:24 +01:00
}
2021-12-28 16:29:06 +01:00
const PrimaryMenu = ({ app, active }) => (
<nav className="sm:max-w-lg grow mb-12">
2021-12-30 18:17:10 +01:00
<ThemePicker app={app} className="w-full sm:hidden"/>
2021-12-28 16:29:06 +01:00
<Navigation app={app} active={active} />
</nav>
)
2021-12-11 14:04:05 +01:00
export default PrimaryMenu