|
1 | | -'use client'; |
2 | | -import { cn } from '../../../lib/cn'; |
3 | | -import { type ComponentProps, useMemo } from 'react'; |
4 | | -import { useSidebar } from 'fumadocs-ui/contexts/sidebar'; |
5 | | -import { useNav } from 'fumadocs-ui/contexts/layout'; |
6 | | -import { buttonVariants } from '../../ui/button'; |
7 | | -import { Sidebar as SidebarIcon } from 'lucide-react'; |
8 | | -import Link from 'fumadocs-core/link'; |
9 | | -import { usePathname } from 'fumadocs-core/framework'; |
10 | | -import { isTabActive } from '../../../lib/is-active'; |
11 | | -import type { Option } from '../../root-toggle'; |
| 1 | +"use client"; |
| 2 | +import { usePathname } from "fumadocs-core/framework"; |
| 3 | +import Link from "fumadocs-core/link"; |
| 4 | +import { useNav } from "fumadocs-ui/contexts/layout"; |
| 5 | +import { useSidebar } from "fumadocs-ui/contexts/sidebar"; |
| 6 | +import { Sidebar as SidebarIcon } from "lucide-react"; |
| 7 | +import { type ComponentProps, useId, useMemo } from "react"; |
| 8 | +import { cn } from "../../../lib/cn"; |
| 9 | +import { isTabActive } from "../../../lib/is-active"; |
| 10 | +import type { Option } from "../../root-toggle"; |
| 11 | +import { buttonVariants } from "../../ui/button"; |
12 | 12 |
|
13 | 13 | export function Navbar({ |
14 | | - mode, |
15 | | - ...props |
16 | | -}: ComponentProps<'header'> & { mode: 'top' | 'auto' }) { |
17 | | - const { open, collapsed } = useSidebar(); |
18 | | - const { isTransparent } = useNav(); |
| 14 | + mode, |
| 15 | + ...props |
| 16 | +}: ComponentProps<"header"> & { mode: "top" | "auto" }) { |
| 17 | + const { open, collapsed } = useSidebar(); |
| 18 | + const { isTransparent } = useNav(); |
| 19 | + const subnavId = useId(); |
19 | 20 |
|
20 | | - return ( |
21 | | - <header |
22 | | - id="nd-subnav" |
23 | | - {...props} |
24 | | - className={cn( |
25 | | - 'fixed flex flex-col top-(--fd-banner-height) left-0 right-(--removed-body-scroll-bar-size,0) z-10 px-(--fd-layout-offset) h-(--fd-nav-height) backdrop-blur-sm transition-colors', |
26 | | - (!isTransparent || open) && 'bg-fd-background/80', |
27 | | - mode === 'auto' && |
28 | | - !collapsed && |
29 | | - 'ps-[calc(var(--fd-layout-offset)+var(--fd-sidebar-width))]', |
30 | | - props.className, |
31 | | - )} |
32 | | - > |
33 | | - {props.children} |
34 | | - </header> |
35 | | - ); |
| 21 | + return ( |
| 22 | + <header |
| 23 | + id={subnavId} |
| 24 | + {...props} |
| 25 | + className={cn( |
| 26 | + "fixed flex flex-col top-(--fd-banner-height) left-0 right-(--removed-body-scroll-bar-size,0) z-10 px-(--fd-layout-offset) h-(--fd-nav-height) backdrop-blur-sm transition-colors", |
| 27 | + (!isTransparent || open) && "bg-fd-background/80", |
| 28 | + mode === "auto" && |
| 29 | + !collapsed && |
| 30 | + "ps-[calc(var(--fd-layout-offset)+var(--fd-sidebar-width))]", |
| 31 | + props.className, |
| 32 | + )} |
| 33 | + > |
| 34 | + {props.children} |
| 35 | + </header> |
| 36 | + ); |
36 | 37 | } |
37 | 38 |
|
38 | | -export function LayoutBody(props: ComponentProps<'main'>) { |
39 | | - const { collapsed } = useSidebar(); |
| 39 | +export function LayoutBody(props: ComponentProps<"main">) { |
| 40 | + const { collapsed } = useSidebar(); |
| 41 | + const layoutId = useId(); |
40 | 42 |
|
41 | | - return ( |
42 | | - <main |
43 | | - id="nd-docs-layout" |
44 | | - {...props} |
45 | | - className={cn( |
46 | | - 'flex flex-1 flex-col transition-[padding] pt-(--fd-nav-height) fd-notebook-layout', |
47 | | - !collapsed && 'mx-(--fd-layout-offset)', |
48 | | - props.className, |
49 | | - )} |
50 | | - style={{ |
51 | | - ...props.style, |
52 | | - paddingInlineStart: collapsed |
53 | | - ? 'min(calc(100vw - var(--fd-page-width)), var(--fd-sidebar-width))' |
54 | | - : 'var(--fd-sidebar-width)', |
55 | | - }} |
56 | | - > |
57 | | - {props.children} |
58 | | - </main> |
59 | | - ); |
| 43 | + return ( |
| 44 | + <main |
| 45 | + id={layoutId} |
| 46 | + {...props} |
| 47 | + className={cn( |
| 48 | + "flex flex-1 flex-col transition-[padding] pt-(--fd-nav-height) fd-notebook-layout", |
| 49 | + !collapsed && "mx-(--fd-layout-offset)", |
| 50 | + props.className, |
| 51 | + )} |
| 52 | + style={{ |
| 53 | + ...props.style, |
| 54 | + paddingInlineStart: collapsed |
| 55 | + ? "min(calc(100vw - var(--fd-page-width)), var(--fd-sidebar-width))" |
| 56 | + : "var(--fd-sidebar-width)", |
| 57 | + }} |
| 58 | + > |
| 59 | + {props.children} |
| 60 | + </main> |
| 61 | + ); |
60 | 62 | } |
61 | 63 |
|
62 | 64 | export function NavbarSidebarTrigger({ |
63 | | - className, |
64 | | - ...props |
65 | | -}: ComponentProps<'button'>) { |
66 | | - const { setOpen } = useSidebar(); |
| 65 | + className, |
| 66 | + ...props |
| 67 | +}: ComponentProps<"button">) { |
| 68 | + const { setOpen } = useSidebar(); |
67 | 69 |
|
68 | | - return ( |
69 | | - <button |
70 | | - {...props} |
71 | | - className={cn( |
72 | | - buttonVariants({ |
73 | | - color: 'ghost', |
74 | | - size: 'icon-sm', |
75 | | - className, |
76 | | - }), |
77 | | - )} |
78 | | - onClick={() => setOpen((prev) => !prev)} |
79 | | - > |
80 | | - <SidebarIcon /> |
81 | | - </button> |
82 | | - ); |
| 70 | + return ( |
| 71 | + <button |
| 72 | + {...props} |
| 73 | + className={cn( |
| 74 | + buttonVariants({ |
| 75 | + color: "ghost", |
| 76 | + size: "icon-sm", |
| 77 | + className, |
| 78 | + }), |
| 79 | + )} |
| 80 | + onClick={() => setOpen((prev) => !prev)} |
| 81 | + > |
| 82 | + <SidebarIcon /> |
| 83 | + </button> |
| 84 | + ); |
83 | 85 | } |
84 | 86 |
|
85 | 87 | export function LayoutTabs({ |
86 | | - options, |
87 | | - ...props |
88 | | -}: ComponentProps<'div'> & { |
89 | | - options: Option[]; |
| 88 | + options, |
| 89 | + ...props |
| 90 | +}: ComponentProps<"div"> & { |
| 91 | + options: Option[]; |
90 | 92 | }) { |
91 | | - const pathname = usePathname(); |
92 | | - const selected = useMemo(() => { |
93 | | - return options.findLast((option) => isTabActive(option, pathname)); |
94 | | - }, [options, pathname]); |
| 93 | + const pathname = usePathname(); |
| 94 | + const selected = useMemo(() => { |
| 95 | + return options.findLast((option) => isTabActive(option, pathname)); |
| 96 | + }, [options, pathname]); |
95 | 97 |
|
96 | | - return ( |
97 | | - <div |
98 | | - {...props} |
99 | | - className={cn( |
100 | | - 'flex flex-row items-end gap-6 overflow-auto', |
101 | | - props.className, |
102 | | - )} |
103 | | - > |
104 | | - {options.map((option) => ( |
105 | | - <LayoutTab |
106 | | - key={option.url} |
107 | | - selected={selected === option} |
108 | | - option={option} |
109 | | - /> |
110 | | - ))} |
111 | | - </div> |
112 | | - ); |
| 98 | + return ( |
| 99 | + <div |
| 100 | + {...props} |
| 101 | + className={cn( |
| 102 | + "flex flex-row items-end gap-6 overflow-auto", |
| 103 | + props.className, |
| 104 | + )} |
| 105 | + > |
| 106 | + {options.map((option) => ( |
| 107 | + <LayoutTab |
| 108 | + key={option.url} |
| 109 | + selected={selected === option} |
| 110 | + option={option} |
| 111 | + /> |
| 112 | + ))} |
| 113 | + </div> |
| 114 | + ); |
113 | 115 | } |
114 | 116 |
|
115 | 117 | function LayoutTab({ |
116 | | - option: { title, url, unlisted, props }, |
117 | | - selected = false, |
| 118 | + option: { title, url, unlisted, props }, |
| 119 | + selected = false, |
118 | 120 | }: { |
119 | | - option: Option; |
120 | | - selected?: boolean; |
| 121 | + option: Option; |
| 122 | + selected?: boolean; |
121 | 123 | }) { |
122 | | - return ( |
123 | | - <Link |
124 | | - href={url} |
125 | | - {...props} |
126 | | - className={cn( |
127 | | - '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', |
128 | | - unlisted && !selected && 'hidden', |
129 | | - selected && 'border-fd-primary text-fd-primary', |
130 | | - props?.className, |
131 | | - )} |
132 | | - > |
133 | | - {title} |
134 | | - </Link> |
135 | | - ); |
| 124 | + return ( |
| 125 | + <Link |
| 126 | + href={url} |
| 127 | + {...props} |
| 128 | + className={cn( |
| 129 | + "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", |
| 130 | + unlisted && !selected && "hidden", |
| 131 | + selected && "border-fd-primary text-fd-primary", |
| 132 | + props?.className, |
| 133 | + )} |
| 134 | + > |
| 135 | + {title} |
| 136 | + </Link> |
| 137 | + ); |
136 | 138 | } |
0 commit comments