design-react-kit
Version:
Componenti React per Bootstrap 5
170 lines • 8.77 kB
JavaScript
import isNumber from 'is-number';
import React, { useCallback, useEffect, useRef, useState } from 'react';
import classNames from 'classnames';
import { Icon } from '../Icon/Icon';
import { notifyDeprecation } from '../utils';
import { InputContainer } from './InputContainer';
import { getClasses, getFormControlClass, getTag, getValidationTextControlClass, useFocus } from './utils';
export const Input = ({ id, className, cssModule, type = 'text', tag, addonText, static: staticInput, plaintext, innerRef, label, incrementLabel, decrementLabel, validationText, infoText, placeholder, normalized, value, wrapperClass: originalWrapperClassOld, wrapperClassName: originalWrapperClass, size, testId, noWrapper = false, hasButtonRight, buttonRight, hasIconLeft, iconLeft, ...attributes }) => {
const [isHidden, setHidden] = useState(true);
const [hasIcon, toggleIcon] = useState(true);
const { toggleFocusLabel, toggleBlurLabel, isFocused } = useFocus({
onFocus: attributes.onFocus,
onBlur: attributes.onBlur
});
const toggleShow = useCallback(() => {
setHidden(!isHidden);
toggleIcon(!hasIcon);
}, [hasIcon, isHidden]);
const divResizeRef = useRef(null);
const inputRef = useRef(null);
const [width, setWidth] = useState('100');
useEffect(() => {
if (divResizeRef.current != null && divResizeRef.current.classList.contains('input-number-adaptive')) {
if (!value) {
setWidth(`calc(70px)`);
}
else {
setWidth(`calc(70px + ${`${value}`.length}ch)`);
}
}
}, [value]);
// eslint-disable-next-line prefer-const
let { bsSize, valid, ...rest } = attributes;
const Tag = getTag({ tag, plaintext, staticInput, type });
const formControlClass = getFormControlClass({
plaintext,
staticInput,
type,
normalized
}, cssModule);
const validationTextControlClass = getValidationTextControlClass({ valid }, cssModule);
const extraAttributes = {};
if (size && !isNumber(size)) {
notifyDeprecation('Please use the prop "bsSize" instead of the "size" to bootstrap\'s input sizing.');
bsSize = size;
}
else {
extraAttributes.size = size;
}
if (Tag === 'input' || typeof tag !== 'string') {
extraAttributes.type = type;
}
// associate the input field with the help text
const infoId = id && infoText ? `${id}Description` : undefined;
if (infoId) {
extraAttributes['aria-describedby'] = infoId;
}
if (attributes.children &&
!(plaintext || staticInput || type === 'select' || typeof Tag !== 'string' || Tag === 'select')) {
notifyDeprecation(`Input with a type of "${type}" cannot have children. Please use "value"/"defaultValue" instead.`);
delete attributes.children;
}
const inputPassword = extraAttributes.type === 'password';
const indeterminateCheckboxInput = type === 'checkbox' && className?.includes('semi-checked');
// Styling
const { activeClass, extraLabelClass, validationTextClass, inputClasses, wrapperClass } = getClasses(className, type, {
valid,
bsSize,
placeholder,
value,
label,
validationText,
normalized: Boolean(normalized),
inputPassword,
formControlClass,
validationTextControlClass,
isFocused: isFocused,
originalWrapperClass: originalWrapperClass || originalWrapperClassOld
}, cssModule);
// set of attributes always shared by the Input components
const sharedAttributes = {
id,
onFocus: toggleFocusLabel,
onBlur: toggleBlurLabel,
value: value,
ref: innerRef
};
// set of attributes always shared by the wrapper component
const containerProps = {
id,
infoId,
infoText,
activeClass,
extraLabelClass,
label,
validationTextClass,
validationText,
wrapperClass,
hasButtonRight,
buttonRight,
hasIconLeft,
iconLeft
};
if (noWrapper) {
return (React.createElement(Tag, { ...rest, ...extraAttributes, className: inputClasses, ...sharedAttributes, placeholder: placeholder, "data-testid": testId }));
}
const clickIncrDecr = (mode) => {
let step = parseFloat(inputRef.current?.step ? inputRef.current.step : '1');
const min = parseFloat(inputRef.current?.min ? inputRef.current.min : 'Nan');
const max = parseFloat(inputRef.current?.max ? inputRef.current.max : 'Nan');
step = isNaN(step) ? 1 : step;
const newValue = parseFloat(inputRef.current?.value ? inputRef.current.value : '0') + mode * step;
if (!isNaN(max) && newValue > max) {
return;
}
if (!isNaN(min) && newValue < min) {
return;
}
const nativeInputValueSetter = Object.getOwnPropertyDescriptor(window.HTMLInputElement.prototype, 'value')?.set;
nativeInputValueSetter?.call(inputRef.current, `${newValue}`);
const ev1 = new Event('change', { bubbles: true });
const ev2 = new Event('input', { bubbles: true });
inputRef.current?.dispatchEvent(ev1);
inputRef.current?.dispatchEvent(ev2);
inputRef.current?.focus();
};
if (['currency', 'percentage', 'adaptive', 'number'].includes(type)) {
if (containerProps.extraLabelClass && ['currency', 'percentage'].includes(type)) {
containerProps.extraLabelClass = containerProps.extraLabelClass + ' input-symbol-label';
}
return (React.createElement(InputContainer, { ...containerProps },
React.createElement("div", { className: classNames({
'input-group': true,
'input-number': true,
disabled: rest.disabled,
'input-number-adaptive': type === 'adaptive'
}), style: { width }, ref: divResizeRef },
['currency', 'percentage'].includes(type) && (React.createElement("span", { className: 'input-group-text fw-semibold' }, addonText)),
React.createElement(Tag, { ...rest, ...extraAttributes, ...sharedAttributes, className: inputClasses, "data-testid": testId, type: 'number', ref: inputRef }),
React.createElement("span", { className: 'input-group-text align-buttons flex-column' },
React.createElement("button", { className: 'input-number-add', onClick: () => clickIncrDecr(1) },
React.createElement("span", { className: 'visually-hidden' }, incrementLabel || '')),
React.createElement("button", { className: 'input-number-sub', onClick: () => clickIncrDecr(-1) },
React.createElement("span", { className: 'visually-hidden' }, decrementLabel || ''))))));
}
if (placeholder) {
return (React.createElement(InputContainer, { ...containerProps },
React.createElement(Tag, { ...rest, ...extraAttributes, ...sharedAttributes, className: inputClasses, placeholder: placeholder, "data-testid": testId })));
}
if (indeterminateCheckboxInput) {
return (React.createElement(InputContainer, { ...containerProps },
React.createElement(Tag, { ...rest, ...extraAttributes, ...sharedAttributes, className: inputClasses, "data-testid": testId, indeterminate: 'true' })));
}
if (inputPassword) {
return (React.createElement(InputContainer, { ...containerProps },
React.createElement(Tag, { ...rest, ...extraAttributes, ...sharedAttributes, type: isHidden ? 'password' : 'text', className: inputClasses, placeholder: placeholder, "data-testid": testId }),
React.createElement("span", { className: 'password-icon', "aria-hidden": 'true' },
React.createElement(Icon, { size: 'sm', icon: `it-password-${hasIcon ? 'visible' : 'invisible'}`, className: 'password-icon-visible', onClick: toggleShow }))));
}
if (normalized) {
return (React.createElement(InputContainer, { ...containerProps },
React.createElement(Tag, { ...rest, ...extraAttributes, ...sharedAttributes, className: inputClasses, "data-testid": testId, readOnly: true })));
}
if (label || validationText) {
return (React.createElement(InputContainer, { ...containerProps },
React.createElement(Tag, { ...rest, ...extraAttributes, ...sharedAttributes, className: inputClasses, "data-testid": testId })));
}
return React.createElement(Tag, { ...rest, ...extraAttributes, className: inputClasses, ...sharedAttributes, "data-testid": testId });
};
//# sourceMappingURL=Input.js.map