learning_ai_common_plat/packages/ui/src/components/Input.tsx

74 lines
2.6 KiB
TypeScript

import * as React from 'react';
import { clsx } from 'clsx';
export interface InputProps extends React.InputHTMLAttributes<HTMLInputElement> {
label?: string;
error?: string;
hint?: string;
controlSize?: 'sm' | 'md' | 'lg';
variant?: 'surface' | 'muted' | 'ghost';
}
export const Input = React.forwardRef<HTMLInputElement, InputProps>(
(
{ label, error, hint, controlSize = 'md', variant = 'surface', className, id, ...props },
ref
) => {
const generatedId = React.useId();
const inputId = id ?? (label || error || hint ? `input-${generatedId}` : undefined);
const sizes: Record<NonNullable<InputProps['controlSize']>, string> = {
sm: 'h-8 px-2.5 text-xs',
md: 'h-10 px-3 text-sm',
lg: 'h-11 px-4 text-sm',
};
const variants: Record<NonNullable<InputProps['variant']>, string> = {
surface: 'bg-[var(--bl-input,var(--bl-surface-card,#1a1a2e))]',
muted: 'bg-[var(--bl-surface-muted,#252540)]',
ghost: 'bg-transparent',
};
return (
<div className="grid gap-1.5">
{label && (
<label
htmlFor={inputId}
className="block text-xs font-semibold uppercase tracking-[0.08em] text-[var(--bl-text-secondary,#a0a0b0)]"
>
{label}
</label>
)}
<input
ref={ref}
id={inputId}
className={clsx(
'w-full rounded-lg border shadow-sm shadow-black/[0.03] outline-none transition duration-150',
variants[variant],
sizes[controlSize],
'text-[var(--bl-text-primary,#fff)]',
'placeholder:text-[var(--bl-text-tertiary,#555)]',
'disabled:cursor-not-allowed disabled:opacity-60',
'focus:border-[var(--bl-focus-ring,var(--bl-accent,#5A8CFF))] focus:ring-2 focus:ring-[var(--bl-focus-ring-muted,var(--bl-accent-muted,rgba(90,140,255,0.2)))] focus:ring-offset-0',
error ? 'border-[var(--bl-danger)]' : 'border-[var(--bl-border,#2a2a4a)]',
className
)}
aria-invalid={error ? 'true' : undefined}
aria-describedby={error ? `${inputId}-error` : hint ? `${inputId}-hint` : undefined}
{...props}
/>
{error && (
<p id={`${inputId}-error`} className="text-xs text-[var(--bl-danger)]" role="alert">
{error}
</p>
)}
{hint && !error && (
<p id={`${inputId}-hint`} className="text-xs text-[var(--bl-text-tertiary,#555)]">
{hint}
</p>
)}
</div>
);
}
);
Input.displayName = 'Input';