/ src / modules / history / HistoryFilterMenu.tsx
HistoryFilterMenu.tsx
  1  import { XCircleIcon } from '@heroicons/react/solid';
  2  import { Trans } from '@lingui/macro';
  3  import { Check as CheckIcon, Sort as SortIcon } from '@mui/icons-material';
  4  import {
  5    Box,
  6    Button,
  7    Divider,
  8    Menu,
  9    MenuItem,
 10    SvgIcon,
 11    Typography,
 12    useMediaQuery,
 13    useTheme,
 14  } from '@mui/material';
 15  import React, { useEffect, useState } from 'react';
 16  import { DarkTooltip } from 'src/components/infoTooltips/DarkTooltip';
 17  import { useRootStore } from 'src/store/root';
 18  import { TRANSACTION_HISTORY } from 'src/utils/mixPanelEvents';
 19  
 20  import { FilterOptions } from './types';
 21  
 22  interface HistoryFilterMenuProps {
 23    onFilterChange: (filter: FilterOptions[]) => void;
 24    currentFilter: FilterOptions[];
 25  }
 26  
 27  interface FilterLabelProps {
 28    filter: FilterOptions;
 29  }
 30  
 31  const FilterLabel: React.FC<FilterLabelProps> = ({ filter }) => {
 32    switch (filter) {
 33      case FilterOptions.SUPPLY:
 34        return <Trans>Supply</Trans>;
 35      case FilterOptions.BORROW:
 36        return <Trans>Borrow</Trans>;
 37      case FilterOptions.WITHDRAW:
 38        return <Trans>Withdraw</Trans>;
 39      case FilterOptions.REPAY:
 40        return <Trans>Repay</Trans>;
 41      case FilterOptions.RATECHANGE:
 42        return <Trans>Rate change</Trans>;
 43      case FilterOptions.COLLATERALCHANGE:
 44        return <Trans>Collateral change</Trans>;
 45      case FilterOptions.LIQUIDATION:
 46        return <Trans>Liquidation</Trans>;
 47    }
 48  };
 49  
 50  export const HistoryFilterMenu: React.FC<HistoryFilterMenuProps> = ({
 51    onFilterChange,
 52    currentFilter,
 53  }) => {
 54    const [anchorEl, setAnchorEl] = useState<null | HTMLElement>(null);
 55    const [localFilter, setLocalFilter] = useState<FilterOptions[]>(currentFilter);
 56    const trackEvent = useRootStore((store) => store.trackEvent);
 57  
 58    useEffect(() => {
 59      onFilterChange(localFilter);
 60    }, [localFilter, onFilterChange]);
 61  
 62    const theme = useTheme();
 63    const downToMD = useMediaQuery(theme.breakpoints.down('md'));
 64  
 65    const allSelected = currentFilter.length === 0;
 66  
 67    const handleClick = (event: React.MouseEvent<HTMLButtonElement>) => {
 68      setAnchorEl(event.currentTarget);
 69    };
 70  
 71    const handleClose = () => {
 72      setAnchorEl(null);
 73      onFilterChange(currentFilter);
 74    };
 75  
 76    const handleFilterClick = (filter: FilterOptions | undefined) => {
 77      let newFilter: FilterOptions[] = [];
 78      if (filter !== undefined) {
 79        if (currentFilter.includes(filter)) {
 80          newFilter = currentFilter.filter((item) => item !== filter);
 81        } else {
 82          trackEvent(TRANSACTION_HISTORY.FILTER, { value: filter });
 83          newFilter = [...currentFilter, filter];
 84          // Checks if all filter options are selected,  enum length is divided by 2 based on how Typescript creates object from enum
 85          if (newFilter.length === Object.keys(FilterOptions).length / 2) {
 86            newFilter = [];
 87          }
 88        }
 89      }
 90  
 91      setLocalFilter(newFilter);
 92    };
 93  
 94    const FilterButtonLabel = () => {
 95      if (allSelected) {
 96        return <Trans>All transactions</Trans>;
 97      } else {
 98        const displayLimit = 2;
 99        const hiddenCount = currentFilter.length - displayLimit;
100        const displayedFilters = currentFilter.slice(0, displayLimit).map((filter) => (
101          <React.Fragment key={filter}>
102            <FilterLabel filter={filter} />
103            {filter !== currentFilter[currentFilter.length - 1] && ','}
104            {filter !== currentFilter[displayLimit - 1] && ' '}
105          </React.Fragment>
106        ));
107  
108        return (
109          <Box sx={{ display: 'flex' }}>
110            <Typography variant="description" color={theme.palette.primary.main} sx={{ mr: 1 }}>
111              TXs:
112            </Typography>
113            {displayedFilters}
114            {hiddenCount > 0 && <React.Fragment>...(+{hiddenCount})</React.Fragment>}
115          </Box>
116        );
117      }
118    };
119  
120    const handleClearFilter = (event: React.MouseEvent) => {
121      trackEvent(TRANSACTION_HISTORY.FILTER, { value: 'cleared' });
122      event.stopPropagation();
123      setLocalFilter([]);
124    };
125  
126    return (
127      <Box>
128        <Button
129          sx={{
130            minWidth: 148,
131            maxWidth: downToMD ? '100%' : 360,
132            display: 'flex',
133            justifyContent: 'space-between',
134            alignItems: 'center',
135            height: 36,
136            border: '1px solid',
137            borderColor: 'divider',
138            borderRadius: '4px',
139            mr: downToMD ? 0 : 2,
140            ml: downToMD ? 4 : 0,
141            pl: 2,
142            pr: 1,
143          }}
144          onClick={handleClick}
145        >
146          <Box display="flex" alignItems="center" overflow="hidden">
147            <SvgIcon height={9} width={9} color="primary">
148              <SortIcon />
149            </SvgIcon>
150            <Typography
151              variant="subheader1"
152              color="text.primary"
153              sx={{
154                ml: 1,
155                textOverflow: 'ellipsis',
156                whiteSpace: 'nowrap',
157                overflow: 'hidden',
158                mr: 1,
159              }}
160            >
161              <FilterButtonLabel />
162            </Typography>
163          </Box>
164          {!allSelected && (
165            <DarkTooltip
166              title={
167                <Typography variant="caption" color="common.white">
168                  <Trans>Reset</Trans>
169                </Typography>
170              }
171            >
172              <Box
173                sx={{
174                  cursor: 'pointer',
175                  color: 'primary',
176                  height: 'auto',
177                  width: 'auto',
178                  display: 'flex',
179                  alignItems: 'center',
180                }}
181                onClick={handleClearFilter}
182              >
183                <XCircleIcon color="#A5A8B6" width={18} height={18} />
184              </Box>
185            </DarkTooltip>
186          )}
187        </Button>
188        <Menu
189          anchorEl={anchorEl}
190          open={Boolean(anchorEl)}
191          onClose={handleClose}
192          PaperProps={{
193            sx: {
194              width: 280,
195              maxHeight: 300,
196              mt: 1,
197              boxShadow: '0px 0px 2px rgba(0, 0, 0, 0.2), 0px 2px 10px rgba(0, 0, 0, 0.1)',
198              borderRadius: '4px',
199            },
200          }}
201        >
202          <MenuItem
203            onClick={() => handleFilterClick(undefined)}
204            sx={{
205              background: allSelected ? theme.palette.background.surface : undefined,
206              display: 'flex',
207              justifyContent: 'space-between',
208            }}
209          >
210            <Typography variant="subheader1" color="text.primary">
211              <Trans>All transactions</Trans>
212            </Typography>
213            {allSelected && (
214              <SvgIcon sx={{ fontSize: '16px' }}>
215                <CheckIcon />
216              </SvgIcon>
217            )}
218          </MenuItem>
219          <Divider sx={{ mt: 1 }} />
220          <Box
221            sx={{
222              overflowY: 'scroll',
223              maxHeight: 200,
224              scrollbarWidth: 'none',
225              msOverflowStyle: 'none',
226              '::-webkit-scrollbar': {
227                display: 'none',
228              },
229            }}
230          >
231            {Object.keys(FilterOptions)
232              .filter((key) => isNaN(Number(key)))
233              .map((optionKey) => {
234                const option = FilterOptions[optionKey as keyof typeof FilterOptions];
235                return (
236                  <MenuItem
237                    key={optionKey}
238                    onClick={() => handleFilterClick(option)}
239                    sx={{
240                      background: currentFilter.includes(option)
241                        ? theme.palette.background.surface
242                        : undefined,
243                      display: 'flex',
244                      justifyContent: 'space-between',
245                    }}
246                  >
247                    <Typography variant="subheader1" color="text.primary">
248                      <FilterLabel filter={option} />
249                    </Typography>
250                    {currentFilter.includes(option) && (
251                      <SvgIcon sx={{ fontSize: '16px' }}>
252                        <CheckIcon />
253                      </SvgIcon>
254                    )}
255                  </MenuItem>
256                );
257              })}
258          </Box>
259        </Menu>
260      </Box>
261    );
262  };