wix-style-react
Version:
wix-style-react
178 lines • 8.53 kB
JavaScript
import React from 'react';
import PropTypes from 'prop-types';
import { ChevronLeftLarge, ChevronRightLarge, ChevronLeftLargeSmall, ChevronRightLargeSmall, ChevronLeftSmall, ChevronRightSmall, } from '@wix/wix-ui-icons-common';
// This is here and not in the test setup because we don't want consumers to need to run it as well
import { register } from './utils/match-media-register';
import Slider from 'react-slick';
import { st, classes } from './Carousel.st.css';
import Pagination from './Pagination';
import SliderArrow from './SliderArrow';
import Loader from '../Loader';
import Proportion from '../Proportion';
const AUTOPLAY_SPEED = 2000;
const TRANSITION_SPEED = 600;
const dataHooks = {
imagesContainer: 'images-container',
carouselImage: 'carousel-img',
loader: 'loader',
prevButton: 'prev-button',
nextButton: 'next-button',
pageNavigation: index => `page-navigation-${index}`,
};
const WrappedSliderArrow = ({ currentSlide, slideCount, ...rest }) => (React.createElement(SliderArrow, { ...rest }));
const isTestEnv = process.env.NODE_ENV === 'test';
if (isTestEnv && typeof window !== 'undefined' && !window.matchMedia) {
register();
}
class Carousel extends React.Component {
constructor(props) {
super(props);
this.leftIconByControlSize = controlSize => {
switch (controlSize) {
case 'tiny':
return React.createElement(ChevronLeftSmall, null);
case 'small':
return React.createElement(ChevronLeftLargeSmall, null);
default:
return React.createElement(ChevronLeftLarge, null);
}
};
this.rightIconByControlSize = controlSize => {
switch (controlSize) {
case 'tiny':
return React.createElement(ChevronRightSmall, null);
case 'small':
return React.createElement(ChevronRightLargeSmall, null);
default:
return React.createElement(ChevronRightLarge, null);
}
};
/** Slide to slide by index */
this.slideTo = index => {
this.sliderRef?.current.slickGoTo(index);
};
this._resolveSliderSettings = ({ infinite, autoplay, dots, variableWidth, buttonSkin, initialSlideIndex, afterChange, beforeChange, controlsPosition, controlsSize, controlsStartEnd, }) => {
return {
infinite,
autoplay,
speed: TRANSITION_SPEED,
autoplaySpeed: AUTOPLAY_SPEED,
initialSlide: initialSlideIndex,
dots,
slidesToShow: 1,
slidesToScroll: 1,
variableWidth,
afterChange,
beforeChange,
nextArrow: (React.createElement(WrappedSliderArrow, { dataHook: dataHooks.nextButton, buttonSkin: buttonSkin, arrowSize: controlsSize, icon: this.rightIconByControlSize(controlsSize), controlsStartEnd: controlsStartEnd })),
prevArrow: (React.createElement(WrappedSliderArrow, { dataHook: dataHooks.prevButton, buttonSkin: buttonSkin, arrowSize: controlsSize, icon: this.leftIconByControlSize(controlsSize), controlsStartEnd: controlsStartEnd })),
arrows: controlsPosition !== 'none',
appendDots: pages => {
/*
* originalClassName is a workaround for stylable API extend to work and pass an extendable className.
* This is because react-slick overrides brutally the className prop with cloneElement().
*/
return (React.createElement(Pagination, { originalClassName: classes.pagination, pages: pages }));
},
customPaging: i => (React.createElement("div", { className: classes.pageNavigation, "data-hook": dataHooks.pageNavigation(i) }, i)),
};
};
this._renderImages = images => {
const { imagesPosition, imagesFit } = this.props;
return images.map((image, index) => (React.createElement(Proportion, { key: `${index}${image.src}`, aspectRatio: Proportion.PREDEFINED_RATIOS.landscape },
React.createElement("div", { className: st(classes.imageContainer, {
isLoading: this._isLoading(image.src),
}), "data-hook": dataHooks.imagesContainer },
React.createElement("img", { ...image, "data-hook": dataHooks.carouselImage, className: classes.image, onLoad: () => this._onImageLoad(image.src), style: {
objectPosition: imagesPosition,
objectFit: imagesFit,
...image.style,
} })),
this._isLoading(image.src) && (React.createElement("div", { className: classes.loader },
React.createElement(Loader, { dataHook: "loader", size: "small" }))))));
};
this.state = {
sliderSettings: this._resolveSliderSettings(props),
loadedImages: [],
};
this.sliderRef = React.createRef();
}
render() {
const { dataHook, className, images, children, controlsPosition, controlsSize, showControlsShadow, } = this.props;
const { sliderSettings } = this.state;
const hasImages = !children && images.length > 0;
return (React.createElement("div", { "data-hook": dataHook, className: st(classes.root, { controlsPosition, controlsSize, showControlsShadow }, className) },
React.createElement(Slider, { ref: this.sliderRef, ...sliderSettings },
children,
hasImages && this._renderImages(images))));
}
_onImageLoad(src) {
this.setState(state => ({
loadedImages: [...state.loadedImages, src],
}));
}
_isLoading(src) {
return !this.state.loadedImages.includes(src);
}
}
Carousel.displayName = 'Carousel';
Carousel.propTypes = {
/** Applies a data-hook HTML attribute that can be used in the tests */
dataHook: PropTypes.string,
/** Specifies a CSS class name to be appended to the component’s root element */
className: PropTypes.string,
/** Defines an array of objects where each contains the `src` of an image (in `<img src="your_src" />`) */
images: PropTypes.array,
/** Sets the image's position */
imagesPosition: PropTypes.string,
/** Sets the image’s fit */
imagesFit: PropTypes.oneOf([
'fill',
'contain',
'cover',
'none',
'scale-down',
]),
/** Accepts any component as a child item. Most commonly used to display `<Card/>`, `<Image/>` or video files. */
children: PropTypes.node,
/** Sets the skin of the control (next / previous) buttons */
buttonSkin: PropTypes.oneOf(['standard', 'inverted', 'light']),
/** Drops a shadow below the control buttons */
showControlsShadow: PropTypes.bool,
/** Loops images endlessly */
infinite: PropTypes.bool,
/** Auto-plays images */
autoplay: PropTypes.bool,
/** Controls if the bottom dots are visible or hidden */
dots: PropTypes.bool,
/** Shows one slide at a time (default) or stacks one slide after another horizontally */
variableWidth: PropTypes.bool,
/** Sets the slide to start a presentation with */
initialSlideIndex: PropTypes.number,
/** Index change callback. `index => ...` */
afterChange: PropTypes.func,
/** Index change callback. `(oldIndex, newIndex) => ...` */
beforeChange: PropTypes.func,
/** Sets the control buttons’ position */
controlsPosition: PropTypes.oneOf(['sides', 'overlay', 'bottom', 'none']),
/** Sets the control buttons’ size */
controlsSize: PropTypes.oneOf(['tiny', 'small', 'medium']),
/** Controls if the next / previous control buttons are visible, hidden or disabled at the start and end of the slideshow */
controlsStartEnd: PropTypes.oneOf(['disabled', 'hidden']),
};
Carousel.defaultProps = {
infinite: true,
dots: true,
variableWidth: false,
initialSlideIndex: 0,
images: [],
imagesPosition: 'center top',
imagesFit: 'contain',
buttonSkin: 'standard',
controlsPosition: 'sides',
controlsSize: 'medium',
controlsStartEnd: 'disabled',
showControlsShadow: false,
};
export default Carousel;
//# sourceMappingURL=Carousel.js.map