Files
mdm-ordinis/ordinis-admin-ui/src/ui/components/input.tsx
T
2026-05-10 22:17:06 +00:00

75 lines
2.7 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import * as React from 'react'
import { cn } from '@/lib/utils'
/**
* Text input — single-line. Использует наши tokens, focus-ring через accent.
*
* Поддерживает 2 режима:
* - "bare" (default) — просто <input>, для inline использования в кастомных
* layout'ах (см. SearchInput, FormGrid с FieldLabel separately).
* - "field" — когда передан label / hint / error, рендерится FormField
* обёртка с label сверху + hint/error снизу. Match'ит @nstart/ui TextInput API.
*/
export type InputProps = React.InputHTMLAttributes<HTMLInputElement> & {
/** Если задан — рендерится field-обёртка с label выше. */
label?: React.ReactNode
/** Optional helper text under input. */
hint?: React.ReactNode
/** Error message — overrides hint визуально, accent в pink. */
error?: React.ReactNode
/** Wrapper className когда rendering field-mode. */
containerClassName?: string
}
export const Input = React.forwardRef<HTMLInputElement, InputProps>(
function Input(
{ className, type = 'text', label, hint, error, required, containerClassName, id, ...props },
ref,
) {
const autoId = React.useId()
const inputId = id ?? autoId
const inputEl = (
<input
id={inputId}
ref={ref}
type={type}
required={required}
aria-required={required || undefined}
aria-invalid={Boolean(error) || undefined}
className={cn(
'flex h-9 w-full rounded-md border border-line bg-surface px-3 py-1 text-sm text-ink',
'placeholder:text-mute',
'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-1 focus-visible:ring-offset-bg',
'disabled:cursor-not-allowed disabled:opacity-50',
'file:border-0 file:bg-transparent file:text-sm file:font-medium',
error && 'border-pink focus-visible:ring-pink',
className,
)}
{...props}
/>
)
if (!label && !hint && !error) return inputEl
return (
<div className={cn('flex flex-col gap-1', containerClassName)}>
{label && (
<label
htmlFor={inputId}
className="text-2xs font-medium font-display uppercase tracking-[0.10em] text-mute leading-none"
>
{label}
{required && <span className="text-pink ml-0.5">*</span>}
</label>
)}
{inputEl}
{error ? (
<span className="text-xs text-pink">{error}</span>
) : hint ? (
<span className="text-xs text-mute">{hint}</span>
) : null}
</div>
)
},
)