import clsx from 'clsx'; import type { ChangeEvent, FocusEvent, ForwardedRef, InputHTMLAttributes, } from 'react'; import React, { forwardRef, useId } from 'react'; type Attributes = Pick< InputHTMLAttributes, | 'autoComplete' | 'disabled' | 'max' | 'maxLength' | 'min' | 'minLength' | 'name' | 'onBlur' | 'onFocus' | 'pattern' | 'placeholder' | 'required' | 'type' >; type StartAddOnProps = | Readonly<{ startAddOn: React.ComponentType>; startAddOnType: 'icon'; }> | Readonly<{ startAddOn: React.ReactNode; startAddOnType: 'element'; }> | Readonly<{ startAddOn: string; startAddOnType: 'label'; }> | Readonly<{ startAddOn?: undefined; startAddOnType?: undefined; }>; type EndAddOnProps = | Readonly<{ endAddOn: React.ComponentType>; endAddOnType: 'icon'; }> | Readonly<{ endAddOn: React.ReactNode; endAddOnType: 'element'; }> | Readonly<{ endAddOn: string; endAddOnType: 'label'; }> | Readonly<{ endAddOn?: undefined; endAddOnType?: undefined; }>; type BaseProps = Readonly<{ defaultValue?: string; endIcon?: React.ComponentType>; errorMessage?: React.ReactNode; id?: string; isLabelHidden?: boolean; label: string; onBlur?: (event: FocusEvent) => void; onChange?: (value: string, event: ChangeEvent) => void; value?: string; }> & Readonly; type Props = BaseProps & EndAddOnProps & StartAddOnProps; type State = 'error' | 'normal'; const stateClasses: Record< State, Readonly<{ container: string; input: string; }> > = { error: { container: 'border-danger-300 focus-within:outline-none focus-within:ring-danger-500 focus-within:border-danger-500', input: 'text-danger-900 placeholder-danger-300', }, normal: { container: 'focus-within:ring-primary-500 focus-within:border-primary-500 border-slate-300', input: 'placeholder:text-slate-400', }, }; function TextInput( { defaultValue, disabled, endAddOn, endAddOnType, errorMessage, id: idParam, isLabelHidden = false, label, required, startAddOn, startAddOnType, type = 'text', value, onChange, ...props }: Props, ref: ForwardedRef, ) { const hasError = errorMessage != null; const generatedId = useId(); const id = idParam ?? generatedId; const errorId = useId(); const state: State = hasError ? 'error' : 'normal'; const { input: inputClass, container: containerClass } = stateClasses[state]; return (
{(() => { if (startAddOnType == null) { return; } switch (startAddOnType) { case 'label': return (
{startAddOn}
); case 'icon': { const StartAddOn = startAddOn; return (
); } case 'element': return startAddOn; } })()} { if (!onChange) { return; } onChange(event.target.value, event); }} {...props} /> {(() => { if (endAddOnType == null) { return; } switch (endAddOnType) { case 'label': return (
{endAddOn}
); case 'icon': { const EndAddOn = endAddOn; return (
); } case 'element': return endAddOn; } })()}
{errorMessage && (

{errorMessage}

)}
); } export default forwardRef(TextInput);