import React, { forwardRef, MutableRefObject } from 'react' import { FlexAlignType, InputModeOptions, KeyboardTypeOptions, Platform, Pressable, TextInput, View, } from 'react-native' import { themed } from '../theme/themed' import { Typography } from '../text/Typography' import { rv } from '../rv' import { rgba } from '../color' import { Icon } from '../Icon/Icon' import { IconNames } from '../Icon/IconNames' import { TypeScale } from '../theme/Theme/SysTokens' import { useTheme } from '../theme/context/ThemeContext' export const OutlinedFileInput = forwardRef((props, ref) => { const { ds, resolve } = useTheme() const disabled = props.disabled ?? false const editable = props.editable ?? true const error = props.error ?? false const [uncontrolledValue, setUncontrolledValue] = React.useState(props.defaultValue) const isControlled = props.value !== undefined const value = isControlled ? props.value : uncontrolledValue const [focused, setFocused] = React.useState(false) const canEnterText = !disabled && editable const inputTypeScale = resolve(ds.comp.outlinedTextInput.inputTextTypeScale) const innerRef = React.useRef(null) const inputRef = (ref !== undefined ? ref : innerRef) as MutableRefObject const forceFocus = React.useCallback(() => inputRef?.current?.focus(), [inputRef]) const handleFocus = (args: any) => { if (!canEnterText) return setFocused(true) props.onFocus?.(args) } const handleBlur = (args: Object) => { setFocused(false) if (canEnterText) props.onBlur?.(args) } const handleChangeText = (value: string) => { if (!canEnterText) return if (!isControlled) setUncontrolledValue(value) props.onChangeText?.(value) } return ( {props.leadingIcon && ( { props.onLeadingIconPress?.(); forceFocus() }} disabled={disabled} style={{ alignSelf: props.iconAlignment ?? 'center' }} > )} {props.uploadText} {props.trailingIcon && ( { props.onTrailingIconPress?.(); forceFocus() }} disabled={disabled} style={{ alignSelf: props.iconAlignment ?? 'center' }} > )} {props.supportingText} {props.errorText} ) }) const Container = themed(View, ({ ds, resolve, error, focused }) => ({ flexDirection: 'row', alignItems: 'center', backgroundColor: resolve(ds.comp.outlinedTextInput.containerColor), borderRadius: resolve(ds.comp.outlinedTextInput.containerShape), borderColor: resolve(ds.comp.outlinedTextInput.outlineColor), borderWidth: resolve(ds.comp.outlinedTextInput.outlineWidth), paddingVertical: resolve(ds.comp.outlinedTextInput.containerPaddingVertical), paddingHorizontal: resolve(ds.comp.outlinedTextInput.containerPaddingHorizontal), gap: resolve(ds.comp.outlinedTextInput.containerGap), ...(focused ? { borderColor: resolve(ds.comp.outlinedTextInput.focusedOutlineColor), borderWidth: resolve(ds.comp.outlinedTextInput.focusedOutlineWidth), paddingVertical: resolve(ds.comp.outlinedTextInput.focusedContainerPaddingVertical), paddingHorizontal: resolve(ds.comp.outlinedTextInput.focusedContainerPaddingHorizontal), } : {}), ...(error ? { borderColor: resolve(ds.comp.outlinedTextInput.errorOutlineColor), } : {}), })) const InputContainer = themed(View, ({ ds, resolve, typeScale }) => ({ flexGrow: 1, flexShrink: 1, height: resolve(typeScale?.lineHeight), justifyContent: 'center', minHeight: Math.max( resolve(ds.comp.outlinedTextInput.leadingIconSize), resolve(ds.comp.outlinedTextInput.trailingIconSize) ), })) const ErrorText = themed(Typography, ({ ds, resolve, error, disabled }) => ({ marginTop: rv(4), paddingHorizontal: resolve(ds.comp.outlinedTextInput.containerPaddingHorizontal), minHeight: resolve(resolve(ds.comp.outlinedTextInput.supportingTextTypeScale).lineHeight), color: resolve(ds.comp.outlinedTextInput.supportingTextColor), ...(error ? { color: resolve(ds.comp.outlinedTextInput.errorSupportingTextColor), } : {}), ...(disabled ? { color: rgba( resolve(ds.comp.outlinedTextInput.disabledSupportingTextColor), resolve(ds.comp.outlinedTextInput.disabledSupportingTextOpacity) ), } : {}), })) const UploadText = themed(Typography, ({ ds, resolve, error, disabled }) => ({ minHeight: resolve(resolve(ds.comp.outlinedTextInput.supportingTextTypeScale).lineHeight), color: resolve(ds.comp.outlinedTextInput.supportingTextColor), ...(disabled ? { color: rgba( resolve(ds.comp.outlinedTextInput.disabledSupportingTextColor), resolve(ds.comp.outlinedTextInput.disabledSupportingTextOpacity) ), } : {}), })) const SupportingText = themed(Typography, ({ ds, resolve, error, disabled }) => ({ minHeight: resolve(resolve(ds.comp.outlinedTextInput.supportingTextTypeScale).lineHeight), color: resolve(ds.comp.outlinedTextInput.supportingTextColor), ...(disabled ? { color: rgba( resolve(ds.comp.outlinedTextInput.disabledSupportingTextColor), resolve(ds.comp.outlinedTextInput.disabledSupportingTextOpacity) ), } : {}), })) const LeadingIcon = themed(Icon, ({ ds, resolve, disabled }) => ({ lineHeight: undefined, color: resolve(ds.sys.color.onSurface), ...(disabled ? { color: rgba( resolve(ds.comp.outlinedTextInput.disabledLeadingIconColor), resolve(ds.comp.outlinedTextInput.disabledLeadingIconOpacity) ), } : {}), })) const TrailingIcon = themed(Icon, ({ ds, resolve, disabled, trailingIconColor }) => ({ lineHeight: undefined, color: trailingIconColor ?? resolve(ds.comp.outlinedTextInput.trailingIconColor), ...(disabled ? { color: rgba( resolve(ds.comp.outlinedTextInput.disabledTrailingIconColor), resolve(ds.comp.outlinedTextInput.disabledTrailingIconOpacity) ), } : {}), })) export interface OutlinedFieldInputProps { value?: string, defaultValue?: string disabled?: boolean editable?: boolean placeholder?: string uploadText?: string supportingText?: string errorText?: string error?: boolean autoCapitalize?: 'none' | 'sentences' | 'words' | 'characters' | undefined; autoFocus?: boolean secureTextEntry?: boolean keyboardType?: KeyboardTypeOptions inputMode?: InputModeOptions maxLength?: number leadingIcon?: IconNames leadingIconColor?: string onLeadingIconPress?: () => void trailingIcon?: IconNames trailingIconColor?: string onTrailingIconPress?: () => void iconAlignment?: FlexAlignType onChangeText?: (text: string) => void onFocus?: (args: any) => void onBlur?: (args: any) => void typeScale?: TypeScale textAlign?: 'auto' | 'center' | 'left' | 'right' | 'justify' textAlignVertical?: 'auto' | 'center' | 'top' | 'bottom' }