UNPKG

@activecollab/components

Version:

ActiveCollab Components

269 lines (267 loc) • 11.4 kB
import { useState, useRef, useCallback, useEffect } from "react"; import { validateNumberInput } from "../utils"; import { currencyMultiplier, formatNumber, getNumberFromString } from "../utils/currencyUtils"; export const useInputNumber = (_ref, inputRef) => { let _ref$decimalSeparator = _ref.decimalSeparator, decimalSeparator = _ref$decimalSeparator === void 0 ? "." : _ref$decimalSeparator, _ref$thousandSeparato = _ref.thousandSeparator, thousandSeparator = _ref$thousandSeparato === void 0 ? "," : _ref$thousandSeparato, disableAbbreviation = _ref.disableAbbreviation, disableMacros = _ref.disableMacros, decimalLength = _ref.decimalLength, _ref$value = _ref.value, value = _ref$value === void 0 ? undefined : _ref$value, onChange = _ref.onChange, onSave = _ref.onSave, onEnterKeyPress = _ref.onEnterKeyPress, onClick = _ref.onClick, onCancel = _ref.onCancel, allowEmptyValue = _ref.allowEmptyValue, _ref$step = _ref.step, step = _ref$step === void 0 ? 1 : _ref$step, _ref$trimDecimals = _ref.trimDecimals, trimDecimals = _ref$trimDecimals === void 0 ? true : _ref$trimDecimals, limit = _ref.limit, _ref$validation = _ref.validation, validation = _ref$validation === void 0 ? validateNumberInput : _ref$validation, min = _ref.min, max = _ref.max, onBlur = _ref.onBlur, update = _ref.update, _ref$shortenThreshold = _ref.shortenThreshold, shortenThreshold = _ref$shortenThreshold === void 0 ? 1000 : _ref$shortenThreshold; const isMaxValid = max === undefined || min === undefined || Number(max) >= Number(min); if (!isMaxValid) { console.warn("Warning: The maximum value is set to be lower than the minimum value. The maximum value will be ignored."); } const _useState = useState(() => value), numericValue = _useState[0], setNumericValue = _useState[1]; const _useState2 = useState(() => value), prevNumericValue = _useState2[0], setPrevNumericValue = _useState2[1]; const _useState3 = useState(() => formatNumber(value, thousandSeparator, decimalSeparator, trimDecimals, decimalLength, disableAbbreviation ? "long" : "short", shortenThreshold)), currentValue = _useState3[0], setCurrentValue = _useState3[1]; const _useState4 = useState(() => formatNumber(value, thousandSeparator, decimalSeparator, trimDecimals, decimalLength, disableAbbreviation ? "long" : "short", shortenThreshold)), prevValue = _useState4[0], setPrevValue = _useState4[1]; const _useState5 = useState(() => formatNumber(value, "", decimalSeparator, false, decimalLength, "long", shortenThreshold)), unformattedValue = _useState5[0], setUnformattedValue = _useState5[1]; const _useState6 = useState(() => formatNumber(value, "", decimalSeparator, false, decimalLength, "long", shortenThreshold)), unformattedPrevValue = _useState6[0], setUnformattedPrevValue = _useState6[1]; const _useState7 = useState(false), focused = _useState7[0], setFocused = _useState7[1]; useEffect(() => { if (value !== prevNumericValue && (!focused || update)) { setCurrentValue(formatNumber(value, thousandSeparator, decimalSeparator, trimDecimals, decimalLength, disableAbbreviation ? "long" : "short", shortenThreshold)); setPrevValue(formatNumber(value, thousandSeparator, decimalSeparator, trimDecimals, decimalLength, disableAbbreviation ? "long" : "short", shortenThreshold)); setUnformattedValue(formatNumber(value, "", decimalSeparator, false, decimalLength, "long", shortenThreshold)); setUnformattedPrevValue(formatNumber(value, "", decimalSeparator, false, decimalLength, "long", shortenThreshold)); setNumericValue(value); setPrevNumericValue(value); } // eslint-disable-next-line react-hooks/exhaustive-deps }, [disableAbbreviation, thousandSeparator, decimalSeparator, decimalLength, trimDecimals, value, focused]); const escapeRef = useRef(false); const handleBlur = useCallback(e => { if (escapeRef.current) { setCurrentValue(prevValue); setUnformattedValue(unformattedPrevValue); setNumericValue(prevNumericValue); if (onChange) onChange(prevNumericValue); } else { if (e.target.value.trim().length > 0 && prevValue !== e.target.value) { const _value = formatNumber(currentValue, thousandSeparator, decimalSeparator, trimDecimals, decimalLength, disableAbbreviation ? "long" : "short", shortenThreshold); const _unformatedValue = formatNumber(currentValue, "", decimalSeparator, false, decimalLength, "long", shortenThreshold); const _numericValue = getNumberFromString(_unformatedValue, thousandSeparator, decimalSeparator); setPrevValue(_value); setUnformattedPrevValue(_unformatedValue); setUnformattedValue(_unformatedValue); setNumericValue(_numericValue); setPrevNumericValue(_numericValue); if (_numericValue !== prevNumericValue) onChange == null || onChange(_numericValue); setCurrentValue(_value); typeof onSave === "function" && onSave(e); } else { if (!allowEmptyValue) { setCurrentValue(prevValue); setUnformattedValue(unformattedPrevValue); setNumericValue(prevNumericValue); onChange == null || onChange(prevNumericValue); typeof onCancel === "function" && onCancel(e); } else { if (prevValue !== e.target.value) { onSave == null || onSave(e); } else { typeof onCancel === "function" && onCancel(e); } } } } setFocused(false); typeof onBlur === "function" && onBlur(e); }, [currentValue, onBlur, prevValue, unformattedPrevValue, prevNumericValue, onChange, thousandSeparator, decimalSeparator, trimDecimals, decimalLength, disableAbbreviation, shortenThreshold, onSave, allowEmptyValue, onCancel]); const updateValue = useCallback(type => { if (max !== undefined && Number(numericValue) > max || min !== undefined && Number(numericValue) < min) { return; } let newValue = numericValue; if (type === "increment") { if (max === undefined || Number(numericValue) + step <= max) { newValue = Number(numericValue) + step; } else if (Number(numericValue) < max) { newValue = max; } else { return; } } else if (type === "decrement") { if (min === undefined || Number(numericValue) - step >= min) { newValue = Number(numericValue) - step; } else if (Number(numericValue) > min) { newValue = min; } else { return; } } let formattedValue; if (decimalLength !== undefined) { formattedValue = Number(newValue).toFixed(decimalLength); } else { formattedValue = Number(newValue).toFixed(2); } if (decimalLength !== undefined && decimalLength === 0) { formattedValue = Math.round(Number(newValue)).toString(); } if (formattedValue.includes(".") || formattedValue.includes(",")) { formattedValue = formattedValue.replace(".", decimalSeparator); } setNumericValue(newValue); setPrevNumericValue(newValue); setUnformattedValue(formattedValue); setCurrentValue(formattedValue); if (onChange) onChange(newValue); }, [max, numericValue, min, decimalLength, onChange, step, decimalSeparator]); const handleKeyDown = useCallback(e => { if (e.key === "Enter") { e.target.blur(); if (typeof onEnterKeyPress === "function") onEnterKeyPress(numericValue); } if (e.key === "ArrowLeft") { return; } if (e.key === "ArrowRight") { return; } if (e.key === "ArrowUp") { e.preventDefault(); updateValue("increment"); } if (e.key === "ArrowDown") { e.preventDefault(); updateValue("decrement"); } if (e.key === "Escape") { escapeRef.current = true; e.target.blur(); typeof onCancel === "function" && onCancel(e); escapeRef.current = false; } if (e.key === "Backspace") { return; } if (e.key === "Delete") { return; } if ((e.metaKey || e.ctrlKey) && e.key === "a") { var _inputRef$current; (_inputRef$current = inputRef.current) == null || _inputRef$current.select(); return; } if (e.key === "Tab") { return; } if ((e.metaKey || e.ctrlKey) && e.key === "v") { e.preventDefault(); return; } // Disallow "-" if min is 0 or greater if (e.key === "-" && min !== undefined && Number(min) >= 0) { e.preventDefault(); return; } // Disallow decimal separator if decimalLength is 0 if (e.key === decimalSeparator && decimalLength === 0) { e.preventDefault(); return; } const input = e.target; const currentValue = input.value; const start = input.selectionStart; const end = input.selectionEnd; const newValue = currentValue.substring(0, start) + e.key + currentValue.substring(end); if (!validation(newValue, Boolean(disableMacros), decimalSeparator, decimalLength != null ? decimalLength : 0, limit)) { e.preventDefault(); return; } }, [decimalLength, decimalSeparator, disableMacros, inputRef, limit, min, numericValue, onCancel, onEnterKeyPress, updateValue, validation]); const handleChange = useCallback(e => { const inputValue = e.target.value; let numericNewValue = undefined; const numericInput = disableMacros ? inputValue : inputValue.replace(/(\d+(?:[.,]\d+)?)([kmbtKMBT])/, (_, num, unit) => { const normalizedNum = num.replace(",", "."); const parts = normalizedNum.split("."); const integerPart = parts[0]; const fractionalPart = parts[1]; const newInteger = parseInt(integerPart, 10) * currencyMultiplier[unit.toLowerCase()]; numericNewValue = parseFloat((newInteger ? newInteger : 0) + "." + (fractionalPart ? fractionalPart : 0)); let result = newInteger.toString(); if (fractionalPart !== undefined) { result += decimalSeparator + fractionalPart; } return result; }); if (numericNewValue === undefined) { if (inputValue) { numericNewValue = parseFloat(inputValue.replace(",", ".")); } else { numericNewValue = undefined; } } setCurrentValue(numericInput); setUnformattedValue(numericInput); setNumericValue(numericNewValue); if (onChange && numericNewValue !== numericValue) onChange(numericNewValue); }, [disableMacros, onChange, numericValue, decimalSeparator]); const handleClick = useCallback(e => { if (typeof onClick === "function") { onClick(e); } }, [onClick]); const handleFocus = useCallback(() => { setCurrentValue(unformattedValue); setFocused(true); }, [unformattedValue]); const handleDoubleClick = useCallback(() => { if (inputRef.current) { var _inputRef$current2; (_inputRef$current2 = inputRef.current) == null || _inputRef$current2.select(); } }, [inputRef]); return { value: currentValue, onBlur: handleBlur, onKeyDown: handleKeyDown, onChange: handleChange, onClick: handleClick, onDoubleClick: handleDoubleClick, onFocus: handleFocus, focused, unformattedValue, numericValue }; }; //# sourceMappingURL=useInputNumber.js.map