ADD new frontend

This commit is contained in:
Urtzi Alfaro
2025-08-28 10:41:04 +02:00
parent 9c247a5f99
commit 0fd273cfce
492 changed files with 114979 additions and 1632 deletions

View File

@@ -0,0 +1,191 @@
import React, { forwardRef, InputHTMLAttributes } from 'react';
import { clsx } from 'clsx';
export interface InputProps extends InputHTMLAttributes<HTMLInputElement> {
label?: string;
error?: string;
helperText?: string;
isRequired?: boolean;
isInvalid?: boolean;
size?: 'sm' | 'md' | 'lg';
variant?: 'outline' | 'filled' | 'unstyled';
leftIcon?: React.ReactNode;
rightIcon?: React.ReactNode;
leftAddon?: React.ReactNode;
rightAddon?: React.ReactNode;
}
const Input = forwardRef<HTMLInputElement, InputProps>(({
label,
error,
helperText,
isRequired = false,
isInvalid = false,
size = 'md',
variant = 'outline',
leftIcon,
rightIcon,
leftAddon,
rightAddon,
className,
id,
...props
}, ref) => {
const inputId = id || `input-${Math.random().toString(36).substr(2, 9)}`;
const hasError = isInvalid || !!error;
const baseInputClasses = [
'w-full transition-colors duration-200',
'focus:outline-none',
'disabled:opacity-50 disabled:cursor-not-allowed',
'placeholder:text-[var(--text-tertiary)]'
];
const variantClasses = {
outline: [
'bg-[var(--bg-primary)] border border-[var(--border-secondary)]',
'focus:border-[var(--color-primary)] focus:ring-1 focus:ring-[var(--color-primary)]',
hasError ? 'border-[var(--color-error)] focus:border-[var(--color-error)] focus:ring-[var(--color-error)]' : ''
],
filled: [
'bg-[var(--bg-secondary)] border border-transparent',
'focus:bg-[var(--bg-primary)] focus:border-[var(--color-primary)]',
hasError ? 'border-[var(--color-error)]' : ''
],
unstyled: [
'bg-transparent border-none',
'focus:ring-0'
]
};
const sizeClasses = {
sm: leftIcon || rightIcon ? 'h-8 px-8 text-sm' : 'h-8 px-3 text-sm',
md: leftIcon || rightIcon ? 'h-10 px-10 text-base' : 'h-10 px-4 text-base',
lg: leftIcon || rightIcon ? 'h-12 px-12 text-lg' : 'h-12 px-5 text-lg'
};
const iconSizeClasses = {
sm: 'w-4 h-4',
md: 'w-5 h-5',
lg: 'w-6 h-6'
};
const inputClasses = clsx(
baseInputClasses,
variantClasses[variant],
sizeClasses[size],
'rounded-lg',
{
'pl-8': leftIcon && size === 'sm',
'pl-10': leftIcon && size === 'md',
'pl-12': leftIcon && size === 'lg',
'pr-8': rightIcon && size === 'sm',
'pr-10': rightIcon && size === 'md',
'pr-12': rightIcon && size === 'lg',
'rounded-l-none': leftAddon,
'rounded-r-none': rightAddon,
},
className
);
const addonClasses = clsx(
'inline-flex items-center px-3 text-sm bg-[var(--bg-tertiary)] border border-[var(--border-secondary)] text-[var(--text-secondary)]',
{
'border-r-0 rounded-l-lg': leftAddon,
'border-l-0 rounded-r-lg': rightAddon,
}
);
const iconClasses = clsx(
'absolute text-[var(--text-tertiary)] pointer-events-none',
iconSizeClasses[size],
{
'left-2': leftIcon && size === 'sm',
'left-2.5': leftIcon && size === 'md',
'left-3': leftIcon && size === 'lg',
'right-2': rightIcon && size === 'sm',
'right-2.5': rightIcon && size === 'md',
'right-3': rightIcon && size === 'lg',
'top-1/2 -translate-y-1/2': true,
}
);
const inputElement = (
<div className="relative">
{leftIcon && (
<div className={iconClasses}>
{leftIcon}
</div>
)}
<input
ref={ref}
id={inputId}
className={inputClasses}
aria-invalid={hasError}
aria-describedby={
error ? `${inputId}-error` :
helperText ? `${inputId}-helper` :
undefined
}
{...props}
/>
{rightIcon && (
<div className={iconClasses}>
{rightIcon}
</div>
)}
</div>
);
const inputWithAddons = (
<div className="flex">
{leftAddon && (
<span className={addonClasses}>{leftAddon}</span>
)}
{inputElement}
{rightAddon && (
<span className={addonClasses}>{rightAddon}</span>
)}
</div>
);
return (
<div className="w-full">
{label && (
<label
htmlFor={inputId}
className="block text-sm font-medium text-[var(--text-primary)] mb-2"
>
{label}
{isRequired && (
<span className="text-[var(--color-error)] ml-1">*</span>
)}
</label>
)}
{leftAddon || rightAddon ? inputWithAddons : inputElement}
{error && (
<p
id={`${inputId}-error`}
className="mt-2 text-sm text-[var(--color-error)]"
>
{error}
</p>
)}
{helperText && !error && (
<p
id={`${inputId}-helper`}
className="mt-2 text-sm text-[var(--text-secondary)]"
>
{helperText}
</p>
)}
</div>
);
});
Input.displayName = 'Input';
export default Input;

View File

@@ -0,0 +1,3 @@
export { default } from './Input';
export { default as Input } from './Input';
export type { InputProps } from './Input';