61 lines
1.9 KiB
TypeScript
61 lines
1.9 KiB
TypeScript
import type { InputHTMLAttributes, ReactNode } from 'react';
|
|
import { cn } from '../../lib/cn';
|
|
|
|
export interface InputProps extends Omit<InputHTMLAttributes<HTMLInputElement>, 'prefix'> {
|
|
label?: string;
|
|
hint?: string;
|
|
error?: string;
|
|
/** Leading adornment, e.g. "₹". */
|
|
prefix?: ReactNode;
|
|
/** Trailing adornment, e.g. "/ year". */
|
|
suffix?: ReactNode;
|
|
/** Class for the outer label wrapper. */
|
|
className?: string;
|
|
}
|
|
|
|
/** Labeled text/number input with optional prefix, suffix, hint and error. */
|
|
export function Input({
|
|
label,
|
|
hint,
|
|
error,
|
|
prefix,
|
|
suffix,
|
|
required,
|
|
className,
|
|
disabled,
|
|
...rest
|
|
}: InputProps) {
|
|
return (
|
|
<label className={cn('flex flex-col gap-1.5 font-sans', disabled && 'cursor-not-allowed', className)}>
|
|
{label && (
|
|
<span className="text-sm font-bold text-slate-700">
|
|
{label}
|
|
{required && <span className="text-ruby-600"> *</span>}
|
|
</span>
|
|
)}
|
|
<div
|
|
className={cn(
|
|
'flex items-center gap-2 rounded-md px-3 h-[42px] border transition-[border-color,box-shadow] duration-150',
|
|
disabled ? 'bg-sunk border-border-subtle' : 'bg-card',
|
|
error ? 'border-ruby-600' : !disabled && 'border-border-default focus-ring',
|
|
)}
|
|
>
|
|
{prefix && <span className="text-base font-semibold text-faint">{prefix}</span>}
|
|
<input
|
|
required={required}
|
|
disabled={disabled}
|
|
className={cn(
|
|
'flex-1 w-full border-none outline-none bg-transparent font-sans text-base',
|
|
disabled ? 'text-muted cursor-not-allowed' : 'text-strong',
|
|
)}
|
|
{...rest}
|
|
/>
|
|
{suffix && <span className="text-faint text-sm">{suffix}</span>}
|
|
</div>
|
|
{(hint || error) && (
|
|
<span className={cn('text-xs', error ? 'text-ruby-600' : 'text-faint')}>{error || hint}</span>
|
|
)}
|
|
</label>
|
|
);
|
|
}
|