@vtex/styleguide
Version:
> VTEX Styleguide React components ([Docs](https://vtex.github.io/styleguide))
298 lines (255 loc) • 10.9 kB
JavaScript
'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
};