@aegov/design-system-react
Version:
A design system for the Government of the United Arab Emirates. Extending the original design system released as @aegov/design-system, this is the package specefically created for the ReactJs enviornment.
145 lines (127 loc) • 3.61 kB
JSX
import React, { forwardRef } from 'react';
import { Root as LabelRoot } from '@radix-ui/react-label';
import { Eye, EyeSlash, MagnifyingGlass } from '@phosphor-icons/react';
import { cn } from '../../utils';
const Input = forwardRef(({
type = 'text',
label,
error,
helperText,
prefix,
suffix,
className,
size = 'base',
variant = 'primary',
disabled,
required,
id,
...props
}, ref) => {
const [showPassword, setShowPassword] = React.useState(false);
const [localType, setLocalType] = React.useState(type);
React.useEffect(() => {
if (type === 'password') {
setLocalType(showPassword ? 'text' : 'password');
}
}, [showPassword, type]);
const sizeClasses = {
sm: 'h-10 text-sm',
base: 'h-12 text-base',
lg: 'h-14 text-lg'
};
const labelSizeClasses = {
sm: 'text-sm',
base: 'text-sm',
lg: 'text-base'
};
const variantClasses = {
primary: 'focus-within:ring-primary-support-400 ring-primary-400',
secondary: 'focus-within:ring-secondary-600 ring-secondary-400',
error: 'focus-within:ring-red-600 ring-red-400 bg-red-50'
};
const inputContainerClasses = cn(
'relative flex rounded-lg shadow-sm ring-2 ring-inset focus-within:ring-2 focus-within:ring-inset bg-whitely-50',
variantClasses[error ? 'error' : variant],
disabled && 'opacity-40',
className
);
const inputClasses = cn(
'w-full flex-1 border-0 bg-transparent ps-4 text-gray-900 placeholder:text-gray-400',
'focus:ring-0 outline-none disabled:cursor-not-allowed',
sizeClasses[size],
prefix && 'pl-1.5',
suffix && 'pr-1.5'
);
const renderPasswordToggle = () => (
<button
type="button"
onClick={() => setShowPassword(!showPassword)}
className="pr-4 rtl:pl-4 text-gray-400 hover:text-gray-600 focus:outline-none"
>
{showPassword ? (
<EyeSlash className="h-6 w-6" />
) : (
<Eye className="h-6 w-6" />
)}
</button>
);
const renderSearchIcon = () => (
<MagnifyingGlass className="h-6 w-6 text-gray-400" />
);
return (
<div className="w-full">
{label && (
<LabelRoot
htmlFor={id}
className={cn(
'mb-1 block font-medium text-gray-900',
labelSizeClasses[size],
error && 'text-red-600',
required && 'after:ml-0.5 after:text-red-500 after:content-["*"]'
)}
>
{label}
</LabelRoot>
)}
<div className={inputContainerClasses}>
{prefix && (
<div className="flex select-none items-center ps-4 text-gray-500">
{prefix}
</div>
)}
{type === 'search' && !prefix && (
<div className="flex select-none items-center ps-4 text-gray-500">
{renderSearchIcon()}
</div>
)}
<input
ref={ref}
type={localType}
id={id}
disabled={disabled}
required={required}
className={inputClasses}
{...props}
/>
{suffix && (
<div className="flex select-none items-center pr-4 text-gray-500">
{suffix}
</div>
)}
{type === 'password' && !suffix && renderPasswordToggle()}
</div>
{(error || helperText) && (
<p
className={cn(
'mt-1 text-sm text-aeblack-500',
error ? 'text-red-600' : 'text-gray-500'
)}
>
{error || helperText}
</p>
)}
</div>
);
});
Input.displayName = 'Input';
export default Input;