Skip to content

Commit

Permalink
Refactor dots
Browse files Browse the repository at this point in the history
  • Loading branch information
matheusps committed Apr 12, 2019
1 parent b9e532e commit 921a3b1
Showing 1 changed file with 109 additions and 131 deletions.
240 changes: 109 additions & 131 deletions react/components/Dots.js
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

0 comments on commit 921a3b1

Please sign in to comment.