UNPKG

@vtex/styleguide

Version:

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

471 lines (376 loc) 14.4 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.default = undefined; var _react = require("react"); var _react2 = _interopRequireDefault(_react); var _propTypes = require("prop-types"); var _propTypes2 = _interopRequireDefault(_propTypes); var _classnames = require("classnames"); var _classnames2 = _interopRequireDefault(_classnames); var _Selector = require("./Selector"); var _Selector2 = _interopRequireDefault(_Selector); 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 UP_EVENTS = ['mouseup', 'pointerup', 'touchend']; var MOVE_EVENT_MAP = { mousedown: 'mousemove', touchstart: 'touchmove', pointerdown: 'pointermove' }; /** * Round the value to the nearest step multiple */ function quantize(value, step) { var numSteps = Math.round(value / step); var quantizedVal = numSteps * step; return quantizedVal; } /** * Get the event pageX attribute, with support for mobile events */ function getPageX(evt) { if (evt.targetTouches && evt.targetTouches.length > 0) { return evt.targetTouches[0].pageX; } return evt.pageX; } /** * Check for the esc key event */ function isEscKeyEvent(evt) { return evt.key === 'Escape' || evt.keyCode === 27; } var Slider = /*#__PURE__*/ function (_Component) { _inheritsLoose(Slider, _Component); function Slider() { 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.sliderRef = _react2.default.createRef(); _this.state = { dragging: null, translate: { left: 0, right: 0 }, values: { left: _this.props.defaultValues && _this.props.defaultValues.length > 0 ? _this.props.defaultValues[0] : _this.props.min, right: _this.props.range && _this.props.defaultValues && _this.props.defaultValues.length >= 2 ? _this.props.defaultValues[1] : _this.props.max } }; _this.updateLayout = function () { _this.updatePositionForValue(_this.state.values.left, 'left'); _this.updatePositionForValue(_this.state.values.right, 'right'); }; _this.getValueForPercent = function (percentageComplete, position) { var _this$props = _this.props, min = _this$props.min, max = _this$props.max, step = _this$props.step, range = _this$props.range; var rawValue = min + percentageComplete * (max - min); var value; if (rawValue !== min && rawValue !== max) { value = quantize(rawValue, step); } else { value = rawValue; } if (value < min) { value = min; } else if (value > max) { value = max; } if (!range) { return value; } if (position === 'left' && value >= _this.state.values.right) { value = _this.state.values.right - step; } else if (position === 'right' && value <= _this.state.values.left) { value = _this.state.values.left + step; } return value; }; _this.getTranslateValueForInputValue = function (value, position) { var _this$props2 = _this.props, max = _this$props2.max, min = _this$props2.min; var rect = _this.sliderRef.current.getBoundingClientRect(); var percentageComplete = (value - min) / (max - min); var translatePx = percentageComplete * rect.width; if (position === 'right') { translatePx = rect.width - translatePx; } return translatePx; }; _this.handleSliderMouseDown = function (e) { var rect = _this.sliderRef.current.getBoundingClientRect(); var xPos = getPageX(e) - rect.left; var leftPos = _this.state.translate.left; var rightPos = rect.width - _this.state.translate.right; var nearestPoint; // Which one has a absolute value closer to 0 if (!_this.props.range || Math.abs(leftPos - xPos) < Math.abs(rightPos - xPos)) { nearestPoint = 'left'; } else { nearestPoint = 'right'; } _this.handleDragStart(nearestPoint)(e); }; _this.handleDragStart = function (position) { return function (e) { e.stopPropagation(); // allow only one handle to be dragged at a time if (_this.props.disabled || _this.state.dragging) { return; } _this.setState({ dragging: position }); _this.valuesBeforeDrag_ = _this.state.values; // https://reactjs.org/docs/events.html#event-pooling e.persist(); var moveHandler = _this.handleDrag(position); // The events bellow are attached to the body because we need // to support the dragging event *outside* of the slider bounds _this.cancelDragEvent_ = function () { _this.valuesBeforeDrag_ = undefined; UP_EVENTS.forEach(function (evtName) { return document.body.removeEventListener(evtName, handleUpEvent); }); document.body.removeEventListener(MOVE_EVENT_MAP[e.type], moveHandler); document.body.removeEventListener('keydown', _this.handleKeyDown); }; var handleUpEvent = function handleUpEvent() { _this.cancelDragEvent_(); _this.handleDragEnd(); }; UP_EVENTS.forEach(function (evtName) { return document.body.addEventListener(evtName, handleUpEvent); }); document.body.addEventListener(MOVE_EVENT_MAP[e.type], moveHandler); document.body.addEventListener('keydown', _this.handleKeyDown); _this.updatePositionFromEvent(e, position); }; }; _this.updatePositionFromEvent = function (e, position) { var slider = _this.sliderRef.current; var rect = slider.getBoundingClientRect(); var xPos = getPageX(e) - rect.left; var percentageComplete = xPos / rect.width; var value = _this.getValueForPercent(percentageComplete, position); _this.updatePositionForValue(value, position); }; _this.handleDrag = function (position) { return function (e) { e.preventDefault(); _this.updatePositionFromEvent(e, position); }; }; _this.updatePositionForValue = function (value, position) { var translatePx = _this.getTranslateValueForInputValue(value, position); requestAnimationFrame(function () { _this.setState(function (state) { var _extends2, _extends3; return { values: _extends({}, state.values, (_extends2 = {}, _extends2[position] = value, _extends2)), translate: _extends({}, state.translate, (_extends3 = {}, _extends3[position] = translatePx, _extends3)) }; }); }); }; _this.handleDragEnd = function () { _this.setState({ dragging: null }); _this.cancelDragEvent_ = undefined; if (_this.props.range) { _this.props.onChange([_this.state.values.left, _this.state.values.right]); } else { _this.props.onChange([_this.state.values.left]); } }; _this.handleKeyDown = function (evt) { if (!isEscKeyEvent(evt) || !_this.state.dragging) { return; } _this.setState({ dragging: false, values: _this.valuesBeforeDrag_ }); _this.cancelDragEvent_(); _this.cancelDragEvent = undefined; _this.updateLayout(); }; return _this; } var _proto = Slider.prototype; _proto.componentDidUpdate = function componentDidUpdate(prevProps) { var _this2 = this; if (prevProps.min !== this.props.min || prevProps.max !== this.props.max) { this.setState(function (prev) { var _prev$values$left, _prev$values$right; return { translate: { left: 0, right: 0 }, values: { left: (_prev$values$left = prev.values.left) != null ? _prev$values$left : _this2.props.min, right: (_prev$values$right = prev.values.right) != null ? _prev$values$right : _this2.props.max } }; }, this.updateLayout); } var _ref = prevProps.values || [], prevLeftValue = _ref[0], prevRightValue = _ref[1]; var _ref2 = this.props.values || [], leftValue = _ref2[0], rightValue = _ref2[1]; var _this$state$values = this.state.values, leftState = _this$state$values.left, rightState = _this$state$values.right; if ((prevLeftValue !== leftValue || prevRightValue !== rightValue) && (leftValue !== leftState || rightValue !== rightState)) { this.setState(function (currentState) { return _extends({}, currentState, { values: { left: leftValue, right: rightValue } }); }, function () { _this2.updateLayout(); _this2.props.onChange([_this2.state.values.left, _this2.state.values.right]); }); } }; _proto.componentDidMount = function componentDidMount() { window.addEventListener('resize', this.updateLayout); if (this.props.defaultValues && this.props.defaultValues.length > 0) { this.updateLayout(); } }; _proto.componentWillUnmount = function componentWillUnmount() { window.removeEventListener('resize', this.updateLayout); if (this.cancelDragEvent_) { this.cancelDragEvent_(); this.cancelDragEvent_ = undefined; } }; _proto.render = function render() { var _this$props3 = this.props, disabled = _this$props3.disabled, alwaysShowCurrentValue = _this$props3.alwaysShowCurrentValue, formatValue = _this$props3.formatValue, range = _this$props3.range, handleIcon = _this$props3.handleIcon; var _this$state$translate = this.state.translate, left = _this$state$translate.left, right = _this$state$translate.right; var lastLeftValue = this.valuesBeforeDrag_ ? this.valuesBeforeDrag_.left : this.state.values.left; var lastRightValue = this.valuesBeforeDrag_ ? this.valuesBeforeDrag_.right : this.state.values.right; var sliderSelectionStyle = range ? { left: left, right: right } : { left: 0, width: left }; return _react2.default.createElement("div", { className: "vtex-slider-container" }, _react2.default.createElement("div", { className: "vtex-slider w-100 relative pointer", style: { height: 24, // since we can't include css with the components, the // prefixed attributes need to be included MozUserSelect: 'none', msUserSelect: 'none', WebkitUserSelect: 'none', userSelect: 'none' }, onMouseDown: this.handleSliderMouseDown, onTouchStart: this.handleSliderMouseDown }, _react2.default.createElement("div", { ref: this.sliderRef, className: "vtex-slider__base w-100 bg-muted-4 absolute br-pill overflow-hidden", style: { height: '0.25rem', top: '0.7rem' } }, _react2.default.createElement("div", { className: (0, _classnames2.default)('vtex-slider__base-internal absolute h-100', { 'bg-action-primary': !disabled, 'bg-muted-4': disabled }), style: sliderSelectionStyle })), _react2.default.createElement(_Selector2.default, { offset: left, onDragStart: this.handleDragStart, position: "left", active: this.state.dragging === 'left', displayPopup: alwaysShowCurrentValue, value: this.state.values.left, formatValue: formatValue, icon: handleIcon }), range && _react2.default.createElement(_Selector2.default, { offset: right, onDragStart: this.handleDragStart, position: "right", active: this.state.dragging === 'right', displayPopup: alwaysShowCurrentValue, value: this.state.values.right, formatValue: formatValue, icon: handleIcon })), _react2.default.createElement("div", { className: "vtex-slider__values-container flex justify-end" }, _react2.default.createElement("label", { className: "vtex-slider__left-value t-small c-muted-1" }, formatValue(lastLeftValue)), range && _react2.default.createElement("label", { className: "vtex-slider__right-value t-small c-muted-1" }, _react2.default.createElement("span", { className: "vtex-slider__dash mh2" }, "\u2013"), formatValue(lastRightValue)))); }; return Slider; }(_react.Component); exports.default = Slider; Slider.defaultProps = { min: 0, max: 10, step: 1, onChange: function onChange() {}, alwaysShowCurrentValue: false, formatValue: function formatValue(a) { return a; }, range: false, handleIcon: null }; Slider.propTypes = { /** Minimum supported value */ min: _propTypes2.default.number, /** Maximum supported value */ max: _propTypes2.default.number, /** onChange event */ onChange: _propTypes2.default.func, /** Step value */ step: _propTypes2.default.number, /** Whether the slider is disabled */ disabled: _propTypes2.default.bool, /** Initial values */ defaultValues: _propTypes2.default.arrayOf(_propTypes2.default.number), /** Whether to always display current value as a popup */ alwaysShowCurrentValue: _propTypes2.default.bool, /** Function to customize the format of the value */ formatValue: _propTypes2.default.func, /** Whether to render as a range input */ range: _propTypes2.default.bool, /** Optional icon to show inside the slider handle */ handleIcon: _propTypes2.default.node, /** Current value: [left, right] */ values: _propTypes2.default.arrayOf(_propTypes2.default.number) };