diff --git a/frontmatter.json b/frontmatter.json index 7a966b5336..d3fc9e759d 100644 --- a/frontmatter.json +++ b/frontmatter.json @@ -71,6 +71,16 @@ "title": "navOrder", "name": "navOrder", "type": "number" + }, + { + "title": "area", + "name": "area", + "type": "choice", + "choices": [ + "docs", + "api" + ], + "required": false } ] } @@ -132,4 +142,4 @@ ] } } -} \ No newline at end of file +} diff --git a/src/components/ApiNavigation.astro b/src/components/ApiNavigation.astro index 15fecfbcc6..5bb1e4e14a 100644 --- a/src/components/ApiNavigation.astro +++ b/src/components/ApiNavigation.astro @@ -8,14 +8,15 @@ stats.start(); type Props = { lang: string; - headings: { depth: number; slug: string; text: string }[]; + // Left off by a layout that lists this page's headings somewhere else, which + // leaves the nav as the flat list of pages with nothing under the current one. + headings?: { depth: number; slug: string; text: string }[]; // One entry per endpoint, in heading order, left on the frontmatter by // plugins/satteri-api-examples.js from the `:endpoint` directive under each // heading. Sections without one — and pages that never reach that plugin — // simply have none. apiMethods?: - | { text: string; method: string | null; deprecated?: boolean }[] - | null; + { text: string; method: string | null; deprecated?: boolean }[] | null; }; const { lang, headings, apiMethods } = Astro.props satisfies Props; diff --git a/src/components/AreaNavigation.astro b/src/components/AreaNavigation.astro new file mode 100644 index 0000000000..bb5c45e394 --- /dev/null +++ b/src/components/AreaNavigation.astro @@ -0,0 +1,38 @@ +--- +import type { Area } from '@lib/areas'; + +// The nav for the docs area +import Navigation from '@components/Navigation.astro'; +// The nav for the API reference +import ApiNavigation from './ApiNavigation.astro'; + +// The area's nav, picked by area rather than by layout, so a page can be laid +// out one way and navigated another. Every layout renders this and none of them +// names a nav component. +// +// Adding an area means adding a line at the bottom of this file, and the rest +// of the checklist is in lib/areas.ts - this is step 3 of four. +type Props = { + area: Area; + lang: string; + // The API nav lists the current page's endpoints under it, and reads both of + // these to do it. A layout that shows this page's headings elsewhere — the + // table of contents in the side column — leaves them off, and the nav is + // then the flat page list on its own. + headings?: { depth: number; slug: string; text: string }[]; + apiMethods?: { text: string; method: string | null }[] | null; +}; +const { area, lang, headings, apiMethods } = Astro.props satisfies Props; +--- + +{ + /* One line per area, each handed only the props its own nav takes. An area + with no line here renders no nav at all, which is louder than quietly showing + it the wrong one. */ +} +{area === 'docs' && } +{ + area === 'api' && ( + + ) +} diff --git a/src/layouts/Api.astro b/src/layouts/Api.astro index 3426547f3d..bf4c84c49b 100644 --- a/src/layouts/Api.astro +++ b/src/layouts/Api.astro @@ -3,7 +3,7 @@ import { accelerator } from '@lib/accelerator'; import { PostFiltering } from 'astro-accelerator-utils'; import type { Frontmatter as OriginalFrontmatter } from 'astro-accelerator-utils/types/Frontmatter'; import { SITE } from '@config'; -import { buildApiCrumbs } from '@lib/apiNavigation'; +import { buildAreaCrumbs, resolveArea, type Area } from '@lib/areas'; import type { Crumb } from '@util/breadcrumbs'; // Theme components @@ -14,7 +14,7 @@ import Authors from '@components/Authors.astro'; import Taxonomy from '@components/Taxonomy.astro'; // Custom components -import ApiNavigation from '../components/ApiNavigation.astro'; +import AreaNavigation from '../components/AreaNavigation.astro'; import ArticleHeader from '../components/ArticleHeader.astro'; import Feedback from '../components/Feedback.astro'; import TopNav from '../components/TopNav.astro'; @@ -23,9 +23,12 @@ import Footer from 'src/components/Footer.astro'; import DocsSearch from '../components/DocsSearch.astro'; type Props = { - // `apiMethods` is not written by hand: plugins/satteri-api-examples.js leaves - // it on the frontmatter for the left nav, an entry per endpoint. frontmatter: OriginalFrontmatter & { + // The nav this page appears in. Optional, and normally left alone: these + // pages live under /docs/api, which is the API area already. + area?: Area; + // `apiMethods` is not written by hand: plugins/satteri-api-examples.js + // leaves it on the frontmatter for the left nav, an entry per endpoint. apiMethods?: { text: string; method: string | null }[]; }; headings: { depth: number; slug: string; text: string }[]; @@ -33,9 +36,10 @@ type Props = { }; const { frontmatter, headings, breadcrumbs } = Astro.props satisfies Props; -// buildApiCrumbs, not buildCrumbs: the section has no page of its own at -// /docs/api for the generic walk to find, so it splices the crumb in. -const crumbs = buildApiCrumbs(Astro.url, breadcrumbs); +const area = resolveArea(frontmatter.area, Astro.url.pathname); +// The API section has no page of its own at /docs/api for the generic crumb +// walk to find, so buildAreaCrumbs splices the crumb in for it. +const crumbs = buildAreaCrumbs(Astro.url, area, breadcrumbs); const lang = frontmatter.lang ?? SITE.default.lang; const textDirection = frontmatter.dir ?? SITE.default.dir; @@ -100,7 +104,12 @@ const lastUpdated = frontmatter.modDate ?? frontmatter.pubDate ?? null; - - + { + /* The nav belongs to the area, not to this layout. The page's own + headings are not passed: the side column below lists them. */ + } +