UNPKG

wix-style-react

Version:
178 lines 8.53 kB
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