blob: 0944577a87c11a30608eb01ccabd802d84266fa5 [file] [edit]
---
import type { MarkdownHeading } from 'astro'
import type { CollectionEntry } from 'astro:content'
import { getConfig } from '@libs/config'
import type { LayoutOverridesHTMLAttributes } from '@libs/layout'
import { getSlug, processMarkdownToHtml } from '@libs/utils'
import { getVersionedDocsPath } from '@libs/path'
import Ads from '@components/Ads.astro'
import BaseLayout from '@layouts/BaseLayout.astro'
import DocsSidebar from '@components/DocsSidebar.astro'
import TableOfContents from '@components/TableOfContents.astro'
import ReferenceTable from '@components/ReferenceTable.astro'
import UtilityReferenceTable from '@components/UtilityReferenceTable.astro'
import HelperReferenceTable from '@components/HelperReferenceTable.astro'
import { getData, type SidebarItem, type SidebarSubItem } from '@libs/data'
import CloseButton from '@components/shortcodes/CloseButton.astro'
import PageMeta from '@components/PageMeta.astro'
interface NavigationPage {
title: string
url: string
groupTitle: string
}
interface Props {
frontmatter: CollectionEntry<'docs'>['data']
headings?: MarkdownHeading[]
id: CollectionEntry<'docs'>['id']
}
// Get sidebar data using the data library
const sidebar = getData('sidebar')
const { frontmatter, headings, id } = Astro.props
const { slug } = Astro.params
// Extract the directory/section from the ID (format: "directory/filename.mdx")
const parentDirectory = id.includes('/') ? id.split('/')[0] : ''
const bodyProps: LayoutOverridesHTMLAttributes<'body'> = {}
if (frontmatter.toc) {
bodyProps['data-bs-spy'] = 'scroll'
bodyProps['data-bs-target'] = '#TableOfContents'
// Align the activation line with the sticky navbar offset (--bd-navbar-offset
// is 5.75rem ~= 92px) so a heading highlights right as it settles below the
// navbar, plus a small epsilon. A fixed px value avoids the viewport-height
// drift of a percentage-based top margin.
bodyProps['data-bs-top-margin'] = '96px'
}
// Find previous and next pages for navigation
let prevPage: NavigationPage | undefined
let nextPage: NavigationPage | undefined
// Create a flat array of all pages with their groups, handling nested groups
const allPages: NavigationPage[] = sidebar.flatMap((group) => {
if (!group.pages) return []
return group.pages.flatMap((item: SidebarItem) => {
// Handle nested groups (e.g., Utilities > Borders > Border)
if (item.group && item.pages) {
const subgroupTitle = item.group
return item.pages.map((page: SidebarSubItem) => ({
title: page.title,
url: `/docs/${getConfig().docs_version}/${getSlug(group.title)}/${getSlug(page.title)}/`,
groupTitle: subgroupTitle
}))
}
// Handle direct pages
if (item.title) {
return [
{
title: item.title,
url: `/docs/${getConfig().docs_version}/${getSlug(group.title)}/${getSlug(item.title)}/`,
groupTitle: group.title
}
]
}
return []
})
})
// Find the current page index
const currentPageIndex = allPages.findIndex((page) => page.url === `/docs/${getConfig().docs_version}/${slug}/`)
if (currentPageIndex > 0) {
prevPage = allPages[currentPageIndex - 1]
}
if (currentPageIndex < allPages.length - 1) {
nextPage = allPages[currentPageIndex + 1]
}
---
<BaseLayout {...Astro.props} layout="docs" overrides={{ body: bodyProps }}>
<div slot="main" class="2xl:container bd-gutter mt-3 md:my-4 bd-layout">
<aside class="bd-sidebar">
<dialog class="lg:drawer drawer-bottom" tabindex="-1" id="bdSidebar" aria-labelledby="bdSidebarDrawerLabel">
<div class="drawer-header border-bottom">
<h5 class="drawer-title" id="bdSidebarDrawerLabel">Browse docs</h5>
<CloseButton dismiss="drawer" target="#bdSidebar" />
</div>
<div class="drawer-body">
<DocsSidebar />
</div>
</dialog>
</aside>
<main class="bd-main order-1">
<div class="bd-intro pt-2 lg:ps-2">
<h1 class="bd-title mb-0" id="content">{frontmatter.title}</h1>
<div class="bd-subtitle">
{frontmatter.description && <Fragment set:html={processMarkdownToHtml(frontmatter.description)} />}
</div>
<div class="mb-3 md:mb-0 d-flex gap-2 text-nowrap">
<PageMeta frontmatter={frontmatter} id={id} parentDirectory={parentDirectory} />
</div>
</div>
<div class="lg:d-none d-flex justify-content-center bd-toc-toggle">
<button
class="btn-solid theme-inverse rounded-pill px-5"
type="button"
data-bs-toggle="drawer"
data-bs-target="#bdTocSidebar"
aria-controls="bdTocSidebar"
>
<svg class="bi" aria-hidden="true"><use href="#list"></use></svg>
On this page
</button>
</div>
<div class="bd-toc mt-3 mb-5 lg:my-0 lg:mb-5 sm:px-1">
<dialog class="lg:drawer drawer-bottom" tabindex="-1" id="bdTocSidebar" aria-labelledby="bdTocDrawerLabel">
<div class="drawer-header border-bottom">
<h5 class="drawer-title" id="bdTocDrawerLabel">On this page</h5>
<CloseButton dismiss="drawer" target="#bdTocSidebar" />
</div>
<div class="drawer-body">
{
frontmatter.toc && headings && (
<nav id="TableOfContents" aria-label="Table of contents">
<TableOfContents headings={headings} />
</nav>
)
}
</div>
</dialog>
<Ads />
</div>
<div class="bd-content prose lg:ps-2 lg:pt-4" data-pagefind-body>
{
frontmatter.sections && (
<div class="grid grid-cols-1 lg:grid-cols-3 mb-5">
{frontmatter.sections.map((section) => (
<a
class="d-block text-decoration-none bg-1 hover-bg-2 rounded-3 p-3"
href={getVersionedDocsPath(
`${parentDirectory ? parentDirectory + '/' : ''}${getSlug(section.title)}/`
)}
>
<strong class="d-block mb-0">{section.title}</strong>
<span class="fg-3">{section.description}</span>
</a>
))}
</div>
)
}
{frontmatter.reference && <ReferenceTable reference={frontmatter.reference} />}
{frontmatter.utility && <UtilityReferenceTable utility={frontmatter.utility} />}
{frontmatter.classes && <HelperReferenceTable classes={frontmatter.classes} />}
<slot />
<nav class="bd-links-nav py-9 mt-9 border-top">
<div class="d-flex flex-column md:flex-row justify-content-stretch gap-3 md:gap-5 lh-1">
{
prevPage && (
<a
href={prevPage.url}
class="d-flex flex-column gap-2 p-5 text-decoration-none border rounded-5 flex-grow-1"
rel="prev"
>
<div class="fg-3">← Previous</div>
<div class="fw-semibold">{prevPage.title}</div>
<div class="fg-3">{prevPage.groupTitle}</div>
</a>
)
}
{
nextPage && (
<a
href={nextPage.url}
class="d-flex flex-column gap-2 p-5 text-decoration-none text-end border rounded-5 flex-grow-1"
rel="next"
>
<div class="fg-3">Next →</div>
<div class="fw-semibold">{nextPage.title}</div>
<div class="fg-3">{nextPage.groupTitle}</div>
</a>
)
}
</div>
</nav>
</div>
</main>
</div>
</BaseLayout>