Input.tsx
1 import * as React from 'react' 2 import { cn } from '../../lib/utils' 3 4 export interface InputProps extends React.InputHTMLAttributes<HTMLInputElement> { 5 label?: string 6 error?: string 7 leftIcon?: React.ReactNode 8 rightIcon?: React.ReactNode 9 } 10 11 export const Input = React.forwardRef<HTMLInputElement, InputProps>( 12 ({ className, label, error, leftIcon, rightIcon, disabled, id, ...props }, ref) => { 13 const inputId = id ?? label?.toLowerCase().replace(/\s+/g, '-') 14 return ( 15 <div className="flex flex-col gap-1"> 16 {label && ( 17 <label 18 htmlFor={inputId} 19 className="text-xs font-medium text-[var(--text-secondary,oklch(65%_0_0))]" 20 > 21 {label} 22 </label> 23 )} 24 <div className="relative flex items-center"> 25 {leftIcon && ( 26 <span className="pointer-events-none absolute left-3 text-[var(--text-secondary,oklch(55%_0_0))]"> 27 {leftIcon} 28 </span> 29 )} 30 <input 31 id={inputId} 32 ref={ref} 33 disabled={disabled} 34 className={cn( 35 'w-full rounded-[var(--radius-base,4px)] border bg-[var(--bg-tertiary,oklch(16%_0_0))]', 36 'px-3 py-2 text-sm text-[var(--text-primary,oklch(95%_0_0))]', 37 'placeholder:text-[var(--text-disabled,oklch(40%_0_0))]', 38 'transition-colors duration-100', 39 'border-[var(--border-default,oklch(25%_0_0))]', 40 'focus:border-[oklch(52.4%_0.22_264)] focus:outline-none focus:ring-1 focus:ring-[oklch(52.4%_0.22_264/0.3)]', 41 'focus-visible:ring-2 focus-visible:ring-[oklch(52.4%_0.22_264)] focus-visible:ring-offset-1 focus-visible:ring-offset-[oklch(8%_0_0)]', 42 disabled && 'cursor-not-allowed opacity-50', 43 error && 'border-[oklch(60%_0.22_25)] focus:border-[oklch(60%_0.22_25)] focus:ring-[oklch(60%_0.22_25/0.3)]', 44 leftIcon && 'pl-9', 45 rightIcon && 'pr-9', 46 className 47 )} 48 aria-invalid={!!error} 49 aria-describedby={error ? `${inputId}-error` : undefined} 50 {...props} 51 /> 52 {rightIcon && ( 53 <span className="pointer-events-none absolute right-3 text-[var(--text-secondary,oklch(55%_0_0))]"> 54 {rightIcon} 55 </span> 56 )} 57 </div> 58 {error && ( 59 <p id={`${inputId}-error`} className="text-xs text-[oklch(60%_0.22_25)]" role="alert"> 60 {error} 61 </p> 62 )} 63 </div> 64 ) 65 } 66 ) 67 Input.displayName = 'Input'