1
0
Fork 0

chore(shared): Shared navigation and wordmark tweaks

This commit is contained in:
Joost De Cock 2023-03-26 09:18:51 +02:00
parent 1745469bb0
commit 91c0627cdd
8 changed files with 163 additions and 452 deletions

View file

@ -2,7 +2,7 @@
import { useRouter } from 'next/router' import { useRouter } from 'next/router'
// Components // Components
import Link from 'next/link' import Link from 'next/link'
import { AsideNavigation } from 'site/components/navigation/aside.mjs' import { AsideNavigation } from 'shared/components/navigation/aside.mjs'
import { ThemePicker } from 'shared/components/theme-picker/index.mjs' import { ThemePicker } from 'shared/components/theme-picker/index.mjs'
import { Breadcrumbs } from 'shared/components/breadcrumbs.mjs' import { Breadcrumbs } from 'shared/components/breadcrumbs.mjs'
import { HomeIcon } from 'shared/components/icons.mjs' import { HomeIcon } from 'shared/components/icons.mjs'

View file

@ -1,28 +0,0 @@
import { MainSections, ActiveSection } from './primary.mjs'
import Link from 'next/link'
export const AsideNavigation = ({ app, mobileOnly = false, before = [], after = [] }) => (
<aside
className={`
fixed top-0 right-0 h-screen
overflow-y-auto z-20
bg-base-100 text-base-content
${app.state?.menu?.main ? '' : 'translate-x-[-120%]'} transition-transform
px-0 pb-20 pt-8 shrink-0
lg:w-auto
lg:sticky lg:relative lg:transform-none
lg:justify-center
lg:border-r-2 lg:border-base-200 lg:bg-base-300 lg:bg-opacity-10
lg:pt-16
${mobileOnly ? 'block lg:hidden w-full ' : ''}
`}
>
<div className="w-screen lg:w-auto">
{before}
<MainSections app={app} />
<ActiveSection app={app} />
{after}
</div>
</aside>
)

View file

@ -1,244 +0,0 @@
import Link from 'next/link'
import orderBy from 'lodash.orderby'
import { TutorialIcon, GuideIcon, HelpIcon, DocsIcon } from 'shared/components/icons.mjs'
import { Breadcrumbs } from 'shared/components/breadcrumbs.mjs'
// List of icons matched to top-level slug
const icons = {
guides: (className = '') => <GuideIcon className={className} />,
howtos: (className = '') => <HelpIcon className={className} />,
reference: (className = '') => <DocsIcon className={className} />,
tutorials: (className = '') => <TutorialIcon className={className} />,
}
/* helper method to order nav entries */
const order = (obj) => orderBy(obj, ['o', 't'], ['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 = `
py-1
text-base 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.s} className="flex flex-row">
<details className="grow" open={isActive(child.s, active)}>
<summary
className={`
flex flex-row
px-2
text-base-content
sm:text-base-content
hover:cursor-row-resize
items-center
`}
>
<Link
href={`${child.s}`}
title={child.t}
className={`
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'
}
`}
>
<span className={`${linkClasses} grow hover:cursor-pointer`}>
<span
className={`
text-3xl mr-2 inline-block p-0 leading-3
${
child.s === active
? 'text-secondary sm:text-secondary translate-y-1'
: 'translate-y-3'
}
`}
>
{child.s === active ? <>&bull;</> : <>&deg;</>}
</span>
<span className={child.s === active ? 'font-bold' : ''}>{child.t}</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.s}>
<Link
href={`${child.s}`}
title={child.t}
className={`
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'
}`}
>
<span className={`${linkClasses} hover:cursor-pointer`}>
<span
className={`
text-3xl mr-2 inline-block p-0 leading-3
${
child.s === active
? 'text-secondary sm:text-secondary translate-y-1'
: 'translate-y-3'
}
`}
>
{child.s === active ? <>&bull;</> : <>&deg;</>}
</span>
<span className={child.s === active ? 'font-bold' : ''}>{child.t}</span>
</span>
</Link>
</li>
)
)}
</ul>
)
export const Icons = ({
app,
ulClasses = '',
linkClasses = `grow text-lg lg:text-xl py-1 text-base-content sm:text-base-content
hover:text-secondary sm:hover:text-secondary hover:cursor-pointer
flex flex-col items-center`,
linkStyle = {},
}) => {
console.log(app.state)
if (!app.state?.nav) return null
const output = []
for (const page of order(app.state.nav)) {
output.push(
<li key={page.s}>
<Link href={`${page.s}`} className={linkClasses} title={page.t} style={linkStyle}>
{icons[page.s] ? icons[page.s]('w-14 h-14') : <HelpIcon />}
<span className="font-bold">{page.t}</span>
</Link>
</li>
)
}
return <ul className={ulClasses}>{output}</ul>
}
export const MainSections = ({ app }) => {
if (!app.state.sections) return null
const output = []
for (const page of app.state.sections) {
const act = isActive(page.s, app.state.slug)
const txt = (
<>
{icons[page.s] ? (
icons[page.s](`w-6 h-6 ${act ? 'text-secondary-content' : ''}`)
) : (
<HelpIcon />
)}
<span className={`font-bold ${act ? 'text-secondary-content' : ''}`}>{page.t}</span>
</>
)
const item = (
<li key={page.s}>
{act ? (
<span
className={`
flex flex-row gap-4 items-center
text-secondary-content
hover:text-base-content
bg-secondary
p-2 px-4 rounded
bg-base-200
rounded-none
`}
title={page.t}
>
{txt}
</span>
) : (
<Link
href={`/${page.s}`}
className={`
flex flex-row gap-4 items-center
hover:bg-secondary hover:bg-opacity-25 hover:cursor-pointer
p-2 px-4 rounded
rounded-none
`}
title={page.t}
>
{txt}
</Link>
)}
</li>
)
output.push(item)
}
return <ul>{output}</ul>
}
export const ActiveSection = ({ app }) => (
<div className="mt-4 pt-4 border-t-2">
{app.state.crumbs ? (
<div className="pl-4">
<Breadcrumbs crumbs={app.state.crumbs.slice(0, 1)} />
</div>
) : null}
<div className="pr-2">
<SubLevel hasChildren={1} nodes={app.state.nav} active={app.state.slug} />
</div>
</div>
)

View file

@ -6,7 +6,7 @@ import { serverSideTranslations } from 'next-i18next/serverSideTranslations'
import Head from 'next/head' import Head from 'next/head'
import { PageWrapper } from 'shared/components/wrappers/page.mjs' import { PageWrapper } from 'shared/components/wrappers/page.mjs'
import { BareLayout } from 'site/components/layouts/bare.mjs' import { BareLayout } from 'site/components/layouts/bare.mjs'
import { Icons } from 'site/components/navigation/primary.mjs' import { Icons } from 'shared/components/navigation/primary.mjs'
import { Highlight } from 'shared/components/mdx/highlight.mjs' import { Highlight } from 'shared/components/mdx/highlight.mjs'
import { Popout } from 'shared/components/popout.mjs' import { Popout } from 'shared/components/popout.mjs'
import { WebLink } from 'shared/components/web-link.mjs' import { WebLink } from 'shared/components/web-link.mjs'

View file

@ -1,26 +1,28 @@
import { PrimaryNavigation } from './primary.mjs' import { MainSections, ActiveSection } from './primary.mjs'
import Link from 'next/link'
export const AsideNavigation = ({ app, slug, mobileOnly = false, before = [], after = [] }) => ( export const AsideNavigation = ({ app, mobileOnly = false, before = [], after = [] }) => (
<aside <aside
className={` className={`
fixed top-0 right-0 h-screen w-screen fixed top-0 right-0 h-screen
overflow-y-auto z-20 overflow-y-auto z-20
bg-base-100 text-base-content md:bg-base-50 bg-base-100 text-base-content
transition-all
${app.state?.menu?.main ? '' : 'translate-x-[-120%]'} transition-transform ${app.state?.menu?.main ? '' : 'translate-x-[-120%]'} transition-transform
md:flex md:sticky md:flex-row-reverse px-0 pb-20 pt-8 shrink-0
md:relative md:transform-none
px-6 py-24 lg:w-auto
shrink-0 lg:sticky lg:relative lg:transform-none
md:w-24 md:px-2 md:justify-center lg:justify-center
lg:w-96 lg:pr-2 lg:border-r-2 lg:border-r-2 lg:border-base-200 lg:bg-base-300 lg:bg-opacity-10
xl:w-lg xl:border-0 lg:pt-16
2xl:pr-8 ${mobileOnly ? 'block lg:hidden w-full ' : ''}
${mobileOnly ? 'block md:hidden' : ''}
`} `}
> >
{before} <div className="w-screen lg:w-auto">
<PrimaryNavigation app={app} active={slug} /> {before}
{after} <MainSections app={app} />
<ActiveSection app={app} />
{after}
</div>
</aside> </aside>
) )

View file

@ -1,42 +1,18 @@
import Link from 'next/link' import Link from 'next/link'
import orderBy from 'lodash.orderby' import orderBy from 'lodash.orderby'
import { import { TutorialIcon, GuideIcon, HelpIcon, DocsIcon } from 'shared/components/icons.mjs'
RssIcon, import { Breadcrumbs } from 'shared/components/breadcrumbs.mjs'
TutorialIcon,
GuideIcon,
HelpIcon,
DocsIcon,
DesignIcon,
BoxIcon,
CogIcon,
UserIcon,
CommunityIcon,
ShowcaseIcon,
} from 'shared/components/icons.mjs'
// Don't show children for blog and showcase posts
const keepClosed = ['blog', 'showcase']
// List of icons matched to top-level slug // List of icons matched to top-level slug
const icons = { const icons = {
accessories: (className = '') => <TutorialIcon className={className} />,
account: (className = '') => <UserIcon className={className} />,
blocks: (className = '') => <BoxIcon className={className} />,
blog: (className = '') => <RssIcon className={className} />,
community: (className = '') => <CommunityIcon className={className} />,
designs: (className = '') => <DesignIcon className={className} />,
docs: (className = '') => <DocsIcon className={className} />,
garments: (className = '') => <DesignIcon className={className} />,
guides: (className = '') => <GuideIcon className={className} />, guides: (className = '') => <GuideIcon className={className} />,
howtos: (className = '') => <HelpIcon className={className} />, howtos: (className = '') => <HelpIcon className={className} />,
reference: (className = '') => <DocsIcon className={className} />, reference: (className = '') => <DocsIcon className={className} />,
showcase: (className = '') => <ShowcaseIcon className={className} />,
tutorials: (className = '') => <TutorialIcon className={className} />, tutorials: (className = '') => <TutorialIcon className={className} />,
utilities: (className = '') => <CogIcon className={className} />,
} }
/* helper method to order nav entries */ /* helper method to order nav entries */
const order = (obj) => orderBy(obj, ['__order', '__title'], ['asc', 'asc']) const order = (obj) => orderBy(obj, ['o', 't'], ['asc', 'asc'])
// Component for the collapse toggle // Component for the collapse toggle
// Exported for re-use // Exported for re-use
@ -59,9 +35,9 @@ const currentChildren = (current) =>
// Shared classes for links // Shared classes for links
// Exported for re-use // Exported for re-use
export const linkClasses = `text-lg lg:text-xl export const linkClasses = `
py-1 py-1
text-base-content sm:text-base-content text-base text-base-content sm:text-base-content
hover:text-secondary hover:text-secondary
sm:hover:text-secondary sm:hover:text-secondary
` `
@ -80,12 +56,12 @@ const isActive = (slug, active) => {
} }
// Component that renders a sublevel of navigation // Component that renders a sublevel of navigation
const SubLevel = ({ nodes = {}, active }) => ( const SubLevel = ({ nodes = {}, active = '' }) => (
<ul className="pl-5 list-inside"> <ul className="pl-5 list-inside">
{currentChildren(nodes).map((child) => {currentChildren(nodes).map((child) =>
Object.keys(child).length > 4 ? ( Object.keys(child).length > 4 ? (
<li key={child.__slug} className="flex flex-row"> <li key={child.s} className="flex flex-row">
<details className="grow" open={isActive(child.__slug, active)}> <details className="grow" open={isActive(child.s, active)}>
<summary <summary
className={` className={`
flex flex-row flex flex-row
@ -97,36 +73,35 @@ const SubLevel = ({ nodes = {}, active }) => (
`} `}
> >
<Link <Link
href={`${child.__slug}`} href={`${child.s}`}
title={child.__title} title={child.t}
className={` className={`
grow pl-2 border-l-2 grow pl-2 border-l-2
hover:cursor-pointer ${linkClasses}
hover:border-secondary hover:cursor-pointer
sm:hover:border-secondary hover:border-secondary
${ sm:hover:border-secondary
child.__slug === active ${
? 'text-secondary border-secondary sm:text-secondary sm:border-secondary' child.s === active
: 'text-base-content sm:text-base-content' ? 'text-secondary border-secondary sm:text-secondary sm:border-secondary'
} : 'text-base-content sm:text-base-content'
`} }
`}
> >
<span className={linkClasses}> <span className={`${linkClasses} grow hover:cursor-pointer`}>
<span <span
className={` className={`
text-3xl mr-2 inline-block p-0 leading-3 text-3xl mr-2 inline-block p-0 leading-3
${ ${
child.__slug === active child.s === active
? 'text-secondary sm:text-secondary translate-y-1' ? 'text-secondary sm:text-secondary translate-y-1'
: 'translate-y-3' : 'translate-y-3'
} }
`} `}
> >
{child.__slug === active ? <>&bull;</> : <>&deg;</>} {child.s === active ? <>&bull;</> : <>&deg;</>}
</span>
<span className={child.__slug === active ? 'font-bold' : ''}>
{child.__linktitle || child.__title}
</span> </span>
<span className={child.s === active ? 'font-bold' : ''}>{child.t}</span>
</span> </span>
</Link> </Link>
<Chevron w={6} m={3} /> <Chevron w={6} m={3} />
@ -135,38 +110,37 @@ const SubLevel = ({ nodes = {}, active }) => (
</details> </details>
</li> </li>
) : ( ) : (
<li className="pl-2 flex flex-row items-center" key={child.__slug}> <li className="pl-2 flex flex-row items-center" key={child.s}>
<Link <Link
href={`${child.__slug}`} href={`${child.s}`}
title={child.__title} title={child.t}
className={` className={`
pl-2 border-l-2 pl-2 border-l-2
grow grow
hover:cursor-pointer ${linkClasses}
hover:border-secondary hover:cursor-pointer
sm:hover:border-secondary hover:border-secondary
${ sm:hover:border-secondary
child.__slug === active ${
? 'text-secondary border-secondary sm:text-secondary sm:border-secondary' child.s === active
: 'text-base-content sm:text-base-content' ? 'text-secondary border-secondary sm:text-secondary sm:border-secondary'
}`} : 'text-base-content sm:text-base-content'
}`}
> >
<span className={linkClasses}> <span className={`${linkClasses} hover:cursor-pointer`}>
<span <span
className={` className={`
text-3xl mr-2 inline-block p-0 leading-3 text-3xl mr-2 inline-block p-0 leading-3
${ ${
child.__slug === active child.s === active
? 'text-secondary sm:text-secondary translate-y-1' ? 'text-secondary sm:text-secondary translate-y-1'
: 'translate-y-3' : 'translate-y-3'
} }
`} `}
> >
{child.__slug === active ? <>&bull;</> : <>&deg;</>} {child.s === active ? <>&bull;</> : <>&deg;</>}
</span>
<span className={child.__slug === active ? 'font-bold' : ''}>
{child.__linktitle || child.__title}
</span> </span>
<span className={child.s === active ? 'font-bold' : ''}>{child.t}</span>
</span> </span>
</Link> </Link>
</li> </li>
@ -175,79 +149,24 @@ const SubLevel = ({ nodes = {}, active }) => (
</ul> </ul>
) )
// Component that renders a toplevel of navigation
const TopLevel = ({ icon, title, current, slug, hasChildren = false, active }) => (
<details className="py-1" open={keepClosed.indexOf(current.__slug) === -1 ? 1 : 0}>
<summary
className={`
flex flex-row uppercase gap-4 font-bold text-lg
hover:cursor-row-resize
p-2
text-base-content
sm:text-base-content
items-center
`}
>
<span className="text-secondary">{icon}</span>
<Link
href={`${slug}`}
className={`
grow ${linkClasses} hover:cursor-pointer
${slug === active ? 'text-secondary sm:text-secondary' : ''}`}
>
{title}
</Link>
{hasChildren && <Chevron />}
</summary>
{hasChildren && <SubLevel nodes={current} active={active} />}
</details>
)
const Navigation = ({ app, active, className = '' }) => {
if (!app.navigation) return null
const output = []
for (const page of order(app.navigation))
output.push(
<TopLevel
key={page.__slug}
icon={
icons[page.__slug] ? (
icons[page.__slug]('w-6 h-6')
) : (
<span className="text-3xl mr-2 translate-y-3 inline-block p-0 leading-3">&deg;</span>
)
}
title={page.__title}
slug={page.__slug}
hasChildren={keepClosed.indexOf(page.__slug) === -1}
nav={app.navigation}
current={order(app.navigation[page.__slug])}
active={active}
/>
)
return <div className={`pb-20 ${className}`}>{output}</div>
}
export const Icons = ({ export const Icons = ({
app, app,
ulClasses = '', ulClasses = '',
liClasses = '',
linkClasses = `grow text-lg lg:text-xl py-1 text-base-content sm:text-base-content linkClasses = `grow text-lg lg:text-xl py-1 text-base-content sm:text-base-content
hover:text-secondary sm:hover:text-secondary hover:cursor-pointer hover:text-secondary sm:hover:text-secondary hover:cursor-pointer
flex flex-col items-center`, flex flex-col items-center`,
linkStyle = {}, linkStyle = {},
}) => { }) => {
if (!app.navigation) return null console.log(app.state)
if (!app.state?.nav) return null
const output = [] const output = []
for (const page of order(app.navigation)) { for (const page of order(app.state.nav)) {
output.push( output.push(
<li key={page.__slug} className={liClasses}> <li key={page.s}>
<Link href={`${page.__slug}`} title={page.__title} style={linkStyle}> <Link href={`${page.s}`} className={linkClasses} title={page.t} style={linkStyle}>
<span className={linkClasses}> {icons[page.s] ? icons[page.s]('w-14 h-14') : <HelpIcon />}
{icons[page.__slug] ? icons[page.__slug]('w-14 h-14') : <HelpIcon />} <span className="font-bold">{page.t}</span>
<span className="font-bold">{page.__title}</span>
</span>
</Link> </Link>
</li> </li>
) )
@ -256,11 +175,70 @@ export const Icons = ({
return <ul className={ulClasses}>{output}</ul> return <ul className={ulClasses}>{output}</ul>
} }
export const PrimaryNavigation = ({ app, active, before = [], after = [] }) => ( export const MainSections = ({ app }) => {
<nav className="mb-12"> if (!app.state.sections) return null
{before} const output = []
<Icons app={app} ulClasses="hidden md:block lg:hidden flex flex-col items-center" /> for (const page of app.state.sections) {
<Navigation app={app} active={active} className="md:hidden lg:block" /> const act = isActive(page.s, app.state.slug)
{after} const txt = (
</nav> <>
{icons[page.s] ? (
icons[page.s](`w-6 h-6 ${act ? 'text-secondary-content' : ''}`)
) : (
<HelpIcon />
)}
<span className={`font-bold ${act ? 'text-secondary-content' : ''}`}>{page.t}</span>
</>
)
const item = (
<li key={page.s}>
{act ? (
<span
className={`
flex flex-row gap-4 items-center
text-secondary-content
hover:text-base-content
bg-secondary
p-2 px-4 rounded
bg-base-200
rounded-none
`}
title={page.t}
>
{txt}
</span>
) : (
<Link
href={`/${page.s}`}
className={`
flex flex-row gap-4 items-center
hover:bg-secondary hover:bg-opacity-25 hover:cursor-pointer
p-2 px-4 rounded
rounded-none
`}
title={page.t}
>
{txt}
</Link>
)}
</li>
)
output.push(item)
}
return <ul>{output}</ul>
}
export const ActiveSection = ({ app }) => (
<div className="mt-4 pt-4 border-t-2">
{app.state.crumbs ? (
<div className="pl-4">
<Breadcrumbs crumbs={app.state.crumbs.slice(0, 1)} />
</div>
) : null}
<div className="pr-2">
<SubLevel hasChildren={1} nodes={app.state.nav} active={app.state.slug} />
</div>
</div>
) )

View file

@ -2,16 +2,16 @@ import Link from 'next/link'
export const InnerWordMark = () => ( export const InnerWordMark = () => (
<span style={{ letterSpacing: '-0.1rem' }}> <span style={{ letterSpacing: '-0.1rem' }}>
<span className="text-red-400">F</span> <span className="text-red-400 hover:text-purple-400">F</span>
<span className="text-orange-400">r</span> <span className="text-orange-400 hover:text-violet-400">r</span>
<span className="text-yellow-400">e</span> <span className="text-yellow-400 hover:text-indigo-400">e</span>
<span className="text-lime-400">e</span> <span className="text-lime-400 hover:text-blue-400">e</span>
<span className="text-green-400">S</span> <span className="text-green-400 hover:text-cyan-400">S</span>
<span className="text-cyan-400">e</span> <span className="text-cyan-400 hover:text-green-400">e</span>
<span className="text-blue-400">w</span> <span className="text-blue-400 hover:text-lime-400">w</span>
<span className="text-indigo-400">i</span> <span className="text-indigo-400 hover:text-yellow-400">i</span>
<span className="text-violet-400">n</span> <span className="text-violet-400 hover:text-orange-400">n</span>
<span className="text-purple-400">g</span> <span className="text-purple-400 hover:text-red-400">g</span>
</span> </span>
) )

View file

@ -19,3 +19,6 @@
<code class="bg-yellow-300 bg-opacity-5" /> <code class="bg-yellow-300 bg-opacity-5" />
<code class="bg-orange-300 bg-opacity-5 opacity-80 line-through decoration-orange-500" /> <code class="bg-orange-300 bg-opacity-5 opacity-80 line-through decoration-orange-500" />
<!-- Colors for the wordmark -->