Some checks failed
CI / lint-typecheck (pull_request) Failing after 10s
CI / tests (pull_request) Has been skipped
CI / contract-tests (pull_request) Has been skipped
CI / e2e-tests (pull_request) Has been skipped
CI / comment-pr (pull_request) Has been skipped
CI / commit-types (pull_request) Has been skipped
112 lines
3.0 KiB
TypeScript
112 lines
3.0 KiB
TypeScript
import React, { forwardRef, InputHTMLAttributes } from 'react';
|
|
import { Box } from './Box';
|
|
import { Icon } from './Icon';
|
|
import { Text } from './Text';
|
|
import { LucideIcon } from 'lucide-react';
|
|
|
|
export interface InputProps extends Omit<InputHTMLAttributes<HTMLInputElement>, 'size'> {
|
|
icon?: LucideIcon | React.ReactNode;
|
|
rightElement?: React.ReactNode;
|
|
variant?: 'default' | 'ghost' | 'search' | 'error';
|
|
fullWidth?: boolean;
|
|
label?: string;
|
|
error?: string;
|
|
errorMessage?: string; // Alias for error
|
|
hint?: string;
|
|
size?: string | number; // Allow size prop
|
|
}
|
|
|
|
export const Input = forwardRef<HTMLInputElement, InputProps>(({
|
|
icon,
|
|
rightElement,
|
|
variant = 'default',
|
|
fullWidth = false,
|
|
className,
|
|
label,
|
|
error,
|
|
errorMessage,
|
|
hint,
|
|
id,
|
|
size,
|
|
...props
|
|
}, ref) => {
|
|
const { 'data-testid': testId, ...restProps } = props as any;
|
|
const variantClasses = {
|
|
default: 'bg-surface-charcoal border border-outline-steel focus:border-primary-accent',
|
|
ghost: 'bg-transparent border-none',
|
|
search: 'bg-surface-charcoal/50 border border-outline-steel focus:border-primary-accent hover:border-text-low/50',
|
|
error: 'bg-surface-charcoal border border-critical-red focus:border-critical-red',
|
|
};
|
|
|
|
const inputId = id || (label ? `input-${label.toLowerCase().replace(/\s+/g, '-')}` : undefined);
|
|
const displayError = error || errorMessage;
|
|
|
|
return (
|
|
<Box width={fullWidth ? 'full' : 'auto'}>
|
|
{label && (
|
|
<Box marginBottom={1.5}>
|
|
<Text
|
|
as="label"
|
|
htmlFor={inputId}
|
|
size="sm"
|
|
weight="medium"
|
|
variant="high"
|
|
>
|
|
{label}
|
|
</Text>
|
|
</Box>
|
|
)}
|
|
|
|
<Box
|
|
data-testid={testId ? `${testId}-container` : undefined}
|
|
display="flex"
|
|
alignItems="center"
|
|
gap={3}
|
|
paddingX={3}
|
|
height="9" // h-9
|
|
rounded="md"
|
|
width="full"
|
|
className={`transition-all duration-200 group ${variantClasses[variant]}`}
|
|
>
|
|
{icon && (
|
|
React.isValidElement(icon) ? icon : (
|
|
<Icon
|
|
icon={icon as LucideIcon}
|
|
size={3.5}
|
|
className="text-text-low group-focus-within:text-text-high transition-colors"
|
|
/>
|
|
)
|
|
)}
|
|
|
|
<input
|
|
ref={ref}
|
|
id={inputId}
|
|
className="bg-transparent border-none outline-none text-sm w-full text-text-high placeholder:text-text-low/50 h-full"
|
|
data-testid={testId}
|
|
{...restProps}
|
|
/>
|
|
|
|
{rightElement}
|
|
</Box>
|
|
|
|
{displayError && (
|
|
<Box marginTop={1}>
|
|
<Text size="xs" variant="critical">
|
|
{displayError}
|
|
</Text>
|
|
</Box>
|
|
)}
|
|
|
|
{hint && !displayError && (
|
|
<Box marginTop={1}>
|
|
<Text size="xs" variant="low">
|
|
{hint}
|
|
</Text>
|
|
</Box>
|
|
)}
|
|
</Box>
|
|
);
|
|
});
|
|
|
|
Input.displayName = 'Input';
|