wix-style-react
Version:
wix-style-react
590 lines (589 loc) • 19.7 kB
JavaScript
"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