UI: separate UI and docs components

This commit is contained in:
Fuma Nama
2025-12-14 19:04:43 +08:00
parent 9fc62d1376
commit b3dd604cd0
12 changed files with 186 additions and 102 deletions
+3 -3
View File
@@ -1,6 +1,6 @@
import {
build,
combineRegistry,
RegistryCompiler,
writeFumadocsRegistry,
writeShadcnRegistry,
} from '@fumadocs/cli/build';
@@ -9,8 +9,8 @@ import * as ui from '../../../packages/ui/src/_registry';
export async function buildRegistry() {
const [mainRegistry, uiRegistry] = await Promise.all([
build(registry),
build(ui.registry),
new RegistryCompiler(registry).compile(),
new RegistryCompiler(ui.registry).compile(),
]);
const all = combineRegistry(mainRegistry, uiRegistry);
+4
View File
@@ -15,6 +15,10 @@
"./style.css": "./dist/style.css",
"./css/*": "./css/*",
"./image-zoom.css": "./dist/image-zoom.css",
"./components/tabs.unstyled": {
"import": "./dist/components/ui/tabs.js",
"types": "./dist/components/ui/tabs.d.ts"
},
"./components/*": {
"import": "./dist/components/*.js",
"types": "./dist/components/*.d.ts",
+6 -2
View File
@@ -252,6 +252,10 @@ export const registry: Registry = {
type: 'components',
path: 'components/accordion.tsx',
},
{
type: 'ui',
path: 'components/ui/accordion.tsx',
},
],
},
{
@@ -358,8 +362,8 @@ export const registry: Registry = {
path: 'components/tabs.tsx',
},
{
type: 'components',
path: 'components/tabs.unstyled.tsx',
type: 'ui',
path: 'components/ui/tabs.tsx',
},
],
},
+42 -60
View File
@@ -1,14 +1,8 @@
'use client';
import type {
AccordionMultipleProps,
AccordionSingleProps,
} from '@radix-ui/react-accordion';
import * as AccordionPrimitive from '@radix-ui/react-accordion';
import { Check, ChevronRight, Link as LinkIcon } from '@icons';
import { Check, Link as LinkIcon } from '@icons';
import {
type ComponentPropsWithoutRef,
forwardRef,
ComponentProps,
type ReactNode,
useEffect,
useRef,
@@ -18,12 +12,21 @@ import { cn } from '@/utils/cn';
import { useCopyButton } from '@/utils/use-copy-button';
import { buttonVariants } from '@/components/ui/button';
import { mergeRefs } from '@/utils/merge-refs';
import {
Accordion as Root,
AccordionContent,
AccordionHeader,
AccordionItem,
AccordionTrigger,
} from '@/components/ui/accordion';
export const Accordions = forwardRef<
HTMLDivElement,
| Omit<AccordionSingleProps, 'value' | 'onValueChange'>
| Omit<AccordionMultipleProps, 'value' | 'onValueChange'>
>(({ type = 'single', className, defaultValue, ...props }, ref) => {
export function Accordions({
type = 'single',
ref,
className,
defaultValue,
...props
}: ComponentProps<typeof Root>) {
const rootRef = useRef<HTMLDivElement>(null);
const composedRef = mergeRefs(ref, rootRef);
const [value, setValue] = useState<string | string[]>(() =>
@@ -45,7 +48,7 @@ export const Accordions = forwardRef<
return (
// @ts-expect-error -- Multiple types
<AccordionPrimitive.Root
<Root
type={type}
ref={composedRef}
value={value}
@@ -58,51 +61,32 @@ export const Accordions = forwardRef<
{...props}
/>
);
});
}
Accordions.displayName = 'Accordions';
export const Accordion = forwardRef<
HTMLDivElement,
Omit<
ComponentPropsWithoutRef<typeof AccordionPrimitive.Item>,
'value' | 'title'
> & {
title: string | ReactNode;
value?: string;
}
>(
(
{ title, className, id, value = String(title), children, ...props },
ref,
) => {
return (
<AccordionPrimitive.Item
ref={ref}
value={value}
className={cn('scroll-m-24', className)}
{...props}
>
<AccordionPrimitive.Header
id={id}
data-accordion-value={value}
className="not-prose flex flex-row items-center text-fd-card-foreground font-medium has-focus-visible:bg-fd-accent"
>
<AccordionPrimitive.Trigger className="group flex flex-1 items-center gap-2 px-3 py-2.5 text-start focus-visible:outline-none">
<ChevronRight className="size-4 shrink-0 text-fd-muted-foreground transition-transform duration-200 group-data-[state=open]:rotate-90" />
{title}
</AccordionPrimitive.Trigger>
{id ? <CopyButton id={id} /> : null}
</AccordionPrimitive.Header>
<AccordionPrimitive.Content className="overflow-hidden data-[state=closed]:animate-fd-accordion-up data-[state=open]:animate-fd-accordion-down">
<div className="px-4 pb-2 text-[0.9375rem] prose-no-margin">
{children}
</div>
</AccordionPrimitive.Content>
</AccordionPrimitive.Item>
);
},
);
export function Accordion({
title,
id,
value = String(title),
children,
...props
}: Omit<ComponentProps<typeof AccordionItem>, 'value' | 'title'> & {
title: string | ReactNode;
value?: string;
}) {
return (
<AccordionItem value={value} {...props}>
<AccordionHeader id={id} data-accordion-value={value}>
<AccordionTrigger>{title}</AccordionTrigger>
{id ? <CopyButton id={id} /> : null}
</AccordionHeader>
<AccordionContent>
<div className="px-4 pb-2 text-[0.9375rem] prose-no-margin">
{children}
</div>
</AccordionContent>
</AccordionItem>
);
}
function CopyButton({ id }: { id: string }) {
const [checked, onClick] = useCopyButton(() => {
@@ -132,5 +116,3 @@ function CopyButton({ id }: { id: string }) {
</button>
);
}
Accordion.displayName = 'Accordion';
+7 -6
View File
@@ -15,18 +15,19 @@ import {
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 {
ScrollArea,
type ScrollAreaProps,
ScrollViewport,
} from '@/components/ui/scroll-area';
import { isActive } from '@/utils/is-active';
import {
Collapsible,
CollapsibleContent,
type CollapsibleContentProps,
CollapsibleTrigger,
type CollapsibleTriggerProps,
} from '@/components/ui/collapsible';
import { type ScrollAreaProps } from '@radix-ui/react-scroll-area';
import type {
CollapsibleContentProps,
CollapsibleTriggerProps,
} from '@radix-ui/react-collapsible';
import { useMediaQuery } from 'fumadocs-core/utils/use-media-query';
import { Presence } from '@radix-ui/react-presence';
import scrollIntoView from 'scroll-into-view-if-needed';
+1 -1
View File
@@ -12,7 +12,7 @@ import {
useState,
} from 'react';
import { cn } from '@/utils/cn';
import * as Unstyled from './tabs.unstyled';
import * as Unstyled from './ui/tabs';
type CollectionKey = string | symbol;
@@ -0,0 +1,88 @@
'use client';
import * as Primitive from '@radix-ui/react-accordion';
import { ChevronRight } from '@icons';
import { type ComponentProps } from 'react';
import { cn } from '@/utils/cn';
export function Accordion({
className,
...props
}: ComponentProps<typeof Primitive.Root>) {
return (
<Primitive.Root
className={cn(
'divide-y divide-fd-border overflow-hidden rounded-lg border bg-fd-card',
className,
)}
{...props}
/>
);
}
export function AccordionItem({
className,
children,
...props
}: ComponentProps<typeof Primitive.Item>) {
return (
<Primitive.Item className={cn('scroll-m-24', className)} {...props}>
{children}
</Primitive.Item>
);
}
export function AccordionHeader({
className,
children,
...props
}: ComponentProps<typeof Primitive.Header>) {
return (
<Primitive.Header
className={cn(
'not-prose flex flex-row items-center text-fd-card-foreground font-medium has-focus-visible:bg-fd-accent',
className,
)}
{...props}
>
{children}
</Primitive.Header>
);
}
export function AccordionTrigger({
className,
children,
...props
}: ComponentProps<typeof Primitive.Trigger>) {
return (
<Primitive.Trigger
className={cn(
'group flex flex-1 items-center gap-2 px-3 py-2.5 text-start focus-visible:outline-none',
className,
)}
{...props}
>
<ChevronRight className="size-4 shrink-0 text-fd-muted-foreground transition-transform duration-200 group-data-[state=open]:rotate-90" />
{children}
</Primitive.Trigger>
);
}
export function AccordionContent({
className,
children,
...props
}: ComponentProps<typeof Primitive.Content>) {
return (
<Primitive.Content
className={cn(
'overflow-hidden data-[state=closed]:animate-fd-accordion-up data-[state=open]:animate-fd-accordion-down',
className,
)}
{...props}
>
{children}
</Primitive.Content>
);
}
+11 -8
View File
@@ -1,15 +1,15 @@
'use client';
import * as CollapsiblePrimitive from '@radix-ui/react-collapsible';
import * as Primitive from '@radix-ui/react-collapsible';
import { forwardRef, useEffect, useState } from 'react';
import { cn } from '@/utils/cn';
const Collapsible = CollapsiblePrimitive.Root;
const Collapsible = Primitive.Root;
const CollapsibleTrigger = CollapsiblePrimitive.CollapsibleTrigger;
const CollapsibleTrigger = Primitive.CollapsibleTrigger;
const CollapsibleContent = forwardRef<
HTMLDivElement,
React.ComponentPropsWithoutRef<typeof CollapsiblePrimitive.CollapsibleContent>
React.ComponentPropsWithoutRef<typeof Primitive.CollapsibleContent>
>(({ children, ...props }, ref) => {
const [mounted, setMounted] = useState(false);
@@ -18,7 +18,7 @@ const CollapsibleContent = forwardRef<
}, []);
return (
<CollapsiblePrimitive.CollapsibleContent
<Primitive.CollapsibleContent
ref={ref}
{...props}
className={cn(
@@ -29,11 +29,14 @@ const CollapsibleContent = forwardRef<
)}
>
{children}
</CollapsiblePrimitive.CollapsibleContent>
</Primitive.CollapsibleContent>
);
});
CollapsibleContent.displayName =
CollapsiblePrimitive.CollapsibleContent.displayName;
CollapsibleContent.displayName = Primitive.CollapsibleContent.displayName;
export { Collapsible, CollapsibleTrigger, CollapsibleContent };
export type CollapsibleProps = Primitive.CollapsibleProps;
export type CollapsibleContentProps = Primitive.CollapsibleContentProps;
export type CollapsibleTriggerProps = Primitive.CollapsibleTriggerProps;
@@ -3,6 +3,9 @@ import * as React from 'react';
import * as Primitive from '@radix-ui/react-navigation-menu';
import { cn } from '@/utils/cn';
export type NavigationMenuContentProps = Primitive.NavigationMenuContentProps;
export type NavigationMenuTriggerProps = Primitive.NavigationMenuTriggerProps;
const NavigationMenu = Primitive.Root;
const NavigationMenuList = Primitive.List;
+19 -18
View File
@@ -1,45 +1,45 @@
import * as ScrollAreaPrimitive from '@radix-ui/react-scroll-area';
import * as Primitive from '@radix-ui/react-scroll-area';
import * as React from 'react';
import { cn } from '@/utils/cn';
const ScrollArea = React.forwardRef<
React.ComponentRef<typeof ScrollAreaPrimitive.Root>,
React.ComponentPropsWithoutRef<typeof ScrollAreaPrimitive.Root>
React.ComponentRef<typeof Primitive.Root>,
React.ComponentPropsWithoutRef<typeof Primitive.Root>
>(({ className, children, ...props }, ref) => (
<ScrollAreaPrimitive.Root
<Primitive.Root
ref={ref}
type="scroll"
className={cn('overflow-hidden', className)}
{...props}
>
{children}
<ScrollAreaPrimitive.Corner />
<Primitive.Corner />
<ScrollBar orientation="vertical" />
</ScrollAreaPrimitive.Root>
</Primitive.Root>
));
ScrollArea.displayName = ScrollAreaPrimitive.Root.displayName;
ScrollArea.displayName = Primitive.Root.displayName;
const ScrollViewport = React.forwardRef<
React.ComponentRef<typeof ScrollAreaPrimitive.Viewport>,
React.ComponentPropsWithoutRef<typeof ScrollAreaPrimitive.Viewport>
React.ComponentRef<typeof Primitive.Viewport>,
React.ComponentPropsWithoutRef<typeof Primitive.Viewport>
>(({ className, children, ...props }, ref) => (
<ScrollAreaPrimitive.Viewport
<Primitive.Viewport
ref={ref}
className={cn('size-full rounded-[inherit]', className)}
{...props}
>
{children}
</ScrollAreaPrimitive.Viewport>
</Primitive.Viewport>
));
ScrollViewport.displayName = ScrollAreaPrimitive.Viewport.displayName;
ScrollViewport.displayName = Primitive.Viewport.displayName;
const ScrollBar = React.forwardRef<
React.ComponentRef<typeof ScrollAreaPrimitive.Scrollbar>,
React.ComponentPropsWithoutRef<typeof ScrollAreaPrimitive.Scrollbar>
React.ComponentRef<typeof Primitive.Scrollbar>,
React.ComponentPropsWithoutRef<typeof Primitive.Scrollbar>
>(({ className, orientation = 'vertical', ...props }, ref) => (
<ScrollAreaPrimitive.Scrollbar
<Primitive.Scrollbar
ref={ref}
orientation={orientation}
className={cn(
@@ -50,9 +50,10 @@ const ScrollBar = React.forwardRef<
)}
{...props}
>
<ScrollAreaPrimitive.ScrollAreaThumb className="relative flex-1 rounded-full bg-fd-border" />
</ScrollAreaPrimitive.Scrollbar>
<Primitive.ScrollAreaThumb className="relative flex-1 rounded-full bg-fd-border" />
</Primitive.Scrollbar>
));
ScrollBar.displayName = ScrollAreaPrimitive.Scrollbar.displayName;
ScrollBar.displayName = Primitive.Scrollbar.displayName;
export { ScrollArea, ScrollBar, ScrollViewport };
export type ScrollAreaProps = Primitive.ScrollAreaProps;
+2 -4
View File
@@ -3,14 +3,12 @@ import Link, { type LinkProps } from 'fumadocs-core/link';
import { cn } from '@/utils/cn';
import {
NavigationMenuContent,
type NavigationMenuContentProps,
NavigationMenuItem,
NavigationMenuLink,
NavigationMenuTrigger,
type NavigationMenuTriggerProps,
} from '@/components/ui/navigation-menu';
import type {
NavigationMenuContentProps,
NavigationMenuTriggerProps,
} from '@radix-ui/react-navigation-menu';
import { navItemVariants } from './client';
export const NavbarMenu = NavigationMenuItem;