/* eslint-disable react-hooks/exhaustive-deps */ import React, { forwardRef, useContext, useEffect, useImperativeHandle, useRef, useState, } from 'react'; import { NativeSyntheticEvent, TextInput, TextInputFocusEventData, TouchableOpacity, View, } from 'react-native'; import { useComponentId } from '../Application'; import { ApplicationContext, ComponentContext, MiniAppContext, } from '../Context'; import { Icon } from '../Icon'; import { useScaleSize } from '../Text'; import { Spacing } from '../Consts'; import { ErrorView, FloatingView, getBorderColor, getSizeStyle, RenderTrailing, } from './common'; import { InputMoneyProps } from './index'; import styles from './styles'; import { formatNumberToMoney } from './utils'; import SystemTextInput from './SystemTextInput'; const InputMoney = forwardRef( ( { value, onChangeText, floatingValue, floatingIcon, size = 'small', loading, onBlur, onFocus, errorMessage, trailing, trailingColor, onPressTrailing, disabled = false, floatingIconColor, required = false, errorSpacing, style, params, hintText, onPressFloatingIcon, placeholder = '0đ', showClearIcon = true, currency = 'đ', ...props }: InputMoneyProps, ref, ) => { const { theme } = useContext(ApplicationContext); const context = useContext(MiniAppContext); const scaleHeight = useScaleSize(size === 'small' ? 48 : 56, 1.1); const scaledFontSize = useScaleSize(20); const [focused, setFocused] = useState(false); const [displayValue, setDisplayValue] = useState(''); const [numericValue, setNumericValue] = useState(''); const [lastDisplayValue, setLastDisplayValue] = useState(''); const inputRef = useRef(null); const componentName = 'InputMoney'; const showBaseLineDebug = context?.features?.showBaseLineDebug ?? false; const { componentId } = useComponentId( `${componentName}/${placeholder}`, props.accessibilityLabel, ); const digitLimit = typeof (props as any)?.maxLength === 'number' ? (props as any).maxLength : Number.POSITIVE_INFINITY; useEffect(() => { if (value !== undefined && typeof value === 'string') { const cleanValue = value.replace(/\D/g, ''); if (cleanValue !== numericValue) { setNumericValue(cleanValue); updateDisplayValue(cleanValue); } } }, [value]); useEffect(() => { setLastDisplayValue(displayValue); }, [displayValue]); const updateDisplayValue = (numeric: string) => { if (!numeric) { setDisplayValue(''); return; } const formatted = formatNumberToMoney(Number(numeric)); setDisplayValue(formatted ? `${formatted}${currency}` : ''); }; const parseInputText = (text: string): string => { if (!text) return ''; const currencyRegex = new RegExp(` ?${currency}$`, 'g'); const textWithoutCurrency = text.replace(currencyRegex, '').trim(); return textWithoutCurrency.replace(/[^\d]/g, ''); }; const updateNumericValue = (val: string) => { let next = val.replace(/[^\d]/g, ''); if (next.length > digitLimit) next = next.slice(0, digitLimit); if (/^\d*$/.test(next)) { setNumericValue(next); updateDisplayValue(next); onChangeText?.(next); } }; const onClearText = () => { inputRef?.current?.clear(); inputRef.current?.setNativeProps({ text: '' }); setDisplayValue(''); setNumericValue(''); onChangeText?.(''); }; const _onChangeText = (text: string) => { if (!text) { setDisplayValue(''); setNumericValue(''); onChangeText?.(''); return; } if (text.length < lastDisplayValue.length) { const lastChar = lastDisplayValue.charAt(lastDisplayValue.length - 1); const isRemovingCurrency = lastDisplayValue.endsWith(currency) || lastChar === ' '; const isRemovingDot = lastChar === '.'; if (isRemovingCurrency || isRemovingDot) { const currentNumericValue = numericValue; if (currentNumericValue.length > 0) { const newNumericValue = currentNumericValue.slice(0, -1); updateNumericValue(newNumericValue); return; } } } const newNumericValue = parseInputText(text); updateNumericValue(newNumericValue); }; const _onFocus = (e: NativeSyntheticEvent) => { setFocused(true); onFocus?.(e); }; const _onBlur = (e: NativeSyntheticEvent) => { setFocused(false); onBlur?.(e); }; const _setText = (text: string) => { if (!text) { setDisplayValue(''); setNumericValue(''); onChangeText?.(''); return; } const newNumericValue = parseInputText(text); updateNumericValue(newNumericValue); }; useImperativeHandle(ref, () => { return { clear: onClearText, focus: () => inputRef.current?.focus(), blur: () => inputRef.current?.blur(), setText: _setText, }; }); const renderInputView = () => { const disabledColor = theme.colors.text.disable; let textColor = theme.colors.text.default; let placeholderColor = theme.colors.text.hint; let iconTintColor = trailingColor; if (disabled) { textColor = disabledColor; placeholderColor = disabledColor; iconTintColor = disabledColor; } return ( {showClearIcon && focused && displayValue ? ( ) : null} ); }; let inputState = 'active'; if (displayValue) { inputState = 'filled'; } if (errorMessage && errorMessage?.length > 0) { inputState = 'error'; } if (disabled) { inputState = 'disabled'; } return ( {renderInputView()} ); }, ); export default InputMoney;