Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 40 additions & 1 deletion src/components/ui/card.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,12 @@ import * as React from "react"

import { cn } from "@/lib/utils"

/**
* A styled container component that provides the Card layout and visual surface.
*
* @param className - Additional CSS class names appended to the component's default classes
* @returns The rendered `div` element with `data-slot="card"` and the card styling applied
*/
function Card({ className, ...props }: React.ComponentProps<"div">) {
return (
<div
Expand All @@ -15,6 +21,13 @@ function Card({ className, ...props }: React.ComponentProps<"div">) {
)
}

/**
* Renders the header region of a Card with responsive grid layout and slot attributes for composition.
*
* The returned element includes data-slot="card-header", applies the component's header styling, merges any provided `className`, and spreads remaining div props onto the element.
*
* @returns A `div` element representing the card header slot
*/
function CardHeader({ className, ...props }: React.ComponentProps<"div">) {
return (
<div
Expand All @@ -28,6 +41,12 @@ function CardHeader({ className, ...props }: React.ComponentProps<"div">) {
)
}

/**
* Renders a card title container with title-specific typography and a `data-slot="card-title"` attribute.
*
* @param className - Additional CSS classes to merge with the component's default typography classes
* @returns The title element with `leading-none` and `font-semibold` classes applied
*/
function CardTitle({ className, ...props }: React.ComponentProps<"div">) {
return (
<div
Expand All @@ -38,6 +57,11 @@ function CardTitle({ className, ...props }: React.ComponentProps<"div">) {
)
}

/**
* Renders the card description slot with muted foreground and small text styling.
*
* @returns A `div` element with `data-slot="card-description"`, applying muted foreground and `text-sm` styles and spreading any additional `div` props onto the element.
*/
function CardDescription({ className, ...props }: React.ComponentProps<"div">) {
return (
<div
Expand All @@ -48,6 +72,11 @@ function CardDescription({ className, ...props }: React.ComponentProps<"div">) {
)
}

/**
* Renders the card action slot positioned in the card's grid (right-aligned and spanning rows).
*
* @returns The div element for the card action slot with `data-slot="card-action"`.
*/
function CardAction({ className, ...props }: React.ComponentProps<"div">) {
return (
<div
Expand All @@ -61,6 +90,11 @@ function CardAction({ className, ...props }: React.ComponentProps<"div">) {
)
}

/**
* Renders the card's content slot with horizontal padding and a `data-slot="card-content"` attribute.
*
* @returns A `div` element used as the card content container
*/
function CardContent({ className, ...props }: React.ComponentProps<"div">) {
return (
<div
Expand All @@ -71,6 +105,11 @@ function CardContent({ className, ...props }: React.ComponentProps<"div">) {
)
}

/**
* Card footer container for actions or metadata at the bottom of a card.
*
* @returns A div element with `data-slot="card-footer"` and footer layout classes; merges any provided `className` and spreads remaining props onto the div.
*/
function CardFooter({ className, ...props }: React.ComponentProps<"div">) {
return (
<div
Expand All @@ -89,4 +128,4 @@ export {
CardAction,
CardDescription,
CardContent,
}
}
31 changes: 30 additions & 1 deletion src/components/ui/form.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,13 @@ const FormItemContext = React.createContext<FormItemContextValue>(
{} as FormItemContextValue
)

/**
* Wraps a form item and provides a generated unique id to descendant form field components via context.
*
* The rendered container is a div with a data-slot of "form-item" and accepts all standard div props.
*
* @returns A JSX element: a div wrapped by FormItemContext.Provider that supplies a generated `id` for descendants to use for accessibility attributes.
*/
function FormItem({ className, ...props }: React.ComponentProps<"div">) {
const id = React.useId()

Expand All @@ -87,6 +94,11 @@ function FormItem({ className, ...props }: React.ComponentProps<"div">) {
)
}

/**
* Renders a label for the current FormField that is bound to the field's input and reflects error state for styling and accessibility.
*
* @returns The rendered Label element with `htmlFor` set to the field's form item id, `data-slot="form-label"`, and a `data-error` attribute indicating presence of an error.
*/
function FormLabel({
className,
...props
Expand All @@ -104,6 +116,11 @@ function FormLabel({
)
}

/**
* Renders a Slot used as the form field's control, wired to the current field's accessibility IDs and error state.
*
* @returns A Slot element with `id`, `data-slot="form-control"`, `aria-describedby` referencing the field's description and message IDs, and `aria-invalid` set when the field has an error.
*/
function FormControl({ ...props }: React.ComponentProps<typeof Slot>) {
const { error, formItemId, formDescriptionId, formMessageId } = useFormField()

Expand All @@ -122,6 +139,11 @@ function FormControl({ ...props }: React.ComponentProps<typeof Slot>) {
)
}

/**
* Renders the form field's descriptive paragraph and wires its id for accessibility.
*
* @returns The paragraph element used as the form field description (bound to the field's description id).
*/
function FormDescription({ className, ...props }: React.ComponentProps<"p">) {
const { formDescriptionId } = useFormField()

Expand All @@ -135,6 +157,13 @@ function FormDescription({ className, ...props }: React.ComponentProps<"p">) {
)
}

/**
* Renders an accessible form message element showing the field's error message or provided children.
*
* The element is omitted when there is no message to display.
*
* @returns A `<p>` element with the field message and `data-slot="form-message"`, or `null` if empty.
*/
function FormMessage({ className, ...props }: React.ComponentProps<"p">) {
const { error, formMessageId } = useFormField()
const body = error ? String(error?.message ?? "") : props.children
Expand Down Expand Up @@ -164,4 +193,4 @@ export {
FormDescription,
FormMessage,
FormField,
}
}
9 changes: 8 additions & 1 deletion src/components/ui/label.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,13 @@ import * as LabelPrimitive from "@radix-ui/react-label"

import { cn } from "@/lib/utils"

/**
* Renders a styled Radix UI Label primitive with merged class names and a fixed data-slot="label".
*
* @param className - Additional CSS class names to merge with the component's default styling.
* @param props - Remaining props are forwarded to `LabelPrimitive.Root`.
* @returns The rendered `LabelPrimitive.Root` element with composed classes and forwarded props.
*/
function Label({
className,
...props
Expand All @@ -21,4 +28,4 @@ function Label({
)
}

export { Label }
export { Label }
22 changes: 21 additions & 1 deletion src/components/ui/resizable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,12 @@ import * as ResizablePrimitive from "react-resizable-panels"

import { cn } from "@/lib/utils"

/**
* Wraps the react-resizable-panels PanelGroup with default flex layout, vertical-direction support, and a data-slot attribute.
*
* @param className - Additional CSS classes to merge with the component's default layout classes.
* @returns The rendered PanelGroup element with merged classes, data-slot="resizable-panel-group", and forwarded props.
*/
function ResizablePanelGroup({
className,
...props
Expand All @@ -22,12 +28,26 @@ function ResizablePanelGroup({
)
}

/**
* Renders a resizable panel wrapper with a `data-slot` of "resizable-panel".
*
* The component forwards all received props to the underlying Panel primitive.
*
* @returns The rendered Panel element with `data-slot="resizable-panel"`.
*/
function ResizablePanel({
...props
}: React.ComponentProps<typeof ResizablePrimitive.Panel>) {
return <ResizablePrimitive.Panel data-slot="resizable-panel" {...props} />
}

/**
* Render a direction-aware resize handle for react-resizable-panels with an optional visual grip.
*
* @param withHandle - When true, renders a small grip element (icon) inside the handle.
* @param className - Additional CSS classes to apply to the handle container.
* @returns A JSX element for a panel resize handle; includes an inner grip when `withHandle` is true.
*/
function ResizableHandle({
withHandle,
className,
Expand All @@ -53,4 +73,4 @@ function ResizableHandle({
)
}

export { ResizablePanelGroup, ResizablePanel, ResizableHandle }
export { ResizablePanelGroup, ResizablePanel, ResizableHandle }