All files / src/components input.tsx

77.77% Statements 14/18
72.72% Branches 16/22
75% Functions 3/4
82.35% Lines 14/17

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112                                    7x     101x 101x   101x                                     101x 32x   32x 32x                     101x                                             7x     7x     47x                                   47x     7x      
import type { InputHTMLAttributes, ReactNode } from 'react';
import { forwardRef, useId } from 'react';
import type { Control, FieldPath, FieldValues } from 'react-hook-form';
import { Controller } from 'react-hook-form';
import { cn } from '@/lib/utils';
 
export interface BaseInputProps extends InputHTMLAttributes<HTMLInputElement> {
  label?: string | ReactNode;
  error?: string;
}
 
// Enhanced Input props that can work with React Hook Form
export interface InputProps<T extends FieldValues = FieldValues>
  extends BaseInputProps {
  name?: FieldPath<T>;
  control?: Control<T>;
}
 
const BaseInput = forwardRef<HTMLInputElement, BaseInputProps>(
  ({ className, type, label, required, error, id, ...props }, ref) => {
    // Always call useId hook, then use provided id if available
    const generatedId = useId();
    const inputId = id || generatedId;
 
    const baseStyles = [
      // Layout & sizing - updated with specific dimensions
      'flex w-full h-[50px]',
      // Appearance - updated with radius and border
      'rounded-[62px] border',
      error ? 'border-red-500' : 'border-[#F0F0F0]',
      'bg-background px-[34px] text-sm',
      // Focus states
      'ring-offset-background',
      'focus-visible:outline-none',
      error ? 'focus-visible:ring-red-500' : 'focus-visible:ring-blue-500',
      // Placeholder styling
      'placeholder:text-muted-foreground',
      // File input styling
      'file:border-0 file:bg-transparent file:text-sm file:font-medium',
      // Disabled state
      'disabled:cursor-not-allowed disabled:opacity-50',
    ];
 
    const renderLabel = () => {
      Iif (!label) return null;
 
      Eif (typeof label === 'string') {
        return (
          <span className="text-sm font-medium text-foreground">
            {label}
            {required && <span className="text-red-500 ml-1">*</span>}
          </span>
        );
      }
 
      return label;
    };
 
    return (
      <div className="flex flex-col space-y-2">
        {label && (
          <label
            htmlFor={inputId}
            className="text-sm font-medium text-foreground"
          >
            {renderLabel()}
          </label>
        )}
        <input
          id={inputId}
          type={type}
          className={cn(baseStyles.join(' '), className)}
          ref={ref}
          required={required}
          {...props}
        />
        {error && <span className="text-sm text-red-500">{error}</span>}
      </div>
    );
  }
);
BaseInput.displayName = 'BaseInput';
 
// Smart Input component that works with or without React Hook Form
const Input = forwardRef<HTMLInputElement, InputProps>(
  ({ name, control, ...props }, ref) => {
    // If control and name are provided, use Controller
    Iif (control && name) {
      return (
        <Controller
          name={name as any}
          control={control as any}
          render={({ field, fieldState: { error } }) => (
            <BaseInput
              {...field}
              {...props}
              error={error?.message || props.error}
              ref={ref}
            />
          )}
        />
      );
    }
 
    // Otherwise, use as regular input and pass name prop
    return <BaseInput {...props} name={name} ref={ref} />;
  }
);
Input.displayName = 'Input';
 
export { Input, BaseInput };