/** * Input (native). Matches the web shape; uses TextInput. */ import { forwardRef, useState } from "react"; import { TextInput, View, type TextInputProps, type ViewStyle } from "react-native"; import { useTheme } from "@plyxui/styles"; import { radius, spacing } from "@plyxui/core"; export type InputSize = "sm" | "md" | "lg"; const sizeMap: Record = { sm: { h: 32, fontSize: 13, padX: spacing[2] }, md: { h: 40, fontSize: 14, padX: spacing[3] }, lg: { h: 48, fontSize: 16, padX: spacing[4] }, }; export interface InputProps extends Omit { value?: string; onChange?: (value: string) => void; size?: InputSize; invalid?: boolean; leading?: React.ReactNode; trailing?: React.ReactNode; style?: ViewStyle; } export const Input = forwardRef(function Input( { value, onChange, size = "md", invalid, leading, trailing, style, defaultValue, ...rest }, ref, ) { const { colors } = useTheme(); const [internal, setInternal] = useState(defaultValue ?? ""); const current = value ?? internal; const dims = sizeMap[size]; const wrap: ViewStyle = { flexDirection: "row", alignItems: "center", gap: spacing[2], height: dims.h, paddingLeft: leading ? spacing[2] : dims.padX, paddingRight: trailing ? spacing[2] : dims.padX, backgroundColor: colors.surfaceFill, borderWidth: 1, borderColor: invalid ? colors.statusError : colors.stroke, borderRadius: radius.md, ...style, }; return ( {leading} { if (value === undefined) setInternal(t); onChange?.(t); }} style={{ flex: 1, minWidth: 0, height: "100%", color: colors.text, fontSize: dims.fontSize, padding: 0, }} placeholderTextColor={colors.textMuted} {...rest} /> {trailing} ); });