diff --git a/src/CSSTranslate.js b/src/CSSTranslate.js index a653b49c..f73597b6 100644 --- a/src/CSSTranslate.js +++ b/src/CSSTranslate.js @@ -1,8 +1,8 @@ -module.exports = function (position, axis) { - var positionCss = (axis === 'horizontal') ? [position, 0, 0] : [0, position, 0]; - var transitionProp = 'translate3d'; +export default (position, axis) => { + const positionCss = (axis === 'horizontal') ? [position, 0, 0] : [0, position, 0]; + const transitionProp = 'translate3d'; - var translatedPosition = '(' + positionCss.join(',') + ')'; + const translatedPosition = '(' + positionCss.join(',') + ')'; return transitionProp + translatedPosition; }; diff --git a/src/__tests__/Carousel.js b/src/__tests__/Carousel.js index e4ed4f21..8f99d50b 100644 --- a/src/__tests__/Carousel.js +++ b/src/__tests__/Carousel.js @@ -2,11 +2,13 @@ import React from 'react'; import ReactDOM from 'react-dom'; import { shallow, mount } from 'enzyme'; import renderer from 'react-test-renderer'; +import * as index from '../index'; describe("Slider", function() { jest.autoMockOff(); - const Carousel = require('../components/Carousel'); + const Carousel = require('../components/Carousel').default; + const Thumbs = require('../components/Thumbs').default; let component, componentInstance, totalChildren, lastItemIndex; @@ -49,6 +51,15 @@ describe("Slider", function() { renderDefaultComponent({}); }); + describe("Exports", () => { + it('should export Carousel from the main index file', () => { + expect(index.Carousel).toBe(Carousel); + }); + it('should export Thumbs from the main index file', () => { + expect(index.Thumbs).toBe(Thumbs); + }); + }); + describe("Basics", () => { describe("DisplayName", () => { it('should be Carousel', () => { diff --git a/src/components/Carousel.js b/src/components/Carousel.js index 549380a0..d7f6e761 100644 --- a/src/components/Carousel.js +++ b/src/components/Carousel.js @@ -1,20 +1,17 @@ -var React = require('react'); -var ReactDOM = require('react-dom'); -var PropTypes = require('prop-types'); -var CreateReactClass = require('create-react-class'); -var klass = require('../cssClasses'); -var merge = require('../object-assign'); -var CSSTranslate = require('../CSSTranslate'); -var Swipe = require('react-easy-swipe'); -var Thumbs = require('./Thumbs'); -var customPropTypes = require('../customPropTypes'); - -// react-swipe was compiled using babel -Swipe = Swipe.default; - -module.exports = CreateReactClass({ - displayName: 'Carousel', - propTypes: { +import React, { Component } from 'react'; +import ReactDOM from 'react-dom'; +import PropTypes from 'prop-types'; +import klass from '../cssClasses'; +import merge from '../object-assign'; +import CSSTranslate from '../CSSTranslate'; +import Swipe from 'react-easy-swipe'; +import Thumbs from './Thumbs'; +import * as customPropTypes from '../customPropTypes'; + +class Carousel extends Component { + static displayName = 'Carousel'; + + static propTypes = { className: PropTypes.string, children: PropTypes.node, showArrows: PropTypes.bool, @@ -36,36 +33,36 @@ module.exports = CreateReactClass({ swipeScrollTolerance: PropTypes.oneOfType([PropTypes.number, PropTypes.string]), dynamicHeight: PropTypes.bool, emulateTouch: PropTypes.bool - }, - - getDefaultProps () { - return { - showIndicators: true, - showArrows: true, - showStatus:true, - showThumbs:true, - infiniteLoop: false, - selectedItem: 0, - axis: 'horizontal', - width: '100%', - useKeyboardArrows: false, - autoPlay: false, - stopOnHover: true, - interval: 3000, - transitionTime: 350, - swipeScrollTolerance: 5, - dynamicHeight: false, - emulateTouch: false - } - }, - - getInitialState () { - return { + }; + + static defaultProps = { + showIndicators: true, + showArrows: true, + showStatus:true, + showThumbs:true, + infiniteLoop: false, + selectedItem: 0, + axis: 'horizontal', + width: '100%', + useKeyboardArrows: false, + autoPlay: false, + stopOnHover: true, + interval: 3000, + transitionTime: 350, + swipeScrollTolerance: 5, + dynamicHeight: false, + emulateTouch: false + }; + + constructor(props) { + super(props); + + this.state = { initialized: false, - selectedItem: this.props.selectedItem, + selectedItem: props.selectedItem, hasMount: false - } - }, + }; + } componentDidMount () { if (!this.props.children) { @@ -73,7 +70,7 @@ module.exports = CreateReactClass({ } this.setupCarousel(); - }, + } componentWillReceiveProps (nextProps) { if (nextProps.selectedItem !== this.state.selectedItem) { @@ -90,17 +87,17 @@ module.exports = CreateReactClass({ this.destroyAutoPlay(); } } - }, + } componentDidUpdate(prevProps) { if (!prevProps.children && this.props.children && !this.state.initialized) { this.setupCarousel(); } - }, + } componentWillUnmount() { this.destroyCarousel(); - }, + } setupCarousel () { this.bindEvents(); @@ -113,37 +110,37 @@ module.exports = CreateReactClass({ initialized: true }); - var initialImage = this.getInitialImage() + const initialImage = this.getInitialImage() if (initialImage) { // if it's a carousel of images, we set the mount state after the first image is loaded initialImage.addEventListener('load', this.setMountState); } else { this.setMountState(); } - }, + } destroyCarousel () { if (this.state.initialized) { this.unbindEvents(); this.destroyAutoPlay(); } - }, + } setupAutoPlay () { this.autoPlay(); if (this.props.stopOnHover) { - var carouselWrapper = this.refs['carouselWrapper']; + const carouselWrapper = this.refs['carouselWrapper']; carouselWrapper.addEventListener('mouseenter', this.stopOnHover); carouselWrapper.addEventListener('touchstart', this.stopOnHover); carouselWrapper.addEventListener('mouseleave', this.autoPlay); carouselWrapper.addEventListener('touchend', this.autoPlay); } - }, + } destroyAutoPlay () { this.clearAutoPlay(); - var carouselWrapper = this.refs['carouselWrapper']; + const carouselWrapper = this.refs['carouselWrapper']; if (this.props.stopOnHover && carouselWrapper) { carouselWrapper.removeEventListener('mouseenter', this.stopOnHover); @@ -151,26 +148,26 @@ module.exports = CreateReactClass({ carouselWrapper.removeEventListener('mouseleave', this.autoPlay); carouselWrapper.removeEventListener('touchend', this.autoPlay); } - }, + } autoPlay () { this.timer = setTimeout(() => { this.increment(); }, this.props.interval); - }, + } clearAutoPlay () { clearTimeout(this.timer); - }, + } resetAutoPlay() { this.clearAutoPlay(); this.autoPlay(); - }, + } stopOnHover () { this.clearAutoPlay(); - }, + } bindEvents () { // as the widths are calculated, we need to resize @@ -182,14 +179,14 @@ module.exports = CreateReactClass({ if (this.props.useKeyboardArrows) { document.addEventListener("keydown", this.navigateWithKeyboard); } - }, + } unbindEvents () { // removing listeners window.removeEventListener("resize", this.updateSizes); window.removeEventListener("DOMContentLoaded", this.updateSizes); - var initialImage = this.getInitialImage(); + const initialImage = this.getInitialImage(); if(initialImage) { initialImage.removeEventListener("load", this.setMountState); } @@ -197,12 +194,12 @@ module.exports = CreateReactClass({ if (this.props.useKeyboardArrows) { document.removeEventListener("keydown", this.navigateWithKeyboard); } - }, + } - navigateWithKeyboard (e) { - var nextKeys = ['ArrowDown', 'ArrowRight']; - var prevKeys = ['ArrowUp', 'ArrowLeft']; - var allowedKeys = nextKeys.concat(prevKeys); + navigateWithKeyboard = (e) => { + const nextKeys = ['ArrowDown', 'ArrowRight']; + const prevKeys = ['ArrowUp', 'ArrowLeft']; + const allowedKeys = nextKeys.concat(prevKeys); if (allowedKeys.indexOf(e.key) > -1) { if (nextKeys.indexOf(e.key) > -1) { @@ -211,29 +208,29 @@ module.exports = CreateReactClass({ this.decrement(); } } - }, + } - updateSizes () { + updateSizes = () => { if (!this.state.initialized) { return; } - var isHorizontal = this.props.axis === 'horizontal'; - var firstItem = this.refs.item0; - var itemSize = isHorizontal ? firstItem.clientWidth : firstItem.clientHeight; + const isHorizontal = this.props.axis === 'horizontal'; + const firstItem = this.refs.item0; + const itemSize = isHorizontal ? firstItem.clientWidth : firstItem.clientHeight; this.setState({ itemSize: itemSize, wrapperSize: isHorizontal ? itemSize * this.props.children.length : itemSize }); - }, + } - setMountState () { + setMountState = () => { this.setState({hasMount: true}); this.updateSizes(); - }, + } - handleClickItem (index, item) { + handleClickItem = (index, item) => { if (this.state.cancelClick) { this.selectItem({ cancelClick: false @@ -242,7 +239,7 @@ module.exports = CreateReactClass({ return; } - var handler = this.props.onClickItem; + const handler = this.props.onClickItem; if (typeof handler === 'function') { handler(index, item); @@ -253,18 +250,18 @@ module.exports = CreateReactClass({ selectedItem: index, }); } - }, + } - handleOnChange (index, item) { - var handler = this.props.onChange; + handleOnChange = (index, item) => { + const handler = this.props.onChange; if (typeof handler === 'function') { handler(index, item); } - }, + } - handleClickThumb(index, item) { - var handler = this.props.onClickThumb; + handleClickThumb = (index, item) => { + const handler = this.props.onClickThumb; if (typeof handler === 'function') { handler(index, item); @@ -273,32 +270,32 @@ module.exports = CreateReactClass({ this.selectItem({ selectedItem: index }); - }, + } - onSwipeStart() { + onSwipeStart = () => { this.setState({ swiping: true }); - }, + } - onSwipeEnd() { + onSwipeEnd = () => { this.setState({ swiping: false, cancelClick: true }); - }, + } - onSwipeMove(delta) { - var list = ReactDOM.findDOMNode(this.refs.itemList); - var isHorizontal = this.props.axis === 'horizontal'; + onSwipeMove = (delta) => { + const list = ReactDOM.findDOMNode(this.refs.itemList); + const isHorizontal = this.props.axis === 'horizontal'; - var initialBoundry = 0; + const initialBoundry = 0; - var currentPosition = - this.state.selectedItem * 100; - var finalBoundry = - (this.props.children.length - 1) * 100; + const currentPosition = - this.state.selectedItem * 100; + const finalBoundry = - (this.props.children.length - 1) * 100; - var axisDelta = isHorizontal ? delta.x : delta.y; - var handledDelta = axisDelta; + const axisDelta = isHorizontal ? delta.x : delta.y; + let handledDelta = axisDelta; // prevent user from swiping left out of boundaries if (currentPosition === initialBoundry && axisDelta > 0) { @@ -310,7 +307,7 @@ module.exports = CreateReactClass({ handledDelta = 0; } - var position = currentPosition + (100 / (this.state.itemSize / handledDelta)) + '%'; + const position = currentPosition + (100 / (this.state.itemSize / handledDelta)) + '%'; [ 'WebkitTransform', @@ -325,18 +322,18 @@ module.exports = CreateReactClass({ // allows scroll if the swipe was within the tolerance return Math.abs(axisDelta) > this.props.swipeScrollTolerance; - }, + } - decrement (positions){ + decrement = (positions) => { this.moveTo(this.state.selectedItem - (typeof positions === 'Number' ? positions : 1)); - }, + } - increment (positions){ + increment = (positions) => { this.moveTo(this.state.selectedItem + (typeof positions === 'Number' ? positions : 1)); - }, + } - moveTo (position) { - var lastPosition = this.props.children.length - 1; + moveTo = (position) => { + const lastPosition = this.props.children.length - 1; if (position < 0 ) { position = this.props.infiniteLoop ? lastPosition : 0; @@ -354,25 +351,25 @@ module.exports = CreateReactClass({ if (this.props.autoPlay) { this.resetAutoPlay(); } - }, + } - changeItem (e) { - var newIndex = e.target.value; + changeItem = (e) => { + const newIndex = e.target.value; this.selectItem({ selectedItem: newIndex }); - }, + } - selectItem (state) { + selectItem = (state) => { this.setState(state); this.handleOnChange(state.selectedItem, this.props.children[state.selectedItem]); - }, + } renderItems () { return React.Children.map(this.props.children, (item, index) => { - var hasMount = this.state.hasMount; - var itemClass = klass.ITEM(true, index === this.state.selectedItem); + const hasMount = this.state.hasMount; + const itemClass = klass.ITEM(true, index === this.state.selectedItem); return (
{this.state.selectedItem + 1} of {this.props.children.length}
; - }, + } renderThumbs () { if (!this.props.showThumbs || this.props.children.length === 0) { @@ -415,14 +412,14 @@ module.exports = CreateReactClass({ {this.props.children} ); - }, + } getInitialImage () { const selectedItem = this.props.selectedItem; const item = this.refs[`item${selectedItem}`]; const images = item && item.getElementsByTagName('img'); return images && images[selectedItem]; - }, + } getVariableImageHeight (position) { const item = this.refs[`item${position}`]; @@ -445,32 +442,32 @@ module.exports = CreateReactClass({ } return null; - }, + } render () { if (!this.props.children || this.props.children.length === 0) { return null; } - var itemsLength = this.props.children.length; + const itemsLength = this.props.children.length; - var isHorizontal = this.props.axis === 'horizontal'; + const isHorizontal = this.props.axis === 'horizontal'; - var canShowArrows = this.props.showArrows && itemsLength > 1; + const canShowArrows = this.props.showArrows && itemsLength > 1; // show left arrow? - var hasPrev = canShowArrows && (this.state.selectedItem > 0 || this.props.infiniteLoop); + const hasPrev = canShowArrows && (this.state.selectedItem > 0 || this.props.infiniteLoop); // show right arrow - var hasNext = canShowArrows && (this.state.selectedItem < itemsLength - 1 || this.props.infiniteLoop); + const hasNext = canShowArrows && (this.state.selectedItem < itemsLength - 1 || this.props.infiniteLoop); // obj to hold the transformations and styles - var itemListStyles = {}; + let itemListStyles = {}; - var currentPosition = - this.state.selectedItem * 100 + '%'; + const currentPosition = - this.state.selectedItem * 100 + '%'; // if 3d is available, let's take advantage of the performance of transform - var transformProp = CSSTranslate(currentPosition, this.props.axis); + const transformProp = CSSTranslate(currentPosition, this.props.axis); - var transitionTime = this.props.transitionTime + 'ms'; + const transitionTime = this.props.transitionTime + 'ms'; itemListStyles = { 'WebkitTransform': transformProp, @@ -487,7 +484,7 @@ module.exports = CreateReactClass({ 'msTransitionDuration': transitionTime }; - var swiperProps = { + let swiperProps = { selectedItem: this.state.selectedItem, className: klass.SLIDER(true, this.state.swiping), onSwipeMove: this.onSwipeMove, @@ -497,7 +494,7 @@ module.exports = CreateReactClass({ ref: 'itemList' }; - var containerStyles = {}; + const containerStyles = {}; if (isHorizontal) { merge(swiperProps, { @@ -540,4 +537,6 @@ module.exports = CreateReactClass({ ); } -}); +} + +export default Carousel; diff --git a/src/components/Thumbs.js b/src/components/Thumbs.js index 4126c337..64b82f0e 100644 --- a/src/components/Thumbs.js +++ b/src/components/Thumbs.js @@ -1,39 +1,36 @@ -var React = require('react'); -var ReactDOM = require('react-dom'); -var PropTypes = require('prop-types'); -var CreateReactClass = require('create-react-class'); -var klass = require('../cssClasses'); -var outerWidth = require('../dimensions').outerWidth; -var CSSTranslate = require('../CSSTranslate'); -var Swipe = require('react-easy-swipe'); - -// react-swipe was compiled using babel -Swipe = Swipe.default; - -module.exports = CreateReactClass({ - - propsTypes: { +import React, { Component } from 'react'; +import ReactDOM from 'react-dom'; +import PropTypes from 'prop-types'; +import klass from '../cssClasses'; +import { outerWidth } from '../dimensions'; +import CSSTranslate from '../CSSTranslate'; +import Swipe from 'react-easy-swipe'; + +class Thumbs extends Component { + static displayName = 'Thumbs'; + + static propsTypes = { children: PropTypes.element.isRequired, transitionTime: PropTypes.number, selectedItem: PropTypes.number - }, + }; - getDefaultProps () { - return { - selectedItem: 0, - transitionTime: 350, - axis: 'horizontal' - } - }, + static defaultProps = { + selectedItem: 0, + transitionTime: 350, + axis: 'horizontal' + }; + + constructor(props) { + super(props); - getInitialState () { - return { + this.state = { initialized: false, - selectedItem: this.props.selectedItem, + selectedItem: props.selectedItem, hasMount: false, - firstItem: this.getFirstItem(this.props.selectedItem) - }; - }, + firstItem: this.getFirstItem(props.selectedItem) + } + } componentDidMount (nextProps) { if (!this.props.children) { @@ -41,7 +38,7 @@ module.exports = CreateReactClass({ } this.setupThumbs(); - }, + } componentWillReceiveProps (props, state) { if (props.selectedItem !== this.state.selectedItem) { @@ -50,17 +47,17 @@ module.exports = CreateReactClass({ firstItem: this.getFirstItem(props.selectedItem) }); } - }, + } componentDidUpdate(prevProps) { if (!prevProps.children && this.props.children && !this.state.initialized) { this.setupThumbs(); } - }, + } componentWillUnmount() { this.destroyThumbs(); - }, + } setupThumbs () { // as the widths are calculated, we need to resize @@ -73,7 +70,7 @@ module.exports = CreateReactClass({ initialized: true }); - var defaultImg = this.getDefaultImage(); + const defaultImg = this.getDefaultImage(); if (defaultImg) { defaultImg.addEventListener('load', this.setMountState); } @@ -81,76 +78,76 @@ module.exports = CreateReactClass({ // when the component is rendered we need to calculate // the container size to adjust the responsive behaviour this.updateSizes(); - }, + } destroyThumbs () { // removing listeners window.removeEventListener("resize", this.updateSizes); window.removeEventListener("DOMContentLoaded", this.updateSizes); - var defaultImg = this.getDefaultImage(); + const defaultImg = this.getDefaultImage(); if (defaultImg) { defaultImg.removeEventListener('load', this.setMountState); } - }, + } + + getDefaultImage () { + const firstItem = ReactDOM.findDOMNode(this.thumb0); + + if (firstItem) { + const firstImage = firstItem.getElementsByTagName('img'); + return firstImage && firstImage[0]; + } + + return null; + } - updateSizes () { + updateSizes = () => { if (!this.state.initialized) { return; } - var total = this.props.children.length; + const total = this.props.children.length; this.wrapperSize = this.itemsWrapper.clientWidth; this.itemSize = outerWidth(this.thumb0); this.visibleItems = Math.floor(this.wrapperSize / this.itemSize); this.lastPosition = total - this.visibleItems; this.showArrows = this.visibleItems < total; - }, - - getDefaultImage () { - var firstItem = ReactDOM.findDOMNode(this.thumb0); - - if (firstItem) { - var firstImage = firstItem.getElementsByTagName('img'); - return firstImage && firstImage[0]; - } - - return null; - }, + } - setMountState () { + setMountState = () => { this.setState({hasMount: true}); this.updateSizes(); - }, + } - handleClickItem (index, item) { - var handler = this.props.onSelectItem; + handleClickItem = (index, item) => { + const handler = this.props.onSelectItem; if (typeof handler === 'function') { handler(index, item); } - }, + } - onSwipeStart() { + onSwipeStart = () => { this.setState({ swiping: true }); - }, + } - onSwipeEnd() { + onSwipeEnd = () => { this.setState({ swiping: false }); - }, + } - onSwipeMove(deltaX) { - var leftBoundry = 0; - var list = ReactDOM.findDOMNode(this.itemList); - var wrapperSize = list.clientWidth; - var visibleItems = Math.floor(wrapperSize / this.itemSize); + onSwipeMove = (deltaX) => { + const leftBoundry = 0; + const list = ReactDOM.findDOMNode(this.itemList); + const wrapperSize = list.clientWidth; + const visibleItems = Math.floor(wrapperSize / this.itemSize); - var currentPosition = - this.state.firstItem * this.itemSize; - var lastLeftBoundry = - this.visibleItems * this.itemSize; + const currentPosition = - this.state.firstItem * this.itemSize; + const lastLeftBoundry = - this.visibleItems * this.itemSize; // prevent user from swiping left out of boundaries @@ -163,7 +160,7 @@ module.exports = CreateReactClass({ deltaX = 0; } - var position = currentPosition + (100 / (wrapperSize / deltaX)) + '%'; + const position = currentPosition + (100 / (wrapperSize / deltaX)) + '%'; // if 3d isn't available we will use left to move [ @@ -176,17 +173,17 @@ module.exports = CreateReactClass({ ].forEach((prop) => { list.style[prop] = CSSTranslate(position, this.props.axis); }); - }, + } - slideRight (positions){ + slideRight = (positions) => { this.moveTo(this.state.firstItem - (typeof positions === 'Number' ? positions : 1)); - }, + } - slideLeft (positions){ + slideLeft = (positions) => { this.moveTo(this.state.firstItem + (typeof positions === 'Number' ? positions : 1)); - }, + } - moveTo (position) { + moveTo = (position) => { // position can't be lower than 0 position = position < 0 ? 0 : position; // position can't be higher than last postion @@ -197,14 +194,14 @@ module.exports = CreateReactClass({ // if it's not a slider, we don't need to set position here selectedItem: this.state.selectedItem }); - }, + } getFirstItem (selectedItem) { if (!this.showArrows) { return 0; } - var firstItem = selectedItem; + let firstItem = selectedItem; if (selectedItem >= this.lastPosition) { firstItem = this.lastPosition; @@ -219,13 +216,13 @@ module.exports = CreateReactClass({ } return firstItem; - }, + } renderItems () { return React.Children.map(this.props.children, (item, index) => { - var itemClass = klass.ITEM(false, index === this.state.selectedItem && this.state.hasMount); + const itemClass = klass.ITEM(false, index === this.state.selectedItem && this.state.hasMount); - var img = item; + let img = item; if (item.type !== "img") { img = React.Children.toArray(item.props.children).filter((children) => children.type === "img")[0]; @@ -243,7 +240,7 @@ module.exports = CreateReactClass({...and the carousel can be used to present something!
-See the source code...
-Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book.
-It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged.-
It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum.
-Contrary to popular belief, Lorem Ipsum is not simply random text.
-It has roots in a piece of classical Latin literature from 45 BC, making it over 2000 years old.
-...and the carousel can be used to present something!
+See the source code...
+Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book.
+It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged.+
It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum.
+Contrary to popular belief, Lorem Ipsum is not simply random text.
+It has roots in a piece of classical Latin literature from 45 BC, making it over 2000 years old.
+