Files
fancy-dns/web/src/components/ui/Input.tsx
T
2026-09-27 11:20:34 +00:00

89 lines
2.3 KiB
TypeScript

import { useId } from 'react'
import type { InputHTMLAttributes, ReactNode } from 'react'
import type { LucideIcon } from 'lucide-react'
import { cn } from '../../lib/cn.ts'
export const FIELD_BASE =
'w-full rounded-lg border bg-surface-2 text-fg placeholder:text-fg-subtle transition-colors focus:outline-none focus-visible:border-accent disabled:opacity-60'
export function FieldFrame({
id,
label,
hint,
error,
children,
}: {
id: string
label?: ReactNode
hint?: ReactNode
error?: ReactNode
children: ReactNode
}) {
return (
<div className="flex min-w-0 flex-col gap-1.5">
{label !== undefined && (
<label htmlFor={id} className="text-xs font-medium text-fg-muted">
{label}
</label>
)}
{children}
{error !== undefined && error !== null && error !== '' ? (
<p id={`${id}-error`} role="alert" className="text-xs text-danger">
{error}
</p>
) : hint !== undefined ? (
<p id={`${id}-hint`} className="text-xs text-fg-subtle">
{hint}
</p>
) : null}
</div>
)
}
export interface InputProps extends InputHTMLAttributes<HTMLInputElement> {
label?: ReactNode
hint?: ReactNode
error?: ReactNode
icon?: LucideIcon
className?: string
}
export function Input({ label, hint, error, icon: Icon, className, id, ...props }: InputProps) {
const autoId = useId()
const inputId = id ?? autoId
const describedBy = error ? `${inputId}-error` : hint !== undefined ? `${inputId}-hint` : undefined
const input = (
<div className="relative">
{Icon && (
<Icon
size={15}
strokeWidth={2}
aria-hidden="true"
className="pointer-events-none absolute top-1/2 left-2.5 -translate-y-1/2 text-fg-subtle"
/>
)}
<input
id={inputId}
aria-invalid={error ? true : undefined}
aria-describedby={describedBy}
className={cn(
FIELD_BASE,
'h-9 px-2.5 text-sm',
Icon && 'pl-8',
error ? 'border-danger' : 'border-line',
className,
)}
{...props}
/>
</div>
)
if (label === undefined && hint === undefined && error === undefined) return input
return (
<FieldFrame id={inputId} label={label} hint={hint} error={error}>
{input}
</FieldFrame>
)
}