@jdcfe/yep-react
Version:
一套移动端的React组件库
707 lines (566 loc) • 23.6 kB
JavaScript
"use strict";
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
var _typeof = require("@babel/runtime/helpers/typeof");
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = void 0;
var _extends2 = _interopRequireDefault(require("@babel/runtime/helpers/extends"));
var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
var _classCallCheck2 = _interopRequireDefault(require("@babel/runtime/helpers/classCallCheck"));
var _createClass2 = _interopRequireDefault(require("@babel/runtime/helpers/createClass"));
var _inherits2 = _interopRequireDefault(require("@babel/runtime/helpers/inherits"));
var _possibleConstructorReturn2 = _interopRequireDefault(require("@babel/runtime/helpers/possibleConstructorReturn"));
var _getPrototypeOf2 = _interopRequireDefault(require("@babel/runtime/helpers/getPrototypeOf"));
var React = _interopRequireWildcard(require("react"));
var ReactDOM = _interopRequireWildcard(require("react-dom"));
var _classnames = _interopRequireDefault(require("classnames"));
var _cssClasses = _interopRequireDefault(require("./cssClasses"));
var _yepGesture = _interopRequireDefault(require("@jdcfe/yep-gesture"));
var _noop = _interopRequireDefault(require("../_utils/noop"));
function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function _getRequireWildcardCache(nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); }
function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || _typeof(obj) !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; }
function _createSuper(Derived) { var hasNativeReflectConstruct = _isNativeReflectConstruct(); return function _createSuperInternal() { var Super = (0, _getPrototypeOf2.default)(Derived), result; if (hasNativeReflectConstruct) { var NewTarget = (0, _getPrototypeOf2.default)(this).constructor; result = Reflect.construct(Super, arguments, NewTarget); } else { result = Super.apply(this, arguments); } return (0, _possibleConstructorReturn2.default)(this, result); }; }
function _isNativeReflectConstruct() { if (typeof Reflect === "undefined" || !Reflect.construct) return false; if (Reflect.construct.sham) return false; if (typeof Proxy === "function") return true; try { Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {})); return true; } catch (e) { return false; } }
var defaultStatusFormatter = function defaultStatusFormatter(current, total) {
return "".concat(current, " / ").concat(total);
};
var CSSTranslate = function CSSTranslate(position, vertical) {
var positionCss = !vertical ? [position, 0, 0] : [0, position, 0];
var transitionProp = 'translate3d';
var translatedPosition = '(' + positionCss.join(',') + ')';
return transitionProp + translatedPosition;
};
var Carousel = /*#__PURE__*/function (_React$Component) {
(0, _inherits2.default)(Carousel, _React$Component);
var _super = _createSuper(Carousel);
function Carousel(props) {
var _this;
(0, _classCallCheck2.default)(this, Carousel);
_this = _super.call(this, props);
_this.setCarouselWrapperRef = function (node) {
_this.carouselWrapperRef = node;
};
_this.setListRef = function (node) {
_this.listRef = node;
};
_this.setItemsWrapperRef = function (node) {
_this.itemsWrapperRef = node;
};
_this.setItemsRef = function (node, index) {
if (!_this.itemsRef) {
_this.itemsRef = [];
}
_this.itemsRef[index] = node;
};
_this.autoPlay = function () {
if (!_this.state.autoPlay || React.Children.count(_this.props.children) <= 1) {
return;
}
clearTimeout(_this.timer);
_this.timer = setTimeout(function () {
_this.increment(); //@ts-ignore
}, _this.props.autoPlay);
};
_this.clearAutoPlay = function () {
if (!_this.state.autoPlay) {
return;
}
clearTimeout(_this.timer);
};
_this.resetAutoPlay = function () {
_this.clearAutoPlay();
_this.autoPlay();
};
_this.updateSizes = function () {
if (!_this.state.initialized) {
return;
}
var isHorizontal = !_this.props.vertical;
var firstItem = _this.itemsRef[0];
var itemSize = isHorizontal ? firstItem.clientWidth : firstItem.clientHeight;
_this.setState(function (_state) {
return {
itemSize: itemSize
};
});
};
_this.setMountState = function () {
_this.setState({
hasMount: true
});
_this.updateSizes();
};
_this.handleClickItem = function (index, item) {
if (React.Children.count(_this.props.children) === 0) {
return;
}
if (_this.state.cancelClick) {
_this.setState({
cancelClick: false
});
return;
}
_this.props.onClickItem && _this.props.onClickItem(index, item);
if (index !== _this.state.selectedItem) {
_this.setState({
selectedItem: index
});
}
};
_this.handleOnChange = function (index, item) {
if (React.Children.count(_this.props.children) <= 1) {
return;
}
_this.props.onTransitionEnd && _this.props.onTransitionEnd(index, item);
_this.props.onChange && _this.props.onChange(index, item);
};
_this.onSwipeStart = function (event) {
_this.setState({
swiping: true
});
_this.props.onSwipeStart && _this.props.onSwipeStart(event);
_this.clearAutoPlay();
};
_this.onSwipeEnd = function (event) {
_this.setState({
swiping: false,
cancelClick: false
});
_this.props.onSwipeEnd && _this.props.onSwipeEnd(event);
_this.autoPlay();
};
_this.onSwipeMove = function (delta, event) {
_this.props.onSwipeMove && _this.props.onSwipeMove(event);
var isHorizontal = _this.props.vertical === false;
var childrenLength = React.Children.count(_this.props.children);
var initialBoundry = 0;
var currentPosition = _this.getPosition(_this.state.selectedItem);
var finalBoundry = _this.props.isInfinite ? _this.getPosition(childrenLength - 1) - 100 : _this.getPosition(childrenLength - 1);
var axisDelta = isHorizontal ? delta.x : delta.y;
var handledDelta = axisDelta; // prevent user from swiping left out of boundaries
if (currentPosition === initialBoundry && axisDelta > 0) {
handledDelta = 0;
} // prevent user from swiping right out of boundaries
if (currentPosition === finalBoundry && axisDelta < 0) {
handledDelta = 0;
}
var position = currentPosition + 100 / (_this.state.itemSize / handledDelta);
if (_this.props.isInfinite) {
// When allowing infinite loop, if we slide left from position 0 we reveal the cloned last slide that appears before it
// if we slide even further we need to jump to other side so it can continue - and vice versa for the last slide
if (_this.state.selectedItem === 0 && position > -100) {
position -= childrenLength * 100;
} else if (_this.state.selectedItem === childrenLength - 1 && position < -childrenLength * 100) {
position += childrenLength * 100;
}
} //@ts-ignore
position += '%'; //@ts-ignore
_this.setPosition(position); // allows scroll if the swipe was within the tolerance
var hasMoved = Math.abs(axisDelta) > _this.props.distance;
if (hasMoved && !_this.state.cancelClick) {
_this.setState({
cancelClick: true
});
}
return hasMoved;
};
_this.getPosition = function (index) {
var centerSlidePercentage = _this.props.centerSlidePercentage;
if (_this.props.isInfinite) {
// index has to be added by 1 because of the first cloned slide
++index;
}
if (index === 0) {
return 0;
}
var childrenLength = React.Children.count(_this.props.children);
if (_this.props.centerMode && _this.props.vertical === false) {
var currentPosition = -index * centerSlidePercentage;
var lastPosition = childrenLength - 1;
if (index && (index !== lastPosition || _this.props.isInfinite)) {
currentPosition += (100 - centerSlidePercentage) / 2;
} else if (index === lastPosition) {
currentPosition += 100 - centerSlidePercentage;
}
return currentPosition;
}
return -index * 100;
};
_this.setPosition = function (position, forceReflow) {
var list = ReactDOM.findDOMNode(_this.listRef);
['WebkitTransform', 'transform'].forEach(function (prop) {
//@ts-ignore
list.style[prop] = CSSTranslate(position, _this.props.vertical);
});
if (forceReflow) {
//@ts-ignore
list.offsetLeft;
}
};
_this.resetPosition = function () {
var currentPosition = _this.getPosition(_this.state.selectedItem) + '%'; //@ts-ignore
_this.setPosition(currentPosition);
};
_this.decrement = function () {
var positions = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 1;
var fromSwipe = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
_this.moveTo(_this.state.selectedItem - (typeof positions === 'number' ? positions : 1), fromSwipe);
};
_this.increment = function () {
var positions = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 1;
var fromSwipe = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
_this.moveTo(_this.state.selectedItem + (typeof positions === 'number' ? positions : 1), fromSwipe);
};
_this.moveTo = function (position, fromSwipe) {
var centerSlidePercentage = _this.props.centerSlidePercentage;
var lastPosition = React.Children.count(_this.props.children) - 1;
var needClonedSlide = _this.props.isInfinite && !fromSwipe && (position < 0 || position > lastPosition);
var oldPosition = position;
if (position < 0) {
position = _this.props.isInfinite ? lastPosition : 0;
}
if (position > lastPosition) {
position = _this.props.isInfinite ? 0 : lastPosition;
}
if (needClonedSlide) {
// set swiping true would disable transition time, then we set slider to cloned position and force a reflow
// this is only needed for non-swiping situation
_this.setState({
swiping: true
}, function () {
if (oldPosition < 0) {
if (_this.props.centerMode && !_this.props.vertical) {
_this.setPosition("-".concat((lastPosition + 2) * centerSlidePercentage - (100 - centerSlidePercentage) / 2, "%"), true);
} else {
_this.setPosition("-".concat((lastPosition + 2) * 100, "%"), true);
}
} else if (oldPosition > lastPosition) {
_this.setPosition(0, true);
} //@ts-ignore
_this.selectItem({
selectedItem: position,
swiping: false
});
});
} else {
//@ts-ignore
_this.selectItem({
// if it's not a slider, we don't need to set position here
selectedItem: position
});
} // don't reset auto play when stop on hover is enabled, doing so will trigger a call to auto play more than once
// and will result in the interval function not being cleared correctly.
if (_this.state.autoPlay && _this.state.isMouseEntered === false) {
_this.resetAutoPlay();
}
};
_this.onSwipeForward = function () {
_this.increment(1, true);
};
_this.onSwipeBackwards = function () {
_this.decrement(1, true);
};
_this.changeItem = function (e) {
if (!e.key || e.key === 'Enter') {
var newIndex = e.target.value; //@ts-ignore
_this.selectItem({
selectedItem: newIndex
});
}
};
_this.selectItem = function (state, cb) {
_this.setState(state, cb);
_this.handleOnChange(state.selectedItem, React.Children.toArray(_this.props.children)[state.selectedItem]);
};
_this.getInitialImage = function () {
var selectedItem = _this.props.initPage;
var item = _this.itemsRef && _this.itemsRef[selectedItem];
var images = item && item.getElementsByTagName('img');
return images && images[selectedItem];
};
_this.getVariableImageHeight = function (position) {
var item = _this.itemsRef && _this.itemsRef[position];
var images = item && item.getElementsByTagName('img');
if (_this.state.hasMount && images.length > 0) {
var image = images[0];
if (!image.complete) {
// if the image is still loading, the size won't be available so we trigger a new render after it's done
var onImageLoad = function onImageLoad() {
_this.forceUpdate();
image.removeEventListener('load', onImageLoad);
};
image.addEventListener('load', onImageLoad);
}
var height = image.clientHeight;
return height > 0 ? height : null;
}
return null;
};
_this.state = {
initialized: false,
selectedItem: props.initPage,
hasMount: false,
isMouseEntered: false,
autoPlay: props.autoPlay
};
return _this;
}
(0, _createClass2.default)(Carousel, [{
key: "componentDidMount",
value: function componentDidMount() {
if (!this.props.children) {
return;
}
this.setupCarousel();
}
}, {
key: "componentDidUpdate",
value: function componentDidUpdate(prevProps, prevState) {
if (!prevProps.children && this.props.children && !this.state.initialized) {
this.setupCarousel();
}
if (prevState.swiping && !this.state.swiping) {
// We stopped swiping, ensure we are heading to the new/current slide and not stuck
this.resetPosition();
}
if (prevProps.initPage !== this.props.initPage || prevProps.centerMode !== this.props.centerMode) {
this.updateSizes();
this.moveTo(this.props.initPage);
}
if (prevProps.autoPlay !== this.props.autoPlay) {
if (this.props.autoPlay) {
this.setupAutoPlay();
} else {
this.destroyAutoPlay();
}
this.setState({
autoPlay: this.props.autoPlay
});
}
}
}, {
key: "componentWillUnmount",
value: function componentWillUnmount() {
this.destroyCarousel();
}
}, {
key: "setupCarousel",
value: function setupCarousel() {
this.bindEvents();
if (this.state.autoPlay && React.Children.count(this.props.children) > 1) {
this.setupAutoPlay();
}
this.setState({
initialized: true
});
var initialImage = this.getInitialImage();
if (initialImage) {
// if it's a carousel of images, we set the mount state after the first image is loaded
initialImage.addEventListener('load', this.setMountState);
} else {
this.setMountState();
}
}
}, {
key: "destroyCarousel",
value: function destroyCarousel() {
if (this.state.initialized) {
this.unbindEvents();
this.destroyAutoPlay();
}
}
}, {
key: "setupAutoPlay",
value: function setupAutoPlay() {
this.autoPlay();
}
}, {
key: "destroyAutoPlay",
value: function destroyAutoPlay() {
this.clearAutoPlay();
}
}, {
key: "bindEvents",
value: function bindEvents() {
// as the widths are calculated, we need to resize
// the carousel when the window is resized
window.addEventListener('resize', this.updateSizes); // issue #2 - image loading smaller
window.addEventListener('DOMContentLoaded', this.updateSizes);
}
}, {
key: "unbindEvents",
value: function unbindEvents() {
// removing listeners
window.removeEventListener('resize', this.updateSizes);
window.removeEventListener('DOMContentLoaded', this.updateSizes);
var initialImage = this.getInitialImage();
if (initialImage) {
initialImage.removeEventListener('load', this.setMountState);
}
}
}, {
key: "renderItems",
value: function renderItems(isClone) {
var _this2 = this;
return React.Children.map(this.props.children, function (item, index) {
var slideProps = {
ref: function ref(e) {
return _this2.setItemsRef(e, index);
},
key: 'itemKey' + index + (isClone ? 'clone' : ''),
className: _cssClasses.default.ITEM(true, index === _this2.state.selectedItem),
onClick: _this2.handleClickItem.bind(_this2, index, item)
};
if (_this2.props.centerMode && !_this2.props.vertical) {
//@ts-ignore
slideProps.style = {
minWidth: _this2.props.centerSlidePercentage + '%'
};
}
return /*#__PURE__*/React.createElement("li", slideProps, _this2.props.renderItem(item, {
isSelected: index === _this2.state.selectedItem
}));
});
}
}, {
key: "renderControls",
value: function renderControls() {
var _this3 = this;
var _this$props = this.props,
dots = _this$props.dots,
dotsClass = _this$props.dotsClass;
if (!dots) {
return null;
}
return /*#__PURE__*/React.createElement("ul", {
className: (0, _classnames.default)('Yep-control-dots', (0, _defineProperty2.default)({}, "".concat(dotsClass), !!dotsClass))
}, React.Children.map(this.props.children, function (item, index) {
return _this3.props.renderIndicator(_this3.changeItem, index === _this3.state.selectedItem, index, item);
}));
}
}, {
key: "renderStatus",
value: function renderStatus() {
if (!this.props.showPager) {
return null;
}
return /*#__PURE__*/React.createElement("p", {
className: "".concat(this.props.prefixCls, "-status")
}, this.props.renderPage(this.state.selectedItem + 1, React.Children.count(this.props.children)));
}
}, {
key: "render",
value: function render() {
if (!this.props.children || React.Children.count(this.props.children) === 0) {
return null;
}
var isHorizontal = !this.props.vertical; // obj to hold the transformations and styles
var itemListStyles = {};
var currentPosition = this.getPosition(this.state.selectedItem); // if 3d is available, let's take advantage of the performance of transform
var transformProp = CSSTranslate(currentPosition + '%', this.props.vertical);
var transitionTime = this.props.transitionTime + 'ms';
itemListStyles = {
WebkitTransform: transformProp,
transform: transformProp
};
if (!this.state.swiping) {
itemListStyles = Object.assign(Object.assign({}, itemListStyles), {
WebkitTransitionDuration: transitionTime,
transitionDuration: transitionTime
});
}
var itemsClone = this.renderItems(true);
var firstClone = itemsClone.shift();
var lastClone = itemsClone.pop();
var swiperProps = {
onSwipeMove: this.onSwipeMove,
onSwipeStart: this.onSwipeStart,
onSwipeEnd: this.onSwipeEnd
};
var ulProps = {
style: {
height: 'auto'
}
};
var containerStyles = {};
if (isHorizontal) {
//@ts-ignore
swiperProps.onSwipeLeft = this.onSwipeForward; //@ts-ignore
swiperProps.onSwipeRight = this.onSwipeBackwards;
if (this.props.dynamicHeight) {
var itemHeight = this.getVariableImageHeight(this.state.selectedItem);
ulProps.style.height = itemHeight; //@ts-ignore
containerStyles.height = itemHeight || 'auto';
}
} else {
//@ts-ignore
swiperProps.onSwipeUp = this.props.verticalSwipe === 'natural' ? this.onSwipeBackwards : this.onSwipeForward; //@ts-ignore
swiperProps.onSwipeDown = this.props.verticalSwipe === 'natural' ? this.onSwipeForward : this.onSwipeBackwards;
ulProps.style.height = this.state.itemSize; //@ts-ignore
containerStyles.height = this.state.itemSize;
}
return /*#__PURE__*/React.createElement("div", {
className: _cssClasses.default.ROOT(this.props.className),
style: this.props.style,
ref: this.setCarouselWrapperRef
}, /*#__PURE__*/React.createElement("div", {
className: _cssClasses.default.CAROUSEL(true),
style: {
width: this.props.width
}
}, /*#__PURE__*/React.createElement("div", {
className: _cssClasses.default.WRAPPER(true, this.props.vertical),
style: containerStyles,
ref: this.setItemsWrapperRef
}, /*#__PURE__*/React.createElement(_yepGesture.default, (0, _extends2.default)({
ref: this.setListRef
}, swiperProps), /*#__PURE__*/React.createElement("ul", {
style: Object.assign(Object.assign({}, itemListStyles), ulProps),
className: _cssClasses.default.SLIDER(true, this.state.swiping)
}, this.props.isInfinite && lastClone, this.renderItems(), this.props.isInfinite && firstClone))), this.renderControls(), this.renderStatus()));
}
}]);
return Carousel;
}(React.Component);
Carousel.defaultProps = {
prefixCls: 'Yep-carousel',
style: {},
dots: true,
showPager: false,
isInfinite: false,
initPage: 0,
vertical: false,
verticalSwipe: 'standard',
width: '100%',
autoPlay: false,
transitionTime: 350,
distance: 5,
dynamicHeight: false,
onClickItem: _noop.default,
onClickThumb: _noop.default,
onChange: _noop.default,
renderPage: defaultStatusFormatter,
centerMode: false,
centerSlidePercentage: 80,
onSwipeStart: function onSwipeStart() {},
onSwipeEnd: function onSwipeEnd() {},
onSwipeMove: function onSwipeMove() {},
renderIndicator: function renderIndicator(onClickHandler, isSelected, index, label) {
return /*#__PURE__*/React.createElement("li", {
className: _cssClasses.default.DOT(isSelected),
onClick: onClickHandler,
onKeyDown: onClickHandler,
value: index,
key: index,
role: "button",
tabIndex: 0,
"aria-label": "".concat(label, " ").concat(index + 1)
});
},
renderItem: function renderItem(item) {
return item;
}
};
var _default = Carousel;
exports.default = _default;