react-iscroll
Version:
React component for wrapping iScroll library.
326 lines (263 loc) • 10.8 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 _react = require('react');
var _react2 = _interopRequireDefault(_react);
var _reactDom = require('react-dom');
var _reactDom2 = _interopRequireDefault(_reactDom);
var _deepEqual = require('deep-equal');
var _deepEqual2 = _interopRequireDefault(_deepEqual);
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 excludePropNames = ['defer', 'iScroll', 'onRefresh', 'options'];
// Events available on iScroll instance
// {`react component event name`: `iScroll event name`}
var availableEventNames = {};
var iScrollEventNames = ['beforeScrollStart', 'scrollCancel', 'scrollStart', 'scroll', 'scrollEnd', 'flick', 'zoomStart', 'zoomEnd'];
for (var i = 0, len = iScrollEventNames.length; i < len; i++) {
var iScrollEventName = iScrollEventNames[i];
var reactEventName = 'on' + iScrollEventName[0].toUpperCase() + iScrollEventName.slice(1);
availableEventNames[reactEventName] = iScrollEventName;
excludePropNames.push(reactEventName);
}
var ReactIScroll = function (_React$Component) {
_inherits(ReactIScroll, _React$Component);
function ReactIScroll(props) {
_classCallCheck(this, ReactIScroll);
var _this = _possibleConstructorReturn(this, (ReactIScroll.__proto__ || Object.getPrototypeOf(ReactIScroll)).call(this, props));
_this._isMounted = false;
_this._initializeTimeout = null;
_this._queuedCallbacks = [];
_this._iScrollBindedEvents = {};
return _this;
}
_createClass(ReactIScroll, [{
key: 'componentDidMount',
value: function componentDidMount() {
this._isMounted = true;
this._initializeIScroll();
}
}, {
key: 'componentWillUnmount',
value: function componentWillUnmount() {
this._isMounted = false;
this._teardownIScroll();
}
// There is no state, we can compare only props.
}, {
key: 'shouldComponentUpdate',
value: function shouldComponentUpdate(nextProps, nextContext) {
return !(0, _deepEqual2.default)(this.props, nextProps) || !(0, _deepEqual2.default)(this.context, nextContext);
}
// Check if iScroll options has changed and recreate instance with new one
}, {
key: 'componentDidUpdate',
value: function componentDidUpdate(prevProps) {
var _this2 = this;
// If options are same, iScroll behaviour will not change. Just refresh events and trigger refresh
if ((0, _deepEqual2.default)(prevProps.options, this.props.options)) {
this._updateIScrollEvents(prevProps, this.props);
this.refresh();
// If options changed, we will destroy iScroll instance and create new one with same scroll position
// TODO test if this will work with indicators
} else {
this.withIScroll(true, function (iScrollInstance) {
// Save current state
var x = iScrollInstance.x,
y = iScrollInstance.y,
scale = iScrollInstance.scale;
// Destroy current and Create new instance of iScroll
_this2._teardownIScroll();
_this2._initializeIScroll();
_this2.withIScroll(true, function (newIScrollInstance) {
// Restore previous state
if (scale && newIScrollInstance.zoom) {
newIScrollInstance.zoom(scale, 0, 0, 0);
}
newIScrollInstance.scrollTo(x, y);
});
});
}
}
}, {
key: 'getIScroll',
value: function getIScroll() {
return this._iScrollInstance;
}
}, {
key: 'getIScrollInstance',
value: function getIScrollInstance() {
console.warn("Function 'getIScrollInstance' is deprecated. Instead use 'getIScroll'");
return this._iScrollInstance;
}
}, {
key: 'withIScroll',
value: function withIScroll(waitForInit, callback) {
if (!callback && typeof waitForInit == 'function') {
callback = waitForInit;
}
if (this.getIScroll()) {
callback(this.getIScroll());
} else if (waitForInit === true) {
this._queuedCallbacks.push(callback);
}
}
}, {
key: 'refresh',
value: function refresh() {
this.withIScroll(function (iScrollInstance) {
return iScrollInstance.refresh();
});
}
}, {
key: '_runInitializeIScroll',
value: function _runInitializeIScroll() {
var _this3 = this;
var _props = this.props,
iScroll = _props.iScroll,
options = _props.options;
// Create iScroll instance with given options
var iScrollInstance = new iScroll(_reactDom2.default.findDOMNode(this), options);
this._iScrollInstance = iScrollInstance;
// TODO there should be new event 'onInitialize'
this._triggerRefreshEvent();
// Patch iScroll instance .refresh() function to trigger our onRefresh event
iScrollInstance.originalRefresh = iScrollInstance.refresh;
iScrollInstance.refresh = function () {
iScrollInstance.originalRefresh.apply(iScrollInstance);
_this3._triggerRefreshEvent();
};
// Bind iScroll events
this._bindIScrollEvents();
this._callQueuedCallbacks();
}
}, {
key: '_initializeIScroll',
value: function _initializeIScroll() {
var _this4 = this;
if (this._isMounted === false) {
return;
}
var defer = this.props.defer;
if (defer === false) {
this._runInitializeIScroll();
} else {
var timeout = defer === true ? 0 : defer;
this._initializeTimeout = setTimeout(function () {
return _this4._runInitializeIScroll();
}, timeout);
}
}
}, {
key: '_callQueuedCallbacks',
value: function _callQueuedCallbacks() {
var callbacks = this._queuedCallbacks,
len = callbacks.length;
this._queuedCallbacks = [];
for (var _i = 0; _i < len; _i++) {
callbacks[_i](this.getIScroll());
}
}
}, {
key: '_teardownIScroll',
value: function _teardownIScroll() {
this._clearInitializeTimeout();
if (this._iScrollInstance) {
this._iScrollInstance.destroy();
this._iScrollInstance = undefined;
}
this._iScrollBindedEvents = {};
this._queuedCallbacks = [];
}
}, {
key: '_clearInitializeTimeout',
value: function _clearInitializeTimeout() {
if (this._initializeTimeout !== null) {
clearTimeout(this._initializeTimeout);
this._initializeTimeout = null;
}
}
}, {
key: '_bindIScrollEvents',
value: function _bindIScrollEvents() {
// Bind events on iScroll instance
this._iScrollBindedEvents = {};
this._updateIScrollEvents({}, this.props);
}
// Iterate through available events and update one by one
}, {
key: '_updateIScrollEvents',
value: function _updateIScrollEvents(prevProps, nextProps) {
for (var _reactEventName in availableEventNames) {
this._updateIScrollEvent(availableEventNames[_reactEventName], prevProps[_reactEventName], nextProps[_reactEventName]);
}
}
// Unbind and/or Bind event if it was changed during update
}, {
key: '_updateIScrollEvent',
value: function _updateIScrollEvent(iScrollEventName, prevPropEvent, currentPropEvent) {
if (prevPropEvent !== currentPropEvent) {
var currentEvents = this._iScrollBindedEvents;
this.withIScroll(true, function (iScrollInstance) {
if (typeof prevPropEvent === 'function') {
iScrollInstance.off(iScrollEventName, currentEvents[iScrollEventName]);
currentEvents[iScrollEventName] = undefined;
}
if (typeof currentPropEvent === 'function') {
var wrappedCallback = function wrappedCallback() {
for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) {
args[_key] = arguments[_key];
}
currentPropEvent.apply(undefined, [iScrollInstance].concat(args));
};
iScrollInstance.on(iScrollEventName, wrappedCallback);
currentEvents[iScrollEventName] = wrappedCallback;
}
});
}
}
}, {
key: '_triggerRefreshEvent',
value: function _triggerRefreshEvent() {
var onRefresh = this.props.onRefresh;
if (typeof onRefresh === 'function') {
this.withIScroll(function (iScrollInstance) {
return onRefresh(iScrollInstance);
});
}
}
}, {
key: 'render',
value: function render() {
// Keep only non ReactIScroll properties
var props = {};
for (var prop in this.props) {
if (!~excludePropNames.indexOf(prop)) {
props[prop] = this.props[prop];
}
}
return _react2.default.createElement('div', props);
}
}]);
return ReactIScroll;
}(_react2.default.Component);
ReactIScroll.displayName = 'ReactIScroll';
ReactIScroll.defaultProps = {
defer: true,
options: {},
style: {
position: 'relative',
height: '100%',
width: '100%',
overflow: 'hidden'
}
};
exports.default = ReactIScroll;
if (process.env.NODE_ENV !== 'production') {
var propTypesMaker = require('./prop_types').default;
ReactIScroll.propTypes = propTypesMaker(availableEventNames);
}