Skip to content
Clone
main
99 lines 3.1 KB
import {
IconAlertCircle as AlertCircle,
IconEye as Eye,
IconEyeOff as EyeOff,
IconKey as KeyRound,
IconLock as LockKeyhole,
IconMail as Mail,
IconUser as UserRound,
} from '@tabler/icons-react'
import { useState } from 'react'
import { t } from '@/shared/i18n'
export interface FieldProps {
name: string
label: string
value: string
onInput?: (value: string) => void
placeholder?: string
type?: 'text' | 'email' | 'password'
icon?: 'mail' | 'lock' | 'user' | 'key'
helper?: string
error?: string
autoComplete?: string
disabled?: boolean
readOnly?: boolean
maxLength?: number
}
const fieldIcons = { mail: Mail, lock: LockKeyhole, user: UserRound, key: KeyRound }
export function Field({
name,
label,
value,
onInput,
placeholder,
type = 'text',
icon = 'mail',
helper,
error,
autoComplete,
disabled,
readOnly,
maxLength,
}: FieldProps) {
const [show, setShow] = useState(false)
const Icon = fieldIcons[icon]
const description = error || helper
return (
<div className="flex min-w-0 flex-col gap-2">
<label htmlFor={name} className="text-sm leading-normal font-medium">
{label}
</label>
<div
className={`flex h-12 items-center gap-3 rounded-lg border bg-field pl-3 transition-colors focus-within:border-focus ${error ? 'border-danger' : 'border-control-border'} ${disabled ? 'bg-disabled text-disabled-ink' : ''}`}
>
<Icon size={18} className="shrink-0 text-icon" strokeWidth={1.6} />
<input
id={name}
name={name}
type={type === 'password' && show ? 'text' : type}
value={value}
onChange={(event) => onInput?.(event.currentTarget.value)}
placeholder={placeholder}
autoComplete={autoComplete}
disabled={disabled}
readOnly={readOnly}
maxLength={maxLength}
autoCapitalize="none"
spellCheck={false}
aria-invalid={!!error}
aria-describedby={description ? `${name}-description` : undefined}
className={`h-full min-w-0 flex-1 border-0 bg-transparent text-base text-ink outline-none placeholder:text-muted disabled:cursor-not-allowed ${type === 'email' ? 'font-latin' : ''} ${type === 'password' ? '' : 'pr-3'}`}
/>
{type === 'password' && (
<button
type="button"
disabled={disabled}
aria-label={show ? t('ui.field.conceal', { label }) : t('ui.field.reveal', { label })}
aria-pressed={show}
onClick={() => setShow(!show)}
className="mr-px flex size-11 shrink-0 items-center justify-center rounded-md text-icon hover:text-ink"
>
{show ? <Eye size={18} strokeWidth={1.6} /> : <EyeOff size={18} strokeWidth={1.6} />}
</button>
)}
</div>
{description && (
<p
id={`${name}-description`}
className={`flex items-start gap-1 text-xs leading-normal ${error ? 'text-danger' : 'text-muted'}`}
>
{error && <AlertCircle size={14} className="mt-0.5 shrink-0" />}
{description}
</p>
)}
</div>
)
}