diff --git a/sites/dev/hooks/use-navigation.mjs b/sites/dev/hooks/use-navigation.mjs index d3e896910a8..690694e3308 100644 --- a/sites/dev/hooks/use-navigation.mjs +++ b/sites/dev/hooks/use-navigation.mjs @@ -1,4 +1,5 @@ import { prebuildNavigation as pbn } from 'site/prebuild/navigation.mjs' +import { orderedSlugLut } from 'shared/hooks/use-navigation-helpers.mjs' /* * prebuildNavvigation[locale] holds the navigation structure based on MDX content. @@ -77,15 +78,18 @@ const sitePages = () => { return pages } -export const useNavigation = (params = {}) => { - const { locale = 'en' } = params - const nav = { ...pbn[locale], ...sitePages() } +export const useNavigation = () => { + // Dev is EN only + const siteNav = { ...pbn.en, ...sitePages() } // Make top-level documentation entries appear in b-list for (const page of ['tutorials', 'guides', 'howtos', 'reference', 'training']) { - nav[page].o = 1000 - nav[page].b = 1 + siteNav[page].o = 1000 + siteNav[page].b = 1 } - nav.contact.h = 1 + siteNav.contact.h = 1 - return nav + return { + siteNav, // Site navigation + slugLut: orderedSlugLut(siteNav), // Slug lookup table + } } diff --git a/sites/dev/pages/[...slug].mjs b/sites/dev/pages/[...slug].mjs index c16b1ca7312..f66b2986e03 100644 --- a/sites/dev/pages/[...slug].mjs +++ b/sites/dev/pages/[...slug].mjs @@ -12,6 +12,7 @@ import { components } from 'shared/components/mdx/index.mjs' import { MdxWrapper } from 'shared/components/wrappers/mdx.mjs' import { Toc } from 'shared/components/mdx/toc.mjs' import { MdxMetaData } from 'shared/components/mdx/meta.mjs' +import { PrevNext } from 'shared/components/prev-next.mjs' /* * This page is auto-generated by the prebuild script. @@ -62,7 +63,10 @@ const DocsPage = ({ page, slug }) => { )} - {MDX} +
+ {MDX} + +
) diff --git a/sites/org/hooks/use-navigation.mjs b/sites/org/hooks/use-navigation.mjs index bfc47c5ac03..6603ea685e8 100644 --- a/sites/org/hooks/use-navigation.mjs +++ b/sites/org/hooks/use-navigation.mjs @@ -4,6 +4,8 @@ import { freeSewingConfig as conf } from 'shared/config/freesewing.config.mjs' import { useAccount } from 'shared/hooks/use-account.mjs' import { designs, tags } from 'shared/config/designs.mjs' import { objUpdate } from 'shared/utils.mjs' +import { orderedSlugLut } from 'shared/hooks/use-navigation-helpers.mjs' +import { useRouter } from 'next/router' /* * prebuildNavvigation[locale] holds the navigation structure based on MDX content. @@ -156,21 +158,27 @@ const sitePages = (t = false, control = 99) => { return pages } -export const useNavigation = (params = {}, extra = []) => { - const { locale = 'en', ignoreControl } = params +export const useNavigation = ({ ignoreControl = false }, extra = []) => { + // Passing in the locale is not very DRY so let's just grab it from the router + const { locale } = useRouter() + // We need translation const { t } = useTranslation(ns) - const { account } = useAccount() + // We need the account if we take control into account + const { account } = ignoreControl ? useAccount() : { account: false } - const navigation = { + const siteNav = { ...pbn[locale], ...sitePages(t, ignoreControl ? undefined : account.control), } for (const [_path, _data] of extra) { - objUpdate(navigation, _path, _data) + objUpdate(siteNav, _path, _data) } - // Set order on docs key (from from prebuild navigation) - navigation.docs.o = 30 + // Set order on docs key (from from prebuild siteNav) + siteNav.docs.o = 30 - return navigation + return { + siteNav, // Site navigation + slugLut: orderedSlugLut(siteNav), // Slug lookup table + } } diff --git a/sites/shared/components/mdx/docs-helpers.mjs b/sites/shared/components/mdx/docs-helpers.mjs index 9a4dbd83175..32726946730 100644 --- a/sites/shared/components/mdx/docs-helpers.mjs +++ b/sites/shared/components/mdx/docs-helpers.mjs @@ -13,7 +13,7 @@ const getPage = { } export const DocsTitle = ({ slug, className = '', site = 'org' }) => { - const siteNav = useNavigation() + const { siteNav } = useNavigation() const page = getPage[site](slug, siteNav) return page ? {page.t} : null diff --git a/sites/shared/components/mdx/prev-next.mjs b/sites/shared/components/mdx/prev-next.mjs deleted file mode 100644 index 029cd6767ba..00000000000 --- a/sites/shared/components/mdx/prev-next.mjs +++ /dev/null @@ -1,113 +0,0 @@ -import get from 'lodash.get' -import orderBy from 'lodash.orderby' -import Link from 'next/link' -import { LeftIcon, RightIcon } from 'shared/components/icons.mjs' - -// helper method to order nav entries -const order = (obj) => orderBy(obj, ['o', 't'], ['asc', 'asc']) - -// Helper method to filter out page nodes -const nodesOnly = (current) => - Object.values(order(current)).filter((entry) => typeof entry === 'object') - -// Helper method to get the siblings -const siblings = (app) => - app.state.slug ? nodesOnly(get(app.state.nav, app.state.slug.split('/').slice(1, -1))) : [] - -// Helper method to get the current parent -const currentParent = (app) => - app.state.slug ? [get(app.state.nav, app.state.slug.split('/').slice(1, -1))] : [] - -// Helper method to get the next parent -const nextParent = (app) => { - if (app.state.slug) - return app.state.slug.split('/').length < 4 - ? nodesOnly(app.state.nav) - : nodesOnly(get(app.state.nav, app.state.slug.split('/').slice(1, -2))) - - return [] -} - -// Helper method to get current node -const current = (app) => (app.state.slug ? get(app.state.nav, app.state.slug.split('/')) : null) - -const previous = (app) => { - // Previous sibling (aside) - const aside = siblings(app) - if (aside.length > 0) { - let next = false - for (const node of aside.reverse()) { - if (next) return node - if (node?.s && node.s === app.state.slug) next = true - } - } - - // Previous parent (up) - const up = currentParent(app) - if (up.length === 1) return up.pop() - - return false -} - -const next = (app) => { - // Next child (down) - const down = nodesOnly(current(app)) - if (down.length > 0) return down[0] - - // Next sibling (aside) - const aside = siblings(app) - if (aside.length > 0) { - let next = false - for (const node of aside) { - if (next) return node - if (node?.s && node.s === app.state.slug) next = true - } - } - - // Next parent (up) - const up = nextParent(app) - if (up.length > 0) { - let next = false - for (const node of up) { - if (next) return node - if (node?.s && node.s === app.state.slug.slice(0, node.s.length)) next = true - } - } - return false -} - -const renderPrevious = (node) => - node ? ( -
- - - {node.t} - -
- ) : ( - - ) - -const renderNext = (node) => - node ? ( -
- - {node.t} - - -
- ) : ( - - ) - -export const PrevNext = ({ app }) => { - return ( -
- {renderPrevious(previous(app))} - {renderNext(next(app))} -
- ) -} - -//
{JSON.stringify(app.state.nav, null ,2)}
-//
{JSON.stringify(app.state.slug, null ,2)}
diff --git a/sites/shared/components/mdx/read-more.mjs b/sites/shared/components/mdx/read-more.mjs index b527ff851c2..ff5eb690f5d 100644 --- a/sites/shared/components/mdx/read-more.mjs +++ b/sites/shared/components/mdx/read-more.mjs @@ -4,6 +4,7 @@ import { useContext } from 'react' import { NavigationContext } from 'shared/context/navigation-context.mjs' import { useNavigation } from 'site/hooks/use-navigation.mjs' import { BulletIcon, RightIcon } from 'shared/components/icons.mjs' +import { pageHasChildren } from 'shared/utils.mjs' const getRoot = { dev: (root, nav) => { @@ -31,7 +32,7 @@ const RenderTree = ({ tree, recurse, depth = 1, level = 0, lead = [] }) => ( * Does this have children? */ const hasChildren = - recurse && (!depth || level < depth) && Object.keys(tree[key]).join('').length > 5 + recurse && (!depth || level < depth) && pageHasChildren(tree[key]) ? tree[key].s.replaceAll('/', '') : false @@ -72,7 +73,7 @@ export const ReadMore = ({ ignoreControl, }) => { const { slug } = useContext(NavigationContext) - const siteNav = useNavigation({ ignoreControl }) + const { siteNav } = useNavigation({ ignoreControl }) // Deal with recurse not being a number if (recurse && recurse !== true) { diff --git a/sites/shared/components/prev-next.mjs b/sites/shared/components/prev-next.mjs new file mode 100644 index 00000000000..2eeedd43c9e --- /dev/null +++ b/sites/shared/components/prev-next.mjs @@ -0,0 +1,65 @@ +// Dependencies +import get from 'lodash.get' +// Hooks +import { useNavigation } from 'site/hooks/use-navigation.mjs' +// Components +import Link from 'next/link' +import { LeftIcon, RightIcon } from 'shared/components/icons.mjs' + +const linkClasses = + 'flex flex-row gap-2 items-center w-full bg-secondary py-4 px-2 ' + + 'rounded-lg border-secondary bg-opacity-10 border border-solid ' + + 'hover:bg-opacity-100 hover:text-secondary-content' + +const PrevPage = ({ t, s }) => + s ? ( + + + {t} + + ) : ( + + ) + +const NextPage = ({ t, s }) => + s ? ( + + {t} + + + ) : ( + + ) + +export const PrevNext = ({ slug }) => { + // Grab site navigation and slug lookup table from the useNavigatin hook + const { siteNav, slugLut } = useNavigation() + + // Lookup the current slug in the LUT + const index = slugLut.indexOf(slug) + + // Add 1 for the next page, unless it's the last page + const iNext = index === slugLut.length ? 0 : index + 1 + + // Subtract 1 for the previous page, unless it's the first page + const iPrev = index === 0 ? slugLut.length - 1 : index - 1 + + // Get the next page from the siteNav object + const next = get(siteNav, slugLut[iNext].split('/')) + + // Get the previous page from the siteNav object + const prev = get(siteNav, slugLut[iPrev].split('/')) + + // Return content + return ( +
+ + +
+ ) +} diff --git a/sites/shared/context/navigation-context.mjs b/sites/shared/context/navigation-context.mjs index 0712e2b8018..701b8b60daf 100644 --- a/sites/shared/context/navigation-context.mjs +++ b/sites/shared/context/navigation-context.mjs @@ -66,7 +66,7 @@ export const NavigationContextProvider = ({ children }) => { }) const [extraPages, setExtraPages] = useState([]) - const siteNav = useNavigation({ path: value.path, locale: value.locale }, extraPages) + const { siteNav } = useNavigation({ path: value.path, locale: value.locale }, extraPages) const navState = buildNavState(value, siteNav) const addPages = (extra) => { diff --git a/sites/shared/hooks/use-navigation-helpers.mjs b/sites/shared/hooks/use-navigation-helpers.mjs new file mode 100644 index 00000000000..143515f57d2 --- /dev/null +++ b/sites/shared/hooks/use-navigation-helpers.mjs @@ -0,0 +1,36 @@ +import { pageHasChildren } from 'shared/utils.mjs' +import orderBy from 'lodash.orderby' + +/* + * A method to recursively add the ordered slugs to the LUT + */ +const flattenOrderedChildPages = (nav) => { + const slugs = [] + for (const page of orderBy(nav, ['o', 't'], ['asc', 'asc'])) { + if (page.s) { + slugs.push(page.s) + if (pageHasChildren(page)) slugs.push(...flattenOrderedChildPages(page)) + } + } + + return slugs +} + +/* + * This builds the slugLut (slug look up table) which makes it trivial to + * build the PrevNext component as it builds a flat list of all pages in + * the order they are naturally presented to the reader. So if you have + * a page's slug, you merely need to look it up in the list and return the + * next entry (or previous) + */ +export const orderedSlugLut = (nav) => { + const slugs = [] + for (const page of orderBy(nav, ['o', 't'], ['asc', 'asc'])) { + if (page.s) { + slugs.push(page.s) + if (pageHasChildren(page)) slugs.push(...flattenOrderedChildPages(page)) + } + } + + return slugs +} diff --git a/sites/shared/utils.mjs b/sites/shared/utils.mjs index 80c06c596c7..4e501a9007e 100644 --- a/sites/shared/utils.mjs +++ b/sites/shared/utils.mjs @@ -304,3 +304,10 @@ export const hasRequiredMeasurements = (Design, measies = {}, DesignIsMeasuremen return [missing.length === 0, missing] } + +/* + * This expects a object from the nav tree and will filter out the know 1-char keys + * and then check if there are any left. If there are, those are child-pages. + */ +export const pageHasChildren = (page) => + Object.keys(page).filter((key) => !['t', 's', 'o', 'b', 'h'].includes(key)).length > 0