@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.
182 lines (181 loc) • 6.29 kB
JavaScript
"use strict";
"use client";
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault").default;
var _interopRequireWildcard = require("@babel/runtime/helpers/interopRequireWildcard").default;
exports.__esModule = true;
exports.default = void 0;
var React = _interopRequireWildcard(require("react"));
var _clsx = _interopRequireDefault(require("clsx"));
var _Stack = _interopRequireDefault(require("../Stack"));
var _mergeRefs = _interopRequireDefault(require("../utils/mergeRefs"));
var _useTheme = _interopRequireDefault(require("../hooks/useTheme"));
var _useScroll = _interopRequireDefault(require("./useScroll"));
var _ChevronBackward = _interopRequireDefault(require("../icons/ChevronBackward"));
var _ChevronForward = _interopRequireDefault(require("../icons/ChevronForward"));
const getSnap = scrollSnap => {
if (scrollSnap === "mandatory") return "x mandatory";
if (scrollSnap === "proximity") return "x proximity";
return scrollSnap;
};
const ArrowButton = ({
children,
className,
isHidden,
onClick,
ariaLabel
}) => {
return /*#__PURE__*/React.createElement("button", {
className: (0, _clsx.default)("z-default absolute flex h-full items-center", isHidden && "invisible", className),
onClick: onClick,
type: "button",
"aria-label": ariaLabel
}, children);
};
const ElevationHaze = ({
className,
style
}) => {
return /*#__PURE__*/React.createElement("div", {
className: (0, _clsx.default)("z-default absolute top-0 h-full", className),
style: style
});
};
const HorizontalScroll = ({
children,
spacing = "300",
arrows,
arrowColor,
arrowLeftAriaLabel,
arrowRightAriaLabel,
scrollSnap = "none",
onOverflow,
elevationColor = "paletteCloudDark",
overflowElevation,
scrollPadding,
dataTest,
id,
minHeight,
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
} = (0, _useScroll.default)(scrollWrapperRef);
const theme = (0, _useTheme.default)();
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 === 0) {
setReachedStart(true);
} else {
setReachedStart(false);
}
if (scrollLeft >= 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, scrollWrapperRef.current?.scrollWidth]);
return /*#__PURE__*/React.createElement("div", {
className: (0, _clsx.default)("orbit-horizontal-scroll relative inline-flex w-full items-center overflow-hidden", isOverflowing && (isDragging ? "cursor-grabbing" : "cursor-grab")),
"data-test": dataTest,
"data-overflowing": isOverflowing || undefined,
id: id,
ref: (0, _mergeRefs.default)([ref, containerRef]),
style: {
minHeight
}
}, overflowElevation && /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(ElevationHaze, {
className: (0, _clsx.default)("left-0", (!isOverflowing || reachedStart) && "invisible"),
style: {
boxShadow: `5px 0px 20px 20px ${theme.orbit[elevationColor]}`
}
}), /*#__PURE__*/React.createElement(ElevationHaze, {
className: (0, _clsx.default)("right-0", (!isOverflowing || reachedEnd) && "invisible"),
style: {
boxShadow: `-5px 0px 20px 20px ${theme.orbit[elevationColor]}`
}
})), arrows && /*#__PURE__*/React.createElement(ArrowButton, {
className: "left-100",
isHidden: reachedStart || !isOverflowing,
onClick: () => handleClick("left"),
ariaLabel: arrowLeftAriaLabel
}, /*#__PURE__*/React.createElement(_ChevronBackward.default, {
ariaHidden: true,
customColor: arrowColor
})), /*#__PURE__*/React.createElement("div", {
className: "scrollbar-none size-full overflow-x-auto overflow-y-hidden",
ref: scrollWrapperRef,
onScroll: handleScroll,
style: {
scrollPadding,
scrollSnapType: isDragging ? "none" : getSnap(scrollSnap)
}
}, /*#__PURE__*/React.createElement("div", {
className: (0, _clsx.default)("relative inline-flex size-full", isDragging && "pointer-events-none")
// eslint-disable-next-line jsx-a11y/no-noninteractive-tabindex
,
tabIndex: isOverflowing ? 0 : undefined
}, /*#__PURE__*/React.createElement(_Stack.default, {
inline: true,
spacing: spacing
}, children))), arrows && /*#__PURE__*/React.createElement(ArrowButton, {
className: "right-100",
isHidden: reachedEnd || !isOverflowing,
onClick: () => handleClick("right"),
ariaLabel: arrowRightAriaLabel
}, /*#__PURE__*/React.createElement(_ChevronForward.default, {
ariaHidden: true,
customColor: arrowColor
})));
};
var _default = exports.default = HorizontalScroll;