2023-09-29 16:01:27 +02:00
|
|
|
// __SDEFILE__ - This file is a dependency for the stand-alone environment
|
2023-04-28 21:23:06 +02:00
|
|
|
import { NavigationContext } from 'shared/context/navigation-context.mjs'
|
|
|
|
import { useContext } from 'react'
|
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'
|
2023-03-26 16:50:46 +02:00
|
|
|
import {
|
2023-04-09 15:57:25 +02:00
|
|
|
CommunityIcon,
|
|
|
|
DesignIcon,
|
|
|
|
DocsIcon,
|
2023-03-26 16:50:46 +02:00
|
|
|
RssIcon,
|
|
|
|
ShowcaseIcon,
|
2023-03-27 18:49:09 +02:00
|
|
|
UserIcon,
|
2023-06-15 19:30:31 +02:00
|
|
|
MeasieIcon,
|
2023-08-30 09:44:55 +02:00
|
|
|
PatternIcon,
|
2023-05-16 10:34:13 +02:00
|
|
|
CodeIcon,
|
|
|
|
I18nIcon,
|
|
|
|
WrenchIcon,
|
|
|
|
FreeSewingIcon,
|
2023-05-21 09:41:20 +02:00
|
|
|
HeartIcon,
|
2023-05-22 19:53:24 +02:00
|
|
|
BulletIcon,
|
2023-07-15 16:55:22 +02:00
|
|
|
PlusIcon,
|
2023-05-26 10:41:19 +02:00
|
|
|
GitHubIcon,
|
2023-03-26 16:50:46 +02:00
|
|
|
} from 'shared/components/icons.mjs'
|
2023-03-26 09:18:51 +02:00
|
|
|
import { Breadcrumbs } from 'shared/components/breadcrumbs.mjs'
|
2021-12-12 18:58:24 +01:00
|
|
|
|
2023-03-28 16:47:07 +02:00
|
|
|
export const ns = ['sections']
|
|
|
|
|
2021-12-27 14:30:10 +01:00
|
|
|
// List of icons matched to top-level slug
|
2023-04-09 15:57:25 +02:00
|
|
|
export const icons = {
|
2023-03-26 16:50:46 +02:00
|
|
|
// FreeSewing.dev
|
2023-05-21 09:41:20 +02:00
|
|
|
api: (className = '') => <DocsIcon className={className} />,
|
|
|
|
design: (className = '') => <DesignIcon className={className} />,
|
|
|
|
contribute: (className = '') => <CodeIcon className={className} />,
|
|
|
|
i18n: (className = '') => <I18nIcon className={className} />,
|
|
|
|
infra: (className = '') => <WrenchIcon className={className} stroke={1.5} />,
|
2023-05-16 10:34:13 +02:00
|
|
|
about: (className = '') => <FreeSewingIcon className={className} stroke={1.5} />,
|
2023-05-21 09:41:20 +02:00
|
|
|
support: (className = '') => <HeartIcon className={className} stroke={1.5} fill />,
|
2023-05-16 10:34:13 +02:00
|
|
|
|
2023-03-26 16:50:46 +02:00
|
|
|
// FreeSewing.org
|
2023-04-09 15:57:25 +02:00
|
|
|
account: (className = '') => <UserIcon className={className} />,
|
2023-03-26 16:50:46 +02:00
|
|
|
blog: (className = '') => <RssIcon className={className} stroke={3} />,
|
2023-04-09 15:57:25 +02:00
|
|
|
designs: (className = '') => <DesignIcon className={className} stroke={3} />,
|
2023-03-26 16:50:46 +02:00
|
|
|
docs: (className = '') => <DocsIcon className={className} />,
|
2023-04-09 15:57:25 +02:00
|
|
|
showcase: (className = '') => <ShowcaseIcon className={className} />,
|
|
|
|
community: (className = '') => <CommunityIcon className={className} />,
|
2023-06-15 19:30:31 +02:00
|
|
|
sets: (className = '') => <MeasieIcon className={className} />,
|
2023-08-30 09:44:55 +02:00
|
|
|
patterns: (className = '') => <PatternIcon className={className} />,
|
2023-07-15 16:55:22 +02:00
|
|
|
new: (className = '') => <PlusIcon className={className} />,
|
2023-05-26 10:41:19 +02:00
|
|
|
|
|
|
|
// Lab
|
|
|
|
code: (className = '') => <GitHubIcon className={className} />,
|
2021-12-27 14:30:10 +01:00
|
|
|
}
|
|
|
|
|
2021-12-25 13:43:41 +01:00
|
|
|
/* helper method to order nav entries */
|
2023-03-26 09:18:51 +02:00
|
|
|
const order = (obj) => orderBy(obj, ['o', 't'], ['asc', 'asc'])
|
2021-12-25 13:43:41 +01:00
|
|
|
|
2021-12-17 17:51:20 +01:00
|
|
|
// Component for the collapse toggle
|
2022-01-22 17:55:03 +01:00
|
|
|
// Exported for re-use
|
2022-12-04 15:04:56 +01:00
|
|
|
export const Chevron = ({ w = 8, m = 2 }) => (
|
|
|
|
<svg
|
|
|
|
className={`
|
2021-12-30 18:42:49 +01:00
|
|
|
fill-current opacity-75 w-${w} h-${w} mr-${m}
|
2022-05-11 16:29:46 +02:00
|
|
|
details-toggle hover:text-secondary sm:hover:text-secondary
|
2021-12-30 18:42:49 +01:00
|
|
|
`}
|
2022-12-04 15:04:56 +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>
|
|
|
|
)
|
2021-12-17 17:51:20 +01:00
|
|
|
|
|
|
|
// Helper method to filter out the real children
|
2022-12-04 15:04:56 +01:00
|
|
|
const currentChildren = (current) =>
|
|
|
|
Object.values(order(current)).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
|
2022-01-22 17:55:03 +01:00
|
|
|
// Exported for re-use
|
2023-03-26 09:18:51 +02:00
|
|
|
export const linkClasses = `
|
2022-01-27 18:07:37 +01:00
|
|
|
py-1
|
2023-03-26 09:18:51 +02:00
|
|
|
text-base text-base-content sm:text-base-content
|
2021-12-30 18:42:49 +01:00
|
|
|
hover:text-secondary
|
2022-05-11 16:29:46 +02:00
|
|
|
sm:hover:text-secondary
|
2021-12-30 18:42:49 +01:00
|
|
|
`
|
2021-12-21 18:50:19 +01:00
|
|
|
|
2021-12-22 17:11:49 +01:00
|
|
|
// Figure out whether a page is on the path to the active page
|
2023-04-09 15:57:25 +02:00
|
|
|
export const isActive = (slug, active) => {
|
2023-04-07 17:10:54 +02:00
|
|
|
if (!slug) return false
|
2021-12-22 17:11:49 +01:00
|
|
|
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
|
|
|
|
}
|
|
|
|
|
2023-05-22 15:43:41 +02:00
|
|
|
const hasChildren = (page) => {
|
|
|
|
const keys = new Set([...Object.keys(page)])
|
|
|
|
for (const key of ['t', 's']) keys.delete(key)
|
|
|
|
|
|
|
|
return keys.size
|
|
|
|
}
|
|
|
|
|
2021-12-17 17:51:20 +01:00
|
|
|
// Component that renders a sublevel of navigation
|
2023-03-26 09:18:51 +02:00
|
|
|
const SubLevel = ({ nodes = {}, active = '' }) => (
|
2021-12-21 20:47:13 +01:00
|
|
|
<ul className="pl-5 list-inside">
|
2022-12-04 15:04:56 +01:00
|
|
|
{currentChildren(nodes).map((child) =>
|
2023-05-22 15:43:41 +02:00
|
|
|
hasChildren(child) ? (
|
2023-03-26 09:18:51 +02:00
|
|
|
<li key={child.s} className="flex flex-row">
|
|
|
|
<details className="grow" open={isActive(child.s, active)}>
|
2022-12-04 15:04:56 +01:00
|
|
|
<summary
|
|
|
|
className={`
|
2021-12-21 20:47:13 +01:00
|
|
|
flex flex-row
|
|
|
|
px-2
|
2021-12-30 18:42:49 +01:00
|
|
|
text-base-content
|
2022-05-11 16:29:46 +02:00
|
|
|
sm:text-base-content
|
2021-12-17 17:51:20 +01:00
|
|
|
hover:cursor-row-resize
|
2021-12-21 20:47:13 +01:00
|
|
|
items-center
|
2022-12-04 15:04:56 +01:00
|
|
|
`}
|
|
|
|
>
|
|
|
|
<Link
|
2023-03-26 16:50:46 +02:00
|
|
|
href={`/${child.s}`}
|
2023-03-26 09:18:51 +02:00
|
|
|
title={child.t}
|
2022-12-04 15:04:56 +01:00
|
|
|
className={`
|
2023-03-26 09:18:51 +02:00
|
|
|
grow pl-2 border-l-2
|
|
|
|
${linkClasses}
|
|
|
|
hover:cursor-pointer
|
|
|
|
hover:border-secondary
|
|
|
|
sm:hover:border-secondary
|
|
|
|
${
|
|
|
|
child.s === active
|
|
|
|
? 'text-secondary border-secondary sm:text-secondary sm:border-secondary'
|
|
|
|
: 'text-base-content sm:text-base-content'
|
|
|
|
}
|
|
|
|
`}
|
2022-12-04 15:04:56 +01:00
|
|
|
>
|
2023-03-26 09:18:51 +02:00
|
|
|
<span className={`${linkClasses} grow hover:cursor-pointer`}>
|
2022-12-04 15:04:56 +01:00
|
|
|
<span
|
|
|
|
className={`
|
2023-03-26 09:18:51 +02:00
|
|
|
text-3xl mr-2 inline-block p-0 leading-3
|
|
|
|
${
|
|
|
|
child.s === active
|
|
|
|
? 'text-secondary sm:text-secondary translate-y-1'
|
|
|
|
: 'translate-y-3'
|
|
|
|
}
|
|
|
|
`}
|
2022-12-04 15:04:56 +01:00
|
|
|
>
|
2023-03-26 09:18:51 +02:00
|
|
|
{child.s === active ? <>•</> : <>°</>}
|
2021-12-27 14:30:10 +01:00
|
|
|
</span>
|
2023-03-26 09:18:51 +02:00
|
|
|
<span className={child.s === active ? 'font-bold' : ''}>{child.t}</span>
|
2022-12-04 15:04:56 +01:00
|
|
|
</span>
|
2021-12-21 20:47:13 +01:00
|
|
|
</Link>
|
2022-12-04 15:04:56 +01:00
|
|
|
<Chevron w={6} m={3} />
|
2021-12-17 17:51:20 +01:00
|
|
|
</summary>
|
2021-12-22 17:06:50 +01:00
|
|
|
<SubLevel nodes={child} active={active} />
|
2021-12-17 17:51:20 +01:00
|
|
|
</details>
|
|
|
|
</li>
|
|
|
|
) : (
|
2023-03-26 09:18:51 +02:00
|
|
|
<li className="pl-2 flex flex-row items-center" key={child.s}>
|
2022-12-04 15:04:56 +01:00
|
|
|
<Link
|
2023-03-26 16:50:46 +02:00
|
|
|
href={`/${child.s}`}
|
2023-03-26 09:18:51 +02:00
|
|
|
title={child.t}
|
2022-12-04 15:04:56 +01:00
|
|
|
className={`
|
2023-03-26 09:18:51 +02:00
|
|
|
pl-2 border-l-2
|
|
|
|
grow
|
|
|
|
${linkClasses}
|
|
|
|
hover:cursor-pointer
|
|
|
|
hover:border-secondary
|
|
|
|
sm:hover:border-secondary
|
|
|
|
${
|
|
|
|
child.s === active
|
|
|
|
? 'text-secondary border-secondary sm:text-secondary sm:border-secondary'
|
|
|
|
: 'text-base-content sm:text-base-content'
|
|
|
|
}`}
|
2022-12-04 15:04:56 +01:00
|
|
|
>
|
2023-03-26 09:18:51 +02:00
|
|
|
<span className={`${linkClasses} hover:cursor-pointer`}>
|
2022-12-04 15:04:56 +01:00
|
|
|
<span
|
|
|
|
className={`
|
2023-03-26 09:18:51 +02:00
|
|
|
text-3xl mr-2 inline-block p-0 leading-3
|
|
|
|
${
|
|
|
|
child.s === active
|
|
|
|
? 'text-secondary sm:text-secondary translate-y-1'
|
|
|
|
: 'translate-y-3'
|
|
|
|
}
|
|
|
|
`}
|
2022-12-04 15:04:56 +01:00
|
|
|
>
|
2023-03-26 09:18:51 +02:00
|
|
|
{child.s === active ? <>•</> : <>°</>}
|
2021-12-27 14:30:10 +01:00
|
|
|
</span>
|
2023-03-26 09:18:51 +02:00
|
|
|
<span className={child.s === active ? 'font-bold' : ''}>{child.t}</span>
|
2022-12-04 15:04:56 +01:00
|
|
|
</span>
|
2021-12-17 17:51:20 +01:00
|
|
|
</Link>
|
|
|
|
</li>
|
|
|
|
)
|
|
|
|
)}
|
|
|
|
</ul>
|
|
|
|
)
|
2021-12-16 19:01:37 +01:00
|
|
|
|
2022-05-12 10:27:42 +02:00
|
|
|
export const Icons = ({
|
2022-12-04 15:04:56 +01:00
|
|
|
ulClasses = '',
|
|
|
|
linkClasses = `grow text-lg lg:text-xl py-1 text-base-content sm:text-base-content
|
2022-05-12 10:27:42 +02:00
|
|
|
hover:text-secondary sm:hover:text-secondary hover:cursor-pointer
|
2022-06-06 14:06:49 -05:00
|
|
|
flex flex-col items-center`,
|
2022-12-04 15:04:56 +01:00
|
|
|
linkStyle = {},
|
2022-05-12 10:27:42 +02:00
|
|
|
}) => {
|
2023-05-16 10:34:13 +02:00
|
|
|
const { nav } = useContext(NavigationContext)
|
|
|
|
if (!nav) return null
|
2023-03-26 09:18:51 +02:00
|
|
|
|
2022-05-11 16:29:46 +02:00
|
|
|
const output = []
|
2023-05-16 10:34:13 +02:00
|
|
|
for (const page of order(nav)) {
|
2022-05-11 16:29:46 +02:00
|
|
|
output.push(
|
2023-03-26 09:18:51 +02:00
|
|
|
<li key={page.s}>
|
2023-03-26 16:50:46 +02:00
|
|
|
<Link href={`/${page.s}`} className={linkClasses} title={page.t} style={linkStyle}>
|
2023-05-22 19:53:24 +02:00
|
|
|
{icons[page.s] ? icons[page.s]('w-14 h-14') : <BulletIcon />}
|
2023-03-26 09:18:51 +02:00
|
|
|
<span className="font-bold">{page.t}</span>
|
2022-05-11 16:29:46 +02:00
|
|
|
</Link>
|
|
|
|
</li>
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
2022-05-12 10:27:42 +02:00
|
|
|
return <ul className={ulClasses}>{output}</ul>
|
2021-12-12 18:58:24 +01:00
|
|
|
}
|
|
|
|
|
2023-05-22 19:53:24 +02:00
|
|
|
export const Spacer = () => (
|
|
|
|
<hr
|
|
|
|
className={`pb-2 mt-2 border-2 border-solid border-neutral
|
|
|
|
border-b-0 border-r-0 border-l-0`}
|
|
|
|
/>
|
|
|
|
)
|
|
|
|
|
2023-04-28 21:23:06 +02:00
|
|
|
export const MainSections = () => {
|
|
|
|
const { sections = false, slug } = useContext(NavigationContext)
|
|
|
|
if (!sections) return null
|
2023-03-28 16:47:07 +02:00
|
|
|
// Ensure each page as an `o` key so we can put them in order
|
2023-04-28 21:23:06 +02:00
|
|
|
const sortableSections = sections.map((s) => ({ ...s, o: s.o ? s.o : s.t }))
|
2023-03-26 09:18:51 +02:00
|
|
|
const output = []
|
2023-03-28 16:47:07 +02:00
|
|
|
for (const page of orderBy(sortableSections, ['o', 't'])) {
|
2023-04-28 21:23:06 +02:00
|
|
|
const act = isActive(page.s, slug)
|
2023-03-26 09:18:51 +02:00
|
|
|
const txt = (
|
|
|
|
<>
|
|
|
|
{icons[page.s] ? (
|
2023-05-22 19:53:24 +02:00
|
|
|
icons[page.s](`w-6 h-6 ${act ? 'text-base-100 opacity-70' : ''}`)
|
2023-03-26 09:18:51 +02:00
|
|
|
) : (
|
2023-05-22 19:53:24 +02:00
|
|
|
<BulletIcon fill={act} className={`w-6 h-6 ${act ? 'text-base-100 opacity-70' : ''}`} />
|
2023-03-26 09:18:51 +02:00
|
|
|
)}
|
|
|
|
<span className={`font-bold ${act ? 'text-secondary-content' : ''}`}>{page.t}</span>
|
|
|
|
</>
|
|
|
|
)
|
|
|
|
|
2023-05-22 19:53:24 +02:00
|
|
|
const item =
|
|
|
|
page.t === 'spacer' ? (
|
|
|
|
<li key={page.s} className="opacity-10">
|
|
|
|
<Spacer />
|
|
|
|
</li>
|
|
|
|
) : (
|
|
|
|
<li key={page.s}>
|
|
|
|
{act ? (
|
|
|
|
<span
|
|
|
|
className={`
|
|
|
|
flex flex-row gap-4 items-center
|
|
|
|
text-secondary-content
|
|
|
|
hover:text-base-content
|
|
|
|
bg-secondary
|
2023-08-27 16:40:25 +02:00
|
|
|
bg-opacity-20
|
2023-05-22 19:53:24 +02:00
|
|
|
p-2 px-4 rounded
|
|
|
|
bg-base-200
|
|
|
|
rounded-none
|
|
|
|
`}
|
|
|
|
title={page.t}
|
|
|
|
>
|
|
|
|
{txt}
|
|
|
|
</span>
|
|
|
|
) : (
|
|
|
|
<Link
|
|
|
|
href={`/${page.s}`}
|
|
|
|
className={`
|
2023-03-26 09:18:51 +02:00
|
|
|
flex flex-row gap-4 items-center
|
2023-08-27 16:40:25 +02:00
|
|
|
hover:bg-secondary hover:bg-opacity-10 hover:cursor-pointer
|
2023-03-26 09:18:51 +02:00
|
|
|
p-2 px-4 rounded
|
|
|
|
rounded-none
|
|
|
|
`}
|
2023-05-22 19:53:24 +02:00
|
|
|
title={page.t}
|
|
|
|
>
|
|
|
|
{txt}
|
|
|
|
</Link>
|
|
|
|
)}
|
|
|
|
</li>
|
|
|
|
)
|
2023-03-26 09:18:51 +02:00
|
|
|
output.push(item)
|
|
|
|
}
|
|
|
|
|
|
|
|
return <ul>{output}</ul>
|
|
|
|
}
|
|
|
|
|
2023-04-28 21:23:06 +02:00
|
|
|
const getCrumb = (index, crumbs) => crumbs[index].s.split('/').pop()
|
|
|
|
|
|
|
|
export const ActiveSection = () => {
|
|
|
|
// Get navigation context
|
2023-05-19 16:31:28 +02:00
|
|
|
const { crumbs = [], nav = {}, slug } = useContext(NavigationContext)
|
2023-04-16 16:19:49 +02:00
|
|
|
|
2023-04-19 20:05:09 +02:00
|
|
|
// Don't bother if we don't know where we are
|
2023-04-28 21:23:06 +02:00
|
|
|
if (!crumbs || !Array.isArray(crumbs) || crumbs.length < 1) return null
|
2023-04-19 20:05:09 +02:00
|
|
|
|
2023-04-16 16:19:49 +02:00
|
|
|
let slice = 1
|
2023-04-28 21:23:06 +02:00
|
|
|
let nodes = nav
|
2023-04-16 16:19:49 +02:00
|
|
|
// Some sections are further trimmed
|
2023-05-19 16:31:28 +02:00
|
|
|
if (crumbs[0].s === 'docs') {
|
2023-04-28 21:23:06 +02:00
|
|
|
if (crumbs.length > 1 && crumbs[1].s === 'docs/faq') {
|
2023-04-16 17:35:44 +02:00
|
|
|
slice = 2
|
2023-04-28 21:23:06 +02:00
|
|
|
nodes = nav[getCrumb(1, crumbs)]
|
|
|
|
} else if (crumbs.length === 2) {
|
2023-04-16 16:19:49 +02:00
|
|
|
slice = 2
|
2023-04-28 21:23:06 +02:00
|
|
|
nodes = nav[getCrumb(1, crumbs)]
|
2023-04-16 16:19:49 +02:00
|
|
|
} else if (
|
2023-04-28 21:23:06 +02:00
|
|
|
crumbs.length === 4 &&
|
|
|
|
crumbs[1].s === 'docs/patterns' &&
|
|
|
|
crumbs[3].s.split('/').pop() === 'options'
|
2023-04-16 16:19:49 +02:00
|
|
|
) {
|
|
|
|
slice = 4
|
2023-04-28 21:23:06 +02:00
|
|
|
nodes = nav[getCrumb(1, crumbs)][getCrumb(2, crumbs)][getCrumb(3, crumbs)]
|
|
|
|
} else if (crumbs.length > 2 && crumbs[1].s === 'docs/patterns') {
|
2023-04-16 16:19:49 +02:00
|
|
|
slice = 3
|
2023-04-28 21:23:06 +02:00
|
|
|
nodes = nav[getCrumb(1, crumbs)][getCrumb(2, crumbs)]
|
2023-04-16 16:19:49 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return (
|
|
|
|
<div>
|
2023-05-19 16:31:28 +02:00
|
|
|
<div className="pl-4 my-2">
|
|
|
|
<Breadcrumbs crumbs={crumbs.slice(0, slice)} />
|
|
|
|
</div>
|
2023-04-16 16:19:49 +02:00
|
|
|
<div className="pr-2">
|
2023-04-28 21:23:06 +02:00
|
|
|
<SubLevel hasChildren={1} nodes={nodes} active={slug} />
|
2023-03-26 09:18:51 +02:00
|
|
|
</div>
|
|
|
|
</div>
|
2023-04-16 16:19:49 +02:00
|
|
|
)
|
|
|
|
}
|