import React from 'react';
import {
Platform,
type StyleProp,
TextInput as ReactNativeTextInput,
type TextStyle,
View,
type ViewStyle,
} from 'react-native';
import { resolveFocusRingStyles } from '../../../../../internal/resolvers';
import type { TextInputProps } from '../../../../../types/text-input';
import { useTheme } from '../../../../theme/runtime';
import { resolveTextInputPresentation } from '../../utils/resolveTextInputPresentation';
/*** Renders a token-aware text input with controlled interaction policy. */
export function TextInput(props: TextInputProps) {
const { theme } = useTheme();
const [focused, setFocused] = React.useState(false);
const {
disabled: _disabled,
interactionPolicy = 'enabled',
invalid: _invalid,
leadingAccessory,
readOnly = false,
size: _size,
trailingAccessory,
...nativeProps
} = props;
const presentation = resolveTextInputPresentation(theme, props, focused);
return (
{renderAccessory(leadingAccessory, presentation.accessorySpacing, 'leading')}
{
setFocused(false);
nativeProps.onBlur?.(event);
}}
onChangeText={(nextValue) => {
if (interactionPolicy !== 'passive') nativeProps.onChangeText?.(nextValue);
}}
onFocus={(event) => {
setFocused(true);
nativeProps.onFocus?.(event);
}}
placeholderTextColor={presentation.placeholderColor}
readOnly={readOnly}
style={resolveInputStyle(presentation.inputStyle, nativeProps.multiline, nativeProps.style)}
/>
{renderAccessory(trailingAccessory, presentation.accessorySpacing, 'trailing')}
);
}
const multilineInputStyle: TextStyle = { textAlignVertical: 'top' };
/*** Resolves TextInput style composition including canonical multiline alignment. */
function resolveInputStyle(
presentationStyle: StyleProp,
multiline: boolean | undefined,
style: StyleProp,
): StyleProp {
return [presentationStyle, multiline ? multilineInputStyle : undefined, style];
}
/*** Renders one optional TextInput accessory with directional spacing. */
function renderAccessory(
accessory: React.ReactNode,
spacing: number,
position: 'leading' | 'trailing',
) {
if (!accessory) return null;
return {accessory};
}
/*** Resolves spacing around a leading or trailing TextInput accessory. */
function resolveAccessoryStyle(spacing: number, position: 'leading' | 'trailing'): ViewStyle {
return position === 'leading' ? { marginRight: spacing } : { marginLeft: spacing };
}