import { useState } from 'react' import { View, Text, TextInput, Pressable, StyleSheet, Platform, type KeyboardTypeOptions } from 'react-native' import { colors, spacing, radii, typography } from '../theme/mobile-theme' import { BottomDrawer } from './BottomDrawer' type Props = { visible: boolean title: string message?: string defaultValue?: string placeholder?: string submitLabel?: string selectTextOnFocus?: boolean allowEmpty?: boolean keyboardType?: KeyboardTypeOptions onSubmit: (value: string) => void onCancel: () => void } export function TextInputModal({ visible, title, message, defaultValue = '', placeholder, submitLabel = 'Save', selectTextOnFocus = false, allowEmpty = false, keyboardType, onSubmit, onCancel }: Props) { const [value, setValue] = useState(defaultValue) const [previousVisible, setPreviousVisible] = useState(visible) const [previousDefaultValue, setPreviousDefaultValue] = useState(defaultValue) // Why: reset before the opening commit so the drawer never paints the // previous modal value while preserving the existing close animation state. const shouldResetValue = visible && (!previousVisible || defaultValue !== previousDefaultValue) if (visible !== previousVisible || shouldResetValue) { setPreviousVisible(visible) if (shouldResetValue) { setPreviousDefaultValue(defaultValue) setValue(defaultValue) } } function handleSubmit() { const trimmed = value.trim() if (trimmed || allowEmpty) { onSubmit(trimmed) } } const canSubmit = allowEmpty || value.trim().length > 0 return ( {title} {message ? {message} : null} [styles.cancelButton, pressed && styles.buttonPressed]} onPress={onCancel} > Cancel [ styles.submitButton, pressed && styles.buttonPressed, !canSubmit && styles.submitButtonDisabled ]} disabled={!canSubmit} onPress={handleSubmit} > {submitLabel} ) } const styles = StyleSheet.create({ header: { paddingHorizontal: spacing.xs, paddingBottom: spacing.sm }, title: { fontSize: 15, fontWeight: '600', color: colors.textPrimary }, message: { fontSize: 13, color: colors.textMuted, marginTop: 2 }, // Why: matches NewWorktreeModal's input — bgRaised on the modal // background reads as a tappable surface (brighter than the wrapper) // rather than a recessed pit (darker than the wrapper, which is what // bgBase looked like inside a bgPanel group). input: { backgroundColor: colors.bgRaised, color: colors.textPrimary, borderRadius: radii.input, paddingHorizontal: spacing.md, paddingVertical: Platform.OS === 'ios' ? spacing.sm + 2 : spacing.sm, fontSize: typography.bodySize, borderWidth: 1, borderColor: colors.borderSubtle }, actions: { flexDirection: 'row', justifyContent: 'flex-end', gap: spacing.sm, marginTop: spacing.md }, cancelButton: { paddingHorizontal: spacing.lg, paddingVertical: spacing.sm, borderRadius: radii.button }, submitButton: { backgroundColor: colors.textPrimary, paddingHorizontal: spacing.lg, paddingVertical: spacing.sm, borderRadius: radii.button }, buttonPressed: { opacity: 0.7 }, submitButtonDisabled: { opacity: 0.4 }, cancelText: { color: colors.textSecondary, fontSize: typography.bodySize, fontWeight: '500' }, submitText: { color: colors.bgBase, fontSize: typography.bodySize, fontWeight: '600' } })