UNPKG

@vtex/styleguide

Version:

> VTEX Styleguide React components ([Docs](https://vtex.github.io/styleguide))

403 lines (327 loc) 14.6 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); var _react = require("react"); var _react2 = _interopRequireDefault(_react); var _propTypes = require("prop-types"); var _propTypes2 = _interopRequireDefault(_propTypes); var _Input = require("../Input/Input.css"); var _Input2 = _interopRequireDefault(_Input); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: 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 _inheritsLoose(subClass, superClass) { subClass.prototype = Object.create(superClass.prototype); subClass.prototype.constructor = subClass; subClass.__proto__ = superClass; } var normalizeMin = function normalizeMin(min) { return min == null ? -Infinity : min; }; var normalizeMax = function normalizeMax(max) { return max == null ? Infinity : max; }; var validateValue = function validateValue(value, min, max, defaultValue, unitMultiplier, isTyping) { // This function always return a valid numeric value from the current input. // Compare with the function validateDisplayValue min = normalizeMin(min); max = normalizeMax(max); if (isNaN(value) || value == null) { if (defaultValue < min) return min; if (defaultValue > max) return max; return defaultValue; } var parsedValue = parseFloat(value, 10); var normalizedValue = isTyping ? Math.round(parsedValue / unitMultiplier) : parsedValue; return Math.max(min, Math.min(max, normalizedValue)); }; var formattedDisplayValue = function formattedDisplayValue(value, unitMultiplier, suffix, isTyping) { var parsedSuffix = suffix ? " " + suffix : suffix; if (!isTyping) { var multipliedValue = Math.round(value * unitMultiplier * 100) / 100; return "" + multipliedValue + parsedSuffix; } return "" + value + parsedSuffix; }; var validateDisplayValue = function validateDisplayValue(value, min, max, suffix, unitMultiplier, isTyping) { // This function validates the input as the user types // It allows for temporarily invalid values (namely, empty string and minus sign without a number following it) // However, it prevents values out of boundaries, and invalid characters, e.g. letters var boundaryMultiplier = isTyping ? unitMultiplier : 1; min = normalizeMin(min) * boundaryMultiplier; max = normalizeMax(max) * boundaryMultiplier; var parsedValue = parseFloat(value); if (value === '') { return formattedDisplayValue(value, unitMultiplier, suffix, isTyping); } // Only allows typing the negative sign if negative values are allowed if (typeof value === 'string' && value.startsWith('-') && min < 0) { return formattedDisplayValue(value, unitMultiplier, suffix, isTyping); } if (isNaN(parsedValue)) { return ''; } // Only limit by lower bounds if the min value is 1 // Otherwise, it could prevent typing, for example, 10 if the min value is 2 if (parsedValue < min && min === 1) { return formattedDisplayValue(min, unitMultiplier, suffix, isTyping); } if (parsedValue > max) { return formattedDisplayValue(max, unitMultiplier, suffix, isTyping); } return formattedDisplayValue(parsedValue, unitMultiplier, suffix, isTyping); }; var NumericStepper = /*#__PURE__*/ function (_Component) { _inheritsLoose(NumericStepper, _Component); function NumericStepper() { var _this; for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) { args[_key] = arguments[_key]; } _this = _Component.call.apply(_Component, [this].concat(args)) || this; _this.state = { inputFocused: false, // used for comparison whether to trigger onChange or not value: 0, // used for temporarily invalid values during typing--specifically, when it's empty displayValue: 0 }; _this.changeValue = function (value, event, isTyping) { var parsedValue = parseFloat(value, 10); var _this$props = _this.props, minValue = _this$props.minValue, maxValue = _this$props.maxValue, defaultValue = _this$props.defaultValue, onChange = _this$props.onChange, suffix = _this$props.suffix, unitMultiplier = _this$props.unitMultiplier; var validatedValue = validateValue(parsedValue, minValue, maxValue, defaultValue, unitMultiplier, isTyping); var displayValue = validateDisplayValue(isTyping ? value : validatedValue, minValue, maxValue, suffix, unitMultiplier, isTyping); _this.setState({ value: validatedValue, displayValue: displayValue }); if (_this.state.value !== validatedValue && onChange) { // React synthetic events are reused for performance reasons. // New properties added to it are never released. // Calling event.persist() releases the event from the pool // https://reactjs.org/docs/events.html#event-pooling event.persist(); event.value = validatedValue; onChange(event); } }; _this.handleTypeQuantity = function (event) { _this.changeValue(event.target.value, event, true); }; _this.handleIncreaseValue = function (event) { event.stopPropagation(); event.preventDefault(); _this.changeValue(_this.state.value + 1, event, false); }; _this.handleDecreaseValue = function (event) { event.stopPropagation(); event.preventDefault(); _this.changeValue(_this.state.value - 1, event, false); }; _this.handleFocusInput = function (e) { e.target.select(); _this.setState({ inputFocused: true }); }; _this.handleBlurInput = function () { var _this$props2 = _this.props, minValue = _this$props2.minValue, maxValue = _this$props2.maxValue, unitMultiplier = _this$props2.unitMultiplier, suffix = _this$props2.suffix; var displayValue = validateDisplayValue(_this.state.value, minValue, maxValue, suffix, unitMultiplier, false); _this.setState({ displayValue: displayValue, inputFocused: false }); }; return _this; } var _proto = NumericStepper.prototype; _proto.componentDidMount = function componentDidMount() { if (this.props.size === 'x-large') { console.warn('NumericStepper: The value "x-large" for the prop "size" is deprecated. In the next major version, it will be equivalent to "large", and removed altogether in future versions'); } }; NumericStepper.getDerivedStateFromProps = function getDerivedStateFromProps(props, state) { var value = props.value, minValue = props.minValue, maxValue = props.maxValue, defaultValue = props.defaultValue, suffix = props.suffix, unitMultiplier = props.unitMultiplier; var validatedValue = validateValue(value, minValue, maxValue, defaultValue, unitMultiplier, false); return _extends({ value: validatedValue }, !state.inputFocused && { displayValue: validateDisplayValue(validatedValue, minValue, maxValue, suffix, unitMultiplier, false) }); }; _proto.render = function render() { var _this$state = this.state, value = _this$state.value, displayValue = _this$state.displayValue; var _this$props3 = this.props, maxValue = _this$props3.maxValue, minValue = _this$props3.minValue, unitMultiplier = _this$props3.unitMultiplier, size = _this$props3.size, block = _this$props3.block, label = _this$props3.label, lean = _this$props3.lean, readOnly = _this$props3.readOnly; var isMin = value <= normalizeMin(minValue); var isMax = value >= normalizeMax(maxValue); var labelClasses = ''; var buttonClasses = ''; var inputClasses = ''; switch (size) { case 'small': { buttonClasses += "h-small " + (lean ? 'f4' : 'f6') + " "; var inputWidth = lean ? 'w1' : 'w3'; inputClasses += "h-small t-small " + (block ? 'flex-grow-1' : inputWidth) + " "; labelClasses += 't-small '; break; } case 'large': { buttonClasses += "h-large " + (lean ? 'f3' : 'f5') + " "; var _inputWidth = lean ? 'w2' : 'w3'; inputClasses += "h-large t-body " + (block ? 'flex-grow-1' : _inputWidth) + " "; labelClasses += 't-body '; break; } case 'x-large': { // DEPRECATED buttonClasses += "pv5 " + (lean ? 'f2' : 'f4') + " "; var _inputWidth2 = lean ? 'w3' : 'w4'; inputClasses += "pv5 t-body " + (block ? 'flex-grow-1' : _inputWidth2) + " "; labelClasses += 't-body '; break; } default: { buttonClasses += "h-regular " + (lean ? 'f4' : 'f6') + " "; var _inputWidth3 = lean ? 'w2' : 'w3'; inputClasses += "h-regular t-body " + (block ? 'flex-grow-1' : _inputWidth3) + " "; labelClasses += 't-small '; break; } } if (lean) inputClasses += 'br2 hover-b--muted-4 ba outline-transparent'; var borderClasses = lean ? 'b--transparent ' : 'ba b--muted-4 bw1 '; var buttonDisabledClasses = lean ? 'c-disabled bg-transparent ' : 'bg-muted-5 c-disabled o-100 '; var buttonEnabledClasses = "pointer bg-base c-action-primary " + (lean ? 'outline-0' : '') + " "; var content = _react2.default.createElement(_react2.default.Fragment, null, label && _react2.default.createElement("span", { className: "vtex-numeric-stepper__label numeric-stepper__label db mb3 w-100 c-on-base " + labelClasses }, label), _react2.default.createElement("div", { className: "vtex-numeric-stepper-container numeric-stepper-container flex self-start" }, _react2.default.createElement("input", { type: "tel", readOnly: readOnly, className: "vtex-numeric-stepper__input numeric-stepper__input z-1 order-1 tc bw1 " + borderClasses + " br0 " + inputClasses + " " + _Input2.default.hideDecorators, style: _extends({}, block && { width: 0 }, { WebkitAppearance: 'none' }), value: displayValue, step: unitMultiplier, onChange: this.handleTypeQuantity, onFocus: this.handleFocusInput, onBlur: this.handleBlurInput }), _react2.default.createElement("div", { className: "vtex-numeric-stepper__plus-button-container numeric-stepper__plus-button-container z-2 order-2 flex-none" }, _react2.default.createElement("button", { type: "button", className: "vtex-numeric-stepper__plus-button numeric-stepper__plus-button br2 pa0 bl-0 flex items-center justify-center " + borderClasses + " " + buttonClasses + " " + (readOnly || isMax ? buttonDisabledClasses : buttonEnabledClasses), style: { borderTopLeftRadius: 0, borderBottomLeftRadius: 0, width: lean ? '2em' : '3em', transition: 'opacity 150ms' }, disabled: readOnly || isMax, "aria-label": "+", tabIndex: 0, onClick: this.handleIncreaseValue }, _react2.default.createElement("div", { className: "vtex-numeric-stepper__plus-button__text numeric-stepper__plus-button__text b" }, "\uFF0B"))), _react2.default.createElement("div", { className: "vtex-numeric-stepper__minus-button-container numeric-stepper__minus-button-container z-2 order-0 flex-none" }, _react2.default.createElement("button", { type: "button", className: "vtex-numeric-stepper__minus-button numeric-stepper__minus-button br2 pa0 br-0 flex items-center justify-center " + borderClasses + " " + buttonClasses + " " + (readOnly || isMin ? buttonDisabledClasses : buttonEnabledClasses), style: { borderTopRightRadius: 0, borderBottomRightRadius: 0, width: lean ? '2em' : '3em', transition: 'opacity 150ms' }, disabled: readOnly || isMin, "aria-label": "\u2212" // This is a minus sign (U+2212), not a regular hyphen (-, U+002D), // which is the default keyboard character. // Used for screen readers. , tabIndex: 0, onClick: this.handleDecreaseValue }, _react2.default.createElement("span", { className: "vtex-numeric-stepper__minus-button__text numeric-stepper__minus-button__text b" }, "\uFF0D"))))); // Refrain from using label tag if not needed, to prevent // iOS from focusing on the text field and popping up the // keyboard when increment/decrement is pressed if (label && !lean) { return _react2.default.createElement("label", { className: "vtex-numeric-stepper-wrapper numeric-stepper-wrapper" }, content); } return _react2.default.createElement("div", { className: "vtex-numeric-stepper-wrapper numeric-stepper-wrapper" }, content); }; return NumericStepper; }(_react.Component); NumericStepper.propTypes = { /** Value of the input */ value: _propTypes2.default.number, /** onChange event handler */ onChange: _propTypes2.default.func.isRequired, /** Minimum value (will be the default value in case of invalid input, e.g. letters). Set to null or -Infinity in case there is no miminum. Default is 0. */ minValue: _propTypes2.default.number, /** Maximum value (null or Infinity in case there is no maximum. Default is Infinity) */ maxValue: _propTypes2.default.number, /** Default value in case of invalid input (e.g. letters) and there is no minimum value */ defaultValue: _propTypes2.default.number, /** Multiplier value (e.g 1, 0.3) */ unitMultiplier: _propTypes2.default.number, /** Suffix (e.g Kg, un) */ suffix: _propTypes2.default.string, /** Makes input readonly and disables buttons */ readOnly: _propTypes2.default.bool, /** Input size */ size: _propTypes2.default.oneOf(['small', 'regular', 'large']), /** Block or default size. */ block: _propTypes2.default.bool, /** Input label */ label: _propTypes2.default.string, /** Lean mode, with subtler styling */ lean: _propTypes2.default.bool }; NumericStepper.defaultProps = { minValue: 0, maxValue: Infinity, defaultValue: 0, unitMultiplier: 1, suffix: '', readOnly: false, size: 'regular', block: false }; exports.default = NumericStepper;