UNPKG

design-react-kit

Version:

Componenti React per Bootstrap 5

170 lines 8.77 kB
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