This repository has been archived on 2022-10-07. You can view files and clone it, but cannot push or open issues or pull requests.
skynet-webportal/packages/dashboard-v2/src/components/Slider/Slider.js

117 lines
3.2 KiB
JavaScript
Raw Normal View History

import * as React from "react";
import PropTypes from "prop-types";
import styled, { css } from "styled-components";
import theme from "../../lib/theme";
import useActiveBreakpoint from "./useActiveBreakpoint";
import Bullets from "./Bullets";
import Slide from "./Slide";
const Container = styled.div.attrs({
className: "slider w-full",
})``;
/**
* Styles applied to the movable element when the number of slide elements
* exceeds the number of visible slides for the current breakpoint
* */
const scrollableStyles = css`
${({ $allSlides, $visibleSlides, $activeIndex }) => `
transform: translateX(calc(-1 * ${$activeIndex} * ((100% + 1rem) / ${$visibleSlides})));
grid-template-columns: repeat(${$allSlides}, calc((100% - ${$visibleSlides - 1}rem) / ${$visibleSlides}));
`}
`;
const Scroller = styled.div.attrs({
className: "slider-scroller grid gap-4 transition-transform",
})`
${({ $scrollable }) => ($scrollable ? scrollableStyles : "")}
`;
const Slider = ({ slides, breakpoints }) => {
const { visibleSlides, scrollable } = useActiveBreakpoint(breakpoints);
const [activeIndex, setActiveIndex] = React.useState(0);
const changeSlide = React.useCallback(
(index) => {
setActiveIndex(Math.min(index, slides.length - visibleSlides)); // Don't let it scroll too far
},
[slides, visibleSlides, setActiveIndex]
);
React.useEffect(() => {
const maxIndex = slides.length - visibleSlides;
// Make sure to not scroll too far when screen size changes.
if (activeIndex > maxIndex) {
setActiveIndex(maxIndex);
}
}, [slides.length, visibleSlides, activeIndex]);
return (
<Container>
<Scroller
$visibleSlides={visibleSlides}
$allSlides={slides.length}
$activeIndex={activeIndex}
$scrollable={scrollable}
>
{slides.map((slide, i) => {
const isVisible = i >= activeIndex && i < activeIndex + visibleSlides;
return (
<div key={`slide-${i}`}>
<Slide
isVisible={isVisible || !scrollable}
onClick={scrollable && !isVisible ? () => changeSlide(i) : null}
>
{slide}
</Slide>
</div>
);
})}
</Scroller>
{scrollable && (
<Bullets
activeIndex={activeIndex}
allSlides={slides.length}
visibleSlides={visibleSlides}
changeSlide={changeSlide}
/>
)}
</Container>
);
};
Slider.propTypes = {
slides: PropTypes.arrayOf(PropTypes.node.isRequired),
breakpoints: PropTypes.arrayOf(
PropTypes.shape({
minWidth: PropTypes.number.isRequired,
visibleSlides: PropTypes.number.isRequired,
})
),
};
Slider.defaultProps = {
breakpoints: [
{
minWidth: parseInt(theme.screens.xl),
scrollable: true,
visibleSlides: 3,
},
{
minWidth: parseInt(theme.screens.md, 10),
scrollable: true,
visibleSlides: 2,
},
{
// For the smallest screens, we won't scroll but instead stack the slides vertically.
minWidth: -Infinity,
scrollable: false,
visibleSlides: 1,
},
],
};
export default Slider;