UNPKG

@jdcfe/yep-react

Version:

一套移动端的React组件库

707 lines (566 loc) 23.6 kB
"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;