@kubit-ui-web/react-components
Version:
Kubit React Components is a customizable, accessible library of React web components, designed to enhance your application's user experience
287 lines • 13.9 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.useInput = void 0;
const react_1 = require("react");
const useInternalValidations_1 = require("../../components/input/hooks/useInternalValidations");
const input_1 = require("../../components/input/types/input");
const inputType_1 = require("../../components/input/types/inputType");
const formatNumber_1 = require("../../components/input/utils/formatNumber");
const state_utils_1 = require("../../components/input/utils/state.utils");
const truncatedValue_1 = require("../../components/inputCurrency/helpers/truncatedValue");
const keyboardKeys_1 = require("../../constants/keyboardKeys/keyboardKeys");
const mask_utility_1 = require("../../utils/maskUtility/mask.utility");
const validationsProvider_1 = require("../../provider/validations/validationsProvider");
const cursor_utility_1 = require("../../utils/cursorUtility/cursor.utility");
const keyboard_utility_1 = require("../../utils/keyboard/keyboard.utility");
const string_utility_1 = require("../../utils/stringUtility/string.utility");
const limitValue_1 = require("./helpers/limitValue");
const modifyInputNumberValue_1 = require("./helpers/modifyInputNumberValue");
const useInput = (props) => {
const [value, setValue] = (0, react_1.useState)(props.currentValue || '');
const [focus, setFocus] = (0, react_1.useState)(false);
const { validationValue } = (0, validationsProvider_1.useValidations)();
const { internalErrors, checkInternalValidations } = (0, useInternalValidations_1.useInternalValidations)(props.type, props.minLength, props.onInternalErrors);
const handleSetValue = value => {
setValue(value);
};
const state = (0, react_1.useMemo)(() => (0, state_utils_1.getState)(value, focus, props.disabled, internalErrors.length > 0 || props.error, props.informationAssociated), [value, focus, props.disabled, internalErrors.length, props.error, props.informationAssociated]);
const eventKeyPressRef = (0, react_1.useRef)();
const positionRef = (0, react_1.useRef)(0);
const inputRef = (0, react_1.useRef)();
const pasteRef = (0, react_1.useRef)(false);
// Return ref of the input element
(0, react_1.useImperativeHandle)(props.ref, () => {
return inputRef.current;
}, []);
const firstRender = (0, react_1.useRef)(true);
(0, react_1.useEffect)(() => {
if (props.currentValue !== undefined) {
let newValue = props.currentValue;
// format with formatNumber configuration
if (firstRender.current && props.formatNumber && !focus) {
// add thousand separator to the value
newValue = addThousandSeparator(String(props.currentValue), props.formatNumber);
}
// limit or truncate the value
controlValue(newValue);
}
else {
handleSetValue('');
}
firstRender.current = false;
}, [props.currentValue]);
(0, react_1.useEffect)(() => {
positionRef.current = (0, string_utility_1.maxCountBetweenChars)('#', props.mask) + 1;
}, []);
(0, react_1.useEffect)(() => {
//avoid change value on input number using wheel mouse
const input = inputRef.current;
if (props.disabledWheelMouse && input) {
const handleWheel = e => {
e.preventDefault();
};
input.addEventListener('wheel', handleWheel);
return () => {
input.removeEventListener('wheel', handleWheel);
};
}
return undefined;
}, []);
(0, react_1.useEffect)(() => {
// prevent the behaviour, when the value is changed by paste method
if (pasteRef.current) {
pasteRef.current = false;
return;
}
if (eventKeyPressRef.current && props.mask && inputRef?.current && value) {
const popoverElements = document.querySelectorAll('[data-calendar], [data-input-dropdown-list], [data-input-search-list]');
const { start, end } = eventKeyPressRef.current.cursor;
// if multiple digits are selected, recovery the selected area
const area = Math.abs(start - end);
// this the mask representation of the selected area
const selected = props.mask.slice(start, end);
// match the mask representation with the mask character
const maskChart = selected.match(/#/g);
// calculate the difference between the selected area and the mask representation
const diffStart = maskChart ? Math.abs(maskChart.length - area) : 0;
const diffEnd = maskChart ? maskChart.length : 0;
const position = (0, cursor_utility_1.getPosition)(eventKeyPressRef.current.key, value, eventKeyPressRef.current.cursor, positionRef.current);
if (popoverElements.length !== 0) {
inputRef.current.focus();
}
inputRef.current.setSelectionRange(start + diffStart + position, end - diffEnd + position);
}
}, [value]);
const truncateFloatValue = value => {
if (value === '') {
return value;
}
const hasMark = value.match(/[^a-zA-Z0-9]/g);
const isLastCharAMark = hasMark &&
props.maxLength &&
value.length >= props.maxLength &&
value[props.maxLength - 1] === hasMark[0];
return isLastCharAMark && props.type === inputType_1.InputTypeType.NUMBER
? value.replace(hasMark[0], '')
: value;
};
const controlValue = value => {
// control character limit
const limitedValue = value && (0, limitValue_1.limitValue)(value, props.min, props.max, props.maxLength);
// truncate the value with maximun of decimals
const truncateValue = props.truncate && props.maxDecimals !== null && props.maxDecimals !== undefined
? (0, truncatedValue_1.truncatedValue)(String(limitedValue), props.maxDecimals, props.locale || props.formatNumber?.locale)
: String(limitedValue);
const valueTruncated = truncateFloatValue(truncateValue);
handleSetValue(valueTruncated);
return valueTruncated;
};
// add thousand separator to the value
const addThousandSeparator = (value, format) => {
if ((0, formatNumber_1.checkValidFormattedNumber)(value, props.locale || format.locale)) {
// remove existing thousand separator
const newValue = (0, formatNumber_1.removeThousandSeparator)(value, props.locale || format.locale);
// format value
const formattedValue = (0, formatNumber_1.formatNumber)(Number(newValue), format, props.locale || format.locale);
return formattedValue;
}
return value;
};
const handleChangeInternal = event => {
let eventValue = event.target.value;
// format value with the mask
if (props.maskType) {
let newMaskedValue = (0, mask_utility_1.cleanInputValue)(eventValue, props.maskType);
if (props.mask) {
newMaskedValue = (0, mask_utility_1.formatMask)(newMaskedValue, props.mask);
}
eventValue = newMaskedValue;
}
else if (props.regex) {
const newMaskedValue = (0, mask_utility_1.matchInputValue)(String(value), eventValue, props.regex);
eventValue = newMaskedValue;
}
// key validation
if (props.errorExecution === input_1.ERROR_EXECUTION.ON_CHANGE && props.keyValidation) {
props.onError?.(!validationValue(props.keyValidation, eventValue));
}
// limit or truncate the value
const valueControlled = controlValue(eventValue);
// check internal validations
if (props.type !== inputType_1.InputTypeType.DATE &&
(props.internalErrorExecution === input_1.INTERNAL_ERROR_EXECUTION.ON_CHANGE ||
props.internalErrorExecution === input_1.INTERNAL_ERROR_EXECUTION.ON_CHANGE_ON_BLUR)) {
checkInternalValidations(eventValue);
}
if ((0, modifyInputNumberValue_1.modifyInputNumberValue)({
value: eventValue,
min: props.min,
max: props.max,
maxLength: props.maxLength,
}) ||
props.type !== inputType_1.InputTypeType.NUMBER) {
event.target.value = valueControlled;
}
// value = previous value
// valueControlled = current value
if (value === valueControlled) {
return;
}
props.onChange?.(event);
};
const handleBlurInternal = event => {
if (props.internalErrorExecution === input_1.INTERNAL_ERROR_EXECUTION.ON_BLUR ||
props.internalErrorExecution === input_1.INTERNAL_ERROR_EXECUTION.ON_CHANGE_ON_BLUR) {
checkInternalValidations(event.target.value);
}
if (props.formatNumber) {
// add thousand separator to the value
event.target.value = addThousandSeparator(event.target.value, props.formatNumber);
handleSetValue(event.target.value);
}
props.onBlur?.(event);
if (props.errorExecution === input_1.ERROR_EXECUTION.ON_BLUR && props.keyValidation) {
props.onError?.(!validationValue(props.keyValidation, event.target.value));
}
setFocus(false);
};
const handleFocusInternal = event => {
if (props.formatNumber) {
// remove thousand separator to the value
event.target.value = (0, formatNumber_1.removeThousandSeparator)(event.target.value, props.locale || props.formatNumber?.locale, false);
handleSetValue(event.target.value);
}
// transform the string value to a number format with dot as decimal separator to avoid errors
const decimalNumber = (0, formatNumber_1.convertDecimalSeparator)(event.target.value, (0, formatNumber_1.getDecimalSeparator)(props.locale || props.formatNumber?.locale));
if (!isNaN(parseFloat(decimalNumber)) && isFinite(parseFloat(decimalNumber))) {
event.target.value = decimalNumber;
}
props.onFocus?.(event);
setFocus(true);
};
const handleKeyDownInternal = event => {
if (props.ignoreKeys?.includes(event.key)) {
event.preventDefault();
}
if (props.type === inputType_1.InputTypeType.NUMBER &&
((0, keyboard_utility_1.isArrowUpPressed)(event.key) || (0, keyboard_utility_1.isArrowDownPressed)(event.key))) {
event.preventDefault();
}
const { key, currentTarget: { selectionStart, selectionEnd }, } = event;
let start = selectionStart ?? 0;
let end = selectionEnd ?? 0;
const isSelected = Math.abs(start - end) > 0;
// Check if the input has a mask
if (props.mask && inputRef.current && start !== null && end !== null) {
// Check if the key pressed is "Backspace" or "Delete"
// Check if the character at the cursor position is a mask character
if (key === keyboardKeys_1.BACKSPACE.key && props.mask[start - 1] && props.mask[start - 1] !== '#') {
if (!isSelected) {
let count = 1;
// Check if the character at the cursor position is a mask character
for (let index = start - 1; index >= 0; index--) {
if (props.mask[index] === '#') {
count = start - 1 - index;
break;
}
}
// Calculate the new cursor position
start = start - count;
end = end - count;
}
// Set the cursor to the new position
inputRef.current.setSelectionRange(start, end);
}
else if (key === keyboardKeys_1.DELETE.key && props.mask[end] && props.mask[end] !== '#') {
if (!isSelected) {
let count = 1;
// Check if the character at the cursor position is a mask character
for (let index = end; index < props.mask.length; index++) {
if (props.mask[index] === '#') {
count = index - end;
break;
}
}
// Calculate the new cursor position
start = start + count;
end = end + count;
}
// Set the cursor to the new position
inputRef.current.setSelectionRange(start, end);
}
}
const cursor = {
start: start,
end: end,
};
eventKeyPressRef.current = { key, cursor };
props.onKeyDown?.(event);
};
const handlePasteInternal = event => {
const clipboardData = event.clipboardData;
const pastedData = clipboardData?.getData('Text');
const newRegex = typeof props.regex === 'string' ? new RegExp(props.regex) : props.regex;
if (props.ignoreKeys?.some(key => pastedData.includes(key)) ||
props.disabledCopyAndPaste ||
(newRegex && !newRegex?.test(pastedData))) {
event.preventDefault();
return;
}
pasteRef.current = true;
props.onPaste?.(event);
};
return {
value,
state,
inputRef,
eventKeyPressRef,
handleChangeInternal,
handleBlurInternal,
handleFocusInternal,
handleKeyDownInternal,
handleSetValue,
handlePasteInternal,
};
};
exports.useInput = useInput;
//# sourceMappingURL=useInput.js.map