UNPKG

wix-style-react

Version:
590 lines (589 loc) • 19.7 kB
"use strict"; var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); exports.__esModule = true; exports.default = void 0; var _react = _interopRequireDefault(require("react")); var _propTypes = _interopRequireDefault(require("prop-types")); var _CarouselWIPSt = require("./CarouselWIP.st.css"); var _wixUiIconsCommon = require("@wix/wix-ui-icons-common"); var _Loader = _interopRequireDefault(require("../Loader")); var _Control = _interopRequireDefault(require("./Control")); var _Slide = _interopRequireDefault(require("./Slide")); var _constants = require("./constants"); var _utils = require("./utils"); var _jsxFileName = "/home/builduser/work/a9c1ac8876d5057c/packages/wix-style-react/dist/cjs/CarouselWIP/CarouselWIP.js"; /** The carousel component creates a slideshow for cycling through a series of content. */ class CarouselWIP extends _react.default.PureComponent { constructor(_props) { var _this; super(_props); _this = this; // 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 this._setImagesOnLoadHandlers = () => { Array.from(this.carousel.children).forEach(child => { var childImages = Array.from(child.getElementsByTagName('img')); childImages.forEach(img => { if (img.complete) { return; } this.setState({ isLoading: true }); this.loadingImagesCount++; img.onload = this._onImageLoad; img.onerror = this._onImageLoad; }); }); }; this._updateChildCount = () => { var _ref, _this$carousel$childr, _this$carousel; var { images } = this.props; this.childCount = (_ref = (_this$carousel$childr = (_this$carousel = this.carousel) == null || (_this$carousel = _this$carousel.children) == null ? void 0 : _this$carousel.length) !== null && _this$carousel$childr !== void 0 ? _this$carousel$childr : images == null ? void 0 : images.length) !== null && _ref !== void 0 ? _ref : 0; }; this._onImageLoad = () => { var { initialSlideIndex } = this.props; this.loadingImagesCount--; if (!this.loadingImagesCount) { this.setState({ isLoading: false }); this._slideTo({ index: initialSlideIndex, immediate: true }).catch(_utils.nop); } }; this._setAutoplayTimer = active => { clearInterval(this.autoplayTimer); if (active) this.autoplayTimer = setInterval(this._next, _constants.AUTOPLAY_SPEED); }; this._setVisibleSlides = () => { var { props, carousel, childCount } = this; var { infinite } = props; var firstVisibleChild = Math.max(Array.from(carousel.children).findIndex(child => (0, _utils.isWhollyInView)(carousel)(child)), 0); var lastVisibleChild = Math.max(Array.from(carousel.children).findIndex((child, i, children) => (0, _utils.isWhollyInView)(carousel)(child) && (i === children.length - 1 || !(0, _utils.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 }); }; this._slideTo = function () { var { index, alignTo, immediate } = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : { index: 0, alignTo: _constants.ALIGNMENT.LEFT, immediate: false }; if (_this.childCount === 0) { return Promise.reject('No children to slide to'); } if (!_this.carousel) { return Promise.reject('The Carousel is not mounted'); } var { afterChange, beforeChange, easing, animationDuration: duration, infinite, startEndOffset } = _this.props; var { children, scrollLeft, offsetWidth } = _this.carousel; var slideIndex = (0, _utils.normalizeIndex)(index, _this.childCount, infinite); var { visibleSlides } = _this.state; var [firstVisibleSlide, lastVisibleSlide] = visibleSlides; var delta; if (alignTo === _constants.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((res, _) => { if (immediate) { _this.carousel.scrollLeft = children[slideIndex].offsetLeft; return res(); } else { var originalOverflowX = 'hidden'; var prop = 'scrollLeft'; return res((0, _utils.animate)(_this.carousel, { prop, delta, easing, duration, originalOverflowX })); } }).then(() => { _this.setState({ isAnimating: false }); _this._setVisibleSlides(); if (firstVisibleSlide !== slideIndex && afterChange) { return afterChange(slideIndex); } }).catch(_ => { _this._setVisibleSlides(); _this.setState({ isAnimating: false }); }); }; this._next = () => { var { slidingType, infinite } = this.props; var { visibleSlides } = this.state; var [firstVisibleSlide, lastVisibleSlide] = visibleSlides; var nextSlide, alignTo; if ([_constants.SLIDING_TYPE.REVEAL_CHUNK, _constants.SLIDING_TYPE.REVEAL_ONE].includes(slidingType)) { if (lastVisibleSlide === this.childCount - 1) { nextSlide = infinite ? 0 : lastVisibleSlide; } else { nextSlide = lastVisibleSlide + 1; } alignTo = slidingType === _constants.SLIDING_TYPE.REVEAL_CHUNK ? _constants.ALIGNMENT.LEFT : _constants.ALIGNMENT.RIGHT; } else { if (firstVisibleSlide === this.childCount - 1) { nextSlide = infinite ? 0 : firstVisibleSlide; } else { nextSlide = firstVisibleSlide + 1; } alignTo = _constants.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 }); }; this._prev = () => { var { slidingType, infinite } = this.props; var { visibleSlides } = this.state; var [firstVisibleSlide, lastVisibleSlide] = visibleSlides; var prevSlide, alignTo; if ([_constants.SLIDING_TYPE.REVEAL_CHUNK, _constants.SLIDING_TYPE.REVEAL_ONE].includes(slidingType)) { if (firstVisibleSlide === 0) { prevSlide = infinite ? this.childCount - 1 : firstVisibleSlide; } else { prevSlide = firstVisibleSlide - 1; } alignTo = slidingType === _constants.SLIDING_TYPE.REVEAL_CHUNK ? _constants.ALIGNMENT.RIGHT : _constants.ALIGNMENT.LEFT; } else { if (firstVisibleSlide === 0) { prevSlide = infinite ? this.childCount - 1 : 0; } else { prevSlide = firstVisibleSlide - 1; } alignTo = _constants.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 }); }; this._setRef = r => { this.carousel = r; }; this._renderLeftControl = () => { var { isLeftArrowDisabled } = this.state; var { controlsPosition, controlsStartEnd, controlsSize, controlsSkin } = this.props; return controlsPosition !== 'none' && (!isLeftArrowDisabled || controlsStartEnd === _constants.CONTROLS_START_END.DISABLED) && /*#__PURE__*/_react.default.createElement(_Control.default, { dataHook: _constants.DATA_HOOKS.prevButton, onClick: this._prev, icon: /*#__PURE__*/_react.default.createElement(_wixUiIconsCommon.ChevronLeftSmall, { __self: this, __source: { fileName: _jsxFileName, lineNumber: 384, columnNumber: 17 } }), size: controlsSize, skin: controlsSkin, disabled: isLeftArrowDisabled, className: (0, _CarouselWIPSt.st)(_CarouselWIPSt.classes.control, _CarouselWIPSt.classes.prev), __self: this, __source: { fileName: _jsxFileName, lineNumber: 381, columnNumber: 9 } }); }; this._renderRightControl = () => { var { isRightArrowDisabled } = this.state; var { controlsPosition, controlsStartEnd, controlsSize, controlsSkin } = this.props; return controlsPosition !== 'none' && (!isRightArrowDisabled || controlsStartEnd === _constants.CONTROLS_START_END.DISABLED) && /*#__PURE__*/_react.default.createElement(_Control.default, { dataHook: _constants.DATA_HOOKS.nextButton, onClick: this._next, icon: /*#__PURE__*/_react.default.createElement(_wixUiIconsCommon.ChevronRightSmall, { __self: this, __source: { fileName: _jsxFileName, lineNumber: 406, columnNumber: 17 } }), size: controlsSize, skin: controlsSkin, disabled: isRightArrowDisabled, className: (0, _CarouselWIPSt.st)(_CarouselWIPSt.classes.control, _CarouselWIPSt.classes.next), __self: this, __source: { fileName: _jsxFileName, lineNumber: 403, columnNumber: 9 } }); }; this._renderSlides = () => { var { images, children, gutter, imagesPosition, imagesFit } = this.props; var slide = _ref2 => { var { i, image, child } = _ref2; return /*#__PURE__*/_react.default.createElement(_Slide.default, { dataHook: _constants.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, __self: this, __source: { fileName: _jsxFileName, lineNumber: 420, columnNumber: 7 } }); }; return /*#__PURE__*/_react.default.createElement("div", { className: _CarouselWIPSt.classes.carousel, role: "list", ref: this._setRef, __self: this, __source: { fileName: _jsxFileName, lineNumber: 434, columnNumber: 7 } }, children.length ? _react.default.Children.map(children, (child, i) => slide({ i, child })) : images.map((image, i) => slide({ i, image }))); }; this._renderLoader = () => /*#__PURE__*/_react.default.createElement("div", { className: _CarouselWIPSt.classes.loader, __self: this, __source: { fileName: _jsxFileName, lineNumber: 443, columnNumber: 5 } }, /*#__PURE__*/_react.default.createElement(_Loader.default, { dataHook: _constants.DATA_HOOKS.loader, size: "small", __self: this, __source: { fileName: _jsxFileName, lineNumber: 444, columnNumber: 7 } })); this._renderDots = () => { var { children, images } = this.props; var { visibleSlides } = this.state; var [firstVisibleSlide, lastVisibleSlide] = visibleSlides; var slidesCount = children.length || images.length || 0; return /*#__PURE__*/_react.default.createElement("div", { className: _CarouselWIPSt.classes.dots, __self: this, __source: { fileName: _jsxFileName, lineNumber: 455, columnNumber: 7 } }, Array(slidesCount).fill(0).map((_, index) => /*#__PURE__*/_react.default.createElement("div", { "data-hook": _constants.DATA_HOOKS.pageNavigation(index), key: index, className: (0, _CarouselWIPSt.st)(_CarouselWIPSt.classes.dot, { active: index >= firstVisibleSlide && index <= lastVisibleSlide }), onClick: () => { if (index < firstVisibleSlide || index > lastVisibleSlide) this._slideTo({ index, alignTo: index > firstVisibleSlide ? _constants.ALIGNMENT.RIGHT : _constants.ALIGNMENT.LEFT }); }, __self: this, __source: { fileName: _jsxFileName, lineNumber: 459, columnNumber: 13 } }))); }; this._renderStartGradient = () => /*#__PURE__*/_react.default.createElement("div", { className: _CarouselWIPSt.classes.start, __self: this, __source: { fileName: _jsxFileName, lineNumber: 481, columnNumber: 32 } }); this._renderEndGradient = () => /*#__PURE__*/_react.default.createElement("div", { className: _CarouselWIPSt.classes.end, __self: this, __source: { fileName: _jsxFileName, lineNumber: 483, columnNumber: 30 } }); this.loadingImagesCount = 0; this.state = { visibleSlides: [], isAnimating: false, isLoading: false, isLeftArrowDisabled: true, isRightArrowDisabled: true, isShowStartGradient: false, isShowEndGradient: false }; } componentDidMount() { var { initialSlideIndex, autoplay } = this.props; this._updateChildCount(); this._setImagesOnLoadHandlers(); if (!this.loadingImagesCount) { this._slideTo({ index: initialSlideIndex, immediate: true }).catch(_utils.nop); this._setVisibleSlides(); } this._setAutoplayTimer(autoplay); } componentDidUpdate(prevProps) { var { autoplay } = this.props; if (prevProps.autoplay !== autoplay) this._setAutoplayTimer(autoplay); var lastCount = this.childCount; this._updateChildCount(); if (this.childCount && lastCount !== this.childCount) { this._setVisibleSlides(); } } render() { var { dataHook, className, controlsPosition, controlsSize, showControlsShadow, sidesGradientColor, hideDots } = this.props; var { isShowStartGradient, isShowEndGradient, isLoading } = this.state; var showSidesGradients = !!sidesGradientColor; return isLoading ? this._renderLoader() : /*#__PURE__*/_react.default.createElement("div", { "data-hook": dataHook, className: (0, _CarouselWIPSt.st)(_CarouselWIPSt.classes.root, { controlsPosition, controlsSize, showControlsShadow, showSidesGradients }, className), style: { [_CarouselWIPSt.vars.sidesGradientColor]: sidesGradientColor }, __self: this, __source: { fileName: _jsxFileName, lineNumber: 501, columnNumber: 7 } }, /*#__PURE__*/_react.default.createElement("div", { style: { position: 'relative' }, __self: this, __source: { fileName: _jsxFileName, lineNumber: 515, columnNumber: 9 } }, showSidesGradients && isShowStartGradient && this._renderStartGradient(), this._renderLeftControl(), this._renderSlides(), this._renderRightControl(), showSidesGradients && isShowEndGradient && this._renderEndGradient()), !hideDots && this._renderDots()); } componentWillUnmount() { this._setAutoplayTimer(false); } } CarouselWIP.displayName = 'CarouselWIP'; CarouselWIP.propTypes = { /** Applied as data-hook HTML attribute that can be used in the tests */ dataHook: _propTypes.default.string, /** A css class to be applied to the component's root element */ className: _propTypes.default.string, /** Any element to render inside */ children: _propTypes.default.node, /** Array of objects where each contains the `src` of an image (in \<img src="your_src" /\>) */ images: _propTypes.default.array, /** Sets the skin of the arrow buttons */ controlsSkin: _propTypes.default.oneOf(['standard', 'inverted', 'light']), /** Show a shadow for the carousel controls */ showControlsShadow: _propTypes.default.bool, /** Images loop endlessly */ infinite: _propTypes.default.bool, /** An index of the slide to start on */ initialSlideIndex: _propTypes.default.number, /** Index change callback. `index => ...` */ afterChange: _propTypes.default.func, /** Index change callback. `(oldIndex, newIndex) => ...` */ beforeChange: _propTypes.default.func, /** Sets the arrows position */ controlsPosition: _propTypes.default.oneOf(['sides', 'overlay', 'bottom', 'none']), /** Sets the arrows position */ controlsSize: _propTypes.default.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.default.oneOf(['disabled', 'hidden']), /** Sliding behaviour type for the carousel */ slidingType: _propTypes.default.oneOf(['align-to-start', 'reveal-one', 'reveal-chunk']), /** Number of pixels for showing "peeking" cards on the edges of the carousel */ startEndOffset: _propTypes.default.number, /** Number of pixels dividing between slides */ gutter: _propTypes.default.number, /** Color for the gradients on the sides of the carousel */ sidesGradientColor: _propTypes.default.string, /** Sets the images position */ imagesPosition: _propTypes.default.string, /** Sets the images fit */ imagesFit: _propTypes.default.oneOf(['fill', 'contain', 'cover', 'none', 'scale-down']), /** Auto-playing of images */ autoplay: _propTypes.default.bool, /** Hide dots */ hideDots: _propTypes.default.bool, // TODO: implement prop /** 🚧 Variable width of children */ variableWidth: _propTypes.default.bool }; 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 }; var _default = exports.default = CarouselWIP; //# sourceMappingURL=CarouselWIP.js.map