wix-style-react
Version:
601 lines (503 loc) • 21.6 kB
JavaScript
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;