-
Notifications
You must be signed in to change notification settings - Fork 7
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
1 changed file
with
109 additions
and
131 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,159 +1,137 @@ | ||
import React, { PureComponent } from 'react' | ||
import React, { useEffect, useState } from 'react' | ||
import PropTypes from 'prop-types' | ||
import classnames from 'classnames' | ||
import debounce from 'debounce' | ||
import EventListener from 'react-event-listener' | ||
|
||
import { constants } from '../utils' | ||
import resolveSlidesNumber from '../utils/resolveSlidesNumber' | ||
import styles from './styles.css' | ||
|
||
class Dots extends PureComponent { | ||
static propTypes = { | ||
/** Classes to style the elements fo the component */ | ||
classes: PropTypes.shape({ | ||
root: PropTypes.string, | ||
dot: PropTypes.string, | ||
activeDot: PropTypes.string, | ||
notActiveDot: PropTypes.string, | ||
}), | ||
/** Extra props to be applied to the dot element */ | ||
dotProps: PropTypes.object, | ||
/** The size of the dots, can be a number (in this case it will use px unit), or a string (you have to pass the number with the unit e.g '3rem') */ | ||
dotSize: PropTypes.oneOfType([PropTypes.number, PropTypes.string]), | ||
/** Tag to be rendered in the dot element */ | ||
dotTag: PropTypes.string, | ||
/** If the slides should be looping */ | ||
loop: PropTypes.bool, | ||
/** Function to change the currentSlide */ | ||
onChangeSlide: PropTypes.func.isRequired, | ||
/** This prop works the same way the perPage of Slider and this component should receive the same value of Slider */ | ||
perPage: PropTypes.oneOfType([PropTypes.number, PropTypes.object]), | ||
/** Debounce time in milliseconds. */ | ||
resizeDebounce: PropTypes.number, | ||
/** Tag of root element of the component to be rendered */ | ||
rootTag: PropTypes.string, | ||
/** Total value of sliders that will be rendered */ | ||
totalSlides: PropTypes.number.isRequired, | ||
/** If this flag is true, then every dot represent a page of slides (e.g. if perPage = 2 and you have 4 elements, | ||
* then you have 2 dots), if false, then it will render a dot to every slide */ | ||
showDotsPerPage: PropTypes.bool, | ||
} | ||
|
||
static defaultProps = { | ||
classes: { | ||
root: '', | ||
dot: '', | ||
activeDot: '', | ||
notActiveDot: '', | ||
}, | ||
dotTag: 'li', | ||
loop: false, | ||
perPage: 1, | ||
rootTag: 'ul', | ||
showDotsPerPage: false, | ||
resizeDebounce: constants.defaultResizeDebounce, | ||
} | ||
|
||
constructor(props) { | ||
super(props) | ||
const Dots = ({ | ||
rootTag: RootTag, | ||
dotTag: DotTag, | ||
classes: classesProp, | ||
dotProps, | ||
showDotsPerPage, | ||
perPage, | ||
currentSlide, | ||
onChangeSlide, | ||
totalSlides, | ||
dotSize, | ||
resizeDebounce, | ||
loop, | ||
treePath, | ||
...otherProps | ||
}) => { | ||
const [state, setState] = useState(0) | ||
|
||
this.handleResize = debounce(this._setPerPageOnResize, props.resizeDebounce) | ||
} | ||
useEffect(() => { | ||
const handleResize = debounce( | ||
setState(resolveSlidesNumber(perPage)), | ||
resizeDebounce | ||
) | ||
window.addEventListener('resize', handleResize) | ||
return window.removeEventListener('resize', handleResize) | ||
}, []) | ||
|
||
get slideIndeces() { | ||
const { showDotsPerPage, totalSlides } = this.props | ||
return this.perPage | ||
const getSlideIndeces = () => | ||
state | ||
? [ | ||
...Array( | ||
showDotsPerPage | ||
? Math.ceil(totalSlides / this.perPage) | ||
: totalSlides | ||
showDotsPerPage ? Math.ceil(totalSlides / state) : totalSlides | ||
).keys(), | ||
] | ||
: [] | ||
} | ||
|
||
get selectedDot() { | ||
const { showDotsPerPage, currentSlide, loop } = this.props | ||
const realCurrentSlide = loop ? currentSlide - this.perPage : currentSlide | ||
const getSelectedDot = () => { | ||
const realCurrentSlide = loop ? currentSlide - state : currentSlide | ||
return showDotsPerPage | ||
? Math.floor(realCurrentSlide / this.perPage) | ||
? Math.floor(realCurrentSlide / state) | ||
: realCurrentSlide | ||
} | ||
|
||
handleDotClick = index => { | ||
const { showDotsPerPage, onChangeSlide, loop } = this.props | ||
const slideToGo = showDotsPerPage ? index * this.perPage : index | ||
onChangeSlide(loop ? slideToGo + this.perPage : slideToGo) | ||
const handleDotClick = index => { | ||
const slideToGo = showDotsPerPage ? index * state : index | ||
onChangeSlide(loop ? slideToGo + state : slideToGo) | ||
} | ||
|
||
componentWillUnmount() { | ||
this.handleResize.clear() | ||
const classes = { | ||
...Dots.defaultProps.classes, | ||
...classesProp, | ||
} | ||
|
||
_setPerPageOnResize = () => { | ||
const { perPage } = this.props | ||
this.perPage = resolveSlidesNumber(perPage) | ||
this.forceUpdate() | ||
} | ||
|
||
render() { | ||
const { | ||
rootTag: RootTag, | ||
dotTag: DotTag, | ||
classes: classesProp, | ||
dotProps, | ||
showDotsPerPage, | ||
perPage, | ||
currentSlide, | ||
onChangeSlide, | ||
totalSlides, | ||
dotSize, | ||
resizeDebounce, | ||
...otherProps | ||
} = this.props | ||
|
||
const classes = { | ||
...Dots.defaultProps.classes, | ||
...classesProp, | ||
} | ||
|
||
if (totalSlides < 2) { | ||
return null | ||
} | ||
return totalSlides >= 2 ? ( | ||
<RootTag | ||
className={classnames( | ||
styles.dotsContainer, | ||
classes.root, | ||
'absolute ma0 pa0 dib list' | ||
)} | ||
{...otherProps} | ||
> | ||
{getSlideIndeces().map(i => { | ||
const dotClasses = classnames(classes.dot, 'dib', { | ||
[classes.activeDot]: i === getSelectedDot(), | ||
[classes.notActiveDot]: i !== getSelectedDot(), | ||
}) | ||
return ( | ||
<DotTag | ||
className={dotClasses} | ||
key={i} | ||
onClick={() => handleDotClick(i)} | ||
{...{ style: { height: dotSize, width: dotSize } }} | ||
{...dotProps} | ||
/> | ||
) | ||
})} | ||
</RootTag> | ||
) : null | ||
} | ||
|
||
if (!this.perPage) { | ||
this.perPage = resolveSlidesNumber(perPage) | ||
} | ||
Dots.propTypes = { | ||
/** Classes to style the elements fo the component */ | ||
classes: PropTypes.shape({ | ||
root: PropTypes.string, | ||
dot: PropTypes.string, | ||
activeDot: PropTypes.string, | ||
notActiveDot: PropTypes.string, | ||
}), | ||
/** Extra props to be applied to the dot element */ | ||
dotProps: PropTypes.object, | ||
/** The size of the dots, can be a number (in this case it will use px unit), or a string (you have to pass the number with the unit e.g '3rem') */ | ||
dotSize: PropTypes.oneOfType([PropTypes.number, PropTypes.string]), | ||
/** Tag to be rendered in the dot element */ | ||
dotTag: PropTypes.string, | ||
/** If the slides should be looping */ | ||
loop: PropTypes.bool, | ||
/** Function to change the currentSlide */ | ||
onChangeSlide: PropTypes.func.isRequired, | ||
/** This prop works the same way the perPage of Slider and this component should receive the same value of Slider */ | ||
perPage: PropTypes.oneOfType([PropTypes.number, PropTypes.object]), | ||
/** Debounce time in milliseconds. */ | ||
resizeDebounce: PropTypes.number, | ||
/** Tag of root element of the component to be rendered */ | ||
rootTag: PropTypes.string, | ||
/** Total value of sliders that will be rendered */ | ||
totalSlides: PropTypes.number.isRequired, | ||
/** If this flag is true, then every dot represent a page of slides (e.g. if perPage = 2 and you have 4 elements, | ||
* then you have 2 dots), if false, then it will render a dot to every slide */ | ||
showDotsPerPage: PropTypes.bool, | ||
} | ||
|
||
return ( | ||
<RootTag | ||
className={classnames( | ||
styles.dotsContainer, | ||
classes.root, | ||
'absolute ma0 pa0 dib list' | ||
)} | ||
{...otherProps} | ||
> | ||
<EventListener target="window" onResize={this.handleResize} /> | ||
{this.slideIndeces.map(i => { | ||
const dotClasses = classnames(classes.dot, 'dib', { | ||
[classes.activeDot]: i === this.selectedDot, | ||
[classes.notActiveDot]: i !== this.selectedDot, | ||
}) | ||
return ( | ||
<DotTag | ||
className={dotClasses} | ||
key={i} | ||
onClick={() => this.handleDotClick(i)} | ||
{...{ style: { height: dotSize, width: dotSize } }} | ||
{...dotProps} | ||
/> | ||
) | ||
})} | ||
</RootTag> | ||
) | ||
} | ||
Dots.defaultProps = { | ||
classes: { | ||
root: '', | ||
dot: '', | ||
activeDot: '', | ||
notActiveDot: '', | ||
}, | ||
dotTag: 'li', | ||
loop: false, | ||
perPage: 1, | ||
rootTag: 'ul', | ||
showDotsPerPage: false, | ||
resizeDebounce: constants.defaultResizeDebounce, | ||
} | ||
|
||
export default Dots |