mirror of
https://github.com/hanzo-docs/docs.git
synced 2026-10-25 04:20:02 +00:00
UI: use fixed positing for TOC
This commit is contained in:
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'fumadocs-ui': patch
|
||||
---
|
||||
|
||||
Use `position: fixed` for TOC
|
||||
@@ -1,18 +1,4 @@
|
||||
import type { Metadata } from 'next';
|
||||
import {
|
||||
PageArticle,
|
||||
PageBreadcrumb,
|
||||
PageFooter,
|
||||
PageLastUpdate,
|
||||
PageRoot,
|
||||
PageTOC,
|
||||
PageTOCItems,
|
||||
PageTOCPopover,
|
||||
PageTOCPopoverContent,
|
||||
PageTOCPopoverItems,
|
||||
PageTOCPopoverTrigger,
|
||||
PageTOCTitle,
|
||||
} from 'fumadocs-ui/layouts/docs/page';
|
||||
import { notFound } from 'next/navigation';
|
||||
import { type ComponentProps, type FC, type ReactNode } from 'react';
|
||||
import * as Twoslash from 'fumadocs-twoslash/ui';
|
||||
@@ -43,6 +29,7 @@ import { Banner } from 'fumadocs-ui/components/banner';
|
||||
import { openapi } from '@/lib/openapi';
|
||||
import { Installation } from '@/components/preview/installation';
|
||||
import { Customisation } from '@/components/preview/customisation';
|
||||
import { DocsPage } from 'fumadocs-ui/page';
|
||||
|
||||
function PreviewRenderer({ preview }: { preview: string }): ReactNode {
|
||||
if (preview && preview in Preview) {
|
||||
@@ -67,101 +54,79 @@ export default async function Page(props: PageProps<'/docs/[...slug]'>) {
|
||||
const { body: Mdx, toc, lastModified } = await page.data.load();
|
||||
|
||||
return (
|
||||
<PageRoot
|
||||
toc={{
|
||||
toc,
|
||||
single: false,
|
||||
<DocsPage
|
||||
toc={toc}
|
||||
lastUpdate={lastModified}
|
||||
tableOfContent={{
|
||||
style: 'clerk',
|
||||
}}
|
||||
>
|
||||
{toc.length > 0 && (
|
||||
<PageTOCPopover>
|
||||
<PageTOCPopoverTrigger />
|
||||
<PageTOCPopoverContent>
|
||||
<PageTOCPopoverItems />
|
||||
</PageTOCPopoverContent>
|
||||
</PageTOCPopover>
|
||||
)}
|
||||
<PageArticle>
|
||||
<PageBreadcrumb />
|
||||
<h1 className="text-3xl font-semibold">{page.data.title}</h1>
|
||||
<p className="text-lg text-fd-muted-foreground">
|
||||
{page.data.description}
|
||||
</p>
|
||||
<div className="flex flex-row gap-2 items-center border-b pt-2 pb-6">
|
||||
<LLMCopyButton markdownUrl={`${page.url}.mdx`} />
|
||||
<ViewOptions
|
||||
markdownUrl={`${page.url}.mdx`}
|
||||
githubUrl={`https://github.com/${owner}/${repo}/blob/dev/apps/docs/content/docs/${page.path}`}
|
||||
/>
|
||||
</div>
|
||||
<div className="prose flex-1 text-fd-foreground/80">
|
||||
{preview ? <PreviewRenderer preview={preview} /> : null}
|
||||
<Mdx
|
||||
components={getMDXComponents({
|
||||
...Twoslash,
|
||||
a: ({ href, ...props }) => {
|
||||
const found = source.getPageByHref(href ?? '', {
|
||||
dir: path.dirname(page.path),
|
||||
});
|
||||
<h1 className="text-3xl font-semibold">{page.data.title}</h1>
|
||||
<p className="text-lg text-fd-muted-foreground">
|
||||
{page.data.description}
|
||||
</p>
|
||||
<div className="flex flex-row gap-2 items-center border-b pt-2 pb-6">
|
||||
<LLMCopyButton markdownUrl={`${page.url}.mdx`} />
|
||||
<ViewOptions
|
||||
markdownUrl={`${page.url}.mdx`}
|
||||
githubUrl={`https://github.com/${owner}/${repo}/blob/dev/apps/docs/content/docs/${page.path}`}
|
||||
/>
|
||||
</div>
|
||||
<div className="prose flex-1 text-fd-foreground/80">
|
||||
{preview ? <PreviewRenderer preview={preview} /> : null}
|
||||
<Mdx
|
||||
components={getMDXComponents({
|
||||
...Twoslash,
|
||||
a: ({ href, ...props }) => {
|
||||
const found = source.getPageByHref(href ?? '', {
|
||||
dir: path.dirname(page.path),
|
||||
});
|
||||
|
||||
if (!found) return <Link href={href} {...props} />;
|
||||
if (!found) return <Link href={href} {...props} />;
|
||||
|
||||
return (
|
||||
<HoverCard>
|
||||
<HoverCardTrigger asChild>
|
||||
<Link
|
||||
href={
|
||||
found.hash
|
||||
? `${found.page.url}#${found.hash}`
|
||||
: found.page.url
|
||||
}
|
||||
{...props}
|
||||
/>
|
||||
</HoverCardTrigger>
|
||||
<HoverCardContent className="text-sm">
|
||||
<p className="font-medium">{found.page.data.title}</p>
|
||||
<p className="text-fd-muted-foreground">
|
||||
{found.page.data.description}
|
||||
</p>
|
||||
</HoverCardContent>
|
||||
</HoverCard>
|
||||
);
|
||||
},
|
||||
Banner,
|
||||
Mermaid,
|
||||
TypeTable,
|
||||
AutoTypeTable: (props) => (
|
||||
<AutoTypeTable generator={generator} {...props} />
|
||||
),
|
||||
Wrapper,
|
||||
blockquote: Callout as unknown as FC<
|
||||
ComponentProps<'blockquote'>
|
||||
>,
|
||||
APIPage: (props) => (
|
||||
<APIPage {...openapi.getAPIPageProps(props)} />
|
||||
),
|
||||
DocsCategory: ({ url }) => {
|
||||
return <DocsCategory url={url ?? page.url} />;
|
||||
},
|
||||
Installation,
|
||||
Customisation,
|
||||
...(await import('@/content/docs/ui/components/tabs.client')),
|
||||
...(await import('@/content/docs/ui/theme.client')),
|
||||
})}
|
||||
/>
|
||||
{page.data.index ? <DocsCategory url={page.url} /> : null}
|
||||
</div>
|
||||
<Rate onRateAction={onRateAction} />
|
||||
{lastModified && <PageLastUpdate date={lastModified} />}
|
||||
<PageFooter />
|
||||
</PageArticle>
|
||||
{toc.length > 0 && (
|
||||
<PageTOC>
|
||||
<PageTOCTitle />
|
||||
<PageTOCItems variant="clerk" />
|
||||
</PageTOC>
|
||||
)}
|
||||
</PageRoot>
|
||||
return (
|
||||
<HoverCard>
|
||||
<HoverCardTrigger asChild>
|
||||
<Link
|
||||
href={
|
||||
found.hash
|
||||
? `${found.page.url}#${found.hash}`
|
||||
: found.page.url
|
||||
}
|
||||
{...props}
|
||||
/>
|
||||
</HoverCardTrigger>
|
||||
<HoverCardContent className="text-sm">
|
||||
<p className="font-medium">{found.page.data.title}</p>
|
||||
<p className="text-fd-muted-foreground">
|
||||
{found.page.data.description}
|
||||
</p>
|
||||
</HoverCardContent>
|
||||
</HoverCard>
|
||||
);
|
||||
},
|
||||
Banner,
|
||||
Mermaid,
|
||||
TypeTable,
|
||||
AutoTypeTable: (props) => (
|
||||
<AutoTypeTable generator={generator} {...props} />
|
||||
),
|
||||
Wrapper,
|
||||
blockquote: Callout as unknown as FC<ComponentProps<'blockquote'>>,
|
||||
APIPage: (props) => <APIPage {...openapi.getAPIPageProps(props)} />,
|
||||
DocsCategory: ({ url }) => {
|
||||
return <DocsCategory url={url ?? page.url} />;
|
||||
},
|
||||
Installation,
|
||||
Customisation,
|
||||
...(await import('@/content/docs/ui/components/tabs.client')),
|
||||
...(await import('@/content/docs/ui/theme.client')),
|
||||
})}
|
||||
/>
|
||||
{page.data.index ? <DocsCategory url={page.url} /> : null}
|
||||
</div>
|
||||
<Rate onRateAction={onRateAction} />
|
||||
</DocsPage>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -29,7 +29,7 @@ import {
|
||||
} from '@/components/ui/collapsible';
|
||||
import { useSidebar } from '@/contexts/sidebar';
|
||||
import { useTOCItems } from '@/components/layout/toc';
|
||||
import { type AnchorProviderProps, useActiveAnchor } from 'fumadocs-core/toc';
|
||||
import { useActiveAnchor } from 'fumadocs-core/toc';
|
||||
|
||||
const TocPopoverContext = createContext<{
|
||||
open: boolean;
|
||||
@@ -217,10 +217,6 @@ export function PageTOCPopover(props: ComponentProps<'div'>) {
|
||||
);
|
||||
}
|
||||
|
||||
export interface RootProps extends ComponentProps<'div'> {
|
||||
toc: Omit<AnchorProviderProps, 'children'>;
|
||||
}
|
||||
|
||||
export function PageLastUpdate({
|
||||
date: value,
|
||||
...props
|
||||
@@ -399,11 +395,12 @@ export function PageTOC(props: ComponentProps<'div'>) {
|
||||
<div
|
||||
id="nd-toc"
|
||||
{...props}
|
||||
className={cn('sticky pb-2 pt-12 max-xl:hidden', props.className)}
|
||||
className={cn('fixed bottom-0 pb-2 pt-12 max-xl:hidden', props.className)}
|
||||
style={{
|
||||
...props.style,
|
||||
top: 'calc(var(--fd-banner-height) + var(--fd-nav-height))',
|
||||
height: 'calc(100dvh - var(--fd-banner-height) - var(--fd-nav-height))',
|
||||
insetInlineEnd:
|
||||
'max(var(--fd-layout-offset), calc((100vw - var(--fd-sidebar-width) - var(--fd-page-width))/2))',
|
||||
}}
|
||||
>
|
||||
<div className="flex h-full w-(--fd-toc-width) max-w-full flex-col pe-4">
|
||||
|
||||
@@ -10,12 +10,12 @@ import {
|
||||
PageTOCPopover,
|
||||
PageTOCPopoverContent,
|
||||
PageTOCPopoverTrigger,
|
||||
type RootProps,
|
||||
} from './page-client';
|
||||
import { TOCItems, TOCProvider, TOCScrollArea } from '@/components/layout/toc';
|
||||
import { Text } from 'lucide-react';
|
||||
import { I18nLabel } from '@/contexts/i18n';
|
||||
import ClerkTOCItems from '@/components/layout/toc-clerk';
|
||||
import type { AnchorProviderProps } from 'fumadocs-core/toc';
|
||||
|
||||
/**
|
||||
* Apply `prose` on div
|
||||
@@ -79,21 +79,26 @@ export function PageArticle(props: ComponentProps<'article'>) {
|
||||
);
|
||||
}
|
||||
|
||||
export function PageRoot({ toc, children, ...props }: RootProps) {
|
||||
return (
|
||||
<TOCProvider {...toc}>
|
||||
<div
|
||||
id="nd-page"
|
||||
{...props}
|
||||
className={cn(
|
||||
'flex flex-1 w-full mx-auto max-w-(--fd-page-width) pt-(--fd-tocnav-height)',
|
||||
props.className,
|
||||
)}
|
||||
>
|
||||
{children}
|
||||
</div>
|
||||
</TOCProvider>
|
||||
export interface RootProps extends ComponentProps<'div'> {
|
||||
toc?: Omit<AnchorProviderProps, 'children'> | false;
|
||||
}
|
||||
|
||||
export function PageRoot({ toc = false, children, ...props }: RootProps) {
|
||||
const content = (
|
||||
<div
|
||||
id="nd-page"
|
||||
{...props}
|
||||
className={cn(
|
||||
'flex flex-1 w-full mx-auto max-w-(--fd-page-width) pt-(--fd-tocnav-height) pe-(--fd-toc-width)',
|
||||
props.className,
|
||||
)}
|
||||
>
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
|
||||
if (toc) return <TOCProvider {...toc}>{content}</TOCProvider>;
|
||||
return content;
|
||||
}
|
||||
|
||||
export {
|
||||
@@ -106,5 +111,4 @@ export {
|
||||
PageTOCPopoverContent,
|
||||
type FooterProps,
|
||||
type BreadcrumbProps,
|
||||
type RootProps,
|
||||
};
|
||||
|
||||
+17
-11
@@ -135,13 +135,12 @@ export function DocsPage({
|
||||
article,
|
||||
children,
|
||||
}: DocsPageProps) {
|
||||
const isTocRequired =
|
||||
toc.length > 0 ||
|
||||
tocOptions.footer !== undefined ||
|
||||
tocOptions.header !== undefined;
|
||||
|
||||
// disable TOC on full mode, you can still enable it with `enabled` option.
|
||||
tocEnabled ??= !full && isTocRequired;
|
||||
tocEnabled ??=
|
||||
!full &&
|
||||
(toc.length > 0 ||
|
||||
tocOptions.footer !== undefined ||
|
||||
tocOptions.header !== undefined);
|
||||
|
||||
tocPopoverEnabled ??=
|
||||
toc.length > 0 ||
|
||||
@@ -150,11 +149,19 @@ export function DocsPage({
|
||||
|
||||
return (
|
||||
<PageRoot
|
||||
toc={{
|
||||
toc,
|
||||
single: tocOptions.single,
|
||||
}}
|
||||
toc={
|
||||
tocEnabled || tocPopoverEnabled
|
||||
? {
|
||||
toc,
|
||||
single: tocOptions.single,
|
||||
}
|
||||
: false
|
||||
}
|
||||
{...container}
|
||||
className={cn(
|
||||
!tocEnabled && '[--fd-toc-width:0px]',
|
||||
container?.className,
|
||||
)}
|
||||
>
|
||||
{tocPopoverEnabled &&
|
||||
(tocPopover ?? (
|
||||
@@ -171,7 +178,6 @@ export function DocsPage({
|
||||
{breadcrumbEnabled &&
|
||||
(breadcrumb ?? <PageBreadcrumb {...breadcrumbProps} />)}
|
||||
{children}
|
||||
<div role="none" className="flex-1" />
|
||||
<div className="flex flex-row flex-wrap items-center justify-between gap-4 empty:hidden">
|
||||
{editOnGithub && (
|
||||
<EditOnGitHub
|
||||
|
||||
Reference in New Issue
Block a user