initial commit
This commit is contained in:
1 parent
5b935301e5
commit
85453852d5
105 files changed
+24846
No files matched your search
@@ -0,0 +1,88 @@
|
||||
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>
|
||||
)
|
||||
}
|
||||
Reference in new issue
Block a user