@navinc/base-react-components
Version:
Nav's Pattern Library
151 lines (146 loc) • 6.97 kB
JavaScript
var __rest = (this && this.__rest) || function (s, e) {
var t = {};
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
t[p] = s[p];
if (s != null && typeof Object.getOwnPropertySymbols === "function")
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
t[p[i]] = s[p[i]];
}
return t;
};
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
import { styled } from 'styled-components';
import { forwardRef, useCallback, useRef, useState } from 'react';
import { useDebouncedCallback } from 'use-debounce';
import { clamp } from '@navinc/utils';
import { useMediaQuery } from '../../use-media-query.js';
import useResizeObserver from 'use-resize-observer';
import { IconButton } from '../icon-button/icon-button.js';
import { elevation1 } from '../../themes/elevation.js';
import { mediaQueryFragments } from '../../themes/media.js';
const MoveButton = styled(IconButton).attrs((props) => (Object.assign({ density: 'loose' }, props))).withConfig({ displayName: "brc-sc-MoveButton", componentId: "brc-sc-7uy9dr" }) `
${elevation1}
border-radius: 100%;
background: ${({ theme }) => theme.surfaceContainerHighest};
width: 40px;
height: 40px;
&:focus:not(:disabled),
&:hover:not(:disabled) {
background:
linear-gradient(180deg, rgb(255 255 255 / 0%) 0%, rgb(27 27 31 / 4%) 100%),
${({ theme }) => theme.surfaceContainerHighest};
}
&:active:not(:disabled) {
background:
linear-gradient(180deg, rgb(27 27 31 / 8%) 0%, rgb(27 27 31 / 8%) 100%),
${({ theme }) => theme.surfaceContainerHighest};
box-shadow:
0 0 0 1px rgb(0 0 0 / 30%),
0 4px 1px -2px rgb(0 0 0 / 8%);
}
`;
const ContainerEl = styled.div.withConfig({ displayName: "brc-sc-ContainerEl", componentId: "brc-sc-gfsuqg" }) `
position: relative;
`;
const ControlsEl = styled.div.withConfig({ displayName: "brc-sc-ControlsEl", componentId: "brc-sc-16gr4do" }) `
position: absolute;
inset: 0;
display: flex;
flex-direction: row;
align-items: stretch;
justify-content: space-between;
pointer-events: none;
`;
const ContentEl = styled.div.withConfig({ displayName: "brc-sc-ContentEl", componentId: "brc-sc-ft7ann" }) `
overflow: scroll;
-webkit-scrollbar-width: none;
/* stylelint-disable-next-line plugin/no-unsupported-browser-features -- No support on iOS Safari, also using ::-webkit-scrollbar selector below */
scrollbar-width: none; /* Firefox */
-ms-overflow-style: none; /* Internet Explorer 10+ */
&::-webkit-scrollbar {
/* WebKit */
width: 0;
height: 0;
}
`;
const FadeBase = styled.div.withConfig({ displayName: "brc-sc-FadeBase", componentId: "brc-sc-1qgazle" }) `
display: flex;
align-items: center;
pointer-events: none;
width: 75px;
height: 100%;
transition: opacity 0.3s;
opacity: 0;
${MoveButton} {
pointer-events: none;
}
${({ show }) => show &&
`
opacity: 1;
${MoveButton} {
pointer-events: all;
}
`}
`;
const FadeLeft = styled(FadeBase).withConfig({ displayName: "brc-sc-FadeLeft", componentId: "brc-sc-1slbk2n" }) `
justify-content: flex-start;
background: linear-gradient(to right, ${({ theme }) => theme.background} 0%, transparent 100%);
`;
const FadeRight = styled(FadeBase).withConfig({ displayName: "brc-sc-FadeRight", componentId: "brc-sc-1fusqq1" }) `
justify-content: flex-end;
background: linear-gradient(to left, ${({ theme }) => theme.background} 0%, transparent 100%);
`;
const MaxContent = styled.div.withConfig({ displayName: "brc-sc-MaxContent", componentId: "brc-sc-w8lngf" }) `
width: max-content;
`;
const Content = (_a) => {
var { childRef, children, onResize } = _a, props = __rest(_a, ["childRef", "children", "onResize"]);
useResizeObserver({ ref: childRef, onResize });
return (_jsx(ContentEl, Object.assign({ ref: childRef }, props, { children: _jsx(MaxContent, { children: children }) })));
};
const Controls = ({ showLeft, showRight, onLeftClick, onRightClick, }) => {
return (_jsxs(ControlsEl, { children: [_jsx(FadeLeft, { show: showLeft, children: _jsx(MoveButton, { tabIndex: showLeft ? undefined : -1, name: "chevron_left", onClick: onLeftClick, children: "Left" }) }), _jsx(FadeRight, { show: showRight, children: _jsx(MoveButton, { tabIndex: showRight ? undefined : -1, name: "chevron_right", onClick: onRightClick, children: "Right" }) })] }));
};
export const HorizontalScrollWidget = styled(forwardRef((_a, forwardedRef) => {
var { children } = _a, props = __rest(_a, ["children"]);
const [state, setState] = useState({
showLeft: false,
showRight: false,
scrollPosition: 0,
});
const ref = useRef(null);
const scrollingTo = useRef(null);
const recalculate = useDebouncedCallback(useCallback(() => {
if (ref.current) {
const shouldShowLeft = ref.current.scrollLeft > 0;
const shouldShowRight = ref.current.scrollLeft < ref.current.scrollWidth - ref.current.clientWidth;
if (state.showLeft !== shouldShowLeft) {
setState((state) => (Object.assign(Object.assign({}, state), { showLeft: shouldShowLeft })));
}
if (state.showRight !== shouldShowRight) {
setState((state) => (Object.assign(Object.assign({}, state), { showRight: shouldShowRight })));
}
}
}, [state, ref]), 50, { maxWait: 100 });
const clearScrollingTo = useDebouncedCallback(() => {
scrollingTo.current = null;
}, 50);
const isLargerThanPhone = useMediaQuery(mediaQueryFragments.largerThanPhone);
const scroll = (direction) => {
var _a;
if (ref.current) {
let scrollLeft = (_a = scrollingTo.current) !== null && _a !== void 0 ? _a : ref.current.scrollLeft;
const pageWidth = ref.current.clientWidth * 0.75;
scrollLeft += direction === 'left' ? -pageWidth : pageWidth;
scrollLeft = clamp(0, ref.current.scrollWidth - ref.current.clientWidth)(scrollLeft);
ref.current.scrollTo({ left: scrollLeft, behavior: 'smooth' });
scrollingTo.current = scrollLeft;
}
};
return (_jsxs(ContainerEl, Object.assign({ ref: forwardedRef }, props, { children: [_jsx(Content, { childRef: ref, onResize: recalculate, onScroll: () => {
clearScrollingTo();
recalculate();
}, "data-testid": "horizontal-scroll-widget:content", children: children }), isLargerThanPhone && (_jsx(Controls, { showLeft: state.showLeft, showRight: state.showRight, onLeftClick: () => scroll('left'), onRightClick: () => scroll('right') }))] })));
})).withConfig({ displayName: "brc-sc-HorizontalScrollWidget", componentId: "brc-sc-1o8u15f" }) ``;
//# sourceMappingURL=horizontal-scroll-widget.js.map