mirror of
https://github.com/hanzo-docs/docs.git
synced 2026-10-26 10:55:57 +00:00
UI: simplify and ship layouts via CLI
This commit is contained in:
Binary file not shown.
|
Before Width: | Height: | Size: 66 KiB |
@@ -4,7 +4,7 @@ import { type HTMLAttributes, type ReactNode, useMemo, useState } from 'react';
|
||||
import Link from 'next/link';
|
||||
import { usePathname } from 'next/navigation';
|
||||
import { cn } from '@/utils/cn';
|
||||
import { isActive } from '@/utils/shared';
|
||||
import { isActive } from '@/utils/is-active';
|
||||
import { useSidebar } from '@/contexts/sidebar';
|
||||
import { Popover, PopoverContent, PopoverTrigger } from '../ui/popover';
|
||||
|
||||
|
||||
@@ -14,6 +14,7 @@ export const registry: Registry = {
|
||||
namespaces: {
|
||||
'': 'components',
|
||||
'../utils': 'lib',
|
||||
'../layouts': 'components',
|
||||
},
|
||||
components: [
|
||||
{
|
||||
@@ -27,13 +28,13 @@ export const registry: Registry = {
|
||||
mapImportPath: contextsMap,
|
||||
},
|
||||
{
|
||||
name: 'layout/sidebar',
|
||||
files: ['layout/sidebar.tsx'],
|
||||
name: 'layouts/docs',
|
||||
files: ['../layouts/docs.tsx'],
|
||||
mapImportPath: contextsMap,
|
||||
},
|
||||
{
|
||||
name: 'layout/dynamic-sidebar',
|
||||
files: ['layout/dynamic-sidebar.tsx'],
|
||||
name: 'layouts/home',
|
||||
files: ['../layouts/home.tsx'],
|
||||
mapImportPath: contextsMap,
|
||||
},
|
||||
{ name: 'accordion', files: ['accordion.tsx'] },
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
'use client';
|
||||
import { createContext, useContext } from 'react';
|
||||
|
||||
export interface Translations {
|
||||
@@ -42,6 +43,12 @@ export const I18nContext = createContext<I18nContextType>({
|
||||
},
|
||||
});
|
||||
|
||||
export function I18nLabel(props: { label: keyof Translations }): string {
|
||||
const { text } = useI18n();
|
||||
|
||||
return text[props.label];
|
||||
}
|
||||
|
||||
export function useI18n(): I18nContextType {
|
||||
return useContext(I18nContext);
|
||||
}
|
||||
|
||||
@@ -76,10 +76,4 @@ export function I18nProvider({
|
||||
);
|
||||
}
|
||||
|
||||
export function I18nLabel(props: { label: keyof Translations }): string {
|
||||
const { text } = useI18n();
|
||||
|
||||
return text[props.label];
|
||||
}
|
||||
|
||||
export { type Translations };
|
||||
|
||||
@@ -113,25 +113,6 @@ export function DocsLayout({
|
||||
tabs = getSidebarTabs(props.tree);
|
||||
}
|
||||
|
||||
const banner = (
|
||||
<>
|
||||
<CustomSidebarHeader {...nav} links={links} />
|
||||
{sidebarBanner}
|
||||
</>
|
||||
);
|
||||
|
||||
const footer = (
|
||||
<>
|
||||
<CustomSidebarFooter
|
||||
sidebarCollapsible={collapsible}
|
||||
i18n={i18n}
|
||||
disableThemeSwitch={props.disableThemeSwitch ?? false}
|
||||
iconItems={links.filter((v) => v.type === 'icon')}
|
||||
/>
|
||||
{sidebarFooter}
|
||||
</>
|
||||
);
|
||||
|
||||
return (
|
||||
<TreeContextProvider tree={props.tree}>
|
||||
<NavProvider transparentMode={transparentMode}>
|
||||
@@ -164,7 +145,8 @@ export function DocsLayout({
|
||||
{ enabled: sidebarEnabled, component: sidebarReplace },
|
||||
<Aside {...sidebar}>
|
||||
<SidebarHeader>
|
||||
{banner}
|
||||
<SidebarHeaderItems {...nav} links={links} />
|
||||
{sidebarBanner}
|
||||
{tabs.length > 0 ? (
|
||||
<RootToggle options={tabs} className="-mx-2" />
|
||||
) : null}
|
||||
@@ -173,7 +155,7 @@ export function DocsLayout({
|
||||
</SearchOnly>
|
||||
</SidebarHeader>
|
||||
<SidebarViewport>
|
||||
<div className="px-4 pt-4 empty:hidden md:hidden">
|
||||
<div className="px-2 pt-4 empty:hidden md:hidden">
|
||||
{links
|
||||
.filter((v) => v.type !== 'icon')
|
||||
.map((item, i) => (
|
||||
@@ -182,13 +164,19 @@ export function DocsLayout({
|
||||
</div>
|
||||
<SidebarItems components={sidebarComponents} />
|
||||
</SidebarViewport>
|
||||
<SidebarFooter>{footer}</SidebarFooter>
|
||||
<SidebarFooter>
|
||||
<SidebarFooterItems
|
||||
sidebarCollapsible={collapsible}
|
||||
i18n={i18n}
|
||||
disableThemeSwitch={props.disableThemeSwitch ?? false}
|
||||
iconItems={links.filter((v) => v.type === 'icon')}
|
||||
/>
|
||||
{sidebarFooter}
|
||||
</SidebarFooter>
|
||||
</Aside>,
|
||||
{
|
||||
...sidebar,
|
||||
tabs,
|
||||
banner,
|
||||
footer,
|
||||
},
|
||||
)}
|
||||
{props.children}
|
||||
@@ -248,7 +236,7 @@ function renderLinkItem(item: LinkItemType): ReactNode {
|
||||
if (item.type === 'custom') return item.children;
|
||||
}
|
||||
|
||||
function CustomSidebarHeader({
|
||||
function SidebarHeaderItems({
|
||||
links,
|
||||
...props
|
||||
}: SharedNavProps & { links: LinkItemType[] }): ReactNode {
|
||||
@@ -284,7 +272,7 @@ function CustomSidebarHeader({
|
||||
);
|
||||
}
|
||||
|
||||
function CustomSidebarFooter({
|
||||
function SidebarFooterItems({
|
||||
iconItems,
|
||||
...props
|
||||
}: {
|
||||
@@ -293,53 +281,33 @@ function CustomSidebarFooter({
|
||||
disableThemeSwitch: boolean;
|
||||
sidebarCollapsible: boolean;
|
||||
}) {
|
||||
const iconItem = cn(
|
||||
buttonVariants({ size: 'icon', color: 'ghost' }),
|
||||
'text-fd-muted-foreground md:hidden',
|
||||
);
|
||||
|
||||
if (props.i18n) {
|
||||
return (
|
||||
<div className="flex flex-row items-center">
|
||||
{iconItems.map((item, i) => (
|
||||
<BaseLinkItem
|
||||
key={i}
|
||||
item={item}
|
||||
className={iconItem}
|
||||
aria-label={item.label}
|
||||
>
|
||||
{item.icon}
|
||||
</BaseLinkItem>
|
||||
))}
|
||||
{!props.disableThemeSwitch ? <ThemeToggle /> : null}
|
||||
<LanguageToggle className="max-md:order-first max-md:me-auto md:ms-auto">
|
||||
<Languages className="size-5" />
|
||||
<LanguageToggleText className="md:hidden" />
|
||||
</LanguageToggle>
|
||||
{props.sidebarCollapsible ? (
|
||||
<SidebarCollapseTrigger className="max-md:hidden" />
|
||||
) : null}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="flex flex-row items-center">
|
||||
{iconItems.map((item, i) => (
|
||||
<BaseLinkItem
|
||||
key={i}
|
||||
item={item}
|
||||
className={iconItem}
|
||||
className={cn(
|
||||
buttonVariants({ size: 'icon', color: 'ghost' }),
|
||||
'text-fd-muted-foreground md:hidden',
|
||||
)}
|
||||
aria-label={item.label}
|
||||
>
|
||||
{item.icon}
|
||||
</BaseLinkItem>
|
||||
))}
|
||||
<div role="separator" className="flex-1" />
|
||||
{props.i18n ? (
|
||||
<LanguageToggle className="me-1.5">
|
||||
<Languages className="size-5" />
|
||||
<LanguageToggleText className="md:hidden" />
|
||||
</LanguageToggle>
|
||||
) : null}
|
||||
{!props.disableThemeSwitch ? (
|
||||
<ThemeToggle className="p-0 max-md:ms-auto" />
|
||||
<ThemeToggle className="p-0 md:order-first" />
|
||||
) : null}
|
||||
{props.sidebarCollapsible ? (
|
||||
<SidebarCollapseTrigger className="-me-1.5 ms-auto max-md:hidden" />
|
||||
<SidebarCollapseTrigger className="-me-1.5 max-md:hidden" />
|
||||
) : null}
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -1,18 +0,0 @@
|
||||
import {
|
||||
Sidebar,
|
||||
SidebarFooter,
|
||||
SidebarHeader,
|
||||
SidebarViewport,
|
||||
} from '@/layouts/docs/sidebar';
|
||||
|
||||
export function Layout() {
|
||||
return (
|
||||
<>
|
||||
<Sidebar>
|
||||
<SidebarHeader></SidebarHeader>
|
||||
<SidebarViewport></SidebarViewport>
|
||||
<SidebarFooter />
|
||||
</Sidebar>
|
||||
</>
|
||||
);
|
||||
}
|
||||
@@ -19,7 +19,7 @@ import Link, { type LinkProps } from 'fumadocs-core/link';
|
||||
import { useOnChange } from 'fumadocs-core/utils/use-on-change';
|
||||
import { cn } from '@/utils/cn';
|
||||
import { ScrollArea, ScrollViewport } from '@/components/ui/scroll-area';
|
||||
import { isActive } from '@/utils/shared';
|
||||
import { isActive } from '@/utils/is-active';
|
||||
import {
|
||||
Collapsible,
|
||||
CollapsibleContent,
|
||||
|
||||
@@ -57,7 +57,7 @@ export function HomeLayout({
|
||||
id="nd-home-layout"
|
||||
{...props}
|
||||
className={cn(
|
||||
'flex flex-1 flex-col pt-[var(--fd-nav-height)] [--fd-nav-height:48px] lg:[--fd-nav-height:56px]',
|
||||
'flex flex-1 flex-col pt-[var(--fd-nav-height)] [--fd-nav-height:56px]',
|
||||
props.className,
|
||||
)}
|
||||
>
|
||||
@@ -126,21 +126,18 @@ export function HomeLayout({
|
||||
/>
|
||||
))}
|
||||
<div className="-ms-1.5 flex flex-row items-center gap-1.5 max-sm:mt-2">
|
||||
{menuItems.filter(isSecondary).map((item, i) => (
|
||||
<MenuLinkItem key={i} item={item} className="-me-1.5" />
|
||||
))}
|
||||
<div role="separator" className="flex-1" />
|
||||
{i18n ? (
|
||||
<LanguageToggle className="me-auto">
|
||||
<LanguageToggle>
|
||||
<Languages className="size-5" />
|
||||
<LanguageToggleText />
|
||||
<ChevronDown className="size-3 text-fd-muted-foreground" />
|
||||
</LanguageToggle>
|
||||
) : null}
|
||||
<div className="flex flex-row items-center empty:hidden">
|
||||
{menuItems.filter(isSecondary).map((item, i) => (
|
||||
<MenuLinkItem key={i} item={item} />
|
||||
))}
|
||||
</div>
|
||||
{!disableThemeSwitch ? (
|
||||
<ThemeToggle className={cn(!i18n && 'ms-auto')} />
|
||||
) : null}
|
||||
{!disableThemeSwitch ? <ThemeToggle /> : null}
|
||||
</div>
|
||||
</NavigationMenuContent>
|
||||
</NavigationMenuItem>
|
||||
|
||||
@@ -45,7 +45,7 @@ export function Navbar(props: HTMLAttributes<HTMLElement>) {
|
||||
props.className,
|
||||
)}
|
||||
>
|
||||
<nav className="flex h-12 w-full flex-row items-center gap-6 px-4">
|
||||
<nav className="flex h-14 w-full flex-row items-center gap-6 px-4 lg:h-12">
|
||||
{props.children}
|
||||
</nav>
|
||||
<NavigationMenuViewport />
|
||||
|
||||
@@ -7,7 +7,7 @@ import {
|
||||
type HTMLAttributes,
|
||||
type ReactNode,
|
||||
} from 'react';
|
||||
import { isActive } from '@/utils/shared';
|
||||
import { isActive } from '@/utils/is-active';
|
||||
|
||||
interface BaseItem {
|
||||
/**
|
||||
|
||||
@@ -30,7 +30,7 @@ import {
|
||||
} from '@/components/layout/toc-popover';
|
||||
import { buttonVariants } from '@/components/ui/button';
|
||||
import { Edit, Text } from 'lucide-react';
|
||||
import { I18nLabel } from '@/i18n';
|
||||
import { I18nLabel } from '@/contexts/i18n';
|
||||
|
||||
const ClerkTOCItems = dynamic(() => import('@/components/layout/toc-clerk'));
|
||||
|
||||
|
||||
Reference in New Issue
Block a user