Download src/components/features/settings/settings-input.tsx from SaylorTwift/openhands: direct link, hf CLI and curl.
- Browser
- Download file 4.38 kB
-
https://huggingface.co/SaylorTwift/openhands/resolve/main/src/components/features/settings/settings-input.tsx
- Command line
-
hf download hf://SaylorTwift/openhands/src/components/features/settings/settings-input.tsx
-
curl -L -o settings-input.tsx https://huggingface.co/SaylorTwift/openhands/resolve/main/src/components/features/settings/settings-input.tsx
4.38 kB
| 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; | |
| /** Validation message shown when pattern doesn't match */ | |
| title?: string; | |
| labelClassName?: string; | |
| /** | |
| * The input's accessible name, for the caller that renders the visible label | |
| * itself. Only for those: a field labelled by this component reads its label, | |
| * and two names would disagree. | |
| */ | |
| ariaLabel?: string; | |
| /** ARIA describedby attribute for accessibility */ | |
| ariaDescribedBy?: string; | |
| /** ARIA invalid attribute for accessibility */ | |
| ariaInvalid?: boolean; | |
| /** | |
| * Validation error message. When set, the input gets a red border and | |
| * the message is rendered below it. Also sets aria-invalid automatically. | |
| */ | |
| error?: string; | |
| /** Renders a red asterisk next to the label to mark the field as required. */ | |
| showRequiredTag?: boolean; | |
| /** | |
| * Short guidance rendered next to the label, above the input. It sits inside | |
| * the `<label>`, so it is announced as part of the field's accessible name — | |
| * keep it to a phrase that reads well after the label text. | |
| */ | |
| hint?: string; | |
| onBlur?: () => void; | |
| /** Extra classes merged onto the `<input>` element. */ | |
| 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> | |
| ); | |
| }, | |
| ); | |