UNPKG

@kiwicom/orbit-components

Version:

Orbit-components is a React component library which provides developers with the easiest possible way of building Kiwi.com’s products.

176 lines 6.58 kB
import _extends from "@babel/runtime/helpers/esm/extends"; import * as React from "react"; import styled, { css } from "styled-components"; import Stack from "../Stack"; import mergeRefs from "../utils/mergeRefs"; import defaultTheme from "../defaultTheme"; import useTheme from "../hooks/useTheme"; import useScrollBox from "./useScroll"; import ChevronBackward from "../icons/ChevronBackward"; import ChevronForward from "../icons/ChevronForward"; const TRIGGER_OFFSET = 20; const shadowMixin = css(["content:\"\";position:absolute;top:0;z-index:1;height:100%;"]); const StyledButton = styled.button.withConfig({ displayName: "HorizontalScroll__StyledButton", componentId: "sc-c9mal1-0" })(["", ""], ({ isHidden }) => css(["display:flex;cursor:pointer;align-items:center;background:transparent;border:0;z-index:10;height:100%;visibility:", ";"], isHidden ? "hidden" : "visible")); const StyledWrapper = styled.div.withConfig({ displayName: "HorizontalScroll__StyledWrapper", componentId: "sc-c9mal1-1" })(["", ";"], ({ isDragging, $minHeight, elevationColor, overflowElevation, isStart, theme, isOverflowing, isEnd }) => css(["position:relative;width:100%;display:inline-flex;align-items:center;min-height:", ";cursor:", ";overflow:hidden;", " ", " ", "{position:absolute;&:first-child{left:", ";}&:nth-child(2){right:", ";}}"], $minHeight && `${$minHeight}px`, isOverflowing && (isDragging ? "grabbing" : "grab"), isOverflowing && overflowElevation && !isStart && css(["&:before{", ";left:0;box-shadow:5px 0px 20px 20px ", ";}"], shadowMixin, elevationColor), isOverflowing && !isEnd && overflowElevation && css(["&:after{", ";right:0;box-shadow:-5px 0px 20px 20px ", ";}"], shadowMixin, elevationColor), StyledButton, theme.orbit.spaceXXSmall, theme.orbit.spaceXXSmall)); StyledWrapper.defaultProps = { theme: defaultTheme }; const getSnap = ({ $scrollSnap }) => { if ($scrollSnap === "mandatory") return "x mandatory"; if ($scrollSnap === "proximity") return "x proximity"; return $scrollSnap; }; const StyledOverflow = styled.div.withConfig({ displayName: "HorizontalScroll__StyledOverflow", componentId: "sc-c9mal1-2" })(["", ";"], ({ isDragging, scrollPadding }) => css(["width:100%;height:100%;overflow-y:hidden;overflow-x:auto;scroll-snap-type:", ";scroll-padding:", ";box-sizing:border-box;-ms-overflow-style:none;overflow:-moz-scrollbars-none;scrollbar-width:none;::-webkit-scrollbar{display:none;}"], isDragging ? "none" : getSnap, scrollPadding && `${scrollPadding}px`)); const StyledContainer = styled.div.withConfig({ displayName: "HorizontalScroll__StyledContainer", componentId: "sc-c9mal1-3" })(["", ";"], ({ isDragging }) => css(["height:100%;position:relative;width:100%;display:inline-flex;pointer-events:", ";"], isDragging && "none")); const HorizontalScroll = /*#__PURE__*/React.forwardRef(({ children, spacing = "small", arrows, scrollSnap = "none", onOverflow, elevationColor = "paletteCloudDark", arrowColor, overflowElevation, scrollPadding, dataTest, id, minHeight, ...props }, ref) => { const scrollWrapperRef = React.useRef(null); const [isOverflowing, setOverflowing] = React.useState(false); const [reachedStart, setReachedStart] = React.useState(true); const [reachedEnd, setReachedEnd] = React.useState(false); const containerRef = React.useRef(null); const { isDragging } = useScrollBox(scrollWrapperRef); const theme = useTheme(); const scrollEl = scrollWrapperRef.current; const handleOverflow = React.useCallback(() => { if (scrollWrapperRef.current?.scrollWidth && containerRef.current?.offsetWidth) { const { scrollWidth: containerScrollWidth } = scrollWrapperRef.current; const { offsetWidth } = containerRef.current; if (containerScrollWidth > offsetWidth) { setOverflowing(true); if (onOverflow) onOverflow(); } else { setOverflowing(false); } } }, [onOverflow]); const handleClick = direction => { if (scrollEl) { const { scrollLeft, offsetWidth } = scrollEl; const scrollAmount = scrollLeft + (direction === "left" ? -offsetWidth / 2 : offsetWidth / 2); scrollEl.scrollTo({ left: scrollAmount, behavior: "smooth" }); } }; const handleScroll = React.useCallback(() => { if (scrollEl) { const scrollWidth = scrollEl.scrollWidth - scrollEl.clientWidth; const { scrollLeft } = scrollEl; if (scrollLeft - TRIGGER_OFFSET <= 0) { setReachedStart(true); } else { setReachedStart(false); } if (scrollLeft + TRIGGER_OFFSET >= scrollWidth) { setReachedEnd(true); } else { setReachedEnd(false); } } }, [scrollEl]); const handleResize = React.useCallback(() => { handleOverflow(); handleScroll(); }, [handleOverflow, handleScroll]); React.useEffect(() => { handleOverflow(); window.addEventListener("resize", handleResize); return () => { window.removeEventListener("resize", handleResize); }; }, [handleOverflow, handleResize]); return /*#__PURE__*/React.createElement(StyledWrapper, _extends({}, props, { $minHeight: minHeight, overflowElevation: overflowElevation, "data-test": dataTest, id: id, isDragging: isDragging, isEnd: reachedEnd, isStart: reachedStart, isOverflowing: isOverflowing, ref: mergeRefs([ref, containerRef]), elevationColor: theme.orbit[elevationColor] }), arrows && /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(StyledButton, { tabIndex: 0, type: "button", isHidden: reachedStart || !isOverflowing, onClick: () => handleClick("left") }, /*#__PURE__*/React.createElement(ChevronBackward, { customColor: arrowColor })), /*#__PURE__*/React.createElement(StyledButton, { tabIndex: 0, type: "button", isHidden: reachedEnd || !isOverflowing, onClick: () => handleClick("right") }, /*#__PURE__*/React.createElement(ChevronForward, { customColor: arrowColor }))), /*#__PURE__*/React.createElement(StyledOverflow, { $scrollSnap: scrollSnap, scrollPadding: scrollPadding, isDragging: isDragging, onScroll: handleScroll, ref: scrollWrapperRef }, /*#__PURE__*/React.createElement(StyledContainer, { isDragging: isDragging }, /*#__PURE__*/React.createElement(Stack, { inline: true, spacing: spacing }, children)))); }); export default HorizontalScroll;