@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
JavaScript
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;