Skip to content

Commit

Permalink
SnapSlider: add hideArrows, scrollToOnMount, sliderRef props; reduce …
Browse files Browse the repository at this point in the history
…number of useEffects
  • Loading branch information
ryanwalters committed Jul 9, 2019
1 parent 7263927 commit bbf8cbe
Showing 1 changed file with 58 additions and 42 deletions.
100 changes: 58 additions & 42 deletions src/components/SnapSlider/SnapSlider.js
Original file line number Diff line number Diff line change
@@ -1,16 +1,15 @@
import { faAngleLeft, faAngleRight } from '@fortawesome/pro-light-svg-icons';
import classnames from 'classnames';
import PropTypes from 'prop-types';
import React, { useCallback, useEffect, useRef, useState } from 'react';
import React, { createRef, useCallback, useEffect, useState } from 'react';
import { debounce } from 'lodash-es';
import { polyfill } from 'smoothscroll-polyfill';
import styles from './SnapSlider.module.scss';
import SnapSliderArrow from './SnapSliderArrow/SnapSliderArrow';

polyfill(); // todo: remove this after safari supports ScrollToOptions https://developer.mozilla.org/en-US/docs/Web/API/Window/scrollBy#Browser_Compatibility

const SnapSlider = ({ children, className, scrollTo }) => {
const containerRef = useRef();
const SnapSlider = ({ children, className, hideArrows, sliderRef, scrollTo, scrollToOnMount }) => {
const [scrollAmount, setScrollAmount] = useState(null);
const [trackWidth, setTrackWidth] = useState(null);
const [position, setPosition] = useState(0);
Expand All @@ -19,93 +18,110 @@ const SnapSlider = ({ children, className, scrollTo }) => {
// Previous and next click action callbacks

const previousSlide = useCallback(() => {
containerRef.current.scrollBy({
sliderRef.current.scrollBy({
left: -scrollAmount,
behavior: 'smooth',
});

setPosition(containerRef.current.scrollLeft);
}, [scrollAmount]);
}, [scrollAmount, sliderRef]);

const nextSlide = useCallback(() => {
containerRef.current.scrollBy({
sliderRef.current.scrollBy({
left: scrollAmount,
behavior: 'smooth',
});

setPosition(containerRef.current.scrollLeft);
}, [scrollAmount]);
}, [scrollAmount, sliderRef]);


// After mounting, set the amount to scroll and the width of the track
// On mount

useEffect(() => {
setScrollAmount(containerRef.current.clientWidth);
setTrackWidth(containerRef.current.scrollWidth);
}, [containerRef]);
// Set the amount to scroll and the width of the track

setScrollAmount(sliderRef.current.clientWidth);
setTrackWidth(sliderRef.current.scrollWidth);

// Scroll to designated `scrollTo` location

useEffect(() => {
containerRef.current.scrollTo({
left: scrollTo,
});
}, [scrollTo]);
// Do a one-time scroll if the scrollToOnMount prop was passed

if (scrollToOnMount) {
sliderRef.current.scrollTo({
left: scrollToOnMount,
});
}

// Add event listeners on mount

useEffect(() => {
// --- Event listeners

// When the window resizes, set the amount to scroll and the width of the container

window.addEventListener('resize', debounce(() => {
setScrollAmount(containerRef.current.clientWidth);
setTrackWidth(containerRef.current.scrollWidth);
}, 1000));
setScrollAmount(sliderRef.current.clientWidth);
setTrackWidth(sliderRef.current.scrollWidth);
}, 200));

// After scrolling, as well as the end of a drag, set the current left position of the track

containerRef.current.addEventListener('scroll', debounce(() => {
setPosition(containerRef.current.scrollLeft);
sliderRef.current.addEventListener('scroll', debounce(() => {
setPosition(sliderRef.current.scrollLeft);
}, 200));

window.addEventListener('dragend', () => {
setPosition(containerRef.current.scrollLeft);
setPosition(sliderRef.current.scrollLeft);
});
}, []);


// Scroll to designated `scrollTo` location

useEffect(() => {
if (scrollTo) {
sliderRef.current.scrollTo({
left: scrollTo,
});
}
}, [scrollTo, sliderRef]);

return (
<div className={classnames(styles.SnapSlider, className)}>
<SnapSliderArrow
className={classnames(styles.arrow, styles.previous, 'd-none d-md-block')}
onClick={previousSlide}
disabled={position <= 0}
icon={faAngleLeft}
/>
<div className={styles.track} ref={containerRef}>
{!hideArrows && (
<SnapSliderArrow
className={classnames(styles.arrow, styles.previous, 'd-none d-md-block')}
onClick={previousSlide}
disabled={position <= 0}
icon={faAngleLeft}
/>
)}
<div className={styles.track} ref={sliderRef}>
{typeof children === 'function' ? children({ scrollAmount, trackWidth }) : children}
</div>
<SnapSliderArrow
className={classnames(styles.arrow, styles.next, 'd-none d-md-block')}
onClick={nextSlide}
disabled={position + scrollAmount >= trackWidth}
icon={faAngleRight}
/>
{!hideArrows && (
<SnapSliderArrow
className={classnames(styles.arrow, styles.next, 'd-none d-md-block')}
onClick={nextSlide}
disabled={position + scrollAmount >= trackWidth}
icon={faAngleRight}
/>
)}
</div>
);
};

SnapSlider.propTypes = {
children: PropTypes.oneOfType([PropTypes.node, PropTypes.func]),
className: PropTypes.string,
hideArrows: PropTypes.bool,
scrollTo: PropTypes.number,
scrollToOnMount: PropTypes.number,
sliderRef: PropTypes.shape(),
};

SnapSlider.defaultProps = {
children: null,
className: null,
hideArrows: false,
scrollTo: 0,
scrollToOnMount: null,
sliderRef: createRef(),
};

export default SnapSlider;

0 comments on commit bbf8cbe

Please sign in to comment.