wix-style-react
Version:
wix-style-react
595 lines (587 loc) • 22.6 kB
JavaScript
"use strict";
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
exports.__esModule = true;
exports.default = void 0;
var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
var _objectWithoutProperties2 = _interopRequireDefault(require("@babel/runtime/helpers/objectWithoutProperties"));
var _react = _interopRequireWildcard(require("react"));
var _propTypes = _interopRequireDefault(require("prop-types"));
var _InputSt = require("./Input.st.css");
var _InputContext = require("./InputContext");
var _constants = require("./constants");
var _constants2 = require("../StatusIndicator/constants.js");
var _Ticker = _interopRequireDefault(require("./Ticker"));
var _IconAffix = _interopRequireDefault(require("./IconAffix"));
var _Affix = _interopRequireDefault(require("./Affix"));
var _Group = _interopRequireDefault(require("./Group"));
var _InputSuffix = _interopRequireWildcard(require("./InputSuffix"));
var _context = require("../WixStyleReactMaskingProvider/context");
var _WixStyleReactDefaultsOverrideProvider = require("../WixStyleReactDefaultsOverrideProvider");
var _StatusContext = require("../FormField/StatusContext");
var _excluded = ["customInput", "ref"],
_excluded2 = ["className"];
var _jsxFileName = "/home/builduser/work/a9c1ac8876d5057c/packages/wix-style-react/dist/cjs/Input/Input.js";
function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function _interopRequireWildcard(e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, default: e }; if (null === e || "object" != typeof e && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (var _t in e) "default" !== _t && {}.hasOwnProperty.call(e, _t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, _t)) && (i.get || i.set) ? o(f, _t, i) : f[_t] = e[_t]); return f; })(e, t); }
function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { (0, _defineProperty2.default)(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
var clearButtonSizeMap = {
[_constants.SIZES.small]: 'small',
[_constants.SIZES.medium]: 'medium',
[_constants.SIZES.large]: 'medium'
};
class Input extends _react.Component {
constructor(_props) {
var _this;
super(_props);
_this = this;
this._onCompositionChange = isComposing => {
if (this.props.onCompositionChange) {
this.props.onCompositionChange(isComposing);
}
this.isComposing = isComposing;
};
this._extractRef = ref => {
var {
inputRef
} = this.props;
this.input = ref;
if (inputRef) {
inputRef(ref);
}
};
this._handleSuffixOnClear = event => {
var {
focusOnClearClick
} = this.props;
focusOnClearClick && this.focus();
this.clear(event);
};
this._onFocus = event => {
var {
onFocus
} = this.props;
this._isMounted && this.setState({
focus: true
});
onFocus && onFocus(event);
if (this.props.autoSelect) {
// Set timeout is needed here since onFocus is called before react
// gets the reference for the input (specifically when autoFocus
// is on. So setTimeout ensures we have the ref.input needed in select)
setTimeout(() => {
/**
here we trying to cover edge case with chrome forms autofill,
after user will trigger chrome form autofill, onFocus will be called for each input,
each input will cause this.select, select may(mostly all time) cause new onFocus,
which will cause new this.select, ..., we have recursion which will all time randomly cause
inputs to become focused.
To prevent this, we check, that current input node is equal to focused node.
*/
if (document && document.activeElement === this.input) {
this.select();
}
}, 0);
}
};
this._onBlur = event => {
this.setState({
focus: false
});
if (this.props.onBlur) {
this.props.onBlur(event);
}
};
this._onClick = event => {
this.props.onInputClicked && this.props.onInputClicked(event);
};
this._onKeyDown = event => {
if (this.isComposing) {
return;
}
var {
onKeyDown,
onEnterPressed,
onEscapePressed
} = this.props;
// On key event
onKeyDown && onKeyDown(event);
// Enter
if (event.key === 'Enter' || event.keyCode === 13) {
onEnterPressed && onEnterPressed(event);
}
// Escape
if (event.key === 'Escape' || event.keyCode === 27) {
onEscapePressed && onEscapePressed(event);
}
};
this._isValidInput = value => {
var {
type
} = this.props;
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(value);
}
return true;
};
this._onChange = event => {
var {
onChange
} = this.props;
if (this._isValidInput(event.target.value)) {
onChange && onChange(event);
}
};
this._onKeyPress = event => {
if (!this._isValidInput(event.target.value + event.key)) {
event.preventDefault();
}
};
this._renderInput = props => {
var {
customInput: CustomInputComponent,
ref
} = props,
rest = (0, _objectWithoutProperties2.default)(props, _excluded);
var inputProps = _objectSpread({}, CustomInputComponent ? _objectSpread(_objectSpread({
ref
}, rest), {}, {
'data-hook': 'wsr-custom-input'
}) : _objectSpread({
ref
}, rest));
return /*#__PURE__*/_react.default.cloneElement(CustomInputComponent ? /*#__PURE__*/_react.default.createElement(CustomInputComponent, {
__self: this,
__source: {
fileName: _jsxFileName,
lineNumber: 201,
columnNumber: 30
}
}) : /*#__PURE__*/_react.default.createElement("input", {
__self: this,
__source: {
fileName: _jsxFileName,
lineNumber: 201,
columnNumber: 57
}
}), inputProps);
};
this._shouldShowStatusSuffix = _ref => {
var {
finalStatus,
statusFromProp
} = _ref;
return !this.props.hideStatusSuffix && (!!statusFromProp || finalStatus === 'loading') && Object.values(_constants2.STATUS).includes(finalStatus);
};
/**
* Sets focus on the input element
* @param {FocusOptions} options
*/
this.focus = function () {
var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
_this.input && _this.input.focus(options);
};
/**
* Removes focus on the input element
*/
this.blur = () => {
this.input && this.input.blur();
};
/**
* Selects all text in the input element
*/
this.select = () => {
this.input && this.input.select();
};
/**
* Sets the start and end positions of the current text selection in the input element
*/
this.setSelectionRange = (start, end) => {
this.input && this.input.setSelectionRange(start, end);
};
/**
* Clears the input.
* Fires onClear with the given event triggered on the clear button
*
* @param event delegated to the onClear call
*/
this.clear = event => {
var {
onClear
} = this.props;
if (!this._isControlled) {
this.input.value = '';
}
onClear && onClear(event);
};
this._isMounted = false;
this.state = {
focus: false
};
}
componentDidMount() {
this._isMounted = true;
var {
autoFocus,
value
} = this.props;
autoFocus && this._onFocus();
/*
* autoFocus doesn't automatically selects text like focus do.
* Therefore we set the selection range, but in order to support prior implementation we set the start position as the end in order to place the cursor there.
*/
if (autoFocus && !!value) {
this.input.setSelectionRange(value.length, value.length);
}
}
componentWillUnmount() {
this._isMounted = false;
}
get _isClearFeatureEnabled() {
var {
onClear,
clearButton
} = this.props;
return !!onClear || !!clearButton;
}
get _isControlled() {
var {
value
} = this.props;
return value !== undefined;
}
render() {
var props = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
var {
input: inputPropsDefaults
} = this.context;
var {
id,
name,
value,
placeholder,
menuArrow,
defaultValue,
tabIndex,
autoFocus,
onKeyUp,
onPaste,
disableEditing,
readOnly,
prefix,
suffix,
type,
maxLength,
textOverflow,
disabled,
status: statusProp,
statusMessage,
tooltipPlacement,
autocomplete,
min,
max,
step,
required,
customInput,
pattern,
size = inputPropsDefaults.size,
clearButtonTooltipContent,
clearButtonTooltipProps
} = this.props;
var onIconClicked = event => {
if (!disabled) {
this.input && this.input.focus();
this._onClick(event);
}
};
// this doesn't work for uncontrolled, "value" refers only to controlled input
var isClearButtonVisible = this._isClearFeatureEnabled && !!value && !disabled;
// Aria Attributes
var shouldRenderSuffix = _ref2 => {
var {
finalStatus,
statusFromProp
} = _ref2;
var visibleSuffixCount = (0, _InputSuffix.getVisibleSuffixCount)({
status: this._shouldShowStatusSuffix({
finalStatus,
statusFromProp
}),
statusMessage,
disabled,
isClearButtonVisible,
menuArrow,
suffix
});
return visibleSuffixCount > 0;
};
var ariaAttribute = {};
Object.keys(this.props).filter(key => key.startsWith('aria')).map(key => ariaAttribute['aria-' + key.substr(4).toLowerCase()] = this.props[key]);
/* eslint-disable no-unused-vars */
var {
className
} = props,
inputElementProps = (0, _objectWithoutProperties2.default)(props, _excluded2);
var inputElement = /*#__PURE__*/_react.default.createElement(_context.WixStyleReactMaskingContext.Consumer, {
__self: this,
__source: {
fileName: _jsxFileName,
lineNumber: 334,
columnNumber: 7
}
}, _ref3 => {
var {
maskingClassNames
} = _ref3;
return /*#__PURE__*/_react.default.createElement(_StatusContext.StatusContext.Consumer, {
__self: this,
__source: {
fileName: _jsxFileName,
lineNumber: 336,
columnNumber: 11
}
}, statusContext => this._renderInput(_objectSpread(_objectSpread(_objectSpread({
min,
max,
step,
'data-hook': 'wsr-input',
'data-mask': !!maskingClassNames,
style: {
textOverflow
},
ref: this._extractRef,
className: (0, _InputSt.st)(_InputSt.classes.input, {}, maskingClassNames),
id,
name,
disabled,
defaultValue,
value,
onChange: this._onChange,
onKeyPress: this._onKeyPress,
maxLength,
onFocus: this._onFocus,
onBlur: this._onBlur,
onWheel: () => {
// Although it's opposed to the native behavior, we decided to blur an input type="number" on wheel event in order to prevent change in value.
if (type === 'number') this.blur();
},
onKeyDown: this._onKeyDown,
onPaste,
placeholder,
tabIndex,
autoFocus,
onClick: this._onClick,
onKeyUp,
readOnly: readOnly || disableEditing,
type,
required,
autoComplete: autocomplete,
onCompositionStart: () => this._onCompositionChange(true),
onCompositionEnd: () => this._onCompositionChange(false),
customInput,
pattern
}, (0, _StatusContext.getAriaAttributesFromContext)(statusContext)), ariaAttribute), inputElementProps)));
});
return /*#__PURE__*/_react.default.createElement("div", {
className: _InputSt.classes.wrapper,
__self: this,
__source: {
fileName: _jsxFileName,
lineNumber: 387,
columnNumber: 7
}
}, prefix && /*#__PURE__*/_react.default.createElement(_InputContext.InputContext.Provider, {
value: _objectSpread(_objectSpread({}, this.props), {}, {
size,
inPrefix: true
}),
__self: this,
__source: {
fileName: _jsxFileName,
lineNumber: 390,
columnNumber: 11
}
}, prefix), inputElement, /*#__PURE__*/_react.default.createElement(_InputContext.InputContext.Provider, {
value: _objectSpread(_objectSpread({}, this.props), {}, {
size,
inSuffix: true
}),
__self: this,
__source: {
fileName: _jsxFileName,
lineNumber: 401,
columnNumber: 9
}
}, /*#__PURE__*/_react.default.createElement(_StatusContext.StatusContext.Consumer, {
__self: this,
__source: {
fileName: _jsxFileName,
lineNumber: 402,
columnNumber: 11
}
}, statusContext => {
var status = (0, _StatusContext.getStatusFromContext)(statusContext, statusProp);
return shouldRenderSuffix({
finalStatus: status,
statusFromProp: statusProp
}) && /*#__PURE__*/_react.default.createElement(_InputSuffix.default, {
status: this._shouldShowStatusSuffix({
finalStatus: status,
statusFromProp: statusProp
}) ? status : undefined,
statusMessage: statusMessage,
disabled: disabled,
onIconClicked: onIconClicked,
isClearButtonVisible: isClearButtonVisible,
onClear: this._handleSuffixOnClear,
clearButtonSize: clearButtonSizeMap[size],
menuArrow: menuArrow,
suffix: suffix,
tooltipPlacement: tooltipPlacement,
clearButtonTooltipContent: clearButtonTooltipContent,
clearButtonTooltipProps: clearButtonTooltipProps,
__self: this,
__source: {
fileName: _jsxFileName,
lineNumber: 410,
columnNumber: 19
}
});
})));
}
}
Input.contextType = _WixStyleReactDefaultsOverrideProvider.WixStyleReactDefaultsOverrideContext;
Input.Ticker = _Ticker.default;
Input.IconAffix = _IconAffix.default;
Input.Affix = _Affix.default;
Input.Group = _Group.default;
Input.StatusError = _constants2.STATUS.ERROR;
Input.StatusWarning = _constants2.STATUS.WARNING;
Input.StatusLoading = _constants2.STATUS.LOADING;
Input.displayName = 'Input';
Input.defaultProps = {
focusOnClearClick: true,
autoSelect: true,
border: 'standard',
textOverflow: 'clip',
maxLength: 524288,
clearButton: false,
hideStatusSuffix: false
};
Input.propTypes = {
/** Applies a data-hook HTML attribute that can be used in the tests */
dataHook: _propTypes.default.string,
/** Specifies a CSS class name to be appended to the component’s root element */
className: _propTypes.default.string,
/** Assigns a unique identifier for the root element */
id: _propTypes.default.string,
/** Associate a control with the regions that it controls */
ariaControls: _propTypes.default.string,
/** Associate a region with its descriptions. Similar to aria-controls but instead associating descriptions to the region and description identifiers are separated with a space. */
ariaDescribedby: _propTypes.default.string,
/** Define a string that labels the current element in case where a text label is not visible on the screen */
ariaLabel: _propTypes.default.string,
/** Focus the element on mount (standard React input autoFocus) */
autoFocus: _propTypes.default.bool,
/** Sets the value of native autocomplete attribute (check the [HTML spec](https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#attr-fe-autocomplete) for possible values */
autocomplete: _propTypes.default.string,
/** Defines the initial value of an input */
defaultValue: _propTypes.default.oneOfType([_propTypes.default.string, _propTypes.default.number]),
/** Specifies whether input should be disabled or not */
disabled: _propTypes.default.bool,
/** Specify the status of a field */
status: _propTypes.default.oneOf(['error', 'warning', 'loading']),
/** Defines the message to display on status icon hover. If not given or empty there will be no tooltip. */
statusMessage: _propTypes.default.node,
/** Specifies whether status suffix should be hidden */
hideStatusSuffix: _propTypes.default.bool,
/** USED FOR TESTING - forces focus state on the input */
forceFocus: _propTypes.default.bool,
/** USED FOR TESTING - forces hover state on the input */
forceHover: _propTypes.default.bool,
/** Sets the maximum number of characters that can be inserted to a field */
maxLength: _propTypes.default.number,
/** Specifies whether input should have a dropdown menu arrow on the right side */
menuArrow: _propTypes.default.bool,
/** Displays clear button (X) on a non-empty input */
clearButton: _propTypes.default.bool,
/** Reference element data when a form is submitted */
name: _propTypes.default.string,
/** Control the border style of input */
border: _propTypes.default.oneOf(['standard', 'round', 'bottomLine', 'none']),
/** Specifies whether input shouldn’t have rounded corners on its left */
noLeftBorderRadius: _propTypes.default.bool,
/** Specifies whether input shouldn’t have rounded corners on its right */
noRightBorderRadius: _propTypes.default.bool,
/** Defines a standard input onBlur callback */
onBlur: _propTypes.default.func,
/** Defines a standard input onChange callback */
onChange: _propTypes.default.func,
/** Displays clear button (X) on a non-empty input and calls a callback function with no arguments */
onClear: _propTypes.default.func,
/** Defines a callback function called on compositionstart/compositionend events */
onCompositionChange: _propTypes.default.func,
/** Defines a callback handler that is called when the user presses -enter- */
onEnterPressed: _propTypes.default.func,
/** Defines a callback handler that is called when the user presses -escape- */
onEscapePressed: _propTypes.default.func,
/** Defines a standard input onFocus callback */
onFocus: _propTypes.default.func,
/** Defines a standard input onClick callback */
onInputClicked: _propTypes.default.func,
/** Defines a standard input onKeyDown callback */
onKeyDown: _propTypes.default.func,
/** Defines a standard input onKeyUp callback */
onKeyUp: _propTypes.default.func,
/** Defines a callback handler that is called when user pastes text from a clipboard (using a mouse or keyboard shortcut) */
onPaste: _propTypes.default.func,
/** Sets a placeholder message to display */
placeholder: _propTypes.default.string,
/** Pass a component you want to show as the prefix of the input, e.g., text, icon */
prefix: _propTypes.default.node,
/** Specifies whether input is read only */
readOnly: _propTypes.default.bool,
/** Restricts input editing */
disableEditing: _propTypes.default.bool,
/** Controls the size of the input. Default value: `medium` */
size: _propTypes.default.oneOf(['tiny', 'small', 'medium', 'large']),
/** Pass a component you want to show as the suffix of the input, e.g., text, icon */
suffix: _propTypes.default.node,
/** Indicates that element can be focused and where it participates in sequential keyboard navigation */
tabIndex: _propTypes.default.number,
/** Handles text overflow behavior. It can either `clip` (default) or display `ellipsis`. */
textOverflow: _propTypes.default.string,
/** Controls placement of a status tooltip */
tooltipPlacement: _propTypes.default.string,
/** Specifies the type of `<input/>` element to display. Default is text. */
type: _propTypes.default.string,
/** Specifies the current value of the element */
value: _propTypes.default.oneOfType([_propTypes.default.string, _propTypes.default.number]),
/** Specifies whether input is mandatory */
required: _propTypes.default.bool,
/** Sets a minimum value of an input. Similar to HTML5 min attribute. */
min: _propTypes.default.number,
/** Sets a maximum value of an input. Similar to html5 max attribute. */
max: _propTypes.default.number,
/** Specifies the interval between number values */
step: _propTypes.default.number,
/** Render a custom input instead of the default html input tag */
customInput: _propTypes.default.elementType ? _propTypes.default.oneOfType([_propTypes.default.func, _propTypes.default.node, _propTypes.default.elementType]) : _propTypes.default.oneOfType([_propTypes.default.func, _propTypes.default.node]),
/** Sets a pattern that typed value must match to be valid (regex) */
pattern: _propTypes.default.string,
/** Specifies whether to focus the field when clear button is clicked */
focusOnClearClick: _propTypes.default.bool,
/** When provided hover will display a tooltip with content */
clearButtonTooltipContent: _propTypes.default.node,
/** Clear button tooltip props */
clearButtonTooltipProps: _propTypes.default.node
};
var _default = exports.default = Input;
//# sourceMappingURL=Input.js.map