Skip to content

Commit 42277f5

Browse files
committed
github button
1 parent 83696fd commit 42277f5

File tree

19 files changed

+1715
-56
lines changed

19 files changed

+1715
-56
lines changed

apps/docs/cli.json

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
{
2+
"aliases": {
3+
"uiDir": "./components/ui",
4+
"componentsDir": "./components",
5+
"blockDir": "./components",
6+
"cssDir": "./styles",
7+
"libDir": "./lib"
8+
},
9+
"baseDir": "src",
10+
"commands": {}
11+
}

apps/docs/package.json

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,18 @@
66
"build": "next build",
77
"dev": "next dev --turbo",
88
"start": "next start",
9-
"postinstall": "fumadocs-mdx"
9+
"postinstall": "fumadocs-mdx",
10+
"check": "tsc --noEmit"
1011
},
1112
"dependencies": {
1213
"@gsap/react": "^2.1.2",
1314
"@radix-ui/react-accordion": "^1.2.12",
1415
"@radix-ui/react-collapsible": "^1.1.12",
1516
"@radix-ui/react-label": "^2.1.8",
17+
"@radix-ui/react-navigation-menu": "^1.2.14",
1618
"@radix-ui/react-popover": "^1.1.15",
19+
"@radix-ui/react-presence": "^1.1.5",
20+
"@radix-ui/react-scroll-area": "^1.2.10",
1721
"@radix-ui/react-separator": "^1.1.8",
1822
"@radix-ui/react-slot": "^1.2.4",
1923
"@radix-ui/react-tabs": "^1.1.13",

apps/docs/src/app/(home)/layout.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { HomeLayout } from "fumadocs-ui/layouts/home";
1+
import { HomeLayout } from "@/components/layout/home";
22
import { baseOptions } from "@/lib/layout.shared";
33

44
export default function Layout({ children }: LayoutProps<"/">) {
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
'use client';
2+
import { type ButtonHTMLAttributes, type HTMLAttributes } from 'react';
3+
import { useI18n } from 'fumadocs-ui/contexts/i18n';
4+
import {
5+
Popover,
6+
PopoverContent,
7+
PopoverTrigger,
8+
} from './ui/popover';
9+
import { cn } from '../lib/cn';
10+
import { buttonVariants } from './ui/button';
11+
12+
export type LanguageSelectProps = ButtonHTMLAttributes<HTMLButtonElement>;
13+
14+
export function LanguageToggle(props: LanguageSelectProps): React.ReactElement {
15+
const context = useI18n();
16+
if (!context.locales) throw new Error('Missing `<I18nProvider />`');
17+
18+
return (
19+
<Popover>
20+
<PopoverTrigger
21+
aria-label={context.text.chooseLanguage}
22+
{...props}
23+
className={cn(
24+
buttonVariants({
25+
variant: 'ghost',
26+
className: 'gap-1.5 p-1.5',
27+
}),
28+
props.className,
29+
)}
30+
>
31+
{props.children}
32+
</PopoverTrigger>
33+
<PopoverContent className="flex flex-col overflow-x-hidden p-0">
34+
<p className="mb-1 p-2 text-xs font-medium text-fd-muted-foreground">
35+
{context.text.chooseLanguage}
36+
</p>
37+
{context.locales.map((item) => (
38+
<button
39+
key={item.locale}
40+
type="button"
41+
className={cn(
42+
'p-2 text-start text-sm',
43+
item.locale === context.locale
44+
? 'bg-fd-primary/10 font-medium text-fd-primary'
45+
: 'hover:bg-fd-accent hover:text-fd-accent-foreground',
46+
)}
47+
onClick={() => {
48+
context.onChange?.(item.locale);
49+
}}
50+
>
51+
{item.name}
52+
</button>
53+
))}
54+
</PopoverContent>
55+
</Popover>
56+
);
57+
}
58+
59+
export function LanguageToggleText(
60+
props: HTMLAttributes<HTMLSpanElement>,
61+
): React.ReactElement {
62+
const context = useI18n();
63+
const text = context.locales?.find(
64+
(item) => item.locale === context.locale,
65+
)?.name;
66+
67+
return <span {...props}>{text}</span>;
68+
}
Lines changed: 246 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,246 @@
1+
'use client';
2+
import { type ComponentProps, Fragment, useState } from 'react';
3+
import { cva } from 'class-variance-authority';
4+
import Link from 'fumadocs-core/link';
5+
import { cn } from '../../../lib/cn';
6+
import { BaseLinkItem, type LinkItemType } from '../shared/index';
7+
import {
8+
NavigationMenu,
9+
NavigationMenuContent,
10+
NavigationMenuItem,
11+
NavigationMenuLink,
12+
NavigationMenuList,
13+
NavigationMenuTrigger,
14+
NavigationMenuViewport,
15+
} from '../../navigation-menu';
16+
import { useNav } from 'fumadocs-ui/contexts/layout';
17+
import { buttonVariants } from '../../ui/button';
18+
19+
export const navItemVariants = cva('[&_svg]:size-4', {
20+
variants: {
21+
variant: {
22+
main: 'inline-flex items-center gap-1 p-2 text-fd-muted-foreground transition-colors hover:text-fd-accent-foreground data-[active=true]:text-fd-primary',
23+
button: buttonVariants({
24+
variant: 'secondary',
25+
className: 'gap-1.5',
26+
}),
27+
icon: buttonVariants({
28+
variant: 'ghost',
29+
size: 'icon',
30+
}),
31+
},
32+
},
33+
defaultVariants: {
34+
variant: 'main',
35+
},
36+
});
37+
38+
export function Navbar(props: ComponentProps<'div'>) {
39+
const [value, setValue] = useState('');
40+
const { isTransparent } = useNav();
41+
42+
return (
43+
<NavigationMenu value={value} onValueChange={setValue} asChild>
44+
<header
45+
id="nd-nav"
46+
{...props}
47+
className={cn(
48+
'fixed top-(--fd-banner-height) z-40 left-0 right-(--removed-body-scroll-bar-size,0) backdrop-blur-lg border-b transition-colors *:mx-auto *:max-w-fd-container',
49+
value.length > 0 && 'max-lg:shadow-lg max-lg:rounded-b-2xl',
50+
(!isTransparent || value.length > 0) && 'bg-fd-background/80',
51+
props.className,
52+
)}
53+
>
54+
<NavigationMenuList
55+
className="flex h-14 w-full items-center px-4"
56+
asChild
57+
>
58+
<nav>{props.children}</nav>
59+
</NavigationMenuList>
60+
61+
<NavigationMenuViewport />
62+
</header>
63+
</NavigationMenu>
64+
);
65+
}
66+
67+
export { NavigationMenuItem };
68+
69+
export function NavigationMenuLinkItem({
70+
item,
71+
...props
72+
}: {
73+
item: LinkItemType;
74+
className?: string;
75+
}) {
76+
if (item.type === 'custom') return <div {...props}>{item.children}</div>;
77+
78+
if (item.type === 'menu') {
79+
const children = item.items.map((child, j) => {
80+
if (child.type === 'custom') {
81+
return <Fragment key={j}>{child.children}</Fragment>;
82+
}
83+
84+
const {
85+
banner = child.icon ? (
86+
<div className="w-fit rounded-md border bg-fd-muted p-1 [&_svg]:size-4">
87+
{child.icon}
88+
</div>
89+
) : null,
90+
...rest
91+
} = child.menu ?? {};
92+
93+
return (
94+
<NavigationMenuLink key={`${j}-${child.url}`} asChild>
95+
<Link
96+
href={child.url}
97+
external={child.external}
98+
{...rest}
99+
className={cn(
100+
'flex flex-col gap-2 rounded-lg border bg-fd-card p-3 transition-colors hover:bg-fd-accent/80 hover:text-fd-accent-foreground',
101+
rest.className,
102+
)}
103+
>
104+
{rest.children ?? (
105+
<>
106+
{banner}
107+
<p className="text-base font-medium">{child.text}</p>
108+
<p className="text-sm text-fd-muted-foreground empty:hidden">
109+
{child.description}
110+
</p>
111+
</>
112+
)}
113+
</Link>
114+
</NavigationMenuLink>
115+
);
116+
});
117+
118+
return (
119+
<NavigationMenuItem {...props}>
120+
<NavigationMenuTrigger className={cn(navItemVariants(), 'rounded-md')}>
121+
{item.url ? (
122+
<Link href={item.url} external={item.external}>
123+
{item.text}
124+
</Link>
125+
) : (
126+
item.text
127+
)}
128+
</NavigationMenuTrigger>
129+
<NavigationMenuContent className="grid grid-cols-1 gap-2 p-4 md:grid-cols-2 lg:grid-cols-3">
130+
{children}
131+
</NavigationMenuContent>
132+
</NavigationMenuItem>
133+
);
134+
}
135+
136+
return (
137+
<NavigationMenuItem {...props}>
138+
<NavigationMenuLink asChild>
139+
<BaseLinkItem
140+
item={item}
141+
aria-label={item.type === 'icon' ? item.label : undefined}
142+
className={cn(navItemVariants({ variant: item.type }))}
143+
>
144+
{item.type === 'icon' ? item.icon : item.text}
145+
</BaseLinkItem>
146+
</NavigationMenuLink>
147+
</NavigationMenuItem>
148+
);
149+
}
150+
151+
export function MobileNavigationMenuLinkItem({
152+
item,
153+
...props
154+
}: {
155+
item: LinkItemType;
156+
className?: string;
157+
}) {
158+
if (item.type === 'custom')
159+
return <div className={cn('grid', props.className)}>{item.children}</div>;
160+
161+
if (item.type === 'menu') {
162+
const header = (
163+
<>
164+
{item.icon}
165+
{item.text}
166+
</>
167+
);
168+
169+
return (
170+
<div className={cn('mb-4 flex flex-col', props.className)}>
171+
<p className="mb-1 text-sm text-fd-muted-foreground">
172+
{item.url ? (
173+
<NavigationMenuLink asChild>
174+
<Link href={item.url} external={item.external}>
175+
{header}
176+
</Link>
177+
</NavigationMenuLink>
178+
) : (
179+
header
180+
)}
181+
</p>
182+
{item.items.map((child, i) => (
183+
<MobileNavigationMenuLinkItem key={i} item={child} />
184+
))}
185+
</div>
186+
);
187+
}
188+
189+
return (
190+
<NavigationMenuLink asChild>
191+
<BaseLinkItem
192+
item={item}
193+
className={cn(
194+
{
195+
main: 'inline-flex items-center gap-2 py-1.5 transition-colors hover:text-fd-popover-foreground/50 data-[active=true]:font-medium data-[active=true]:text-fd-primary [&_svg]:size-4',
196+
icon: buttonVariants({
197+
size: 'icon',
198+
variant: 'ghost',
199+
}),
200+
button: buttonVariants({
201+
variant: 'secondary',
202+
className: 'gap-1.5 [&_svg]:size-4',
203+
}),
204+
}[item.type ?? 'main'],
205+
props.className,
206+
)}
207+
aria-label={item.type === 'icon' ? item.label : undefined}
208+
>
209+
{item.icon}
210+
{item.type === 'icon' ? undefined : item.text}
211+
</BaseLinkItem>
212+
</NavigationMenuLink>
213+
);
214+
}
215+
216+
export function MobileNavigationMenuTrigger({
217+
enableHover = false,
218+
...props
219+
}: ComponentProps<typeof NavigationMenuTrigger> & {
220+
/**
221+
* Enable hover to trigger
222+
*/
223+
enableHover?: boolean;
224+
}) {
225+
return (
226+
<NavigationMenuTrigger
227+
{...props}
228+
onPointerMove={enableHover ? undefined : (e) => e.preventDefault()}
229+
>
230+
{props.children}
231+
</NavigationMenuTrigger>
232+
);
233+
}
234+
235+
export function MobileNavigationMenuContent(
236+
props: ComponentProps<typeof NavigationMenuContent>,
237+
) {
238+
return (
239+
<NavigationMenuContent
240+
{...props}
241+
className={cn('flex flex-col p-4', props.className)}
242+
>
243+
{props.children}
244+
</NavigationMenuContent>
245+
);
246+
}

0 commit comments

Comments
 (0)