docs(docs): add cli configuration and components

This commit is contained in:
Prad Nukala committed 2026-07-12 18:50:57 -04:00
1 parent 3a238c34d9
commit 94b4bc509b
10 files changed
+293 -4

No files matched your search

+105
View File
@@ -0,0 +1,105 @@
'use client';
import { Check, LinkIcon } from 'lucide-react';
import { type ComponentProps, type ReactNode, useEffect, useRef, useState } from 'react';
import { cn } from '../lib/cn';
import { useCopyButton } from '@fumadocs/base-ui/utils/use-copy-button';
import { buttonVariants } from './ui/button';
import { mergeRefs } from '../lib/merge-refs';
import { useTranslations } from '@fuma-translate/react';
import {
Accordion as Root,
AccordionContent,
AccordionHeader,
AccordionItem,
AccordionTrigger,
} from './ui/accordion';
export function Accordions({
ref,
className,
defaultValue,
...props
}: ComponentProps<typeof Root>) {
const rootRef = useRef<HTMLDivElement>(null);
const composedRef = mergeRefs(ref, rootRef);
const [value, setValue] = useState<unknown[]>(defaultValue ?? []);
useEffect(() => {
const id = window.location.hash.substring(1);
const element = rootRef.current;
if (!element || id.length === 0) return;
const selected = document.getElementById(id);
if (!selected || !element.contains(selected)) return;
const value = selected.getAttribute('data-accordion-value');
if (value) setValue((prev) => [value, ...prev]);
}, []);
return (
<Root
ref={composedRef}
value={value}
onValueChange={setValue}
className={(s) =>
cn(
'divide-y divide-fd-border overflow-hidden rounded-lg border bg-fd-card',
typeof className === 'function' ? className(s) : className,
)
}
{...props}
/>
);
}
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 hiddenUntilFound>
<div className="px-4 pb-2 text-[0.9375rem] prose-no-margin [&[hidden]:not([hidden='until-found'])]:hidden">
{children}
</div>
</AccordionContent>
</AccordionItem>
);
}
function CopyButton({ id }: { id: string }) {
const t = useTranslations({ note: 'accordion' });
const [checked, onClick] = useCopyButton(() => {
const url = new URL(window.location.href);
url.hash = id;
return navigator.clipboard.writeText(url.toString());
});
return (
<button
type="button"
aria-label={t('Copy Link', { note: 'aria-label' })}
className={cn(
buttonVariants({
color: 'ghost',
className: 'text-fd-muted-foreground me-2',
}),
)}
onClick={onClick}
>
{checked ? <Check className="size-3.5" /> : <LinkIcon className="size-3.5" />}
</button>
);
}
+9
View File
@@ -0,0 +1,9 @@
import type { ReactNode } from 'react';
export function Steps({ children }: { children: ReactNode }) {
return <div className="fd-steps">{children}</div>;
}
export function Step({ children }: { children: ReactNode }) {
return <div className="fd-step">{children}</div>;
}
+85
View File
@@ -0,0 +1,85 @@
'use client';
import { Accordion as Primitive } from '@base-ui/react/accordion';
import { ChevronRight } from 'lucide-react';
import { type ComponentProps } from 'react';
import { cn } from '../../lib/cn';
export function Accordion({ className, ...props }: ComponentProps<typeof Primitive.Root>) {
return (
<Primitive.Root
className={(s) =>
cn(
'divide-y divide-fd-border overflow-hidden rounded-lg border bg-fd-card',
typeof className === 'function' ? className(s) : className,
)
}
{...props}
/>
);
}
export function AccordionItem({ children, ...props }: ComponentProps<typeof Primitive.Item>) {
return <Primitive.Item {...props}>{children}</Primitive.Item>;
}
export function AccordionHeader({
className,
children,
...props
}: ComponentProps<typeof Primitive.Header>) {
return (
<Primitive.Header
className={(s) =>
cn(
'scroll-m-24 not-prose flex flex-row items-center text-fd-card-foreground font-medium has-focus-visible:bg-fd-accent',
typeof className === 'function' ? className(s) : className,
)
}
{...props}
>
{children}
</Primitive.Header>
);
}
export function AccordionTrigger({
className,
children,
...props
}: ComponentProps<typeof Primitive.Trigger>) {
return (
<Primitive.Trigger
className={(s) =>
cn(
'group flex flex-1 items-center gap-2 px-3 py-2.5 text-start focus-visible:outline-none',
typeof className === 'function' ? className(s) : className,
)
}
{...props}
>
<ChevronRight className="size-4 shrink-0 text-fd-muted-foreground transition-transform duration-200 group-data-[panel-open]:rotate-90" />
{children}
</Primitive.Trigger>
);
}
export function AccordionContent({
className,
children,
...props
}: ComponentProps<typeof Primitive.Panel>) {
return (
<Primitive.Panel
className={(s) =>
cn(
'h-(--accordion-panel-height) overflow-hidden transition-[height] ease-out data-[ending-style]:h-0 data-[starting-style]:h-0',
typeof className === 'function' ? className(s) : className,
)
}
{...props}
>
{children}
</Primitive.Panel>
);
}
+28
View File
@@ -0,0 +1,28 @@
import { cva, type VariantProps } from 'class-variance-authority';
const variants = {
primary: 'bg-fd-primary text-fd-primary-foreground hover:bg-fd-primary/80',
outline: 'border hover:bg-fd-accent hover:text-fd-accent-foreground',
ghost: 'hover:bg-fd-accent hover:text-fd-accent-foreground',
secondary:
'border bg-fd-secondary text-fd-secondary-foreground hover:bg-fd-accent hover:text-fd-accent-foreground',
} as const;
export const buttonVariants = cva(
'inline-flex items-center justify-center rounded-md p-2 text-sm font-medium transition-colors duration-100 disabled:pointer-events-none disabled:opacity-50 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-fd-ring',
{
variants: {
variant: variants,
// fumadocs use `color` instead of `variant`
color: variants,
size: {
sm: 'gap-1 px-2 py-1.5 text-xs',
icon: 'p-1.5 [&_svg]:size-5',
'icon-sm': 'p-1.5 [&_svg]:size-4.5',
'icon-xs': 'p-1 [&_svg]:size-4',
},
},
},
);
export type ButtonProps = VariantProps<typeof buttonVariants>;
+1
View File
@@ -0,0 +1 @@
export { cn } from 'cnfast';
+13
View File
@@ -0,0 +1,13 @@
import type * as React from 'react';
export function mergeRefs<T>(...refs: (React.Ref<T> | undefined)[]): React.RefCallback<T> {
return (value) => {
refs.forEach((ref) => {
if (typeof ref === 'function') {
ref(value);
} else if (ref) {
ref.current = value;
}
});
};
}