UI: Introduce more pre-built components on page

This commit is contained in:
fuma-nama
2024-08-16 18:21:49 +08:00
parent b43d19699b
commit 8f5b19e869
4 changed files with 94 additions and 40 deletions
+5
View File
@@ -0,0 +1,5 @@
---
'fumadocs-ui': minor
---
Introduce `DocsTitle`, `DocsDescription` and `DocsCategory` components
+12 -31
View File
@@ -1,7 +1,12 @@
import { Edit } from 'lucide-react';
import type { Metadata } from 'next';
import { Card, Cards } from 'fumadocs-ui/components/card';
import { DocsPage, DocsBody } from 'fumadocs-ui/page';
import {
DocsPage,
DocsBody,
DocsTitle,
DocsDescription,
DocsCategory,
} from 'fumadocs-ui/page';
import { notFound } from 'next/navigation';
import { type ReactNode } from 'react';
import { utils, type Page } from '@/utils/source';
@@ -54,13 +59,9 @@ export default function Page({
}}
tableOfContentPopover={{ footer }}
>
<h1 className="text-3xl font-bold text-fd-foreground sm:text-4xl">
{page.data.title}
</h1>
<DocsTitle>{page.data.title}</DocsTitle>
{page.data.description ? (
<p className="mb-8 text-lg text-fd-muted-foreground">
{page.data.description}
</p>
<DocsDescription>{page.data.description}</DocsDescription>
) : null}
<DocsBody>
{preview && preview in Preview ? Preview[preview] : null}
@@ -76,34 +77,14 @@ export default function Page({
: () => undefined,
}}
/>
{page.data.index ? <Category page={page} /> : null}
{page.data.index ? (
<DocsCategory page={page} pages={utils.getPages()} />
) : null}
</DocsBody>
</DocsPage>
);
}
function Category({ page }: { page: Page }): React.ReactElement {
const filtered = utils
.getPages()
.filter(
(item) =>
item.file.dirname === page.file.dirname && item.file.name !== 'index',
);
return (
<Cards>
{filtered.map((item) => (
<Card
key={item.url}
title={item.data.title}
description={item.data.description ?? 'No Description'}
href={item.url}
/>
))}
</Cards>
);
}
export function generateMetadata({ params }: { params: Param }): Metadata {
const page = utils.getPage(params.slug);
@@ -1,9 +1,11 @@
import { ChevronRight } from 'lucide-react';
import { BreadcrumbOptions, useBreadcrumb } from 'fumadocs-core/breadcrumb';
import {
type BreadcrumbOptions,
useBreadcrumb,
} from 'fumadocs-core/breadcrumb';
import Link from 'next/link';
import { usePathname } from 'next/navigation';
import { Fragment } from 'react';
import { cn } from '@/utils/cn';
import { useTreeContext } from '@/contexts/tree';
export interface BreadcrumbProps
@@ -19,7 +21,7 @@ export interface BreadcrumbProps
export function Breadcrumb({
full = false,
...options
}: BreadcrumbProps): React.ReactElement {
}: BreadcrumbProps): React.ReactNode {
const { root } = useTreeContext();
const pathname = usePathname();
const items = useBreadcrumb(pathname, root, {
@@ -27,13 +29,10 @@ export function Breadcrumb({
...options,
});
if (items.length === 0) return null;
return (
<div
className={cn(
'-mb-3 flex flex-row items-center gap-1 text-sm font-medium text-fd-muted-foreground',
items.length === 0 && 'hidden',
)}
>
<div className="-mb-3 flex flex-row items-center gap-1 text-sm font-medium text-fd-muted-foreground">
{items.map((item, i) => (
<Fragment key={i}>
{i !== 0 && (
+69
View File
@@ -1,5 +1,7 @@
import { type TableOfContents } from 'fumadocs-core/server';
import { forwardRef, type HTMLAttributes, type ReactNode } from 'react';
import type { Page } from 'fumadocs-core/source';
import { Card, Cards } from '@/components/card';
import { replaceOrDefault } from './utils/shared';
import { cn } from './utils/cn';
import type { BreadcrumbProps, FooterProps, TOCProps } from './page.client';
@@ -130,6 +132,73 @@ export const DocsBody = forwardRef<
DocsBody.displayName = 'DocsBody';
export const DocsDescription = forwardRef<
HTMLParagraphElement,
HTMLAttributes<HTMLParagraphElement>
>((props, ref) => {
return (
<p
ref={ref}
{...props}
className={cn('mb-8 text-lg text-fd-muted-foreground', props.className)}
>
{props.children}
</p>
);
});
DocsDescription.displayName = 'DocsDescription';
export const DocsTitle = forwardRef<
HTMLHeadingElement,
HTMLAttributes<HTMLHeadingElement>
>((props, ref) => {
return (
<h1
ref={ref}
{...props}
className={cn('text-3xl font-bold sm:text-4xl', props.className)}
>
{props.children}
</h1>
);
});
DocsTitle.displayName = 'DocsTitle';
export function DocsCategory({
page,
pages,
...props
}: HTMLAttributes<HTMLDivElement> & {
page: Page;
pages: Page[];
}): React.ReactElement {
const filtered = pages.filter(
(item) =>
item.file.dirname === page.file.dirname &&
item.file.name !== page.file.name,
);
return (
<Cards {...props}>
{filtered.map((item) => (
<Card
key={item.url}
title={item.data.title}
description={
(item.data as { description?: string }).description ??
'No Description'
}
href={item.url}
/>
))}
</Cards>
);
}
DocsBody.displayName = 'DocsBody';
/**
* For separate MDX page
*/