@wix/design-system
Version:
@wix/design-system
259 lines • 10.6 kB
JavaScript
import React, { useContext, useRef, useCallback, useImperativeHandle, forwardRef, useEffect, } from 'react';
import PropTypes from 'prop-types';
import { st, classes } from './Input.st.css.js';
import { InputContext } from './InputContext';
import { STATUS } from '../StatusIndicator/StatusIndicator.constants';
import { DATA_ATTR } from './Input.constants';
import Ticker from './components/Ticker';
import IconAffix from './components/IconAffix';
import Affix from './components/Affix';
import Group from './components/Group';
import { WixStyleReactMaskingContext } from '../WixStyleReactMaskingProvider/context';
import { WixStyleReactDefaultsOverrideContext } from '../WixStyleReactDefaultsOverrideProvider';
import { StatusContext, getAriaAttributesFromContext, getStatusFromContext, } from '../FormField/StatusContext';
import { useAutoFocusAndSelect } from './hooks/useAutoFocusAndSelect';
import { transformAriaKebabCase } from './utils/transformAriaKebabCase';
import { Suffix } from './components/Suffix/Suffix';
export const Input = forwardRef((props, ref) => {
const { input: inputPropsDefaults } = useContext(WixStyleReactDefaultsOverrideContext);
const { role, autoSelect = true, textOverflow = 'clip', size = inputPropsDefaults.size, hideStatusSuffix = false, clearButton = false, focusOnClearClick = true, border = 'standard', maxLength = 524288, autoFocus, onFocus, onBlur, value, customInput, min, max, step, inputRef, id, name, disabled, defaultValue, onChange, type, onKeyDown, onEnterPressed, onEscapePressed, onPaste, onCopy, placeholder, tabIndex, onInputClicked, onKeyUp, readOnly, disableEditing, required, autocomplete, onCompositionChange, pattern, prefix, suffix, className, status, statusMessage, onClear, menuArrow, rtl, dataHook, forceFocus, noRightBorderRadius, noLeftBorderRadius, forceHover, tooltipPlacement, clearButtonTooltipContent, clearButtonTooltipProps, clearButtonAriaLabel, statusMessageTooltipProps, ...rest } = props;
const isComposing = useRef(false);
const localInputRef = useRef(null);
const { maskingClassNames } = useContext(WixStyleReactMaskingContext);
const statusContext = useContext(StatusContext);
const { _onFocus, _onBlur, isFocused } = useAutoFocusAndSelect({
localInputRef,
onFocus,
onBlur,
autoFocus,
autoSelect,
value,
});
useImperativeHandle(ref, () => ({
/**
* Sets focus on the input element
*/
focus: (options = {}) => localInputRef.current?.focus(options),
/**
* Removes focus from the input element
*/
blur: () => localInputRef.current?.blur(),
/**
* Selects the text in the input element
*/
select: () => localInputRef.current?.select(),
/**
* Sets the start and end positions of the current text selection in the input element
*/
setSelectionRange: (start, end) => localInputRef.current?.setSelectionRange(start, end),
/**
* Clears the input value
*/
clear: (event) => {
// If the value is undefined, we are dealing with an uncontrolled input
if (value === undefined && localInputRef.current) {
localInputRef.current.value = '';
}
onClear?.(event);
},
/**
* Gets the input DOM element.
*/
input: localInputRef.current,
}));
/**
* Legacy reason to still support inputRef prop as prop.
*/
useEffect(() => {
if (inputRef && localInputRef.current) {
inputRef(localInputRef.current);
}
}, [inputRef]);
const isValid = useCallback((val) => {
if (type === 'number') {
/*
* Limit our number input to contain only:
* - \d - digits
* - . - a dot
* - , - a comma
* - \- - a hyphen minus
* - + - a plus sign
*/
return /^[\d.,\-+]*$/.test(val);
}
return true;
}, [type]);
const _onChange = useCallback((event) => {
if (isValid(event.target.value)) {
onChange?.(event);
}
}, [isValid, onChange]);
const _onKeyPress = useCallback((event) => {
if (!isValid(event.currentTarget.value + event.key)) {
event.preventDefault();
}
}, [isValid]);
const _onWheel = useCallback(() => {
if (type === 'number') {
localInputRef.current?.blur();
}
}, [type]);
const _onKeyDown = useCallback((event) => {
if (isComposing.current) {
return;
}
// On key event
onKeyDown?.(event);
// Enter
if (event.key === 'Enter' || event.keyCode === 13) {
onEnterPressed?.(event);
}
// Escape
if (event.key === 'Escape' || event.keyCode === 27) {
onEscapePressed?.(event);
}
}, [isComposing, onEnterPressed, onEscapePressed, onKeyDown]);
const _onClick = useCallback((event) => onInputClicked?.(event), [onInputClicked]);
const _onCompositionChange = useCallback((composing) => {
isComposing.current = composing;
onCompositionChange?.(composing);
}, [onCompositionChange]);
const commonProps = {
role,
id,
min,
max,
step,
'data-mask': !!maskingClassNames,
className: st(classes.input, {}, maskingClassNames),
style: { textOverflow },
name,
disabled,
defaultValue,
value,
onFocus: _onFocus,
onBlur: _onBlur,
onChange: _onChange,
onKeyPress: _onKeyPress,
onWheel: _onWheel,
onKeyDown: _onKeyDown,
onClick: _onClick,
onPaste,
onCopy,
placeholder,
tabIndex,
onKeyUp,
readOnly: readOnly || disableEditing,
type,
required,
autoComplete: autocomplete,
onCompositionStart: () => _onCompositionChange(true),
onCompositionEnd: () => _onCompositionChange(false),
pattern,
maxLength,
...getAriaAttributesFromContext(statusContext),
...transformAriaKebabCase(rest),
};
const CustomInputComponent = customInput;
const inputElement = customInput ? (React.createElement(CustomInputComponent, { "data-hook": "wsr-custom-input", ref: localInputRef, ...commonProps })) : (React.createElement("input", { "data-hook": "wsr-input", ref: localInputRef, ...commonProps }));
const dataAttributes = {
[DATA_ATTR.ROOT]: true,
[DATA_ATTR.SIZE]: size,
[DATA_ATTR.STATUS]: getStatusFromContext(statusContext, status),
[DATA_ATTR.PREFIX]: !!prefix,
[DATA_ATTR.DISABLED]: disabled,
[DATA_ATTR.HOVER]: forceHover,
[DATA_ATTR.FOCUS]: forceFocus || isFocused,
[DATA_ATTR.LEFTBORDERRADIUS]: noLeftBorderRadius,
[DATA_ATTR.RIGHTBORDERRADIUS]: noRightBorderRadius,
};
return (React.createElement("div", { className: st(classes.root, {
size,
hasFocus: forceFocus || isFocused,
status: getStatusFromContext(statusContext, status),
forceHover,
readOnly,
disabled,
border,
noRightBorderRadius,
noLeftBorderRadius,
}, className), dir: rtl ? 'rtl' : undefined, "data-hook": dataHook, ...dataAttributes },
React.createElement("div", { className: classes.wrapper },
prefix && (React.createElement(InputContext.Provider, { value: { ...props, size, inPrefix: true } }, prefix)),
inputElement,
React.createElement(InputContext.Provider, { value: { ...props, size, inSuffix: true } },
React.createElement(Suffix, { statusMessage: statusMessage, status: status, disabled: disabled, menuArrow: menuArrow, suffix: suffix, tooltipPlacement: tooltipPlacement, clearButtonTooltipContent: clearButtonTooltipContent, clearButtonTooltipProps: clearButtonTooltipProps, clearButtonAriaLabel: clearButtonAriaLabel, statusMessageTooltipProps: statusMessageTooltipProps, hideStatusSuffix: hideStatusSuffix, onClear: onClear, clearButton: clearButton, value: value, size: size, onInputClicked: onInputClicked, focusOnClearClick: focusOnClearClick, inputElementRef: localInputRef })))));
});
Input.propTypes = {
role: PropTypes.any,
dataHook: PropTypes.any,
className: PropTypes.any,
id: PropTypes.any,
ariaControls: PropTypes.any,
ariaDescribedby: PropTypes.any,
ariaLabel: PropTypes.any,
autoFocus: PropTypes.any,
autocomplete: PropTypes.any,
defaultValue: PropTypes.any,
disabled: PropTypes.any,
status: PropTypes.any,
statusMessage: PropTypes.any,
hideStatusSuffix: PropTypes.any,
forceFocus: PropTypes.any,
forceHover: PropTypes.any,
maxLength: PropTypes.any,
menuArrow: PropTypes.any,
clearButton: PropTypes.any,
name: PropTypes.any,
border: PropTypes.any,
noLeftBorderRadius: PropTypes.any,
noRightBorderRadius: PropTypes.any,
onBlur: PropTypes.any,
onChange: PropTypes.any,
onClear: PropTypes.any,
onCompositionChange: PropTypes.any,
onEnterPressed: PropTypes.any,
onEscapePressed: PropTypes.any,
onFocus: PropTypes.any,
onInputClicked: PropTypes.any,
onKeyDown: PropTypes.any,
onKeyUp: PropTypes.any,
onPaste: PropTypes.any,
onCopy: PropTypes.any,
placeholder: PropTypes.any,
prefix: PropTypes.any,
readOnly: PropTypes.any,
disableEditing: PropTypes.any,
size: PropTypes.any,
suffix: PropTypes.any,
tabIndex: PropTypes.any,
textOverflow: PropTypes.any,
/**
* @deprecated use statusMessageTooltipProps instead
*/
tooltipPlacement: PropTypes.any,
type: PropTypes.any,
value: PropTypes.any,
required: PropTypes.any,
min: PropTypes.any,
max: PropTypes.any,
step: PropTypes.any,
customInput: PropTypes.any,
pattern: PropTypes.any,
focusOnClearClick: PropTypes.any,
clearButtonTooltipContent: PropTypes.any,
clearButtonTooltipProps: PropTypes.any,
clearButtonAriaLabel: PropTypes.any,
statusMessageTooltipProps: PropTypes.any,
};
Input.displayName = 'Input';
export default Object.assign(Input, {
Ticker,
IconAffix,
Affix,
Group,
StatusError: STATUS.ERROR,
StatusWarning: STATUS.WARNING,
StatusLoading: STATUS.LOADING,
});
//# sourceMappingURL=Input.js.map