react-view-pager
Version:
View-Pager/Slider/Carousel powered by React Motion.
347 lines (283 loc) • 12.6 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", {
value: true
});
var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; };
var _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; };
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 _react = require('react');
var _react2 = _interopRequireDefault(_react);
var _propTypes = require('prop-types');
var _propTypes2 = _interopRequireDefault(_propTypes);
var _reactDom = require('react-dom');
var _reactMotion = require('react-motion');
var _Pager = require('./Pager');
var _Pager2 = _interopRequireDefault(_Pager);
var _getIndex = require('./get-index');
var _getIndex2 = _interopRequireDefault(_getIndex);
var _specialAssign = require('./special-assign');
var _specialAssign2 = _interopRequireDefault(_specialAssign);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
function _objectWithoutProperties(obj, keys) { var target = {}; for (var i in obj) { if (keys.indexOf(i) >= 0) continue; if (!Object.prototype.hasOwnProperty.call(obj, i)) continue; target[i] = obj[i]; } return target; }
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 noop = function noop() {
return null;
};
var checkedProps = {
tag: _propTypes2.default.any,
currentView: _propTypes2.default.any,
viewsToShow: _propTypes2.default.oneOfType([_propTypes2.default.number, _propTypes2.default.oneOf(['auto'])]),
viewsToMove: _propTypes2.default.number,
align: _propTypes2.default.number,
contain: _propTypes2.default.bool,
axis: _propTypes2.default.oneOf(['x', 'y']),
animations: _propTypes2.default.array,
infinite: _propTypes2.default.bool,
instant: _propTypes2.default.bool,
swipe: _propTypes2.default.oneOf([true, false, 'mouse', 'touch']),
swipeThreshold: _propTypes2.default.number,
flickTimeout: _propTypes2.default.number,
// rightToLeft: PropTypes.bool,
// lazyLoad: PropTypes.bool,
springConfig: _propTypes2.default.objectOf(_propTypes2.default.number),
onSwipeStart: _propTypes2.default.func,
onSwipeMove: _propTypes2.default.func,
onSwipeEnd: _propTypes2.default.func,
onScroll: _propTypes2.default.func,
onViewChange: _propTypes2.default.func,
onRest: _propTypes2.default.func
};
var isNotEqual = function isNotEqual(current, next) {
return current.viewsToShow !== next.viewsToShow || current.viewsToMove !== next.viewsToMove || current.align !== next.align || current.axis !== next.axis || current.animations !== next.animations || current.infinite !== next.infinite || current.swipe !== next.swipe || current.swipeThreshold !== next.swipeThreshold || current.flickTimeout !== next.flickTimeout;
};
// Track scroller is an intermediate component that allows us to provide the
// React Motion value to onScroll and lets any user of onScroll use setState
var TrackScroller = function (_Component) {
_inherits(TrackScroller, _Component);
function TrackScroller() {
var _ref;
var _temp, _this, _ret;
_classCallCheck(this, TrackScroller);
for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) {
args[_key] = arguments[_key];
}
return _ret = (_temp = (_this = _possibleConstructorReturn(this, (_ref = TrackScroller.__proto__ || Object.getPrototypeOf(TrackScroller)).call.apply(_ref, [this].concat(args))), _this), _this.state = {
x: 0,
y: 0
}, _temp), _possibleConstructorReturn(_this, _ret);
}
_createClass(TrackScroller, [{
key: 'componentWillReceiveProps',
value: function componentWillReceiveProps(_ref2) {
var _this2 = this;
var trackPosition = _ref2.trackPosition;
var pager = this.context.pager;
// update view styles with current position tween
// this method can get called hundreds of times, let's make sure to optimize as much as we can
pager.setViewStyles(trackPosition);
// update onScroll callback, we use requestAnimationFrame to avoid bouncing
// back from updates from onScroll while React Motion is trying to update it's own tree
// https://github.com/chenglou/react-motion/issues/357#issuecomment-262393424
if (this.props.trackPosition !== trackPosition) {
requestAnimationFrame(function () {
return _this2.props.onScroll(trackPosition / pager.getTrackSize(false) * -1, trackPosition);
});
}
}
}, {
key: '_renderViews',
value: function _renderViews() {
// we need Children map in order for the infinite option to work
// not actually sure why this is the case
return _react.Children.map(this.props.children, function (child) {
return child;
});
}
}, {
key: 'render',
value: function render() {
var pager = this.context.pager;
var _props = this.props,
tag = _props.tag,
trackPosition = _props.trackPosition,
children = _props.children,
restProps = _objectWithoutProperties(_props, ['tag', 'trackPosition', 'children']);
var style = _extends({}, restProps.style);
if (pager.track) {
style = _extends({}, style, pager.track.getStyles(trackPosition));
}
return (0, _react.createElement)(tag, _extends({}, restProps, {
style: style
}), this._renderViews());
}
}]);
return TrackScroller;
}(_react.Component);
TrackScroller.propTypes = checkedProps;
TrackScroller.contextTypes = {
pager: _propTypes2.default.instanceOf(_Pager2.default)
};
var Track = function (_Component2) {
_inherits(Track, _Component2);
function Track() {
var _ref3;
var _temp2, _this3, _ret2;
_classCallCheck(this, Track);
for (var _len2 = arguments.length, args = Array(_len2), _key2 = 0; _key2 < _len2; _key2++) {
args[_key2] = arguments[_key2];
}
return _ret2 = (_temp2 = (_this3 = _possibleConstructorReturn(this, (_ref3 = Track.__proto__ || Object.getPrototypeOf(Track)).call.apply(_ref3, [this].concat(args))), _this3), _this3.state = {
instant: false
}, _this3._currentTween = 0, _this3._hydrate = false, _this3._handleOnRest = function () {
if (_this3.props.infinite && !_this3.state.instant) {
// reset back to a normal index
_this3.context.pager.resetViewIndex();
// set instant flag so we can prime track for next move
_this3._setValueInstantly(true, true);
}
_this3.props.onRest();
}, _temp2), _possibleConstructorReturn(_this3, _ret2);
}
_createClass(Track, [{
key: 'componentWillMount',
value: function componentWillMount() {
this.context.pager.setOptions(this.props);
}
}, {
key: 'componentDidMount',
value: function componentDidMount() {
var _this4 = this;
var pager = this.context.pager;
// add track to pager
pager.addTrack((0, _reactDom.findDOMNode)(this));
// set initial view index and listen for any incoming view index changes
this.scrollTo((0, _getIndex2.default)(this.props.currentView, this.props.children));
// set values instantly on respective events
pager.on('hydrated', function () {
return _this4._setValueInstantly(true, true);
});
pager.on('swipeMove', function () {
return _this4._setValueInstantly(true);
});
pager.on('swipeEnd', function () {
return _this4._setValueInstantly(false);
});
// prop callbacks
pager.on('swipeStart', this.props.onSwipeStart);
pager.on('swipeMove', this.props.onSwipeMove);
pager.on('swipeEnd', this.props.onSwipeEnd);
pager.on('viewChange', this.props.onViewChange);
}
}, {
key: 'componentWillReceiveProps',
value: function componentWillReceiveProps(nextProps) {
var currentView = nextProps.currentView,
instant = nextProps.instant,
children = nextProps.children;
// update instant state from props
if (this.props.instant !== instant) {
this._setValueInstantly(instant);
}
// update state with new index if necessary
if ((typeof currentView === 'undefined' ? 'undefined' : _typeof(currentView)) !== undefined && this.props.currentView !== currentView) {
this.scrollTo((0, _getIndex2.default)(currentView, children));
}
// update any options that have changed
if (isNotEqual(this.props, nextProps)) {
this.context.pager.setOptions(nextProps);
this._hydrate = true;
}
}
}, {
key: 'componentDidUpdate',
value: function componentDidUpdate(nextProps) {
if (this._hydrate) {
this.context.pager.hydrate();
this._hydrate = false;
}
}
}, {
key: 'prev',
value: function prev() {
this.context.pager.prev();
}
}, {
key: 'next',
value: function next() {
this.context.pager.next();
}
}, {
key: 'scrollTo',
value: function scrollTo(index) {
this.context.pager.setCurrentView({ index: index });
}
}, {
key: '_setValueInstantly',
value: function _setValueInstantly(instant, reset) {
var _this5 = this;
this.setState({ instant: instant }, function () {
if (reset) {
_this5.setState({ instant: false });
}
});
}
}, {
key: '_getTrackStyle',
value: function _getTrackStyle() {
var trackPosition = this.context.pager.trackPosition;
if (!this.state.instant) {
trackPosition = (0, _reactMotion.spring)(trackPosition, this.props.springConfig);
}
return { trackPosition: trackPosition };
}
}, {
key: 'render',
value: function render() {
var _props2 = this.props,
tag = _props2.tag,
onScroll = _props2.onScroll,
restProps = _objectWithoutProperties(_props2, ['tag', 'onScroll']);
return _react2.default.createElement(
_reactMotion.Motion,
{
style: this._getTrackStyle(),
onRest: this._handleOnRest
},
function (_ref4) {
var trackPosition = _ref4.trackPosition;
return (0, _react.createElement)(TrackScroller, (0, _specialAssign2.default)({ trackPosition: trackPosition, tag: tag, onScroll: onScroll }, restProps, checkedProps));
}
);
}
}]);
return Track;
}(_react.Component);
Track.propTypes = checkedProps;
Track.defaultProps = {
tag: 'div',
currentView: 0,
viewsToShow: 1,
viewsToMove: 1,
align: 0,
contain: false,
axis: 'x',
infinite: false,
instant: false,
swipe: true,
swipeThreshold: 0.5,
flickTimeout: 300,
springConfig: _reactMotion.presets.noWobble,
onSwipeStart: noop,
onSwipeMove: noop,
onSwipeEnd: noop,
onScroll: noop,
onViewChange: noop,
onRest: noop
};
Track.contextTypes = {
pager: _propTypes2.default.instanceOf(_Pager2.default)
};
exports.default = Track;
module.exports = exports['default'];