mirror of
https://github.com/hanzo-docs/docs.git
synced 2026-10-26 06:31:49 +00:00
UI: Show i18n language toggle on home layout
This commit is contained in:
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'fumadocs-ui': minor
|
||||
---
|
||||
|
||||
Show i18n language toggle on home layout
|
||||
@@ -5,4 +5,11 @@ export const baseOptions: BaseLayoutProps = {
|
||||
nav: {
|
||||
title: 'I18n Docs',
|
||||
},
|
||||
links: [
|
||||
{
|
||||
type: 'main',
|
||||
text: 'Documentation',
|
||||
url: '/docs',
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
'use client';
|
||||
import { useState } from 'react';
|
||||
import type { PopoverProps } from '@radix-ui/react-popover';
|
||||
import { LanguagesIcon } from 'lucide-react';
|
||||
import { type ButtonHTMLAttributes, type HTMLAttributes } from 'react';
|
||||
import { useI18n } from '@/contexts/i18n';
|
||||
import {
|
||||
Popover,
|
||||
@@ -11,22 +9,28 @@ import {
|
||||
import { cn } from '@/utils/cn';
|
||||
import { buttonVariants } from '@/components/ui/button';
|
||||
|
||||
export type LanguageSelectProps = Omit<PopoverProps, 'open' | 'onOpenChange'>;
|
||||
export type LanguageSelectProps = ButtonHTMLAttributes<HTMLButtonElement>;
|
||||
|
||||
export function LanguageToggle(props: LanguageSelectProps): React.ReactElement {
|
||||
const context = useI18n();
|
||||
const [open, setOpen] = useState(false);
|
||||
if (!context.locales) throw new Error('Missing `<I18nProvider />`');
|
||||
|
||||
return (
|
||||
<Popover open={open} onOpenChange={setOpen} {...props}>
|
||||
<Popover>
|
||||
<PopoverTrigger
|
||||
aria-label={context.text.chooseLanguage}
|
||||
className={cn(buttonVariants({ size: 'icon', color: 'ghost' }))}
|
||||
{...props}
|
||||
className={cn(
|
||||
buttonVariants({
|
||||
color: 'ghost',
|
||||
className: 'gap-1.5 p-1.5',
|
||||
}),
|
||||
props.className,
|
||||
)}
|
||||
>
|
||||
<LanguagesIcon />
|
||||
{props.children}
|
||||
</PopoverTrigger>
|
||||
<PopoverContent className="flex flex-col p-1">
|
||||
<PopoverContent className="flex flex-col overflow-hidden p-0">
|
||||
<p className="mb-1 p-2 text-xs font-medium text-fd-muted-foreground">
|
||||
{context.text.chooseLanguage}
|
||||
</p>
|
||||
@@ -35,7 +39,7 @@ export function LanguageToggle(props: LanguageSelectProps): React.ReactElement {
|
||||
key={item.locale}
|
||||
type="button"
|
||||
className={cn(
|
||||
'rounded-md p-2 text-left text-sm transition-colors duration-100',
|
||||
'p-2 text-start text-sm',
|
||||
item.locale === context.locale
|
||||
? 'bg-fd-primary/10 font-medium text-fd-primary'
|
||||
: 'hover:bg-fd-accent hover:text-fd-accent-foreground',
|
||||
@@ -51,3 +55,14 @@ export function LanguageToggle(props: LanguageSelectProps): React.ReactElement {
|
||||
</Popover>
|
||||
);
|
||||
}
|
||||
|
||||
export function LanguageToggleText(
|
||||
props: HTMLAttributes<HTMLSpanElement>,
|
||||
): React.ReactElement {
|
||||
const context = useI18n();
|
||||
const text = context.locales?.find(
|
||||
(item) => item.locale === context.locale,
|
||||
)?.name;
|
||||
|
||||
return <span {...props}>{text}</span>;
|
||||
}
|
||||
|
||||
@@ -236,7 +236,7 @@ export function LinksMenu({
|
||||
return (
|
||||
<Popover open={open} onOpenChange={setOpen}>
|
||||
<PopoverTrigger {...props} />
|
||||
<PopoverContent className="flex flex-col">
|
||||
<PopoverContent className="flex flex-col p-1">
|
||||
{items.map((item, i) => (
|
||||
<LinkItem key={i} item={item} on="menu" />
|
||||
))}
|
||||
|
||||
@@ -81,5 +81,8 @@ export { RootToggle } from '@/components/layout/root-toggle';
|
||||
export { Sidebar } from '@/components/layout/sidebar';
|
||||
export { TreeContextProvider } from '@/contexts/tree';
|
||||
export { ThemeToggle } from '@/components/layout/theme-toggle';
|
||||
export { LanguageToggle } from '@/components/layout/language-toggle';
|
||||
export {
|
||||
LanguageToggle,
|
||||
LanguageToggleText,
|
||||
} from '@/components/layout/language-toggle';
|
||||
export { DynamicSidebar } from '@/components/layout/dynamic-sidebar';
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import type { PageTree } from 'fumadocs-core/server';
|
||||
import { ReactNode, HTMLAttributes, Fragment } from 'react';
|
||||
import Link from 'next/link';
|
||||
import { MoreHorizontal } from 'lucide-react';
|
||||
import { Languages, MoreHorizontal } from 'lucide-react';
|
||||
import { notFound } from 'next/navigation';
|
||||
import { cn } from '@/utils/cn';
|
||||
import { buttonVariants } from '@/components/ui/button';
|
||||
@@ -18,6 +18,7 @@ declare const {
|
||||
ThemeToggle,
|
||||
SubNav,
|
||||
LanguageToggle,
|
||||
LanguageToggleText,
|
||||
LinksMenu,
|
||||
RootToggle,
|
||||
LinkItem,
|
||||
@@ -104,7 +105,12 @@ export function DocsLayout({
|
||||
}
|
||||
|
||||
if (i18n) {
|
||||
footer.push(<LanguageToggle key="i18n" />);
|
||||
footer.push(
|
||||
<LanguageToggle key="i18n">
|
||||
<Languages className="size-5" />
|
||||
<LanguageToggleText className="md:hidden" />
|
||||
</LanguageToggle>,
|
||||
);
|
||||
}
|
||||
|
||||
if (links.some((v) => v.type === 'icon')) {
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
'use client';
|
||||
|
||||
import { MoreVertical } from 'lucide-react';
|
||||
import { ChevronDown, Languages, MoreVertical } from 'lucide-react';
|
||||
import { useSearchContext } from '@/contexts/search';
|
||||
import { useI18n } from '@/contexts/i18n';
|
||||
import {
|
||||
LinkItem,
|
||||
type LinkItemType,
|
||||
@@ -16,7 +15,10 @@ import { ThemeToggle } from '@/components/layout/theme-toggle';
|
||||
import { NavBox, Title } from '@/components/layout/nav';
|
||||
import { cn } from '@/utils/cn';
|
||||
import { buttonVariants } from '@/components/ui/button';
|
||||
import { LanguageToggle } from '@/components/layout/language-toggle';
|
||||
import {
|
||||
LanguageToggle,
|
||||
LanguageToggleText,
|
||||
} from '@/components/layout/language-toggle';
|
||||
import type { SharedNavProps } from './shared';
|
||||
|
||||
export function Nav({
|
||||
@@ -29,7 +31,6 @@ export function Nav({
|
||||
items: LinkItemType[];
|
||||
}): React.ReactElement {
|
||||
const search = useSearchContext();
|
||||
const { text } = useI18n();
|
||||
|
||||
return (
|
||||
<NavBox
|
||||
@@ -54,6 +55,11 @@ export function Nav({
|
||||
) : null}
|
||||
|
||||
<ThemeToggle className="max-lg:hidden" />
|
||||
{props.i18n ? (
|
||||
<LanguageToggle className="max-lg:hidden">
|
||||
<Languages className="size-5" />
|
||||
</LanguageToggle>
|
||||
) : null}
|
||||
{items.filter(isSecondary).map((item, i) => (
|
||||
<LinkItem key={i} item={item} className="max-lg:hidden" />
|
||||
))}
|
||||
@@ -68,24 +74,20 @@ export function Nav({
|
||||
}),
|
||||
)}
|
||||
footer={
|
||||
<>
|
||||
{!props.disableThemeSwitch ? (
|
||||
<div className="flex flex-row items-center justify-between px-2 pt-2">
|
||||
<p className="font-medium text-fd-muted-foreground">
|
||||
{text.chooseTheme}
|
||||
</p>
|
||||
<ThemeToggle />
|
||||
</div>
|
||||
) : null}
|
||||
{props.i18n ? (
|
||||
<div className="flex flex-row items-center justify-between px-2 pt-2">
|
||||
<p className="font-medium text-fd-muted-foreground">
|
||||
{text.chooseLanguage}
|
||||
</p>
|
||||
<LanguageToggle />
|
||||
</div>
|
||||
) : null}
|
||||
</>
|
||||
Boolean(props.i18n) || !props.disableThemeSwitch ? (
|
||||
<div className="flex flex-row items-center gap-1.5 px-2 pt-1.5">
|
||||
{props.i18n ? (
|
||||
<LanguageToggle className="-ms-1.5">
|
||||
<Languages className="size-5" />
|
||||
<LanguageToggleText />
|
||||
<ChevronDown className="size-3 text-fd-muted-foreground" />
|
||||
</LanguageToggle>
|
||||
) : null}
|
||||
{!props.disableThemeSwitch ? (
|
||||
<ThemeToggle className="ms-auto" />
|
||||
) : null}
|
||||
</div>
|
||||
) : null
|
||||
}
|
||||
>
|
||||
<MoreVertical />
|
||||
|
||||
Reference in New Issue
Block a user