From 3fea89dbd466c8b9f26e881f3ab60f0fd1853aa4 Mon Sep 17 00:00:00 2001 From: Hyko Date: Sun, 26 Apr 2026 11:37:42 -0400 Subject: [PATCH] feat(ui): add size and variant props to Input component MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 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 --- src/shared/components/Input.js | 62 +++++++++++++++++++++++++++------- 1 file changed, 49 insertions(+), 13 deletions(-) diff --git a/src/shared/components/Input.js b/src/shared/components/Input.js index 80d820a..aa623f3 100644 --- a/src/shared/components/Input.js +++ b/src/shared/components/Input.js @@ -1,6 +1,6 @@ 'use client'; -import React from 'react'; +import React, { useState } from 'react'; const Input = ({ type = 'text', @@ -16,15 +16,37 @@ const Input = ({ min, max, step, + size = 'md', + variant = 'default', + onFocus, + onBlur, ...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 [focused, setFocused] = useState(false); + + 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) => { let newValue = e.target.value; - + // Handle number type conversions if (type === 'number') { // Convert empty string to 0 for numeric inputs to prevent database errors @@ -38,11 +60,23 @@ const Input = ({ } } } - + 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 = () => { return (
@@ -55,7 +89,7 @@ const Input = ({ disabled={disabled} {...props} /> -
*} )} - + {type === 'color' ? ( renderColorInput() ) : ( @@ -91,6 +125,8 @@ const Input = ({ type={type} value={value} onChange={handleChange} + onFocus={handleFocus} + onBlur={handleBlur} placeholder={placeholder} className={baseInputClassName} disabled={disabled} @@ -100,11 +136,11 @@ const Input = ({ {...props} /> )} - + {error && (

{error}

)} - + {description && !error && (

{description}

)} @@ -112,4 +148,4 @@ const Input = ({ ); }; -export default Input; \ No newline at end of file +export default Input;