1
0
Fork 0

wip(shared): Nav prebuild changes

This commit is contained in:
Joost De Cock 2023-03-28 16:47:07 +02:00
parent 847598f059
commit 995bb23551
17 changed files with 103 additions and 349 deletions

View file

@ -2,13 +2,13 @@
import { useRouter } from 'next/router'
// Components
import Link from 'next/link'
import { AsideNavigation } from 'shared/components/navigation/aside.mjs'
import { AsideNavigation, ns as navNs } from 'shared/components/navigation/aside.mjs'
import { ThemePicker } from 'shared/components/theme-picker/index.mjs'
import { Breadcrumbs } from 'shared/components/breadcrumbs.mjs'
import { HomeIcon } from 'shared/components/icons.mjs'
import { useState, useEffect } from 'react'
export const ns = []
export const ns = navNs
export const DocsLayout = ({ app, children = [], title }) => (
<div className="grid grid-cols-4 m-auto justify-center place-items-stretch">

View file

@ -1,25 +0,0 @@
import { PrimaryNavigation } from './primary.mjs'
export const AsideNavigation = ({ app, slug, mobileOnly = false, before = [], after = [] }) => (
<aside
className={`
fixed top-0 right-0 h-screen w-screen
overflow-y-auto z-20
text-base-content
transition-all
bg-base-100 px-4 py-4
md:bg-transparent md:px-0 md:py-0
${app.primaryMenu ? '' : 'translate-x-[-120%]'} transition-transform
md:flex md:flex-col md:sticky md:top-16
md:relative md:transform-none
md:max-w-xl
shrink-0
md:w-full
${mobileOnly ? 'block md:hidden' : ''}
`}
>
{before}
<PrimaryNavigation app={app} active={slug} />
{after}
</aside>
)

View file

@ -1,280 +0,0 @@
import { useState, useEffect } from 'react'
import Link from 'next/link'
import orderBy from 'lodash.orderby'
import { FreeSewingIcon } from 'shared/components/icons.mjs'
import { TopLevelNavigation as TopLevel } from './top-level.mjs'
/* 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 = `
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
href={`/${child.__slug}`}
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 ? <>&bull;</> : <>&deg;</>}
</span>
<span className={child.__slug === active ? 'font-medium' : ''}>
{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
href={`/${child.__slug}`}
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 ? <>&bull;</> : <>&deg;</>}
</span>
<span className={child.__slug === active ? 'font-medium' : ''}>
{child.__linktitle || child.__title}
</span>
</span>
</Link>
</li>
)
)}
</ul>
)
const LevelHomeButton = ({ setShowLevel, level }) => (
<>
<button
className="h-8 mb-1 flex flex-row p-0 items-center -ml-7"
title="FreeSewing.org"
onClick={() => setShowLevel(level)}
>
<div
className={`bg-neutral h-8 pl-2 pr-1 pt-1.5 font-medium text-secondary-content rounded-l-full`}
>
<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>
</button>
</>
)
const colors = ['primary', 'secondary', 'accent']
const LevelButton = ({ title, level, showLevel, setShowLevel, href = false }) => {
const props = {
className: `h-8 mb-1 flex flex-row p-0 items-center -ml-7 max-w-1/3 ${
showLevel < level ? 'opacity-50' : ''
}`,
}
const content = (
<>
<div
className={`border-${colors[level]}`}
style={{
width: 0,
height: 0,
borderWidth: '1rem',
borderLeftColor: 'transparent',
}}
></div>
<div
className={`
bg-${colors[level]} h-8 pr-1 pt-0.5 -ml-2 font-medium
text-secondary-content overflow-hidden`}
>
{title}
</div>
<div
className={`border-${colors[level]} h-12`}
style={{
width: 0,
height: 0,
borderWidth: '1rem',
borderRightColor: 'transparent',
borderTopColor: 'transparent',
borderBottomColor: 'transparent',
}}
></div>
</>
)
return href ? (
<Link {...props} href={href}>
{content}
</Link>
) : (
<button {...props} onClick={() => setShowLevel(level)}>
{content}
</button>
)
}
const Navigation = ({ app, active, className = '' }) => {
// Levels
const levels = active.split('/')
useEffect(() => {
setShowLevel(Math.max(levels.length, 2))
}, [active])
const [showLevel, setShowLevel] = useState(Math.max(levels.length, 2))
if (!app.navigation) return null
if (levels.length < 1) return null
let navigation = app.navigation
const shared = { showLevel, setShowLevel }
const levelButtons = []
if (levels[0]) {
const title = app.navigation[levels[0].__title] || levels[0]
navigation = app.navigation[levels[0]]
levelButtons.push(<LevelHomeButton key="home" {...shared} level={-1} />)
levelButtons.push(<LevelButton title={title} key={0} level={0} {...shared} />)
}
if (levels[1]) {
const title = app.navigation[levels[0]][levels[1]]?.__title || levels[1]
if (showLevel > 0) navigation = navigation[levels[1]]
levelButtons.push(<LevelButton title={title} key={1} level={1} {...shared} />)
}
if (levels[2] && levels.length > 3) {
if (showLevel > 1) navigation = navigation[levels[2]]
levelButtons.push(
<LevelButton
title={app.navigation[levels[0]][levels[1]][levels[2]].__title}
key={2}
level={2}
{...shared}
/>
)
}
const output = [
<div key="levelButtons" className="pl-8 flex flex-row flex-wrap mb-4">
{levelButtons}
</div>,
]
if (showLevel < 0) output.push(<TopLevel />)
else output.push(<SubLevel nodes={order(navigation)} active={active} />)
return <div className={`pb-20 ${className}`}>{output}</div>
}
export const PrimaryNavigation = ({ app, active, before = [], after = [] }) => (
<nav className="mb-12">
{before}
<Navigation app={app} active={active} />
{after}
</nav>
)

View file

@ -1,3 +0,0 @@
export const TopLevelNavigation = () => {
return <p>Top-level menu here</p>
}

View file

@ -15,12 +15,16 @@ import { freeSewingConfig as conf } from 'site/freesewing.config.mjs'
* Note: Set 'c' to set the control level to hide things from users
*/
const ns = ['account']
const ns = ['account', 'sections']
const sitePages = (locale, t = false) => {
// Handle t not being present
if (!t) t = (string) => string
const pages = {
account: {
t: t('sections:account'),
s: 'account',
},
profile: {
t: t('yourProfile'),
s: 'profile',
@ -31,10 +35,6 @@ const sitePages = (locale, t = false) => {
s: 'typography',
h: 1,
},
account: {
t: t('account'),
s: 'account',
},
}
for (const section in conf.account.fields) {
for (const field in conf.account.fields[section]) {

View file

@ -0,0 +1 @@
docs: Documentation

View file

@ -1,17 +1,19 @@
// Hooks
import { useApp } from 'shared/hooks/use-app.mjs'
// Dependencies
import Head from 'next/head'
import { mdxLoader } from 'shared/mdx/loader.mjs'
import { serverSideTranslations } from 'next-i18next/serverSideTranslations'
// Components
import { PageWrapper } from 'shared/components/wrappers/page.mjs'
import { PageWrapper, ns as pageNs } from 'shared/components/wrappers/page.mjs'
import { MdxWrapper } from 'shared/components/wrappers/mdx.mjs'
import { ReadMore } from 'shared/components/mdx/read-more.mjs'
import { jargon } from 'site/jargon.mjs'
const DocsPage = ({ title, mdx, bugsnag }) => {
const app = useApp({ bugsnag })
const fullTitle = title + ' - FreeSewing.org'
// Translation namespaces used on this page
const namespaces = [...new Set(['docs', ...pageNs])]
const DocsPage = (props) => {
const app = useApp(props)
// We don't need all MDX components here, just ReadMore
const components = {
@ -19,15 +21,11 @@ const DocsPage = ({ title, mdx, bugsnag }) => {
}
return (
<PageWrapper app={app} title={title}>
<Head>
<title>{fullTitle}</title>
</Head>
<PageWrapper app={app}>
<div className="w-full"></div>
</PageWrapper>
)
}
//<MdxWrapper mdx={mdx} app={app} components={components} />
export default DocsPage
@ -43,13 +41,15 @@ export default DocsPage
* To learn more, see: https://nextjs.org/docs/basic-features/data-fetching
*/
export async function getStaticProps({ locale }) {
const { mdx, frontmatter } = await mdxLoader(locale, 'org', ['docs'], jargon[locale])
const { title = 'FIXME: Please give this page a title' } = frontmatter
const { mdx } = await mdxLoader(locale, 'org', ['docs'], jargon[locale])
return {
props: {
...(await serverSideTranslations(locale, namespaces)),
mdx,
title,
page: {
path: ['docs'],
},
},
}
}

View file

@ -26,6 +26,31 @@ const HomePage = (props) => {
<Popout fixme>
Create homepage. Meanwhile check <PageLink href="/signup" txt="the signup flow" />
</Popout>
<h2>What is FreeSewing?</h2>
<small>(by ChatGPT)</small>
<p>
Freesewing is an open-source pattern making software that allows users to generate
custom sewing patterns based on their own measurements. It is designed to be flexible
and customizable, and can be used to create a wide range of garments, from simple
t-shirts and skirts to more complex dresses and jackets.
</p>
<p>
Freesewing is available for free, and users can access a wide range of pre-made patterns
or create their own from scratch. The software is designed to be easy to use, with an
intuitive interface that guides users through the process of creating a pattern
step-by-step.
</p>
<p>
In addition to the pattern making software, freesewing also has an active online
community of sewists and pattern makers who share tips, techniques, and advice on all
aspects of sewing. The community also collaborates on creating new patterns and
improving existing ones, and users can contribute their own patterns to the project as
well.
</p>
<p>
Overall, freesewing is a powerful tool for anyone interested in sewing and pattern
making, whether they are seasoned professionals or beginners just starting out.
</p>
</div>
</div>
</PageWrapper>

View file

@ -25,15 +25,15 @@ const TypographyPage = (props) => {
<p>This typography page shows an overview of different elements and how they are styled.</p>
<p>It&apos;s a good starting point for theme development.</p>
<h2>Headings (this is h2)</h2>
{p}
{p} {p}
<h3>This is h3</h3>
{p}
{p} {p}
<h4>This is h4</h4>
{p}
{p} {p}
<h5>This is h5</h5>
{p}
{p} {p}
<h6>This is h6</h6>
{p}
{p} {p}
<h2>Links and buttons</h2>
<p>
A regular link <a href="#">looks like this</a>, whereas buttons look like this:

View file

@ -1,6 +1,8 @@
import { MainSections, ActiveSection } from './primary.mjs'
import { MainSections, ActiveSection, ns as navNs } from './primary.mjs'
import Link from 'next/link'
export const ns = navNs
export const AsideNavigation = ({ app, mobileOnly = false, before = [], after = [] }) => (
<aside
className={`

View file

@ -11,6 +11,8 @@ import {
} from 'shared/components/icons.mjs'
import { Breadcrumbs } from 'shared/components/breadcrumbs.mjs'
export const ns = ['sections']
// List of icons matched to top-level slug
const icons = {
// FreeSewing.dev
@ -191,8 +193,10 @@ export const Icons = ({
export const MainSections = ({ app }) => {
if (!app.state.sections) return null
// Ensure each page as an `o` key so we can put them in order
const sortableSections = app.state.sections.map((s) => ({ ...s, o: s.o ? s.o : s.t }))
const output = []
for (const page of app.state.sections) {
for (const page of orderBy(sortableSections, ['o', 't'])) {
const act = isActive(page.s, app.state.slug)
const txt = (
<>

View file

@ -0,0 +1,4 @@
blog: Blog
showcase: Showcase
docs: Documentation
account: Account

View file

@ -6,10 +6,10 @@ import { useHotkeys } from 'react-hotkeys-hook'
import { useTheme } from 'shared/hooks/use-theme.mjs'
// Components
import { LayoutWrapper, ns as layoutNs } from 'site/components/wrappers/layout.mjs'
import { DocsLayout } from 'site/components/layouts/docs.mjs'
import { DocsLayout, ns as docsNs } from 'site/components/layouts/docs.mjs'
import { Feeds } from 'site/components/feeds.mjs'
export const ns = [...layoutNs]
export const ns = [...new Set([...layoutNs, ...docsNs])]
/* This component should wrap all page content */
export const PageWrapper = ({

View file

@ -16,7 +16,7 @@ const locales = ['en', 'es', 'de', 'fr', 'nl']
* code-adjacent translation source files
*/
const sitesFolder = path.join(fileURLToPath(import.meta.url), '..', '..', '..')
const folders = {
export const folders = {
org: [path.join(sitesFolder, 'org', 'pages'), path.join(sitesFolder, 'org', 'components')],
dev: [path.join(sitesFolder, 'dev', 'pages'), path.join(sitesFolder, 'dev', 'components')],
shared: [path.join(sitesFolder, 'shared', 'components')],
@ -79,12 +79,12 @@ const languageAndNamespaceFromFilename = (file) => {
/*
* Helper method to load a YAML file from disk
*/
const loadYaml = (file) => {
export const loadYaml = (file, complain = true) => {
let data
try {
data = yaml.load(fs.readFileSync(file, 'utf-8'))
} catch (err) {
console.log(err)
if (complain) console.log(err)
}
return data

View file

@ -8,7 +8,6 @@ import { prebuildLab } from './lab.mjs'
import { generateOgImage } from './og/index.mjs'
const run = async () => {
console.log('in run')
const SITE = process.env.SITE || 'lab'
if (SITE === 'org') {
const mdxPages = await prebuildMdx(SITE)

View file

@ -1,10 +1,25 @@
import path from 'path'
import fs from 'fs'
import set from 'lodash.set'
import { loadYaml, folders } from './i18n.mjs'
// Some arbitrary future time
const future = new Date('10-12-2026').getTime()
// We need to load the translation for blog + showcase
const loadTranslation = (locale) => {
const file = `${folders.shared}/navigation/sections.${locale}.yaml`
let data
try {
data = loadYaml(`${folders.shared}/navigation/sections.${locale}.yaml`, false)
} catch (err) {
data = {}
}
if (!data) data = {}
return data
}
/*
* Main method that does what needs doing
*/
@ -20,6 +35,7 @@ export const prebuildNavigation = (mdxPages, strapiPosts, site) => {
*/
const nav = {}
for (const lang in mdxPages) {
const translations = loadTranslation(lang)
nav[lang] = {}
// Handle MDX content
@ -37,10 +53,10 @@ export const prebuildNavigation = (mdxPages, strapiPosts, site) => {
// Handle strapi content
for (const type in strapiPosts) {
set(nav, [lang, type], {
t: type,
t: translations[type] ? translations[type] : type,
l: type,
s: type,
o: type,
o: translations[type] ? translations[type] : type,
})
for (const [slug, page] of Object.entries(strapiPosts[type][lang])) {
const chunks = slug.split('/')

View file

@ -7,22 +7,22 @@
@layer components {
/* Applied styles for common HTML tags */
h1 {
@apply text-base-content text-4xl py-5 font-bold lg:text-6xl font-light;
@apply text-base-content text-4xl py-5 font-bold lg:text-6xl font-black;
}
h2 {
@apply text-base-content text-3xl py-4 pb-2 mb-1 font-light lg:text-4xl border-b-2 border-base-200;
@apply text-base-content text-3xl pt-4 pb-3 m-0 font-black lg:text-4xl border-b-2 border-base-200;
}
h3 {
@apply text-base-content text-2xl py-3 font-light lg:text-3xl;
@apply text-base-content text-2xl pt-3 pb-2 m-0 font-black lg:text-3xl;
}
h4 {
@apply text-base-content text-xl py-2 font-medium lg:text-2xl;
@apply text-base-content text-xl pt-2 pb-1 m-0 font-black lg:text-2xl;
}
h5 {
@apply text-base-content text-lg py-1 font-medium lg:text-xl;
@apply text-base-content text-lg py-1 m-0 font-black lg:text-xl;
}
h6 {
@apply text-base-content text-base font-medium lg:text-lg;
@apply text-base-content text-base py-0 m-0 font-black lg:text-lg;
}
p {
@apply text-base-content my-1 py-2 text-base leading-6;
@ -36,6 +36,17 @@
max-height: 80vh;
}
/* Keep spacing after headings between p and non-p consistent */
h1 + p,
h2 + p,
h3 + p,
h4 + p,
h5 + p,
h6 + p {
padding-top: 0;
margin-top: 0;
}
/* mdx styles */
.mdx a {
@apply text-secondary underline decoration-1;