UNPKG

react-cm-ui

Version:
791 lines (672 loc) 27.5 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports["default"] = void 0; var _lodash = require("lodash"); var _classnames = _interopRequireDefault(require("classnames")); var _reactTextMask = _interopRequireDefault(require("react-text-mask")); var _propTypes = _interopRequireDefault(require("prop-types")); var _react = _interopRequireDefault(require("react")); var _icon = _interopRequireDefault(require("../../dataDisplay/icon")); var _keyCode = _interopRequireDefault(require("../../global/keyCode.js")); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; } function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); if (enumerableOnly) symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; }); keys.push.apply(keys, symbols); } return keys; } function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; if (i % 2) { ownKeys(Object(source), true).forEach(function (key) { _defineProperty(target, key, source[key]); }); } else if (Object.getOwnPropertyDescriptors) { Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)); } else { ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } } return target; } function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } function _extends() { _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); } function _typeof(obj) { "@babel/helpers - typeof"; if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") { _typeof = function _typeof(obj) { return typeof obj; }; } else { _typeof = function _typeof(obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; } return _typeof(obj); } function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; } function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function"); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, writable: true, configurable: true } }); if (superClass) _setPrototypeOf(subClass, superClass); } function _setPrototypeOf(o, p) { _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) { o.__proto__ = p; return o; }; return _setPrototypeOf(o, p); } function _createSuper(Derived) { var hasNativeReflectConstruct = _isNativeReflectConstruct(); return function _createSuperInternal() { var Super = _getPrototypeOf(Derived), result; if (hasNativeReflectConstruct) { var NewTarget = _getPrototypeOf(this).constructor; result = Reflect.construct(Super, arguments, NewTarget); } else { result = Super.apply(this, arguments); } return _possibleConstructorReturn(this, result); }; } function _possibleConstructorReturn(self, call) { if (call && (_typeof(call) === "object" || typeof call === "function")) { return call; } return _assertThisInitialized(self); } function _assertThisInitialized(self) { if (self === void 0) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return self; } function _isNativeReflectConstruct() { if (typeof Reflect === "undefined" || !Reflect.construct) return false; if (Reflect.construct.sham) return false; if (typeof Proxy === "function") return true; try { Date.prototype.toString.call(Reflect.construct(Date, [], function () {})); return true; } catch (e) { return false; } } function _getPrototypeOf(o) { _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf : function _getPrototypeOf(o) { return o.__proto__ || Object.getPrototypeOf(o); }; return _getPrototypeOf(o); } var propTypes = { /** * Indicates whether the value of the control can be automatically completed by the browser. */ autoComplete: _propTypes["default"].oneOf(['off', 'on']), /** * Indicates whether or not the field allows decimal values (or just whole numbers) * for Input of type "number". */ allowDecimals: _propTypes["default"].bool, /** * Gives Input immediate focus. */ autoFocus: _propTypes["default"].bool, /** * Indicates whether or not the field allows negative numbers (or just positive numbers) * for Input of type "number". */ allowNegativeNumbers: _propTypes["default"].bool, /** * Additional classes. */ className: _propTypes["default"].string, /** * Used for DOM testing. https://testing-library.com/docs/queries/bytestid/ */ dataTestId: _propTypes["default"].string, /** * Deprecated prop. Please use `disabled` instead. */ disable: _propTypes["default"].bool, /** * An Input can be disabled. */ disabled: _propTypes["default"].bool, /** * Indicates that the input has an error. */ error: _propTypes["default"].oneOfType([_propTypes["default"].bool, _propTypes["default"].string]), /** * An input can take on the size of its container. */ fluid: _propTypes["default"].bool, /** * Forwarded Ref */ forwardedRef: _propTypes["default"].oneOfType([// Either a function _propTypes["default"].func, // Or the instance of a DOM native element (see the note about SSR) _propTypes["default"].shape({ current: _propTypes["default"].instanceOf(Element) })]), /** * Indicates whether or not the Input should be in guide mode. */ guide: _propTypes["default"].bool, /** * Optional icon to display inside the input. */ icon: _propTypes["default"].oneOfType([_propTypes["default"].shape({}), _propTypes["default"].string]), /** * Assign an element ID to the Input. */ id: _propTypes["default"].string, /** * Format to appear on dark backgrounds. */ inverse: _propTypes["default"].bool, /** * Changes the general behavior of the Text Mask component. */ keepCharPositions: _propTypes["default"].bool, /** * Optional Label to display with the Input. */ label: _propTypes["default"].string, /** * Position the label above or below the input. */ labelPosition: _propTypes["default"].oneOf(['bottom', 'top']), /** * Supply any inline styles to the Input. */ labelStyle: _propTypes["default"].shape({}), /** * An icon input field can show that it is currently loading data. */ loading: _propTypes["default"].bool, /** * Define an input mask to aid the user in inputting specific kinds of values * (e.g. phone numbers and the like) by providing either an array of characters * or a function. */ mask: _propTypes["default"].oneOfType([_propTypes["default"].array, _propTypes["default"].func]), /** * Specifies the maximum value the field can have. */ max: _propTypes["default"].number, /** * Specifies the maximum number of characters that the user can enter. */ maxLength: _propTypes["default"].number, /** * Specifies the minimum value the field can have. */ min: _propTypes["default"].number, /** * Specifies the minimum number of characters that the user needs to enter. */ minLength: _propTypes["default"].number, /** * Specifies name of the field */ name: _propTypes["default"].string, /** * Specify an event handler function to be called in response to `onBlur` event. */ onBlur: _propTypes["default"].func, /** * Specify an event handler functionto be called to handle `onChange` event. * Necessary for using the Input as a controlled component, in conjunction with * the `value` prop. */ onChange: _propTypes["default"].func, /** * Specify an event handler function to be called in response to `onClick` event. */ onClick: _propTypes["default"].func, /** * Specify an event handler function to be called in response to `onFocus` event. */ onFocus: _propTypes["default"].func, /** * Specify an event handler function to be called in response to `onKeyDown` event. */ onKeyDown: _propTypes["default"].func, /** * A hint to the user of what can be entered in the input. */ placeholder: _propTypes["default"].string, /** * Specifies that the user must fill in a value before submitting a form. */ required: _propTypes["default"].bool, /** * Specifies whether or not to show the spinners for the numeric control. This attribute is optional. */ showSpinners: _propTypes["default"].bool, /** * Supply any inline styles to the Input\'s container. Mainly used for padding and margins. */ style: _propTypes["default"].shape({}), /** * An Input can receive focus. */ tabIndex: _propTypes["default"].oneOfType([_propTypes["default"].number, _propTypes["default"].string]), /** * The HTML input type. */ type: _propTypes["default"].oneOf(['email', 'number', 'password', 'tel', 'text']), /** * The initial value of the control. This attribute is optional; however, you must use it * if using `onChange` prop and using the Input as a controlled component. */ value: _propTypes["default"].oneOfType([_propTypes["default"].number, _propTypes["default"].string]) }; var defaultProps = { autoComplete: null, allowDecimals: true, allowNegativeNumbers: true, autoFocus: null, className: null, dataTestId: undefined, disable: false, disabled: false, error: null, fluid: false, forwardedRef: undefined, guide: false, icon: null, id: null, inverse: false, keepCharPositions: false, label: null, labelPosition: null, labelStyle: null, loading: false, mask: null, max: null, maxLength: null, min: null, minLength: null, name: null, onBlur: null, onChange: null, onClick: null, onFocus: null, onKeyDown: null, placeholder: null, required: false, showSpinners: true, style: null, tabIndex: null, type: null, value: undefined }; /** * The Input represents a field for storing a value. */ var Input = /*#__PURE__*/function (_React$PureComponent) { _inherits(Input, _React$PureComponent); var _super = _createSuper(Input); function Input(props) { var _props$forwardedRef; var _this; _classCallCheck(this, Input); _this = _super.call(this, props); _this.state = { isFocused: false, inputActionsTopPosition: 0 }; _this.onBlur = _this.onBlur.bind(_assertThisInitialized(_this)); _this.onChange = _this.onChange.bind(_assertThisInitialized(_this)); _this.onClick = _this.onClick.bind(_assertThisInitialized(_this)); _this.onFocus = _this.onFocus.bind(_assertThisInitialized(_this)); _this.onKeyDown = _this.onKeyDown.bind(_assertThisInitialized(_this)); _this.onNumberToggleDownClick = _this.onNumberToggleDownClick.bind(_assertThisInitialized(_this)); _this.onNumberToggleUpClick = _this.onNumberToggleUpClick.bind(_assertThisInitialized(_this)); _this.inputTimer = null; _this.previousInputValue = ''; _this.input = (_props$forwardedRef = props.forwardedRef) !== null && _props$forwardedRef !== void 0 ? _props$forwardedRef : /*#__PURE__*/_react["default"].createRef(); return _this; } _createClass(Input, [{ key: "componentDidMount", value: function componentDidMount() { var _this$props = this.props, autoFocus = _this$props.autoFocus, icon = _this$props.icon, loading = _this$props.loading, mask = _this$props.mask; var type = this.getType(); if ((0, _lodash.isString)(icon) || (0, _lodash.isObject)(icon) || loading || type === 'number') { var inputTop = mask ? this.input.current.inputElement.offsetTop : this.input.current.offsetTop; if (inputTop > 0) { this.setState({ inputActionsTopPosition: inputTop }); } } if (autoFocus) { if (mask) { this.input.current.inputElement.focus(); } else { this.input.current.focus(); } this.setState({ isFocused: true }); } } }, { key: "onBlur", value: function onBlur(event) { var onBlur = this.props.onBlur; if ((0, _lodash.isFunction)(onBlur)) { onBlur(event.target.value); } this.setState({ isFocused: false }); } }, { key: "onChange", value: function onChange(event) { var _this2 = this; var _this$props2 = this.props, disable = _this$props2.disable, disabled = _this$props2.disabled, max = _this$props2.max, min = _this$props2.min, required = _this$props2.required; var isDisabled = disable || disabled; if (!isDisabled) { var type = this.getType(); var newValue = event.target.value; if (type === 'number') { if (this.inputTimer) { clearTimeout(this.inputTimer); } this.inputTimer = setTimeout(function () { if ((0, _lodash.isEmpty)(newValue)) { if (required) { if ((0, _lodash.isNumber)(min)) { newValue = min; } else if ((0, _lodash.isNumber)(max)) { newValue = max; } else { newValue = 0; } } } else { newValue = +newValue; if ((0, _lodash.isNumber)(max)) { newValue = Math.min(max, newValue); } if ((0, _lodash.isNumber)(min)) { newValue = Math.max(min, newValue); } } _this2.setNewValue(newValue); }, 500); } this.setNewValue(newValue); } } }, { key: "onClick", value: function onClick(event) { var onClick = this.props.onClick; if ((0, _lodash.isFunction)(onClick)) { onClick(event.target.value); } } }, { key: "onFocus", value: function onFocus(event) { var onFocus = this.props.onFocus; if ((0, _lodash.isFunction)(onFocus)) { onFocus(event); } this.setState(function (prevState) { return { isFocused: !prevState.isFocused }; }); } }, { key: "onKeyDown", value: function onKeyDown(event) { var _this$props3 = this.props, allowDecimals = _this$props3.allowDecimals, allowNegativeNumbers = _this$props3.allowNegativeNumbers, onKeyDown = _this$props3.onKeyDown; var type = this.getType(); if ((0, _lodash.isFunction)(onKeyDown)) { onKeyDown(event); } if (type === 'number') { var isCtrlKey = event.metaKey || // detects Apple Command key - https://stackoverflow.com/a/3922353/7415670 | https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/metaKey event.ctrlKey || // CTRL key modifier flag event.keyCode === _keyCode["default"].Ctrl; // key code signifies the CTRL key var shouldAllowCharacter = event.keyCode === _keyCode["default"].Backspace || // allow the backspace key event.keyCode === _keyCode["default"].LeftArrow || // allow left arraw event.keyCode === _keyCode["default"].RightArrow || // allow right arrow event.keyCode === _keyCode["default"].Tab || // allow tab key /* eslint-disable max-len */ event.keyCode >= _keyCode["default"].NormalNumber_0 && event.keyCode <= _keyCode["default"].NormalNumber_9 || // allow digits 0-9 from the "normal" number keys at the top of the keybaord event.keyCode >= _keyCode["default"].NumberPad_0 && event.keyCode <= _keyCode["default"].NumberPad_9 || // allow digits 0-9 from the numeric keypad to the left of the keyboard - https://stackoverflow.com/a/13196983/7415670 /* eslint-enable max-len */ allowDecimals && event.keyCode === _keyCode["default"].Dot || // allow dot as the decimal separator if `allowDecimals` is `true` / TODO/FIXME: some locales use comma instead of dot! allowNegativeNumbers && event.keyCode === _keyCode["default"].Minus || // allow minus sign if `allowNegativeNumbers` is true isCtrlKey && event.keyCode === _keyCode["default"].Letter_A || // allow CTRL+A for Select All isCtrlKey && event.keyCode === _keyCode["default"].Letter_C || // allow CTRL+C for Copy isCtrlKey && event.keyCode === _keyCode["default"].Letter_V || // allow CTRL+V for Paste isCtrlKey && event.keyCode === _keyCode["default"].Letter_X || // allow CTRL+X for Cut isCtrlKey && event.keyCode === _keyCode["default"].Letter_Y || // allow CTRL+Y for Redo isCtrlKey && event.keyCode === _keyCode["default"].Letter_Z; // allow CTRL+Z for Undo if (!shouldAllowCharacter) { event.preventDefault(); } } } }, { key: "onNumberToggleClick", value: function onNumberToggleClick(action) { var _this$props4 = this.props, disable = _this$props4.disable, disabled = _this$props4.disabled, mask = _this$props4.mask, max = _this$props4.max, min = _this$props4.min, type = _this$props4.type; var isDisabled = disable || disabled; var value; if (mask) { value = this.input.current.inputElement.value; } else { value = this.input.current.value; } if (!isDisabled) { var newValue = value ? (0, _lodash.toNumber)(value) : 0; switch (action) { case 'down': if (type === 'number' && (0, _lodash.isNumber)(min) && newValue - 1 < min) { if (!newValue) { newValue = min; } } else { newValue -= 1; } break; case 'up': if (type === 'number' && (0, _lodash.isNumber)(max) && newValue + 1 > max) { newValue = max; } else { newValue += 1; } break; default: } this.setNewValue(newValue); } } }, { key: "onNumberToggleDownClick", value: function onNumberToggleDownClick() { this.onNumberToggleClick('down'); } }, { key: "onNumberToggleUpClick", value: function onNumberToggleUpClick() { this.onNumberToggleClick('up'); } }, { key: "getType", value: function getType() { var type = this.props.type; var newType; switch (type) { case 'email': newType = 'email'; break; case 'number': newType = 'number'; break; case 'password': newType = 'password'; break; case 'telephone': case 'tel': case 'phone': newType = 'tel'; break; case 'text': default: newType = 'text'; break; } return newType; } }, { key: "setNewValue", value: function setNewValue(value) { var _this$props5 = this.props, mask = _this$props5.mask, onChange = _this$props5.onChange; if ((0, _lodash.isFunction)(onChange)) { onChange(value); } else if (mask) { this.input.current.inputElement.value = value; } else { this.input.current.value = value; } } }, { key: "render", value: function render() { var _this3 = this; var _this$props6 = this.props, autoComplete = _this$props6.autoComplete, className = _this$props6.className, dataTestId = _this$props6.dataTestId, disable = _this$props6.disable, disabled = _this$props6.disabled, error = _this$props6.error, fluid = _this$props6.fluid, guide = _this$props6.guide, icon = _this$props6.icon, id = _this$props6.id, inverse = _this$props6.inverse, keepCharPositions = _this$props6.keepCharPositions, label = _this$props6.label, labelPosition = _this$props6.labelPosition, labelStyle = _this$props6.labelStyle, loading = _this$props6.loading, mask = _this$props6.mask, max = _this$props6.max, maxLength = _this$props6.maxLength, min = _this$props6.min, minLength = _this$props6.minLength, name = _this$props6.name, placeholder = _this$props6.placeholder, required = _this$props6.required, style = _this$props6.style, tabIndex = _this$props6.tabIndex, showSpinners = _this$props6.showSpinners, value = _this$props6.value; var _this$state = this.state, isFocused = _this$state.isFocused, inputActionsTopPosition = _this$state.inputActionsTopPosition; var type = this.getType(); var newLabelPosition = labelPosition || 'top'; var isDisabled = disable || disabled; var containerClasses = (0, _classnames["default"])('ui', 'input', className, { 'input-disabled': isDisabled, 'input-error': error, 'input-fluid': fluid, 'input-has-value': value, 'input-icon': icon, 'input-focused': isFocused, 'input-inverse': inverse, 'input-loading': loading, 'input-type-email': type === 'email', 'input-type-number': type === 'number', 'input-type-password': type === 'password', 'input-type-tel': type === 'tel', 'input-type-text': type === 'text' }); var labelContainerClassNames = (0, _classnames["default"])('label', { 'label-bottom': newLabelPosition === 'bottom', 'label-top': newLabelPosition === 'top' }); var renderLabel = function renderLabel() { if (!label) { return null; } var shouldShowRequiredIndicator = required && !value; return /*#__PURE__*/_react["default"].createElement("label", { className: labelContainerClassNames, htmlFor: id, style: labelStyle }, label, shouldShowRequiredIndicator && /*#__PURE__*/_react["default"].createElement("span", { className: "input-required-indicator" }, "*")); }; return /*#__PURE__*/_react["default"].createElement("div", { className: containerClasses, style: style }, newLabelPosition === 'top' && renderLabel(), mask ? /*#__PURE__*/_react["default"].createElement(_reactTextMask["default"], { autoComplete: autoComplete, "data-testid": dataTestId, disabled: isDisabled, guide: guide, id: id, keepCharPositions: keepCharPositions, name: name, mask: mask, maxLength: maxLength, minLength: minLength, onBlur: this.onBlur, onChange: this.onChange, onClick: this.onClick, onFocus: this.onFocus, onKeyDown: this.onKeyDown, placeholder: placeholder, ref: this.input, required: required, tabIndex: tabIndex, type: type, value: value }) : /*#__PURE__*/_react["default"].createElement("input", { autoComplete: autoComplete, "data-testid": dataTestId, disabled: isDisabled, id: id, name: name, max: max, maxLength: maxLength, min: min, minLength: minLength, onBlur: this.onBlur, onChange: this.onChange, onClick: this.onClick, onFocus: this.onFocus, onKeyDown: this.onKeyDown, placeholder: placeholder, ref: this.input, required: required, tabIndex: tabIndex, type: type, value: value }), newLabelPosition === 'bottom' && renderLabel(), ((0, _lodash.isString)(icon) || (0, _lodash.isObject)(icon) || loading || type === 'number') && /*#__PURE__*/_react["default"].createElement("div", { className: "input-actions" // eslint-disable-next-line no-underscore-dangle , ref: function ref(_ref) { _this3._inputActions = _ref; }, style: { pointerEvents: 'none', top: inputActionsTopPosition } }, ((0, _lodash.isString)(icon) || loading) && /*#__PURE__*/_react["default"].createElement(_icon["default"], { compact: true, spin: loading, type: loading ? 'spinner' : icon }), (0, _lodash.isObject)(icon) && /*#__PURE__*/_react["default"].createElement("div", { className: "input-icon-custom", style: { pointerEvents: 'auto' } }, icon), type === 'number' && showSpinners && /*#__PURE__*/_react["default"].createElement("div", { className: "input-number-controls", style: { pointerEvents: isDisabled ? 'none' : 'auto' } }, /*#__PURE__*/_react["default"].createElement(_icon["default"], { compact: true, onClick: this.onNumberToggleUpClick, size: "xsmall", title: "Increase", type: "caret-up" }), /*#__PURE__*/_react["default"].createElement(_icon["default"], { compact: true, onClick: this.onNumberToggleDownClick, size: "xsmall", title: "Decrease", type: "caret-down" }))), error && (0, _lodash.isString)(error) && /*#__PURE__*/_react["default"].createElement("p", { className: "input-error-message" }, error)); } }]); return Input; }(_react["default"].PureComponent); Input.propTypes = propTypes; Input.defaultProps = defaultProps; var InputWrapper = /*#__PURE__*/_react["default"].forwardRef(function (props, ref) { return ( /*#__PURE__*/ // eslint-disable-next-line react/jsx-props-no-spreading _react["default"].createElement(Input, _extends({}, props, { forwardedRef: ref })) ); }); InputWrapper.displayName = 'Input'; var wrapperPropTypes = _objectSpread({}, propTypes); delete wrapperPropTypes.forwardedRef; var wrapperDefaultProps = _objectSpread({}, defaultProps); delete wrapperDefaultProps.forwardedRef; InputWrapper.propTypes = wrapperPropTypes; InputWrapper.defaultProps = wrapperDefaultProps; var _default = InputWrapper; exports["default"] = _default;