"use client"; import * as React from "react"; import {Button} from "@/components/ui/button"; import {Input} from "@/components/ui/input"; import {Textarea} from "@/components/ui/textarea"; import {cn} from "@/lib/utilities"; import styles from "./input-group.module.css"; /** Supported alignment options for {@link InputGroupAddon}. */ export type InputGroupAddonAlign = "inline-start" | "inline-end" | "block-start" | "block-end"; /** Supported compact button sizes for {@link InputGroupButton}. */ export type InputGroupButtonSize = "xs" | "sm" | "icon-xs" | "icon-sm"; /** * Props for the {@link InputGroup} component. */ export type InputGroupProps = React.ComponentPropsWithoutRef<"div">; /** * Props for the {@link InputGroupAddon} component. */ export interface InputGroupAddonProps extends React.ComponentPropsWithoutRef<"div"> { /** Position of the addon relative to the input control. @default "inline-start" */ align?: InputGroupAddonAlign; } /** * Props for the {@link InputGroupButton} component. */ export interface InputGroupButtonProps extends Omit, "size"> { /** Compact button size used within the group chrome. @default "xs" */ size?: InputGroupButtonSize; } /** * Props for the {@link InputGroupText} component. */ export type InputGroupTextProps = React.ComponentPropsWithoutRef<"span">; /** * Props for the {@link InputGroupInput} component. */ export type InputGroupInputProps = React.ComponentPropsWithoutRef<"input">; /** * Props for the {@link InputGroupTextarea} component. */ export type InputGroupTextareaProps = React.ComponentPropsWithoutRef<"textarea">; function getAddonAlignClass(align: InputGroupAddonAlign): string { switch (align) { case "block-end": { return styles.blockEnd; } case "block-start": { return styles.blockStart; } case "inline-end": { return styles.inlineEnd; } default: { return styles.inlineStart; } } } function getButtonSizeClass(size: InputGroupButtonSize): string { switch (size) { case "icon-sm": { return styles.buttonIconSm; } case "icon-xs": { return styles.buttonIconXs; } case "sm": { return styles.buttonSm; } default: { return styles.buttonXs; } } } /** * Creates a composable shell for grouped text inputs and controls. * * @remarks * - Pure CSS component (no Base UI primitive) * - Renders a `
` element * - Styling via CSS Modules with `--ac-*` custom properties * * @example * ```tsx * * * * ``` * * @see {@link InputGroupProps} for available props */ const InputGroup = React.forwardRef( ({className, ...props}: Readonly, ref): React.JSX.Element => (
), ); /** * Renders supplementary content around an input group control. * * @remarks * - Pure CSS component (no Base UI primitive) * - Renders a `
` element * - Styling via CSS Modules with `--ac-*` custom properties * * @example * ```tsx * .com * ``` * * @see {@link InputGroupAddonProps} for available props */ const InputGroupAddon = React.forwardRef( ({className, align = "inline-start", onClick, ...props}: Readonly, ref): React.JSX.Element => ( // eslint-disable-next-line jsx-a11y/click-events-have-key-events, jsx-a11y/no-noninteractive-element-interactions -- clicking the addon focuses the related control.
{ if ((event.target as HTMLElement).closest("button")) { onClick?.(event); return; } onClick?.(event); if (event.defaultPrevented) { return; } event.currentTarget.parentElement?.querySelector("input, textarea")?.focus(); }} {...props} /> ), ); /** * Renders a compact button matched to input group dimensions. * * @remarks * - Pure CSS component (no Base UI primitive) * - Renders a wrapped `Button` component * - Styling via CSS Modules with `--ac-*` custom properties * * @example * ```tsx * Search * ``` * * @see {@link InputGroupButtonProps} for available props */ const InputGroupButton = React.forwardRef( ({className, type = "button", variant = "ghost", size = "xs", ...props}: Readonly, ref): React.JSX.Element => (