mirror of
https://github.com/hanzo-docs/docs.git
synced 2026-10-25 04:20:02 +00:00
UI: Support built-in edit on github button
This commit is contained in:
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'fumadocs-ui': patch
|
||||
---
|
||||
|
||||
Support built-in edit on github button
|
||||
@@ -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>
|
||||
|
||||
@@ -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 @@
|
||||
{"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';
|
||||
@@ -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',
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
@@ -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>
|
||||
|
||||
Reference in New Issue
Block a user