115 lines
4.0 KiB
JavaScript
115 lines
4.0 KiB
JavaScript
'use client';
|
|
|
|
import React from 'react';
|
|
|
|
const Input = ({
|
|
type = 'text',
|
|
value,
|
|
onChange,
|
|
placeholder = '',
|
|
label,
|
|
error,
|
|
required = false,
|
|
disabled = false,
|
|
className = '',
|
|
description,
|
|
min,
|
|
max,
|
|
step,
|
|
...props
|
|
}) => {
|
|
const baseInputClassName = `w-full px-[10px] py-[7px] rounded-lg text-[13px] focus:outline-none transition-all duration-[120ms] ease-out disabled:opacity-50 disabled:cursor-not-allowed bg-white border border-neutral-300 text-neutral-900 placeholder-neutral-400 focus:border-neutral-500 focus:ring-1 focus:ring-neutral-500/20 dark:bg-neutral-900/60 dark:border-neutral-700/50 dark:text-white dark:placeholder-neutral-500 dark:focus:border-neutral-600 dark:focus:ring-neutral-600/20 ${
|
|
error ? 'border-red-500/50 dark:border-red-500/50' : ''
|
|
} ${className}`;
|
|
|
|
const handleChange = (e) => {
|
|
let newValue = e.target.value;
|
|
|
|
// Handle number type conversions
|
|
if (type === 'number') {
|
|
// Convert empty string to 0 for numeric inputs to prevent database errors
|
|
if (newValue === '' || newValue === null || newValue === undefined) {
|
|
newValue = 0;
|
|
} else {
|
|
newValue = parseFloat(newValue);
|
|
// Handle NaN case
|
|
if (isNaN(newValue)) {
|
|
newValue = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
onChange?.(newValue);
|
|
};
|
|
|
|
// Enhanced color input renderer
|
|
const renderColorInput = () => {
|
|
return (
|
|
<div className="flex gap-2">
|
|
<div className="relative">
|
|
<input
|
|
type="color"
|
|
className="absolute inset-0 w-12 h-10 opacity-0 cursor-pointer"
|
|
value={value || '#000000'}
|
|
onChange={handleChange}
|
|
disabled={disabled}
|
|
{...props}
|
|
/>
|
|
<div
|
|
className={`w-12 h-10 border rounded-lg cursor-pointer transition-all duration-[120ms] ease-out ${
|
|
error ? 'border-red-500/50' : 'border-neutral-300 dark:border-neutral-700/50 dark:hover:border-neutral-600'
|
|
} ${disabled ? 'opacity-50 cursor-not-allowed' : ''}`}
|
|
style={{ backgroundColor: value || '#000000' }}
|
|
></div>
|
|
</div>
|
|
<input
|
|
type="text"
|
|
className={`${baseInputClassName} flex-1 min-w-0`}
|
|
value={value || ''}
|
|
onChange={handleChange}
|
|
placeholder={placeholder || 'Enter hex color'}
|
|
disabled={disabled}
|
|
{...props}
|
|
/>
|
|
</div>
|
|
);
|
|
};
|
|
|
|
return (
|
|
<div className="space-y-2">
|
|
{label && (
|
|
<label className="block text-xs font-medium text-neutral-700 dark:text-white">
|
|
{label}
|
|
{required && <span className="text-red-500 dark:text-red-400 ml-1">*</span>}
|
|
</label>
|
|
)}
|
|
|
|
{type === 'color' ? (
|
|
renderColorInput()
|
|
) : (
|
|
<input
|
|
type={type}
|
|
value={value}
|
|
onChange={handleChange}
|
|
placeholder={placeholder}
|
|
className={baseInputClassName}
|
|
disabled={disabled}
|
|
min={min}
|
|
max={max}
|
|
step={step}
|
|
{...props}
|
|
/>
|
|
)}
|
|
|
|
{error && (
|
|
<p className="text-red-600 dark:text-red-400 text-xs">{error}</p>
|
|
)}
|
|
|
|
{description && !error && (
|
|
<p className="text-xs text-neutral-500 dark:text-neutral-400 opacity-75">{description}</p>
|
|
)}
|
|
</div>
|
|
);
|
|
};
|
|
|
|
export default Input;
|