UNPKG

@chayns-components/core

Version:

A set of beautiful React components for developing your own applications with chayns.

146 lines (143 loc) 5.56 kB
import React, { useEffect, useState } from 'react'; import { NUMBER_CLEAR_REGEX } from '../../constants/numberInput'; import { formateNumber, isValidString, parseFloatWithDecimals } from '../../utils/numberInput'; import Input from '../input/Input'; const NumberInput = _ref => { let { isDecimalInput, isMoneyInput, isTimeInput, isInvalid, maxNumber = Infinity, value, shouldTriggerChangeOnFormat = true, placeholder, onBlur, isDisabled, onChange, shouldShowOnlyBottomBorder, minNumber = -Infinity } = _ref; // the plainText will be shown in the input, when it is in focus const [plainText, setPlainText] = useState(''); // the formattedValue will be shown in the input, when it is not in focus const [formattedValue, setFormattedValue] = useState(''); const [hasFocus, setHasFocus] = useState(false); const [shouldRemainPlaceholder, setShouldRemainPlaceholder] = useState(false); const [isValueInvalid, setIsValueInvalid] = useState(false); const localPlaceholder = placeholder ?? (isMoneyInput ? '€' : undefined); const onLocalChange = event => { const newValue = event.target.value; const sanitizedValueString = newValue // Removes everything except numbers, commas and points .replace(NUMBER_CLEAR_REGEX, ''); if (isTimeInput && (sanitizedValueString.includes(':') && sanitizedValueString.length > 5 || !sanitizedValueString.includes(':') && sanitizedValueString.length > 4)) { return; } const valueToCheck = sanitizedValueString.replaceAll(',', '.'); if (!isValidString({ string: valueToCheck, isMoneyInput, isDecimalInput, isTimeInput })) { return; } if (maxNumber && Number(valueToCheck) > maxNumber || minNumber && Number(valueToCheck) < minNumber) { return; } if (newValue.length === 1 && newValue.match(/^[0-9]+$/) === null) { setShouldRemainPlaceholder(true); } else { setShouldRemainPlaceholder(false); } setPlainText(sanitizedValueString.replaceAll('.', ',')); if (typeof onChange === 'function') { onChange(sanitizedValueString.replaceAll('.', ',')); } }; const onLocalBlur = () => { const sanitizedValue = plainText.length === 0 ? '0' : plainText; let newIsInvalid = false; let parsedNumber = null; if (!isTimeInput) { parsedNumber = parseFloatWithDecimals({ stringValue: sanitizedValue.replace(',', '.').replaceAll(':', '').replace('€', ''), decimals: isMoneyInput ? 2 : undefined }); if (parsedNumber !== 0 && (parsedNumber > maxNumber || parsedNumber < minNumber)) { newIsInvalid = true; } setIsValueInvalid(newIsInvalid); } const newStringValue = plainText.length === 0 ? '' : formateNumber({ number: isTimeInput ? sanitizedValue : parsedNumber, isMoneyInput, isTimeInput }); setFormattedValue(`${newStringValue} ${isMoneyInput ? '€' : ''}`); setPlainText(newStringValue.replaceAll('.', '')); setHasFocus(false); if (typeof onChange === 'function' && shouldTriggerChangeOnFormat) { onChange(newStringValue.replaceAll('.', '')); } if (typeof onBlur === 'function') { if (isTimeInput) { onBlur(newStringValue, newIsInvalid); } else { onBlur(parsedNumber === 0 ? null : parsedNumber, newIsInvalid); } } }; const onLocalFocus = () => { // formattedValue will be a number string with german number format (e.g. 1.000,00) // It will remove all dots, so that the user can type in the number setPlainText(formattedValue.replaceAll('.', '').replace('€', '').replaceAll(' ', '')); // This will update the external state if (typeof onChange === 'function' && shouldTriggerChangeOnFormat) { onChange(formattedValue.replaceAll('.', '').replace('€', '').replaceAll(' ', '')); } setIsValueInvalid(false); setHasFocus(true); }; // updates the formattedValue, when the value changes useEffect(() => { let parsedNumber = null; if (!isTimeInput) { parsedNumber = parseFloatWithDecimals({ stringValue: plainText.replace(',', '.').replaceAll(':', '').replace('€', '').replaceAll(' ', ''), decimals: isMoneyInput ? 2 : undefined }); // checks, if a given number is invalid, if the input is not in focus if (!hasFocus) { setIsValueInvalid(parsedNumber > maxNumber || parsedNumber < minNumber); } } setFormattedValue(plainText.length === 0 ? '' : `${formateNumber({ number: isTimeInput ? plainText : parsedNumber, isMoneyInput, isTimeInput })}${isMoneyInput ? ' €' : ''}`); }, [hasFocus, isMoneyInput, isTimeInput, maxNumber, minNumber, plainText]); useEffect(() => { if (typeof value === 'string') { setPlainText(value.replace('€', '').replaceAll(' ', '')); } }, [value]); return /*#__PURE__*/React.createElement(Input, { shouldRemainPlaceholder: shouldRemainPlaceholder, shouldShowOnlyBottomBorder: shouldShowOnlyBottomBorder, inputMode: "decimal", onChange: onLocalChange, value: hasFocus ? plainText : formattedValue, placeholder: localPlaceholder, onBlur: onLocalBlur, onFocus: onLocalFocus, isDisabled: isDisabled, isInvalid: typeof isInvalid === 'boolean' ? isInvalid : isValueInvalid, shouldShowCenteredContent: shouldShowOnlyBottomBorder }); }; NumberInput.displayName = 'NumberInput'; export default NumberInput; //# sourceMappingURL=NumberInput.js.map