Spaces:
Sleeping
Sleeping
File size: 3,818 Bytes
0cfd364 | 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 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 | import React, { forwardRef } from 'react';
import { classNames } from '@/utils/helpers';
interface InputProps extends React.InputHTMLAttributes<HTMLInputElement> {
label?: string;
error?: string;
hint?: string;
leftIcon?: React.ReactNode;
rightIcon?: React.ReactNode;
variant?: 'default' | 'filled';
}
export const Input = forwardRef<HTMLInputElement, InputProps>(
(
{
label,
error,
hint,
leftIcon,
rightIcon,
variant = 'default',
className,
id,
...props
},
ref
) => {
const inputId = id || `input-${Math.random().toString(36).slice(2, 9)}`;
const variantStyles = {
default: 'bg-dark-900 border-dark-600',
filled: 'bg-dark-700 border-transparent',
};
return (
<div className="w-full">
{label && (
<label
htmlFor={inputId}
className="block text-sm font-medium text-dark-300 mb-1.5"
>
{label}
</label>
)}
<div className="relative">
{leftIcon && (
<div className="absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none text-dark-500">
{leftIcon}
</div>
)}
<input
ref={ref}
id={inputId}
className={classNames(
'w-full px-3 py-2 border rounded-lg text-dark-100 placeholder-dark-500',
'focus:outline-none focus:border-accent-primary focus:ring-1 focus:ring-accent-primary/50',
'transition-colors duration-200',
variantStyles[variant],
leftIcon ? 'pl-10' : '',
rightIcon ? 'pr-10' : '',
error ? 'border-red-500 focus:border-red-500 focus:ring-red-500/50' : '',
className
)}
{...props}
/>
{rightIcon && (
<div className="absolute inset-y-0 right-0 pr-3 flex items-center text-dark-500">
{rightIcon}
</div>
)}
</div>
{(error || hint) && (
<p
className={classNames(
'mt-1.5 text-sm',
error ? 'text-red-400' : 'text-dark-500'
)}
>
{error || hint}
</p>
)}
</div>
);
}
);
Input.displayName = 'Input';
interface TextareaProps
extends React.TextareaHTMLAttributes<HTMLTextAreaElement> {
label?: string;
error?: string;
hint?: string;
}
export const Textarea = forwardRef<HTMLTextAreaElement, TextareaProps>(
({ label, error, hint, className, id, ...props }, ref) => {
const inputId = id || `textarea-${Math.random().toString(36).slice(2, 9)}`;
return (
<div className="w-full">
{label && (
<label
htmlFor={inputId}
className="block text-sm font-medium text-dark-300 mb-1.5"
>
{label}
</label>
)}
<textarea
ref={ref}
id={inputId}
className={classNames(
'w-full px-3 py-2 bg-dark-900 border border-dark-600 rounded-lg',
'text-dark-100 placeholder-dark-500 resize-none',
'focus:outline-none focus:border-accent-primary focus:ring-1 focus:ring-accent-primary/50',
'transition-colors duration-200',
error && 'border-red-500 focus:border-red-500 focus:ring-red-500/50',
className
)}
{...props}
/>
{(error || hint) && (
<p
className={classNames(
'mt-1.5 text-sm',
error ? 'text-red-400' : 'text-dark-500'
)}
>
{error || hint}
</p>
)}
</div>
);
}
);
Textarea.displayName = 'Textarea';
export default Input;
|