frontend/components/Banner/index.tsx (view raw)
1import {Icon} from '@material-ui/core';
2import Button from '@material-ui/core/Button';
3import {makeStyles} from '@material-ui/core/styles';
4import {useEffect} from 'react';
5import {useElementSize, useEventListener} from 'usehooks-ts';
6import Markdown from '../Markdown';
7import useBannerStore from '../../stores/useBannerStore';
8
9interface Props {
10 message: string;
11 open: boolean;
12 onClear?: () => void;
13}
14
15const Banner = (props: Props) => {
16 const {message, open, onClear} = props;
17 const classes = useStyles();
18 const [bannerRef, {height}] = useElementSize();
19 const setBannerHeight = useBannerStore(s => s.setBannerHeight);
20 const setBannerOffset = useBannerStore(s => s.setBannerOffset);
21 useEffect(() => setBannerHeight({height}), [height]);
22
23 if (typeof document != 'undefined') {
24 useEventListener('scroll', () => {
25 const y = window.scrollY;
26 if (y > height) {
27 setBannerOffset({offset: 0})
28 }
29 if (y <= height) {
30 setBannerOffset({offset: height - y});
31 }
32 })
33 }
34
35 if (!open) return null;
36
37 return (
38 <div className={classes.banner} ref={bannerRef}>
39 <Markdown className={classes.htmlReset}>{message}</Markdown>
40 <Button
41 className={classes.clear}
42 onClick={e => {
43 e.stopPropagation();
44 onClear();
45 }}
46 >
47 <Icon>close</Icon>
48 </Button>
49 </div>
50 );
51};
52
53const useStyles = makeStyles(theme => ({
54 banner: {
55 position: 'relative',
56 background: `linear-gradient(90deg, ${theme.palette.secondary.main} 20%, ${theme.palette.primary.main} 90%)`,
57 width: '100%',
58 padding: '12px 60px',
59 textAlign: 'center',
60 zIndex: theme.zIndex.appBar - 1,
61 },
62 clear: {
63 position: 'absolute',
64 right: '12px',
65 bottom: '0',
66 minWidth: '44px',
67 padding: '12px',
68 lineHeight: '1.4em',
69 },
70 htmlReset: {
71 '& a': {
72 color: 'inherit',
73 margin: 0,
74 },
75 '& p': {
76 margin: 0,
77 },
78 },
79}));
80
81export default Banner;