2022-12-26 14:22:46 +01:00
|
|
|
import { useState } from 'react'
|
2022-12-24 18:16:09 +01:00
|
|
|
import Link from 'next/link'
|
|
|
|
import orderBy from 'lodash.orderby'
|
2022-12-25 19:10:21 +01:00
|
|
|
import FreeSewingIcon from 'shared/components/icons/freesewing.js'
|
2022-12-26 14:22:46 +01:00
|
|
|
import TopLevel from './top-level.js'
|
2022-12-24 18:16:09 +01:00
|
|
|
|
|
|
|
/* helper method to order nav entries */
|
|
|
|
const order = (obj) => orderBy(obj, ['__order', '__title'], ['asc', 'asc'])
|
|
|
|
|
|
|
|
// Component for the collapse toggle
|
|
|
|
// Exported for re-use
|
|
|
|
export 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
|
|
|
|
`}
|
|
|
|
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
|
|
|
|
const currentChildren = (current) =>
|
|
|
|
Object.values(order(current)).filter((entry) => typeof entry === 'object')
|
|
|
|
|
|
|
|
// Shared classes for links
|
|
|
|
// Exported for re-use
|
|
|
|
export const linkClasses = `text-lg lg:text-xl
|
|
|
|
py-1
|
|
|
|
text-base-content sm:text-base-content
|
|
|
|
hover:text-secondary
|
|
|
|
sm:hover:text-secondary
|
|
|
|
`
|
|
|
|
|
|
|
|
// 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
|
|
|
|
}
|
|
|
|
|
|
|
|
// Component that renders a sublevel of navigation
|
|
|
|
const SubLevel = ({ nodes = {}, active }) => (
|
|
|
|
<ul className="pl-5 list-inside">
|
|
|
|
{currentChildren(nodes).map((child) =>
|
|
|
|
Object.keys(child).length > 4 ? (
|
|
|
|
<li key={child.__slug} className="flex flex-row">
|
|
|
|
<details className="grow" open={isActive(child.__slug, active)}>
|
|
|
|
<summary
|
|
|
|
className={`
|
|
|
|
flex flex-row
|
|
|
|
px-2
|
|
|
|
text-base-content
|
|
|
|
sm:text-base-content
|
|
|
|
hover:cursor-row-resize
|
|
|
|
items-center
|
|
|
|
`}
|
|
|
|
>
|
|
|
|
<Link
|
2022-12-25 19:10:21 +01:00
|
|
|
href={`/${child.__slug}`}
|
2022-12-24 18:16:09 +01:00
|
|
|
title={child.__title}
|
|
|
|
className={`
|
|
|
|
grow pl-2 border-l-2
|
|
|
|
hover:cursor-pointer
|
|
|
|
hover:border-secondary
|
|
|
|
sm:hover:border-secondary
|
|
|
|
${
|
|
|
|
child.__slug === active
|
|
|
|
? 'text-secondary border-secondary sm:text-secondary sm:border-secondary'
|
|
|
|
: 'text-base-content sm:text-base-content'
|
|
|
|
}
|
|
|
|
`}
|
|
|
|
>
|
|
|
|
<span className={linkClasses}>
|
|
|
|
<span
|
|
|
|
className={`
|
|
|
|
text-3xl mr-2 inline-block p-0 leading-3
|
|
|
|
${
|
|
|
|
child.__slug === active
|
|
|
|
? 'text-secondary sm:text-secondary translate-y-1'
|
|
|
|
: 'translate-y-3'
|
|
|
|
}
|
|
|
|
`}
|
|
|
|
>
|
|
|
|
{child.__slug === active ? <>•</> : <>°</>}
|
|
|
|
</span>
|
2022-12-25 19:10:21 +01:00
|
|
|
<span className={child.__slug === active ? 'font-medium' : ''}>
|
2022-12-24 18:16:09 +01:00
|
|
|
{child.__linktitle || child.__title}
|
|
|
|
</span>
|
|
|
|
</span>
|
|
|
|
</Link>
|
|
|
|
<Chevron w={6} m={3} />
|
|
|
|
</summary>
|
|
|
|
<SubLevel nodes={child} active={active} />
|
|
|
|
</details>
|
|
|
|
</li>
|
|
|
|
) : (
|
|
|
|
<li className="pl-2 flex flex-row items-center" key={child.__slug}>
|
|
|
|
<Link
|
2022-12-25 19:10:21 +01:00
|
|
|
href={`/${child.__slug}`}
|
2022-12-24 18:16:09 +01:00
|
|
|
title={child.__title}
|
|
|
|
className={`
|
|
|
|
pl-2 border-l-2
|
|
|
|
grow
|
|
|
|
hover:cursor-pointer
|
|
|
|
hover:border-secondary
|
|
|
|
sm:hover:border-secondary
|
|
|
|
${
|
|
|
|
child.__slug === active
|
|
|
|
? 'text-secondary border-secondary sm:text-secondary sm:border-secondary'
|
|
|
|
: 'text-base-content sm:text-base-content'
|
|
|
|
}`}
|
|
|
|
>
|
|
|
|
<span className={linkClasses}>
|
|
|
|
<span
|
|
|
|
className={`
|
|
|
|
text-3xl mr-2 inline-block p-0 leading-3
|
|
|
|
${
|
|
|
|
child.__slug === active
|
|
|
|
? 'text-secondary sm:text-secondary translate-y-1'
|
|
|
|
: 'translate-y-3'
|
|
|
|
}
|
|
|
|
`}
|
|
|
|
>
|
|
|
|
{child.__slug === active ? <>•</> : <>°</>}
|
|
|
|
</span>
|
2022-12-25 19:10:21 +01:00
|
|
|
<span className={child.__slug === active ? 'font-medium' : ''}>
|
2022-12-24 18:16:09 +01:00
|
|
|
{child.__linktitle || child.__title}
|
|
|
|
</span>
|
|
|
|
</span>
|
|
|
|
</Link>
|
|
|
|
</li>
|
|
|
|
)
|
|
|
|
)}
|
|
|
|
</ul>
|
|
|
|
)
|
|
|
|
|
2022-12-26 14:22:46 +01:00
|
|
|
const LevelHomeButton = ({ setShowLevel, level }) => (
|
2022-12-25 19:10:21 +01:00
|
|
|
<>
|
2022-12-26 14:22:46 +01:00
|
|
|
<button
|
|
|
|
className="h-8 mb-1 flex flex-row p-0 items-center -ml-7"
|
|
|
|
title="FreeSewing.org"
|
|
|
|
onClick={() => setShowLevel(level)}
|
|
|
|
>
|
2022-12-25 19:10:21 +01:00
|
|
|
<div
|
|
|
|
className={`bg-neutral h-8 pl-2 pr-1 pt-1.5 font-medium text-secondary-content rounded-l-full`}
|
2022-12-24 18:16:09 +01:00
|
|
|
>
|
2022-12-25 19:10:21 +01:00
|
|
|
<FreeSewingIcon className="w-5 h-5 text-neutral-content" />
|
|
|
|
</div>
|
|
|
|
<div
|
|
|
|
className={`border-neutral h-12`}
|
|
|
|
style={{
|
|
|
|
width: 0,
|
|
|
|
height: 0,
|
|
|
|
borderWidth: '1rem',
|
|
|
|
borderRightColor: 'transparent',
|
|
|
|
borderTopColor: 'transparent',
|
|
|
|
borderBottomColor: 'transparent',
|
|
|
|
}}
|
|
|
|
></div>
|
2022-12-26 14:22:46 +01:00
|
|
|
</button>
|
2022-12-25 19:10:21 +01:00
|
|
|
</>
|
|
|
|
)
|
|
|
|
|
2022-12-26 14:22:46 +01:00
|
|
|
const colors = ['primary', 'secondary', 'accent']
|
2022-12-26 14:31:50 +01:00
|
|
|
const LevelButton = ({ title, level, showLevel, setShowLevel }) => (
|
2022-12-26 14:22:46 +01:00
|
|
|
<button
|
|
|
|
className={`h-8 mb-1 flex flex-row p-0 items-center -ml-7 max-w-1/3
|
|
|
|
${showLevel < level ? 'opacity-50' : ''}
|
|
|
|
`}
|
|
|
|
onClick={() => setShowLevel(level)}
|
|
|
|
>
|
2022-12-25 19:10:21 +01:00
|
|
|
<div
|
2022-12-26 14:22:46 +01:00
|
|
|
className={`border-${colors[level]}`}
|
2022-12-25 19:10:21 +01:00
|
|
|
style={{
|
|
|
|
width: 0,
|
|
|
|
height: 0,
|
|
|
|
borderWidth: '1rem',
|
|
|
|
borderLeftColor: 'transparent',
|
|
|
|
}}
|
|
|
|
></div>
|
|
|
|
<div
|
2022-12-26 14:22:46 +01:00
|
|
|
className={`bg-${colors[level]} h-8 pr-1 pt-0.5 -ml-2 font-medium text-secondary-content overflow-hidden`}
|
2022-12-25 19:10:21 +01:00
|
|
|
>
|
|
|
|
{title}
|
|
|
|
</div>
|
|
|
|
<div
|
2022-12-26 14:22:46 +01:00
|
|
|
className={`border-${colors[level]} h-12`}
|
2022-12-25 19:10:21 +01:00
|
|
|
style={{
|
|
|
|
width: 0,
|
|
|
|
height: 0,
|
|
|
|
borderWidth: '1rem',
|
|
|
|
borderRightColor: 'transparent',
|
|
|
|
borderTopColor: 'transparent',
|
|
|
|
borderBottomColor: 'transparent',
|
|
|
|
}}
|
|
|
|
></div>
|
2022-12-26 14:22:46 +01:00
|
|
|
</button>
|
2022-12-24 18:16:09 +01:00
|
|
|
)
|
|
|
|
|
|
|
|
const Navigation = ({ app, active, className = '' }) => {
|
2022-12-26 14:22:46 +01:00
|
|
|
// Levels
|
|
|
|
const levels = active.split('/')
|
|
|
|
|
|
|
|
const [showLevel, setShowLevel] = useState(Math.max(levels.length, 2))
|
2022-12-24 18:16:09 +01:00
|
|
|
if (!app.navigation) return null
|
2022-12-26 14:22:46 +01:00
|
|
|
if (levels.length < 1) return null
|
2022-12-25 19:10:21 +01:00
|
|
|
|
|
|
|
let navigation = app.navigation
|
|
|
|
|
2022-12-26 14:22:46 +01:00
|
|
|
const shared = { showLevel, setShowLevel }
|
2022-12-25 19:10:21 +01:00
|
|
|
const levelButtons = []
|
|
|
|
if (levels[0]) {
|
|
|
|
navigation = app.navigation[levels[0]]
|
2022-12-26 14:22:46 +01:00
|
|
|
levelButtons.push(<LevelHomeButton key="home" {...shared} level={-1} />)
|
2022-12-25 19:10:21 +01:00
|
|
|
levelButtons.push(
|
2022-12-26 14:31:50 +01:00
|
|
|
<LevelButton title={app.navigation[levels[0]].__title} key={0} level={0} {...shared} />
|
2022-12-25 19:10:21 +01:00
|
|
|
)
|
|
|
|
}
|
|
|
|
if (levels[1]) {
|
2022-12-26 14:22:46 +01:00
|
|
|
if (showLevel > 0) navigation = navigation[levels[1]]
|
2022-12-25 19:10:21 +01:00
|
|
|
levelButtons.push(
|
|
|
|
<LevelButton
|
2022-12-26 14:22:46 +01:00
|
|
|
title={app.navigation[levels[0]][levels[1]].__title}
|
|
|
|
key={1}
|
|
|
|
level={1}
|
|
|
|
{...shared}
|
2022-12-24 18:16:09 +01:00
|
|
|
/>
|
|
|
|
)
|
2022-12-25 19:10:21 +01:00
|
|
|
}
|
|
|
|
if (levels[2]) {
|
2022-12-26 14:22:46 +01:00
|
|
|
if (showLevel > 1) navigation = navigation[levels[2]]
|
2022-12-25 19:10:21 +01:00
|
|
|
levelButtons.push(
|
|
|
|
<LevelButton
|
2022-12-26 14:22:46 +01:00
|
|
|
title={app.navigation[levels[0]][levels[1]][levels[2]].__title}
|
|
|
|
key={2}
|
|
|
|
level={2}
|
|
|
|
{...shared}
|
2022-12-25 19:10:21 +01:00
|
|
|
/>
|
2022-12-24 18:16:09 +01:00
|
|
|
)
|
|
|
|
}
|
|
|
|
|
2022-12-25 19:10:21 +01:00
|
|
|
const output = [
|
|
|
|
<div key="levelButtons" className="pl-8 flex flex-row flex-wrap mb-4">
|
|
|
|
{levelButtons}
|
|
|
|
</div>,
|
|
|
|
]
|
2022-12-26 14:22:46 +01:00
|
|
|
if (showLevel < 0) output.push(<TopLevel />)
|
|
|
|
else output.push(<SubLevel nodes={order(navigation)} active={active} />)
|
2022-12-25 19:10:21 +01:00
|
|
|
|
|
|
|
return <div className={`pb-20 ${className}`}>{output}</div>
|
2022-12-24 18:16:09 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
const PrimaryMenu = ({ app, active, before = [], after = [] }) => (
|
|
|
|
<nav className="mb-12">
|
|
|
|
{before}
|
2022-12-25 19:10:21 +01:00
|
|
|
<Navigation app={app} active={active} />
|
2022-12-24 18:16:09 +01:00
|
|
|
{after}
|
|
|
|
</nav>
|
|
|
|
)
|
|
|
|
|
|
|
|
export default PrimaryMenu
|