UNPKG

wix-style-react

Version:
601 lines (503 loc) • 21.6 kB
import _slicedToArray from "@babel/runtime/helpers/slicedToArray"; import _classCallCheck from "@babel/runtime/helpers/classCallCheck"; import _createClass from "@babel/runtime/helpers/createClass"; import _assertThisInitialized from "@babel/runtime/helpers/assertThisInitialized"; import _inherits from "@babel/runtime/helpers/inherits"; import _possibleConstructorReturn from "@babel/runtime/helpers/possibleConstructorReturn"; import _getPrototypeOf from "@babel/runtime/helpers/getPrototypeOf"; import _defineProperty from "@babel/runtime/helpers/defineProperty"; function _createSuper(Derived) { var hasNativeReflectConstruct = _isNativeReflectConstruct(); return function _createSuperInternal() { var Super = _getPrototypeOf(Derived), result; if (hasNativeReflectConstruct) { var NewTarget = _getPrototypeOf(this).constructor; result = Reflect.construct(Super, arguments, NewTarget); } else { result = Super.apply(this, arguments); } return _possibleConstructorReturn(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; } } import React from 'react'; import PropTypes from 'prop-types'; import { st, classes, vars } from './CarouselWIP.st.css'; import { ChevronLeftSmall, ChevronRightSmall } from 'wix-ui-icons-common'; import Loader from '../Loader'; import Control from './Control'; import Slide from './Slide'; import { CONTROLS_START_END, SLIDING_TYPE, ALIGNMENT, DATA_HOOKS, AUTOPLAY_SPEED } from './constants'; import { isWhollyInView, animate, nop, normalizeIndex } from './utils'; /** The carousel component creates a slideshow for cycling through a series of content. */ var CarouselWIP = /*#__PURE__*/function (_React$PureComponent) { _inherits(CarouselWIP, _React$PureComponent); var _super = _createSuper(CarouselWIP); function CarouselWIP(_props) { var _this; _classCallCheck(this, CarouselWIP); _this = _super.call(this, _props); _defineProperty(_assertThisInitialized(_this), "_setImagesOnLoadHandlers", function () { Array.from(_this.carousel.children).forEach(function (child) { var childImages = Array.from(child.getElementsByTagName('img')); childImages.forEach(function (img) { _this.setState({ isLoading: true }); _this.loadingImagesCount++; img.onload = _this._onImageLoad; img.onerror = _this._onImageLoad; }); }); }); _defineProperty(_assertThisInitialized(_this), "_updateChildCount", function () { var _ref, _this$carousel$childr, _this$carousel, _this$carousel$childr2; var images = _this.props.images; _this.childCount = (_ref = (_this$carousel$childr = (_this$carousel = _this.carousel) === null || _this$carousel === void 0 ? void 0 : (_this$carousel$childr2 = _this$carousel.children) === null || _this$carousel$childr2 === void 0 ? void 0 : _this$carousel$childr2.length) !== null && _this$carousel$childr !== void 0 ? _this$carousel$childr : images === null || images === void 0 ? void 0 : images.length) !== null && _ref !== void 0 ? _ref : 0; }); _defineProperty(_assertThisInitialized(_this), "_onImageLoad", function () { var initialSlideIndex = _this.props.initialSlideIndex; _this.loadingImagesCount--; if (!_this.loadingImagesCount) { _this.setState({ isLoading: false }); _this._slideTo({ index: initialSlideIndex, immediate: true })["catch"](nop); } }); _defineProperty(_assertThisInitialized(_this), "_setAutoplayTimer", function (active) { clearInterval(_this.autoplayTimer); if (active) _this.autoplayTimer = setInterval(_this._next, AUTOPLAY_SPEED); }); _defineProperty(_assertThisInitialized(_this), "_setVisibleSlides", function () { var _assertThisInitialize = _assertThisInitialized(_this), props = _assertThisInitialize.props, carousel = _assertThisInitialize.carousel, childCount = _assertThisInitialize.childCount; var infinite = props.infinite; var firstVisibleChild = Math.max(Array.from(carousel.children).findIndex(function (child) { return isWhollyInView(carousel)(child); }), 0); var lastVisibleChild = Math.max(Array.from(carousel.children).findIndex(function (child, i, children) { return isWhollyInView(carousel)(child) && (i === children.length - 1 || !isWhollyInView(carousel)(children[i + 1])); }), 0); _this.setState({ visibleSlides: [firstVisibleChild, lastVisibleChild], isLeftArrowDisabled: !infinite && firstVisibleChild === 0, isRightArrowDisabled: !infinite && lastVisibleChild === childCount - 1, isShowStartGradient: firstVisibleChild > 0, isShowEndGradient: lastVisibleChild < childCount - 1 }); }); _defineProperty(_assertThisInitialized(_this), "_slideTo", function () { var _ref2 = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : { index: 0, alignTo: ALIGNMENT.LEFT, immediate: false }, index = _ref2.index, alignTo = _ref2.alignTo, immediate = _ref2.immediate; if (_this.childCount === 0) { return Promise.reject('No children to slide to'); } if (!_this.carousel) { return Promise.reject('The Carousel is not mounted'); } var _this$props = _this.props, afterChange = _this$props.afterChange, beforeChange = _this$props.beforeChange, easing = _this$props.easing, duration = _this$props.animationDuration, infinite = _this$props.infinite, startEndOffset = _this$props.startEndOffset; var _this$carousel2 = _this.carousel, children = _this$carousel2.children, scrollLeft = _this$carousel2.scrollLeft, offsetWidth = _this$carousel2.offsetWidth; var slideIndex = normalizeIndex(index, _this.childCount, infinite); var visibleSlides = _this.state.visibleSlides; var _visibleSlides = _slicedToArray(visibleSlides, 2), firstVisibleSlide = _visibleSlides[0], lastVisibleSlide = _visibleSlides[1]; var delta; if (alignTo === ALIGNMENT.RIGHT) { delta = children[slideIndex].offsetWidth - (offsetWidth - children[slideIndex].offsetLeft) - scrollLeft + startEndOffset; } else { delta = children[slideIndex].offsetLeft - scrollLeft - startEndOffset; } if (firstVisibleSlide !== slideIndex && beforeChange) { beforeChange(firstVisibleSlide, index); } _this.setState({ isAnimating: true }); return new Promise(function (res, _) { if (immediate) { _this.carousel.scrollLeft = children[slideIndex].offsetLeft; return res(); } else { var originalOverflowX = 'hidden'; var prop = 'scrollLeft'; return res(animate(_this.carousel, { prop: prop, delta: delta, easing: easing, duration: duration, originalOverflowX: originalOverflowX })); } }).then(function () { _this.setState({ isAnimating: false }); _this._setVisibleSlides(); if (firstVisibleSlide !== slideIndex && afterChange) { return afterChange(slideIndex); } })["catch"](function (_) { _this._setVisibleSlides(); _this.setState({ isAnimating: false }); }); }); _defineProperty(_assertThisInitialized(_this), "_next", function () { var _this$props2 = _this.props, slidingType = _this$props2.slidingType, infinite = _this$props2.infinite; var visibleSlides = _this.state.visibleSlides; var _visibleSlides2 = _slicedToArray(visibleSlides, 2), firstVisibleSlide = _visibleSlides2[0], lastVisibleSlide = _visibleSlides2[1]; var nextSlide, alignTo; if ([SLIDING_TYPE.REVEAL_CHUNK, SLIDING_TYPE.REVEAL_ONE].includes(slidingType)) { if (lastVisibleSlide === _this.childCount - 1) { nextSlide = infinite ? 0 : lastVisibleSlide; } else { nextSlide = lastVisibleSlide + 1; } alignTo = slidingType === SLIDING_TYPE.REVEAL_CHUNK ? ALIGNMENT.LEFT : ALIGNMENT.RIGHT; } else { if (firstVisibleSlide === _this.childCount - 1) { nextSlide = infinite ? 0 : firstVisibleSlide; } else { nextSlide = firstVisibleSlide + 1; } alignTo = ALIGNMENT.LEFT; } if (nextSlide === _this.childCount - 1) { _this.setState({ isRightArrowDisabled: true, isShowEndGradient: false }); } if (firstVisibleSlide === 0) { _this.setState({ isLeftArrowDisabled: false, isShowStartGradient: true }); } return _this._slideTo({ index: nextSlide, alignTo: alignTo }); }); _defineProperty(_assertThisInitialized(_this), "_prev", function () { var _this$props3 = _this.props, slidingType = _this$props3.slidingType, infinite = _this$props3.infinite; var visibleSlides = _this.state.visibleSlides; var _visibleSlides3 = _slicedToArray(visibleSlides, 2), firstVisibleSlide = _visibleSlides3[0], lastVisibleSlide = _visibleSlides3[1]; var prevSlide, alignTo; if ([SLIDING_TYPE.REVEAL_CHUNK, SLIDING_TYPE.REVEAL_ONE].includes(slidingType)) { if (firstVisibleSlide === 0) { prevSlide = infinite ? _this.childCount - 1 : firstVisibleSlide; } else { prevSlide = firstVisibleSlide - 1; } alignTo = slidingType === SLIDING_TYPE.REVEAL_CHUNK ? ALIGNMENT.RIGHT : ALIGNMENT.LEFT; } else { if (firstVisibleSlide === 0) { prevSlide = infinite ? _this.childCount - 1 : 0; } else { prevSlide = firstVisibleSlide - 1; } alignTo = ALIGNMENT.LEFT; } if (prevSlide === 0) { _this.setState({ isLeftArrowDisabled: true, isShowStartGradient: false }); } if (lastVisibleSlide === _this.childCount - 1) { _this.setState({ isRightArrowDisabled: false, isShowEndGradient: true }); } return _this._slideTo({ index: prevSlide, alignTo: alignTo }); }); _defineProperty(_assertThisInitialized(_this), "_setRef", function (r) { _this.carousel = r; }); _defineProperty(_assertThisInitialized(_this), "_renderLeftControl", function () { var isLeftArrowDisabled = _this.state.isLeftArrowDisabled; var _this$props4 = _this.props, controlsPosition = _this$props4.controlsPosition, controlsStartEnd = _this$props4.controlsStartEnd, controlsSize = _this$props4.controlsSize, controlsSkin = _this$props4.controlsSkin; return controlsPosition !== 'none' && (!isLeftArrowDisabled || controlsStartEnd === CONTROLS_START_END.DISABLED) && /*#__PURE__*/React.createElement(Control, { dataHook: DATA_HOOKS.prevButton, onClick: _this._prev, icon: /*#__PURE__*/React.createElement(ChevronLeftSmall, null), size: controlsSize, skin: controlsSkin, disabled: isLeftArrowDisabled, className: st(classes.control, classes.prev) }); }); _defineProperty(_assertThisInitialized(_this), "_renderRightControl", function () { var isRightArrowDisabled = _this.state.isRightArrowDisabled; var _this$props5 = _this.props, controlsPosition = _this$props5.controlsPosition, controlsStartEnd = _this$props5.controlsStartEnd, controlsSize = _this$props5.controlsSize, controlsSkin = _this$props5.controlsSkin; return controlsPosition !== 'none' && (!isRightArrowDisabled || controlsStartEnd === CONTROLS_START_END.DISABLED) && /*#__PURE__*/React.createElement(Control, { dataHook: DATA_HOOKS.nextButton, onClick: _this._next, icon: /*#__PURE__*/React.createElement(ChevronRightSmall, null), size: controlsSize, skin: controlsSkin, disabled: isRightArrowDisabled, className: st(classes.control, classes.next) }); }); _defineProperty(_assertThisInitialized(_this), "_renderSlides", function () { var _this$props6 = _this.props, images = _this$props6.images, children = _this$props6.children, gutter = _this$props6.gutter, imagesPosition = _this$props6.imagesPosition, imagesFit = _this$props6.imagesFit; var slide = function slide(_ref3) { var i = _ref3.i, image = _ref3.image, child = _ref3.child; return /*#__PURE__*/React.createElement(Slide, { dataHook: DATA_HOOKS.child, key: "slide-".concat(i), role: "listitem", width: "auto", gutter: i > 0 ? "".concat(gutter, "px") : '', image: image, children: child, imagePosition: imagesPosition, imageFit: imagesFit }); }; return /*#__PURE__*/React.createElement("div", { className: classes.carousel, role: "list", ref: _this._setRef }, children.length ? React.Children.map(children, function (child, i) { return slide({ i: i, child: child }); }) : images.map(function (image, i) { return slide({ i: i, image: image }); })); }); _defineProperty(_assertThisInitialized(_this), "_renderLoader", function () { return /*#__PURE__*/React.createElement("div", { className: classes.loader }, /*#__PURE__*/React.createElement(Loader, { dataHook: DATA_HOOKS.loader, size: "small" })); }); _defineProperty(_assertThisInitialized(_this), "_renderDots", function () { var _this$props7 = _this.props, children = _this$props7.children, images = _this$props7.images; var visibleSlides = _this.state.visibleSlides; var _visibleSlides4 = _slicedToArray(visibleSlides, 2), firstVisibleSlide = _visibleSlides4[0], lastVisibleSlide = _visibleSlides4[1]; var slidesCount = children.length || images.length || 0; return /*#__PURE__*/React.createElement("div", { className: classes.dots }, Array(slidesCount).fill(0).map(function (_, index) { return /*#__PURE__*/React.createElement("div", { "data-hook": DATA_HOOKS.pageNavigation(index), key: index, className: st(classes.dot, { active: index >= firstVisibleSlide && index <= lastVisibleSlide }), onClick: function onClick() { if (index < firstVisibleSlide || index > lastVisibleSlide) _this._slideTo({ index: index, alignTo: index > firstVisibleSlide ? ALIGNMENT.RIGHT : ALIGNMENT.LEFT }); } }); })); }); _defineProperty(_assertThisInitialized(_this), "_renderStartGradient", function () { return /*#__PURE__*/React.createElement("div", { className: classes.start }); }); _defineProperty(_assertThisInitialized(_this), "_renderEndGradient", function () { return /*#__PURE__*/React.createElement("div", { className: classes.end }); }); _this.loadingImagesCount = 0; _this.state = { visibleSlides: [], isAnimating: false, isLoading: false, isLeftArrowDisabled: true, isRightArrowDisabled: true, isShowStartGradient: false, isShowEndGradient: false }; return _this; } _createClass(CarouselWIP, [{ key: "componentDidMount", value: function componentDidMount() { var _this$props8 = this.props, initialSlideIndex = _this$props8.initialSlideIndex, autoplay = _this$props8.autoplay; this._updateChildCount(); this._setImagesOnLoadHandlers(); if (!this.loadingImagesCount) { this._slideTo({ index: initialSlideIndex, immediate: true })["catch"](nop); this._setVisibleSlides(); } this._setAutoplayTimer(autoplay); } }, { key: "componentDidUpdate", value: function componentDidUpdate(prevProps) { var autoplay = this.props.autoplay; if (prevProps.autoplay !== autoplay) this._setAutoplayTimer(autoplay); var lastCount = this.childCount; this._updateChildCount(); if (this.childCount && lastCount !== this.childCount) { this._setVisibleSlides(); } } // Need to wait for images to load so we know which images are visible // Adding onLoad and onError callbacks to all images under the component }, { key: "render", value: function render() { var _this$props9 = this.props, dataHook = _this$props9.dataHook, className = _this$props9.className, controlsPosition = _this$props9.controlsPosition, controlsSize = _this$props9.controlsSize, showControlsShadow = _this$props9.showControlsShadow, sidesGradientColor = _this$props9.sidesGradientColor, hideDots = _this$props9.hideDots; var _this$state = this.state, isShowStartGradient = _this$state.isShowStartGradient, isShowEndGradient = _this$state.isShowEndGradient, isLoading = _this$state.isLoading; var showSidesGradients = !!sidesGradientColor; return isLoading ? this._renderLoader() : /*#__PURE__*/React.createElement("div", { "data-hook": dataHook, className: st(classes.root, { controlsPosition: controlsPosition, controlsSize: controlsSize, showControlsShadow: showControlsShadow, showSidesGradients: showSidesGradients }, className), style: _defineProperty({}, vars.sidesGradientColor, sidesGradientColor) }, /*#__PURE__*/React.createElement("div", { style: { position: 'relative' } }, showSidesGradients && isShowStartGradient && this._renderStartGradient(), this._renderLeftControl(), this._renderSlides(), this._renderRightControl(), showSidesGradients && isShowEndGradient && this._renderEndGradient()), !hideDots && this._renderDots()); } }, { key: "componentWillUnmount", value: function componentWillUnmount() { this._setAutoplayTimer(false); } }]); return CarouselWIP; }(React.PureComponent); _defineProperty(CarouselWIP, "displayName", 'CarouselWIP'); _defineProperty(CarouselWIP, "propTypes", { /** Applied as data-hook HTML attribute that can be used in the tests */ dataHook: PropTypes.string, /** A css class to be applied to the component's root element */ className: PropTypes.string, /** Any element to render inside */ children: PropTypes.node, /** Array of objects where each contains the `src` of an image (in \<img src="your_src" /\>) */ images: PropTypes.array, /** Sets the skin of the arrow buttons */ controlsSkin: PropTypes.oneOf(['standard', 'inverted', 'light']), /** Show a shadow for the carousel controls */ showControlsShadow: PropTypes.bool, /** Images loop endlessly */ infinite: PropTypes.bool, /** An index of the slide to start on */ initialSlideIndex: PropTypes.number, /** Index change callback. `index => ...` */ afterChange: PropTypes.func, /** Index change callback. `(oldIndex, newIndex) => ...` */ beforeChange: PropTypes.func, /** Sets the arrows position */ controlsPosition: PropTypes.oneOf(['sides', 'overlay', 'bottom', 'none']), /** Sets the arrows position */ controlsSize: PropTypes.oneOf(['tiny', 'small', 'medium']), /** Configure the start and end controls to be shown disabled or hidden. Relevant when infinite prop is set to false. */ controlsStartEnd: PropTypes.oneOf(['disabled', 'hidden']), /** Sliding behaviour type for the carousel */ slidingType: PropTypes.oneOf(['align-to-start', 'reveal-one', 'reveal-chunk']), /** Number of pixels for showing "peeking" cards on the edges of the carousel */ startEndOffset: PropTypes.number, /** Number of pixels dividing between slides */ gutter: PropTypes.number, /** Color for the gradients on the sides of the carousel */ sidesGradientColor: PropTypes.string, /** Sets the images position */ imagesPosition: PropTypes.string, /** Sets the images fit */ imagesFit: PropTypes.oneOf(['fill', 'contain', 'cover', 'none', 'scale-down']), /** Auto-playing of images */ autoplay: PropTypes.bool, /** Hide dots */ hideDots: PropTypes.bool, // TODO: implement prop /** 🚧 Variable width of children */ variableWidth: PropTypes.bool }); _defineProperty(CarouselWIP, "defaultProps", { children: [], infinite: true, controlsSkin: 'standard', controlsStartEnd: 'disabled', showControlsShadow: false, images: [], initialSlideIndex: 0, controlsPosition: 'sides', controlsSize: 'medium', slidingType: 'align-to-start', startEndOffset: 0, gutter: 0, hideDots: false, autoplay: false }); export default CarouselWIP;