import { type HTMLAttributes, type ReactNode } from "react"; import { HintText } from "@/components/base/input/hint-text"; import type { TextFieldProps } from "@/components/base/input/input"; import { TextField } from "@/components/base/input/input"; import { Label } from "@/components/base/input/label"; import { cx, sortCx } from "@/utils/cx"; interface InputPrefixProps extends HTMLAttributes { /** The position of the prefix. */ position?: "leading" | "trailing"; /** Indicates that the prefix is disabled. */ isDisabled?: boolean; } export const InputPrefix = ({ children, ...props }: InputPrefixProps) => ( {children} ); // `${string}ClassName` is used to omit any className prop that ends with a `ClassName` suffix interface InputGroupProps extends TextFieldProps { /** A prefix text that is displayed in the same box as the input.*/ prefix?: string; /** A leading addon that is displayed with visual separation from the input. */ leadingAddon?: ReactNode; /** A trailing addon that is displayed with visual separation from the input. */ trailingAddon?: ReactNode; /** The class name to apply to the input group. */ className?: string; /** The children of the input group (i.e ``) */ children: ReactNode; /** Label text for the input */ label?: string; /** Helper text displayed below the input */ hint?: ReactNode; /** Whether to hide the required indicator from the label. */ hideRequiredIndicator?: boolean; } export const InputGroup = ({ size = "md", prefix, leadingAddon, trailingAddon, label, hint, hideRequiredIndicator, children, ...props }: InputGroupProps) => { const hasLeading = !!leadingAddon; const hasTrailing = !!trailingAddon; const paddings = sortCx({ sm: { input: cx( // Apply padding styles when select element is passed as a child hasLeading && "group-has-[&>select]:pr-9 group-has-[&>select]:pl-2", hasTrailing && (prefix ? "group-has-[&>select]:pr-6 group-has-[&>select]:pl-0" : "group-has-[&>select]:pr-6 group-has-[&>select]:pl-3"), ), leadingText: "pr-1.5 pl-3", }, md: { input: cx( // Apply padding styles when select element is passed as a child hasLeading && "group-has-[&>select]:pr-9 group-has-[&>select]:pl-2.5", hasTrailing && (prefix ? "group-has-[&>select]:pr-6 group-has-[&>select]:pl-0" : "group-has-[&>select]:pr-6 group-has-[&>select]:pl-3"), ), leadingText: "pr-2 pl-3", }, lg: { input: cx( // Apply padding styles when select element is passed as a child hasLeading && "group-has-[&>select]:pr-9.5 group-has-[&>select]:pl-3", hasTrailing && (prefix ? "group-has-[&>select]:pr-6 group-has-[&>select]:pl-0" : "group-has-[&>select]:pr-6 group-has-[&>select]:pl-3"), ), leadingText: "pr-2 pl-3.5", }, }); return ( select]:right-0")} wrapperClassName={cx( "z-10", // Apply styles based on the presence of leading or trailing elements hasLeading && "rounded-l-none", hasTrailing && "rounded-r-none", // When select element is passed as a child "group-has-[&>select]:bg-transparent group-has-[&>select]:shadow-none group-has-[&>select]:ring-0 group-has-[&>select]:focus-within:ring-0", )} {...props} > {({ isDisabled, isInvalid, isRequired }) => ( <> {label && }
select]:shadow-xs has-[&>select]:ring-1 has-[&>select]:ring-border-primary has-[&>select]:ring-inset has-[&>select]:has-[input:focus]:ring-2 has-[&>select]:has-[input:focus]:ring-border-brand", isDisabled && "cursor-not-allowed", isInvalid && "has-[&>select]:ring-border-error_subtle has-[&>select]:has-[input:focus]:ring-border-error", )} > {leadingAddon && (
{leadingAddon}
)} {prefix && (

{prefix}

)} {children} {trailingAddon && (
{trailingAddon}
)}
{hint && ( {hint} )} )}
); }; InputGroup.Prefix = InputPrefix; InputGroup.displayName = "InputGroup";