mirror of
https://github.com/hanzo-docs/docs.git
synced 2026-10-25 04:20:02 +00:00
UI: Support tabMode on <DocsLayout />
This commit is contained in:
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'fumadocs-ui': patch
|
||||
---
|
||||
|
||||
Support `tabMode` on `<DocsLayout />`
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'fumadocs-core': patch
|
||||
---
|
||||
|
||||
Support Negotiation API
|
||||
@@ -0,0 +1 @@
|
||||
export * from './vite';
|
||||
@@ -1,4 +1,4 @@
|
||||
import { PluginOption } from 'vite';
|
||||
import type { PluginOption } from 'vite';
|
||||
import rsc from '@vitejs/plugin-rsc';
|
||||
import tailwindcss from '@tailwindcss/vite';
|
||||
import { unstable_reactRouterRSC as reactRouterRSC } from '@react-router/dev/vite';
|
||||
|
||||
@@ -4,5 +4,5 @@ export default defineConfig({
|
||||
dts: true,
|
||||
target: 'es2022',
|
||||
format: 'esm',
|
||||
entry: [],
|
||||
entry: ['src/index.ts'],
|
||||
});
|
||||
|
||||
@@ -1,13 +1,17 @@
|
||||
'use client';
|
||||
|
||||
import { Sidebar as SidebarIcon } from 'lucide-react';
|
||||
import { type ComponentProps } from 'react';
|
||||
import { type ComponentProps, useMemo } from 'react';
|
||||
import { cn } from '@/utils/cn';
|
||||
import { buttonVariants } from '@/components/ui/button';
|
||||
import { useSidebar } from '@/contexts/sidebar';
|
||||
import { useNav } from '@/contexts/layout';
|
||||
import { SidebarCollapseTrigger } from '@/components/layout/sidebar';
|
||||
import { SearchToggle } from '@/components/layout/search-toggle';
|
||||
import type { Option } from '@/components/layout/root-toggle';
|
||||
import { usePathname } from 'fumadocs-core/framework';
|
||||
import { isTabActive } from '@/utils/is-active';
|
||||
import Link from 'fumadocs-core/link';
|
||||
|
||||
export function Navbar(props: ComponentProps<'header'>) {
|
||||
const { isTransparent } = useNav();
|
||||
@@ -79,3 +83,56 @@ export function CollapsibleControl() {
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export function LayoutTabs({
|
||||
options,
|
||||
...props
|
||||
}: ComponentProps<'div'> & {
|
||||
options: Option[];
|
||||
}) {
|
||||
const pathname = usePathname();
|
||||
const selected = useMemo(() => {
|
||||
return options.findLast((option) => isTabActive(option, pathname));
|
||||
}, [options, pathname]);
|
||||
|
||||
return (
|
||||
<div
|
||||
{...props}
|
||||
className={cn(
|
||||
'flex flex-row items-end gap-6 overflow-auto',
|
||||
props.className,
|
||||
)}
|
||||
>
|
||||
{options.map((option) => (
|
||||
<LayoutTab
|
||||
key={option.url}
|
||||
selected={selected === option}
|
||||
option={option}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function LayoutTab({
|
||||
option: { title, url, unlisted, props },
|
||||
selected = false,
|
||||
}: {
|
||||
option: Option;
|
||||
selected?: boolean;
|
||||
}) {
|
||||
return (
|
||||
<Link
|
||||
href={url}
|
||||
{...props}
|
||||
className={cn(
|
||||
'inline-flex border-b-2 border-transparent transition-colors items-center pb-1.5 font-medium gap-2 text-fd-muted-foreground text-sm text-nowrap hover:text-fd-accent-foreground',
|
||||
unlisted && !selected && 'hidden',
|
||||
selected && 'border-fd-primary text-fd-primary',
|
||||
props?.className,
|
||||
)}
|
||||
>
|
||||
{title}
|
||||
</Link>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -38,7 +38,12 @@ import {
|
||||
LanguageToggle,
|
||||
LanguageToggleText,
|
||||
} from '@/components/layout/language-toggle';
|
||||
import { CollapsibleControl, LayoutBody, Navbar } from '@/layouts/docs/client';
|
||||
import {
|
||||
CollapsibleControl,
|
||||
LayoutBody,
|
||||
LayoutTabs,
|
||||
Navbar,
|
||||
} from '@/layouts/docs/client';
|
||||
import { TreeContextProvider } from '@/contexts/tree';
|
||||
import { ThemeToggle } from '@/components/layout/theme-toggle';
|
||||
import { NavProvider } from '@/contexts/layout';
|
||||
@@ -58,6 +63,8 @@ export interface DocsLayoutProps extends BaseLayoutProps {
|
||||
|
||||
sidebar?: SidebarOptions;
|
||||
|
||||
tabMode?: 'top' | 'auto';
|
||||
|
||||
/**
|
||||
* Props for the `div` container
|
||||
*/
|
||||
@@ -95,8 +102,8 @@ export function DocsLayout({
|
||||
...sidebarProps
|
||||
} = {},
|
||||
searchToggle = {},
|
||||
disableThemeSwitch = false,
|
||||
themeSwitch = { enabled: !disableThemeSwitch },
|
||||
themeSwitch = {},
|
||||
tabMode = 'auto',
|
||||
i18n = false,
|
||||
children,
|
||||
...props
|
||||
@@ -231,8 +238,9 @@ export function DocsLayout({
|
||||
(searchToggle.components?.lg ?? (
|
||||
<LargeSearchToggle hideIfDisabled />
|
||||
))}
|
||||
{tabs.length > 0 && <RootToggle options={tabs} />}
|
||||
|
||||
{tabs.length > 0 && tabMode === 'auto' && (
|
||||
<RootToggle options={tabs} />
|
||||
)}
|
||||
{banner}
|
||||
</SidebarHeader>
|
||||
{viewport}
|
||||
@@ -321,6 +329,12 @@ export function DocsLayout({
|
||||
)}
|
||||
>
|
||||
{sidebarEnabled && sidebar()}
|
||||
{tabMode === 'top' && tabs.length > 0 && (
|
||||
<LayoutTabs
|
||||
options={tabs}
|
||||
className="sticky top-[calc(var(--fd-nav-height)+var(--fd-tocnav-height))] z-10 bg-fd-background border-b px-6 pt-3 xl:px-8 max-md:hidden"
|
||||
/>
|
||||
)}
|
||||
{children}
|
||||
</LayoutBody>
|
||||
</NavProvider>
|
||||
|
||||
@@ -106,8 +106,7 @@ export function DocsLayout(props: DocsLayoutProps) {
|
||||
nav: { transparentMode, ...nav } = {},
|
||||
sidebar: { tabs: tabOptions, ...sidebarProps } = {},
|
||||
i18n = false,
|
||||
disableThemeSwitch = false,
|
||||
themeSwitch = { enabled: !disableThemeSwitch },
|
||||
themeSwitch = {},
|
||||
} = props;
|
||||
|
||||
const navMode = nav.mode ?? 'auto';
|
||||
|
||||
@@ -32,13 +32,6 @@ export interface BaseLayoutProps {
|
||||
}>;
|
||||
}>;
|
||||
|
||||
/**
|
||||
* Remove theme switcher component
|
||||
*
|
||||
* @deprecated Use `themeSwitch.enabled` instead.
|
||||
*/
|
||||
disableThemeSwitch?: boolean;
|
||||
|
||||
/**
|
||||
* I18n options
|
||||
*
|
||||
|
||||
Reference in New Issue
Block a user