feat(ui): add size and variant props to Input component

- add `size` prop (sm | md | lg) with corresponding tailwind size classes
- add `variant` prop supporting `default` and `ghost` styles
- add focus state tracking to enable ghost→default transition on focus
- forward `onFocus` and `onBlur` callbacks with internal focus handling
- isolate color input styling from size/variant logic
This commit is contained in:
2026-04-26 11:37:42 -04:00
parent 649c69f408
commit 3fea89dbd4
+42 -6
View File
@@ -1,6 +1,6 @@
'use client'; 'use client';
import React from 'react'; import React, { useState } from 'react';
const Input = ({ const Input = ({
type = 'text', type = 'text',
@@ -16,11 +16,33 @@ const Input = ({
min, min,
max, max,
step, step,
size = 'md',
variant = 'default',
onFocus,
onBlur,
...props ...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 ${ const [focused, setFocused] = useState(false);
error ? 'border-red-500/50 dark:border-red-500/50' : ''
} ${className}`; const sizeClasses = {
sm: 'px-[8px] py-[5px] text-[12px]',
md: 'px-[10px] py-[7px] text-[13px]',
lg: 'px-[14px] py-[10px] text-[20px] font-semibold',
};
const defaultVariant = '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';
// Ghost : bordure transparente + texte gris au repos. Au focus, prend l'apparence default.
const ghostRest = 'bg-transparent border border-transparent text-neutral-500 placeholder-neutral-400 dark:text-neutral-400 dark:placeholder-neutral-600';
const ghostFocus = defaultVariant;
const variantClasses = variant === 'ghost'
? (focused ? ghostFocus : ghostRest)
: defaultVariant;
const errorClasses = error ? 'border-red-500/50 dark:border-red-500/50' : '';
const baseInputClassName = `w-full rounded-lg focus:outline-none transition-all duration-[120ms] ease-out disabled:opacity-50 disabled:cursor-not-allowed ${sizeClasses[size] || sizeClasses.md} ${variantClasses} ${errorClasses} ${className}`;
const handleChange = (e) => { const handleChange = (e) => {
let newValue = e.target.value; let newValue = e.target.value;
@@ -42,7 +64,19 @@ const Input = ({
onChange?.(newValue); onChange?.(newValue);
}; };
// Enhanced color input renderer const handleFocus = (e) => {
setFocused(true);
onFocus?.(e);
};
const handleBlur = (e) => {
setFocused(false);
onBlur?.(e);
};
// Color input — non concerné par size/variant, garde son apparence dédiée.
const colorBaseClassName = `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 ${defaultVariant} ${errorClasses}`;
const renderColorInput = () => { const renderColorInput = () => {
return ( return (
<div className="flex gap-2"> <div className="flex gap-2">
@@ -64,7 +98,7 @@ const Input = ({
</div> </div>
<input <input
type="text" type="text"
className={`${baseInputClassName} flex-1 min-w-0`} className={`${colorBaseClassName} flex-1 min-w-0`}
value={value || ''} value={value || ''}
onChange={handleChange} onChange={handleChange}
placeholder={placeholder || 'Enter hex color'} placeholder={placeholder || 'Enter hex color'}
@@ -91,6 +125,8 @@ const Input = ({
type={type} type={type}
value={value} value={value}
onChange={handleChange} onChange={handleChange}
onFocus={handleFocus}
onBlur={handleBlur}
placeholder={placeholder} placeholder={placeholder}
className={baseInputClassName} className={baseInputClassName}
disabled={disabled} disabled={disabled}