react-pullable
Version:
Simple, customizable pull to refresh.
263 lines (224 loc) • 10.3 kB
JavaScript
var _templateObject = _taggedTemplateLiteralLoose(['\n display: flex;\n overflow: hidden;\n justify-content: center;\n pointer-events: none;\n'], ['\n display: flex;\n overflow: hidden;\n justify-content: center;\n pointer-events: none;\n']),
_templateObject2 = _taggedTemplateLiteralLoose(['\n transform-origin: center;\n'], ['\n transform-origin: center;\n']),
_templateObject3 = _taggedTemplateLiteralLoose([''], ['']),
_templateObject4 = _taggedTemplateLiteralLoose(['\n 0% { transform: scale(1.3); }\n 100% { transform: scale(1); }\n'], ['\n 0% { transform: scale(1.3); }\n 100% { transform: scale(1); }\n']),
_templateObject5 = _taggedTemplateLiteralLoose(['\n from { transform: rotate(0deg); }\n to { transform: rotate(360deg); }\n'], ['\n from { transform: rotate(0deg); }\n to { transform: rotate(360deg); }\n']);
function _taggedTemplateLiteralLoose(strings, raw) { strings.raw = raw; return strings; }
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; }
import React from 'react';
import PropTypes from 'prop-types';
import styled, { keyframes } from 'styled-components';
var Pullable = function (_React$Component) {
_inherits(Pullable, _React$Component);
function Pullable(props) {
_classCallCheck(this, Pullable);
var _this = _possibleConstructorReturn(this, _React$Component.call(this, props));
_this.clearTouchStatus = function () {
_this.pullStartY = null;
_this.pullMoveY = null;
_this.dist = 0;
_this.distResisted = 0;
_this.ignoreTouches = false;
};
_this.onTouchStart = function (e) {
if (_this.props.disabled || _this.ignoreTouches) return;
if (_this.state.status === 'ready' && _this.props.shouldPullToRefresh()) {
_this.pullStartY = e.touches[0].screenY;
} else {
_this.pullStartY = null;
}
};
_this.onTouchMove = function (e) {
if (_this.props.disabled || _this.ignoreTouches || _this.pullStartY === null) return;
_this.pullMoveY = e.touches[0].screenY;
_this.dist = _this.pullMoveY - _this.pullStartY;
if (_this.dist > 0) {
e.preventDefault();
_this.distResisted = Math.min(_this.dist / _this.props.resistance, _this.props.distThreshold);
_this.setState({ status: 'pulling', height: _this.distResisted }, function () {
if (_this.distResisted === _this.props.distThreshold) _this.refresh();
});
}
};
_this.onTouchEnd = function (e) {
if (_this.props.disabled || _this.ignoreTouches) return;
if (_this.state.status === 'pulling') {
_this.ignoreTouches = true;
_this.setState({ status: 'pullAborted', height: 0 }, function () {
_this.reset(_this.props.resetDuration);
});
} else {
_this.reset();
}
};
_this.refresh = function () {
_this.ignoreTouches = true;
_this.setState({ status: 'refreshing' }, function () {
_this.props.onRefresh();
_this.refreshCompletedTimeout = setTimeout(function () {
_this.setState({ status: 'refreshCompleted', height: 0 }, function () {
_this.reset(_this.props.resetDuration);
});
}, _this.props.refreshDuration);
});
};
_this.reset = function () {
var delay = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 0;
_this.resetTimeout = setTimeout(function () {
_this.clearTouchStatus();
_this.setState({ status: 'ready' });
}, delay);
};
_this.clearTouchStatus();
_this.state = {
status: 'ready',
height: 0
};
return _this;
}
Pullable.prototype.componentDidMount = function componentDidMount() {
window.addEventListener('touchstart', this.onTouchStart);
window.addEventListener('touchmove', this.onTouchMove, { passive: false });
window.addEventListener('touchend', this.onTouchEnd);
};
Pullable.prototype.componentWillUnmount = function componentWillUnmount() {
window.removeEventListener('touchstart', this.onTouchStart);
window.removeEventListener('touchmove', this.onTouchMove, { passive: false });
window.removeEventListener('touchend', this.onTouchEnd);
clearTimeout(this.refreshCompletedTimeout);
clearTimeout(this.resetTimeout);
};
Pullable.prototype.render = function render() {
var status = this.state.status;
var shouldSpin = status === 'refreshing' || status === 'refreshCompleted';
var shouldReset = status === 'pullAborted' || status === 'refreshCompleted';
var pctPulled = this.state.height / this.props.distThreshold;
return React.createElement(
React.Fragment,
null,
React.createElement(
Container,
{
className: this.props.className,
height: this.state.height,
centerSpinner: this.props.centerSpinner,
resetDuration: this.props.resetDuration,
resetEase: this.props.resetEase,
shouldReset: shouldReset
},
React.createElement(
Spinner,
{
pctPulled: pctPulled,
fadeSpinner: this.props.fadeSpinner,
rotateSpinner: this.props.rotateSpinner,
spinnerSize: this.props.spinnerSize,
spinnerOffset: this.props.spinnerOffset,
resetDuration: this.props.resetDuration,
resetEase: this.props.resetEase,
shouldReset: shouldReset,
shouldSpin: shouldSpin
},
React.createElement(
SpinnerSVG,
{
spinnerSize: this.props.spinnerSize,
spinnerColor: this.props.spinnerColor,
popDuration: this.props.popDuration,
spinSpeed: this.props.spinSpeed,
shouldSpin: shouldSpin
},
React.createElement('line', { x1: '12', y1: '2', x2: '12', y2: '6' }),
React.createElement('line', { x1: '12', y1: '18', x2: '12', y2: '22' }),
React.createElement('line', { x1: '4.93', y1: '4.93', x2: '7.76', y2: '7.76' }),
React.createElement('line', { x1: '16.24', y1: '16.24', x2: '19.07', y2: '19.07' }),
React.createElement('line', { x1: '2', y1: '12', x2: '6', y2: '12' }),
React.createElement('line', { x1: '18', y1: '12', x2: '22', y2: '12' }),
React.createElement('line', { x1: '4.93', y1: '19.07', x2: '7.76', y2: '16.24' }),
React.createElement('line', { x1: '16.24', y1: '7.76', x2: '19.07', y2: '4.93' })
)
)
),
this.props.children
);
};
return Pullable;
}(React.Component);
Pullable.defaultProps = {
className: 'pullable',
centerSpinner: true,
fadeSpinner: true,
rotateSpinner: true,
spinnerSize: 24,
spinnerOffset: 0,
spinnerColor: '#000000',
spinSpeed: 1200,
popDuration: 200,
distThreshold: 72,
resistance: 2.5,
refreshDuration: 1000,
resetDuration: 400,
resetEase: 'cubic-bezier(0.215, 0.61, 0.355, 1)',
shouldPullToRefresh: function shouldPullToRefresh() {
return window.scrollY <= 0;
},
disabled: false
};
Pullable.propTypes = process.env.NODE_ENV !== "production" ? {
onRefresh: PropTypes.func.isRequired,
className: PropTypes.string,
centerSpinner: PropTypes.bool,
fadeSpinner: PropTypes.bool,
rotateSpinner: PropTypes.bool,
spinnerSize: PropTypes.number,
spinnerOffset: PropTypes.number,
spinnerColor: PropTypes.string,
spinSpeed: PropTypes.number,
popDuration: PropTypes.number,
distThreshold: PropTypes.number,
resistance: PropTypes.number,
refreshDuration: PropTypes.number,
resetDuration: PropTypes.number,
resetEase: PropTypes.string,
shouldPullToRefresh: PropTypes.func,
disabled: PropTypes.bool
} : {};
// Styled Components
var Container = styled.div.attrs({
style: function style(props) {
return {
height: props.height,
alignItems: props.centerSpinner ? 'center' : 'flex-start',
transition: props.shouldReset ? 'height ' + props.resetDuration + 'ms ' + props.resetEase : 'none'
};
}
})(_templateObject);
var Spinner = styled.div.attrs({
style: function style(props) {
return {
opacity: props.fadeSpinner ? props.pctPulled : 1,
transform: props.shouldReset ? 'translateY(' + (props.pctPulled * (props.spinnerSize + props.spinnerOffset) - props.spinnerSize) + 'px) rotate(' + (props.rotateSpinner && props.shouldSpin ? 90 : 0) + 'deg)' : 'translateY(' + (props.pctPulled * (props.spinnerSize + props.spinnerOffset) - props.spinnerSize) + 'px) rotate(' + (props.rotateSpinner ? props.pctPulled * 90 : 0) + 'deg)',
transition: props.shouldReset ? 'opacity ' + props.resetDuration + 'ms ' + props.resetEase + ', transform ' + props.resetDuration + 'ms ' + props.resetEase : 'none'
};
}
})(_templateObject2);
var SpinnerSVG = styled.svg.attrs({
viewBox: '0 0 24 24',
fill: 'none',
strokeWidth: '2',
strokeLinecap: 'round',
strokeLinejoin: 'round',
style: function style(props) {
return {
width: props.spinnerSize,
height: props.spinnerSize,
stroke: props.spinnerColor,
animation: props.shouldSpin ? scale + ' ' + props.popDuration + 'ms cubic-bezier(0.55, 0.055, 0.675, 0.19), ' + rotate360 + ' ' + props.spinSpeed + 'ms linear ' + props.popDuration + 'ms infinite' : 'none'
};
}
})(_templateObject3);
var scale = keyframes(_templateObject4);
var rotate360 = keyframes(_templateObject5);
export default Pullable;