2017-06-26 00:21:33 +00:00
|
|
|
// @flow
|
2020-06-20 20:59:15 +00:00
|
|
|
import { observer } from "mobx-react";
|
2020-08-09 05:53:59 +00:00
|
|
|
import * as React from "react";
|
2020-06-20 20:59:15 +00:00
|
|
|
import styled from "styled-components";
|
2021-01-21 15:22:20 +00:00
|
|
|
import useWindowSize from "hooks/useWindowSize";
|
2017-06-26 00:21:33 +00:00
|
|
|
|
2021-01-21 15:22:20 +00:00
|
|
|
type Props = {|
|
2018-05-06 19:46:52 +00:00
|
|
|
shadow?: boolean,
|
2021-01-21 15:22:20 +00:00
|
|
|
topShadow?: boolean,
|
|
|
|
bottomShadow?: boolean,
|
2021-02-26 18:51:14 +00:00
|
|
|
flex?: boolean,
|
2021-01-21 15:22:20 +00:00
|
|
|
|};
|
2018-05-06 19:46:52 +00:00
|
|
|
|
2021-02-26 18:51:14 +00:00
|
|
|
function Scrollable({ shadow, topShadow, bottomShadow, flex, ...rest }: Props) {
|
2021-01-21 15:22:20 +00:00
|
|
|
const ref = React.useRef<?HTMLDivElement>();
|
|
|
|
const [topShadowVisible, setTopShadow] = React.useState(false);
|
|
|
|
const [bottomShadowVisible, setBottomShadow] = React.useState(false);
|
|
|
|
const { height } = useWindowSize();
|
2018-05-06 19:46:52 +00:00
|
|
|
|
2021-01-21 15:22:20 +00:00
|
|
|
const updateShadows = React.useCallback(() => {
|
|
|
|
const c = ref.current;
|
|
|
|
if (!c) return;
|
2018-05-06 19:46:52 +00:00
|
|
|
|
2021-01-21 15:22:20 +00:00
|
|
|
const scrollTop = c.scrollTop;
|
|
|
|
const tsv = !!((shadow || topShadow) && scrollTop > 0);
|
|
|
|
if (tsv !== topShadowVisible) {
|
|
|
|
setTopShadow(tsv);
|
|
|
|
}
|
2018-05-06 19:46:52 +00:00
|
|
|
|
2021-01-21 15:22:20 +00:00
|
|
|
const wrapperHeight = c.scrollHeight - c.clientHeight;
|
|
|
|
const bsv = !!((shadow || bottomShadow) && wrapperHeight - scrollTop !== 0);
|
|
|
|
|
|
|
|
if (bsv !== bottomShadowVisible) {
|
|
|
|
setBottomShadow(bsv);
|
|
|
|
}
|
|
|
|
}, [shadow, topShadow, bottomShadow, topShadowVisible, bottomShadowVisible]);
|
|
|
|
|
|
|
|
React.useEffect(() => {
|
|
|
|
updateShadows();
|
|
|
|
}, [height, updateShadows]);
|
|
|
|
|
|
|
|
return (
|
|
|
|
<Wrapper
|
|
|
|
ref={ref}
|
|
|
|
onScroll={updateShadows}
|
2021-02-26 18:51:14 +00:00
|
|
|
$flex={flex}
|
2021-01-21 15:22:20 +00:00
|
|
|
$topShadowVisible={topShadowVisible}
|
|
|
|
$bottomShadowVisible={bottomShadowVisible}
|
|
|
|
{...rest}
|
|
|
|
/>
|
|
|
|
);
|
2018-05-06 19:46:52 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
const Wrapper = styled.div`
|
2021-02-26 18:51:14 +00:00
|
|
|
display: ${(props) => (props.$flex ? "flex" : "block")};
|
|
|
|
flex-direction: column;
|
2017-06-26 00:21:33 +00:00
|
|
|
height: 100%;
|
|
|
|
overflow-y: auto;
|
|
|
|
overflow-x: hidden;
|
2018-03-10 19:26:29 +00:00
|
|
|
overscroll-behavior: none;
|
2017-06-26 00:21:33 +00:00
|
|
|
-webkit-overflow-scrolling: touch;
|
2021-01-21 15:22:20 +00:00
|
|
|
box-shadow: ${(props) => {
|
|
|
|
if (props.$topShadowVisible && props.$bottomShadowVisible) {
|
|
|
|
return "0 1px inset rgba(0,0,0,.1), 0 -1px inset rgba(0,0,0,.1)";
|
|
|
|
}
|
|
|
|
if (props.$topShadowVisible) {
|
|
|
|
return "0 1px inset rgba(0,0,0,.1)";
|
|
|
|
}
|
|
|
|
if (props.$bottomShadowVisible) {
|
|
|
|
return "0 -1px inset rgba(0,0,0,.1)";
|
|
|
|
}
|
|
|
|
|
|
|
|
return "none";
|
|
|
|
}};
|
|
|
|
transition: all 100ms ease-in-out;
|
2017-06-26 00:21:33 +00:00
|
|
|
`;
|
|
|
|
|
2021-01-21 15:22:20 +00:00
|
|
|
export default observer(Scrollable);
|