UNPKG

@vtex/styleguide

Version:

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

298 lines (255 loc) 10.9 kB
'use strict'; Object.defineProperty(exports, "__esModule", { value: true }); var _createClass = 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); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); var _propTypes = require('prop-types'); var _propTypes2 = _interopRequireDefault(_propTypes); var _react = require('react'); var _react2 = _interopRequireDefault(_react); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : 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) { // 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; } else if (value < min) { return min; } else if (value > max) { return max; } return parseInt(value, 10); }; var validateDisplayValue = function validateDisplayValue(value, min, max) { // 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 min = normalizeMin(min); max = normalizeMax(max); var parsedValue = parseInt(value, 10); if (value === '') { return value; } // Only allows typing the negative sign if negative values are allowed if (value === '-' && min < 0) { return value; } 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 min; } if (parsedValue > max) { return max; } return parsedValue; }; var NumericStepper = function (_React$Component) { _inherits(NumericStepper, _React$Component); function NumericStepper() { var _ref; var _temp, _this, _ret; _classCallCheck(this, NumericStepper); for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) { args[_key] = arguments[_key]; } return _ret = (_temp = (_this = _possibleConstructorReturn(this, (_ref = NumericStepper.__proto__ || Object.getPrototypeOf(NumericStepper)).call.apply(_ref, [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) { var parsedValue = parseInt(value, 10); var _this$props = _this.props, minValue = _this$props.minValue, maxValue = _this$props.maxValue, defaultValue = _this$props.defaultValue, onChange = _this$props.onChange; var validatedValue = validateValue(parsedValue, minValue, maxValue, defaultValue); var displayValue = validateDisplayValue(value, minValue, maxValue); _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); }, _this.handleIncreaseValue = function (event) { _this.changeValue(_this.state.value + 1, event); }, _this.handleDecreaseValue = function (event) { _this.changeValue(_this.state.value - 1, event); }, _this.handleFocusInput = function (e) { e.target.select(); _this.setState({ inputFocused: true }); }, _this.handleBlurInput = function () { _this.setState({ displayValue: _this.state.value, inputFocused: false }); }, _temp), _possibleConstructorReturn(_this, _ret); } _createClass(NumericStepper, [{ key: 'render', value: function render() { var _state = this.state, value = _state.value, displayValue = _state.displayValue; var _props = this.props, maxValue = _props.maxValue, minValue = _props.minValue, size = _props.size, block = _props.block, label = _props.label; var isMin = value <= normalizeMin(minValue); var isMax = value >= normalizeMax(maxValue); var buttonSizeClasses = { 'regular': 'pv3 f6', 'large': 'pv4 f5', 'x-large': 'pv5 f4' }; var inputSizeClasses = { 'regular': 'pv3 f6 ' + (block ? 'flex-grow-1' : 'w3'), 'large': 'pv4 f5 ' + (block ? 'flex-grow-1' : 'w3'), 'x-large': 'pv5 f4 ' + (block ? 'flex-grow-1' : 'w4') }; return _react2.default.createElement( 'label', null, label && _react2.default.createElement( 'span', { className: 'db mb3 w-100' }, label ), _react2.default.createElement( 'div', { className: 'flex self-start' }, _react2.default.createElement('input', { type: 'tel', className: 'z-1 order-1 tc bw1 ba b--light-gray br0 ' + inputSizeClasses[size], style: Object.assign({}, block && { width: 0 }), value: displayValue, onChange: this.handleTypeQuantity, onFocus: this.handleFocusInput, onBlur: this.handleBlurInput }), _react2.default.createElement( 'div', { className: 'z-2 order-2 flex-none' }, _react2.default.createElement( 'button', { className: 'br2 ph0 tc ba bl-0 bw1 b--light-gray ' + buttonSizeClasses[size] + ' ' + (isMax ? 'bg-light-silver silver' : 'pointer bg-white blue'), style: { borderTopLeftRadius: 0, borderBottomLeftRadius: 0, width: '3em' }, disabled: isMax, 'aria-label': '+', onClick: this.handleIncreaseValue }, _react2.default.createElement( 'span', { className: 'b' }, '\uFF0B' ) ) ), _react2.default.createElement( 'div', { className: 'z-2 order-0 flex-none' }, _react2.default.createElement( 'button', Object.assign({ className: 'br2 ph0 ba br-0 bw1 b--light-gray ' + buttonSizeClasses[size] + ' ' + (isMin ? 'bg-light-silver silver' : 'pointer bg-white blue'), style: { borderTopRightRadius: 0, borderBottomRightRadius: 0, width: '3em' }, disabled: 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. */}, { onClick: this.handleDecreaseValue }), _react2.default.createElement( 'span', { className: 'b' }, '\uFF0D' ) ) ) ) ); } }], [{ key: 'getDerivedStateFromProps', value: function getDerivedStateFromProps(props, state) { var value = props.value, minValue = props.minValue, maxValue = props.maxValue, defaultValue = props.defaultValue; var validatedValue = validateValue(value, minValue, maxValue, defaultValue); return Object.assign({ value: validatedValue }, !state.inputFocused && { displayValue: validateDisplayValue(value, minValue, maxValue) }); } }]); return NumericStepper; }(_react2.default.Component); exports.default = NumericStepper; 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, /** Input size */ size: _propTypes2.default.oneOf(['regular', 'large', 'x-large']), /** Block or default size. */ block: _propTypes2.default.bool, /** Input label */ label: _propTypes2.default.string }; NumericStepper.defaultProps = { minValue: 0, maxValue: Infinity, defaultValue: 0, size: 'regular', block: false };