UI: Support built-in edit on github button

This commit is contained in:
fuma-nama
2024-08-18 23:33:56 +08:00
parent 0e0ef8ceac
commit 17746a6111
7 changed files with 105 additions and 31 deletions
+5
View File
@@ -0,0 +1,5 @@
---
'fumadocs-ui': patch
---
Support built-in edit on github button
+7 -23
View File
@@ -12,8 +12,6 @@ import { Fragment } from 'react';
import { utils } from '@/utils/source';
import { createMetadata } from '@/utils/metadata';
import Preview from '@/components/preview';
import { cn } from '@/utils/cn';
import { buttonVariants } from '@/components/ui/button';
interface Param {
slug: string[];
@@ -31,35 +29,21 @@ export default function Page({
const path = `apps/docs/content/docs/${page.file.path}`;
const preview = page.data.preview;
const footer = (
<a
href={`https://github.com/fuma-nama/fumadocs/blob/main/${path}`}
target="_blank"
rel="noreferrer noopener"
className={cn(
buttonVariants({
variant: 'secondary',
size: 'sm',
className: 'text-xs gap-1.5',
}),
)}
>
<Edit className="size-3" />
Edit on Github
</a>
);
return (
<DocsPage
toc={page.data.exports.toc}
lastUpdate={page.data.exports.lastModified}
full={page.data.full}
tableOfContent={{
footer,
style: 'clerk',
single: true,
single: false,
}}
editOnGithub={{
repo: 'fumadocs',
owner: 'fuma-nama',
sha: 'main',
path,
}}
tableOfContentPopover={{ footer }}
>
<DocsTitle>{page.data.title}</DocsTitle>
<DocsDescription>{page.data.description}</DocsDescription>
+8
View File
@@ -25,6 +25,11 @@ export interface GetGithubLastCommitOptions {
*/
token?: string;
/**
* SHA or ref (branch or tag) name.
*/
sha?: string;
/**
* Custom query parameters
*/
@@ -44,6 +49,7 @@ export async function getGithubLastEdit({
token,
owner,
path,
sha,
options = {},
params: customParams = {},
}: GetGithubLastCommitOptions): Promise<Date | null> {
@@ -53,6 +59,8 @@ export async function getGithubLastEdit({
params.set('page', '1');
params.set('per_page', '1');
if (sha) params.set('sha', sha);
for (const [key, value] of Object.entries(customParams)) {
params.set(key, value);
}
+1 -1
View File
@@ -1 +1 @@
{"fumadocs-core":"13.2.2","fumadocs-ui":"13.2.2","fumadocs-mdx":"9.0.2","@fumadocs/content-collections":"1.1.2"}
{"fumadocs-core":"13.3.1","fumadocs-ui":"13.3.1","fumadocs-mdx":"9.0.2","@fumadocs/content-collections":"1.1.2"}
@@ -0,0 +1,52 @@
'use client';
import { type AnchorHTMLAttributes, forwardRef } from 'react';
import { Edit } from 'lucide-react';
import { cn } from '@/utils/cn';
import { buttonVariants } from '@/theme/variants';
import { useI18n } from '@/contexts/i18n';
export interface EditOnGitHubOptions
extends Omit<AnchorHTMLAttributes<HTMLAnchorElement>, 'href' | 'children'> {
owner: string;
repo: string;
/**
* SHA or ref (branch or tag) name.
*
* @defaultValue main
*/
sha?: string;
/**
* File path in the repo
*/
path: string;
}
export const EditOnGitHub = forwardRef<HTMLAnchorElement, EditOnGitHubOptions>(
({ owner, repo, sha = 'main', path, ...props }, ref) => {
const { text } = useI18n();
return (
<a
ref={ref}
href={`https://github.com/${owner}/${repo}/blob/${sha}/${path.startsWith('/') ? path.slice(1) : path}`}
target="_blank"
rel="noreferrer noopener"
{...props}
className={cn(
buttonVariants({
color: 'secondary',
className: 'text-xs gap-1.5',
}),
props.className,
)}
>
<Edit className="size-3" />
{text.editOnGithub}
</a>
);
},
);
EditOnGitHub.displayName = 'EditOnGitHub';
+2
View File
@@ -12,6 +12,7 @@ export interface Translations {
nextPage: string;
previousPage: string;
chooseTheme: string;
editOnGithub: string;
}
export interface LocaleItem {
@@ -37,6 +38,7 @@ export const I18nContext = createContext<I18nContextType>({
nextPage: 'Next',
previousPage: 'Previous',
chooseTheme: 'Theme',
editOnGithub: 'Edit on GitHub',
},
});
+30 -7
View File
@@ -4,6 +4,7 @@ import dynamic from 'next/dynamic';
import type { Page } from 'fumadocs-core/source';
import { type AnchorProviderProps, AnchorProvider } from 'fumadocs-core/toc';
import { Card, Cards } from '@/components/card';
import type { EditOnGitHubOptions } from '@/components/layout/edit-on-github';
import { replaceOrDefault } from './utils/shared';
import { cn } from './utils/cn';
import type { BreadcrumbProps, FooterProps, TOCProps } from './page.client';
@@ -18,6 +19,9 @@ declare const {
}: typeof import('./page.client');
const ClerkTOCItems = dynamic(() => import('@/components/layout/toc-clerk'));
const EditOnGitHub = dynamic(() =>
import('@/components/layout/edit-on-github').then((mod) => mod.EditOnGitHub),
);
type TableOfContentOptions = Omit<TOCProps, 'items' | 'children'> &
Pick<AnchorProviderProps, 'single'> & {
@@ -65,6 +69,7 @@ export interface DocsPageProps {
*/
footer?: Partial<FooterOptions>;
editOnGithub?: EditOnGitHubOptions;
lastUpdate?: Date | string | number;
children: ReactNode;
@@ -72,22 +77,38 @@ export interface DocsPageProps {
export function DocsPage({
toc = [],
tableOfContent = {},
breadcrumb = {},
tableOfContentPopover: tocPopoverOptions = {},
lastUpdate,
full = false,
footer = {},
...props
}: DocsPageProps): React.ReactElement {
const tocPopoverOptions = {
...props.tableOfContentPopover,
};
const tocOptions = {
// disable TOC on full mode, you can still enable it with `enabled` option.
enabled: tableOfContent.enabled ?? !full,
...tableOfContent,
enabled: props.tableOfContent?.enabled ?? !full,
...props.tableOfContent,
};
if (props.editOnGithub) {
tocOptions.footer = (
<>
{tocOptions.footer}
<EditOnGitHub {...props.editOnGithub} />
</>
);
tocPopoverOptions.footer = (
<>
{tocPopoverOptions.footer}
<EditOnGitHub {...props.editOnGithub} />
</>
);
}
return (
<AnchorProvider toc={toc} single={tableOfContent.single}>
<AnchorProvider toc={toc} single={tocOptions.single}>
<div
className={cn(
'mx-auto flex min-w-0 max-w-[860px] flex-1 flex-col',
@@ -121,7 +142,9 @@ export function DocsPage({
{replaceOrDefault(breadcrumb, <Breadcrumb {...breadcrumb} />)}
{props.children}
<div className="mt-auto" />
{lastUpdate ? <LastUpdate date={new Date(lastUpdate)} /> : null}
{props.lastUpdate ? (
<LastUpdate date={new Date(props.lastUpdate)} />
) : null}
{replaceOrDefault(footer, <Footer items={footer.items} />)}
</article>
</div>