101 lines
4.0 KiB
TypeScript
101 lines
4.0 KiB
TypeScript
import React, { useId } from 'react';
|
|
import { X } from 'lucide-react';
|
|
|
|
interface FilledInputProps {
|
|
label: string;
|
|
value: string | number;
|
|
onChange: (e: React.ChangeEvent<HTMLInputElement>) => void;
|
|
onClear?: () => void;
|
|
onFocus?: (e: React.FocusEvent<HTMLInputElement>) => void;
|
|
onBlur?: (e: React.FocusEvent<HTMLInputElement>) => void;
|
|
type?: string;
|
|
icon?: React.ReactNode;
|
|
autoFocus?: boolean;
|
|
step?: string;
|
|
inputMode?: "search" | "text" | "email" | "tel" | "url" | "none" | "numeric" | "decimal";
|
|
autocapitalize?: "off" | "none" | "on" | "sentences" | "words" | "characters";
|
|
autoComplete?: string;
|
|
rightElement?: React.ReactNode;
|
|
multiline?: boolean;
|
|
rows?: number;
|
|
}
|
|
|
|
const FilledInput: React.FC<FilledInputProps> = ({
|
|
label, value, onChange, onClear, onFocus, onBlur, type = "number", icon,
|
|
autoFocus, step, inputMode, autocapitalize, autoComplete, rightElement,
|
|
multiline = false, rows = 3
|
|
}) => {
|
|
const id = useId();
|
|
const inputRef = React.useRef<HTMLInputElement | HTMLTextAreaElement>(null);
|
|
|
|
const handleClear = () => {
|
|
const syntheticEvent = {
|
|
target: { value: '' }
|
|
} as React.ChangeEvent<HTMLInputElement>;
|
|
onChange(syntheticEvent);
|
|
if (onClear) onClear();
|
|
inputRef.current?.focus();
|
|
};
|
|
|
|
return (
|
|
<div className="relative group bg-surface-container-high rounded-t-[4px] border-b border-on-surface-variant hover:bg-on-surface/10 focus-within:border-primary focus-within:border-b-2 transition-colors min-h-[56px]">
|
|
<label htmlFor={id} className="absolute top-2 left-4 text-label-sm font-medium text-on-surface-variant flex items-center gap-1 group-focus-within:text-primary">
|
|
{icon ? <>{icon} {label}</> : label}
|
|
</label>
|
|
|
|
{!multiline ? (
|
|
<input
|
|
ref={inputRef as React.RefObject<HTMLInputElement>}
|
|
id={id}
|
|
type={type}
|
|
step={step}
|
|
inputMode={inputMode || (type === 'number' ? 'decimal' : 'text')}
|
|
autoFocus={autoFocus}
|
|
className={`w-full h-[56px] pt-5 pb-1 pl-4 bg-transparent text-body-lg text-on-surface focus:outline-none placeholder-transparent ${rightElement ? 'pr-20' : 'pr-10'}`}
|
|
placeholder=" "
|
|
value={value}
|
|
onChange={onChange}
|
|
onFocus={onFocus}
|
|
onBlur={onBlur}
|
|
autoCapitalize={autocapitalize}
|
|
autoComplete={autoComplete}
|
|
/>
|
|
) : (
|
|
<textarea
|
|
ref={inputRef as React.RefObject<HTMLTextAreaElement>}
|
|
id={id}
|
|
rows={rows}
|
|
className={`w-full pt-6 pb-2 pl-4 bg-transparent text-body-lg text-on-surface focus:outline-none placeholder-transparent resize-none ${rightElement ? 'pr-20' : 'pr-10'}`}
|
|
placeholder=" "
|
|
value={value}
|
|
onChange={onChange as any}
|
|
onFocus={onFocus as any}
|
|
onBlur={onBlur as any}
|
|
autoCapitalize={autocapitalize}
|
|
/>
|
|
)}
|
|
|
|
{value !== '' && value !== 0 && (
|
|
<button
|
|
type="button"
|
|
onClick={handleClear}
|
|
aria-label="Clear input"
|
|
className={`absolute top-1/2 -translate-y-1/2 p-2 text-on-surface-variant hover:text-on-surface rounded-full transition-opacity ${rightElement ? 'right-12' : 'right-2'}`}
|
|
tabIndex={-1}
|
|
>
|
|
<X size={16} />
|
|
</button>
|
|
)}
|
|
{
|
|
rightElement && (
|
|
<div className="absolute right-2 top-1/2 -translate-y-1/2 z-10">
|
|
{rightElement}
|
|
</div>
|
|
)
|
|
}
|
|
</div >
|
|
);
|
|
};
|
|
|
|
export default FilledInput;
|