| import { forwardRef } from "react"; |
| import { cn } from "#/utils/utils"; |
| import { formControlSettingsFieldClassName } from "#/utils/form-control-classes"; |
| import { OptionalTag } from "./optional-tag"; |
|
|
| interface SettingsInputProps { |
| testId?: string; |
| name?: string; |
| label: string; |
| type: React.HTMLInputTypeAttribute; |
| defaultValue?: string; |
| value?: string; |
| placeholder?: string; |
| showOptionalTag?: boolean; |
| isDisabled?: boolean; |
| startContent?: React.ReactNode; |
| className?: string; |
| onChange?: (value: string) => void; |
| onKeyDown?: (e: React.KeyboardEvent<HTMLInputElement>) => void; |
| required?: boolean; |
| min?: number; |
| max?: number; |
| step?: number; |
| pattern?: string; |
| |
| title?: string; |
| labelClassName?: string; |
| |
| |
| |
| |
| |
| ariaLabel?: string; |
| |
| ariaDescribedBy?: string; |
| |
| ariaInvalid?: boolean; |
| |
| |
| |
| |
| error?: string; |
| |
| showRequiredTag?: boolean; |
| |
| |
| |
| |
| |
| hint?: string; |
| onBlur?: () => void; |
| |
| inputClassName?: string; |
| } |
|
|
| export const SettingsInput = forwardRef<HTMLInputElement, SettingsInputProps>( |
| function SettingsInput( |
| { |
| testId, |
| name, |
| label, |
| type, |
| defaultValue, |
| value, |
| placeholder, |
| showOptionalTag, |
| isDisabled, |
| startContent, |
| className, |
| onChange, |
| onKeyDown, |
| required, |
| min, |
| max, |
| step, |
| pattern, |
| title, |
| labelClassName, |
| ariaLabel, |
| ariaDescribedBy, |
| ariaInvalid, |
| error, |
| showRequiredTag, |
| hint, |
| onBlur, |
| inputClassName, |
| }, |
| ref, |
| ) { |
| const errorId = error && testId ? `${testId}-error` : undefined; |
| return ( |
| <label className={cn("flex flex-col gap-2.5 w-full min-w-0", className)}> |
| <div className="flex items-center gap-2"> |
| {startContent} |
| <span className={cn("text-sm", labelClassName)}>{label}</span> |
| {showRequiredTag && ( |
| <span className="text-red-400 text-sm leading-none" aria-hidden> |
| * |
| </span> |
| )} |
| {showOptionalTag && <OptionalTag />} |
| {hint && ( |
| <span |
| data-testid={testId ? `${testId}-hint` : undefined} |
| className="min-w-0 text-xs text-[var(--oh-muted)]" |
| > |
| {hint} |
| </span> |
| )} |
| </div> |
| <input |
| ref={ref} |
| data-testid={testId} |
| onChange={(e) => onChange?.(e.target.value)} |
| onKeyDown={onKeyDown} |
| onBlur={onBlur} |
| name={name} |
| disabled={isDisabled} |
| type={type} |
| defaultValue={defaultValue} |
| value={value} |
| placeholder={placeholder} |
| min={min} |
| max={max} |
| step={step} |
| required={required} |
| pattern={pattern} |
| title={title} |
| aria-label={ariaLabel} |
| aria-describedby={errorId ?? ariaDescribedBy} |
| aria-invalid={!!error || ariaInvalid} |
| className={cn( |
| formControlSettingsFieldClassName, |
| "disabled:bg-[var(--oh-surface-raised)] disabled:border-[var(--oh-border-subtle)]", |
| error && "border-red-500", |
| inputClassName, |
| )} |
| /> |
| {error && ( |
| <p |
| id={errorId} |
| role="alert" |
| data-testid={testId ? `${testId}-error` : undefined} |
| className="text-xs text-red-400 -mt-1" |
| > |
| {error} |
| </p> |
| )} |
| </label> |
| ); |
| }, |
| ); |
|
|