diff options
author | Michael Muré <batolettre@gmail.com> | 2021-04-07 21:29:50 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2021-04-07 21:29:50 +0200 |
commit | abbed0ff129755386ccbf89409ff8c3877f86d20 (patch) | |
tree | 01d1a61d5f775eaef585130be15ea8a26d32ede4 /webui/src/pages/list/Filter.tsx | |
parent | c7a441bad6a7ca247d955b63d2ba604435fdaab6 (diff) | |
parent | ccdaa16052a1da7ea5c87fc2104363377df98b95 (diff) | |
download | git-bug-abbed0ff129755386ccbf89409ff8c3877f86d20.tar.gz |
Merge pull request #623 from GlancingMind/upstream-additional-filters-for-bug-list
WebUI: Additional filters for bug list
Diffstat (limited to 'webui/src/pages/list/Filter.tsx')
-rw-r--r-- | webui/src/pages/list/Filter.tsx | 74 |
1 files changed, 61 insertions, 13 deletions
diff --git a/webui/src/pages/list/Filter.tsx b/webui/src/pages/list/Filter.tsx index 66702078..2e99eedf 100644 --- a/webui/src/pages/list/Filter.tsx +++ b/webui/src/pages/list/Filter.tsx @@ -1,14 +1,33 @@ import clsx from 'clsx'; import { LocationDescriptor } from 'history'; -import React, { useState, useRef } from 'react'; +import React, { useRef, useState, useEffect } from 'react'; import { Link } from 'react-router-dom'; import Menu from '@material-ui/core/Menu'; import MenuItem from '@material-ui/core/MenuItem'; import { SvgIconProps } from '@material-ui/core/SvgIcon'; -import { makeStyles } from '@material-ui/core/styles'; +import TextField from '@material-ui/core/TextField'; +import { makeStyles, withStyles } from '@material-ui/core/styles'; import ArrowDropDown from '@material-ui/icons/ArrowDropDown'; +const CustomTextField = withStyles((theme) => ({ + root: { + margin: '0 8px 12px 8px', + '& label.Mui-focused': { + margin: '0 2px', + color: theme.palette.text.secondary, + }, + '& .MuiInput-underline::before': { + borderBottomColor: theme.palette.divider, + }, + '& .MuiInput-underline::after': { + borderBottomColor: theme.palette.divider, + }, + }, +}))(TextField); + +const ITEM_HEIGHT = 48; + export type Query = { [key: string]: string[] }; function parse(query: string): Query { @@ -90,6 +109,7 @@ type FilterDropdownProps = { itemActive: (key: string) => boolean; icon?: React.ComponentType<SvgIconProps>; to: (key: string) => LocationDescriptor; + hasFilter?: boolean; } & React.ButtonHTMLAttributes<HTMLButtonElement>; function FilterDropdown({ @@ -98,12 +118,19 @@ function FilterDropdown({ itemActive, icon: Icon, to, + hasFilter, ...props }: FilterDropdownProps) { const [open, setOpen] = useState(false); + const [filter, setFilter] = useState<string>(''); const buttonRef = useRef<HTMLButtonElement>(null); + const searchRef = useRef<HTMLButtonElement>(null); const classes = useStyles({ active: false }); + useEffect(() => { + searchRef && searchRef.current && searchRef.current.focus(); + }, [filter]); + const content = ( <> {Icon && <Icon fontSize="small" classes={{ root: classes.icon }} />} @@ -124,6 +151,7 @@ function FilterDropdown({ </button> <Menu getContentAnchorEl={null} + ref={searchRef} anchorOrigin={{ vertical: 'bottom', horizontal: 'left', @@ -135,18 +163,37 @@ function FilterDropdown({ open={open} onClose={() => setOpen(false)} anchorEl={buttonRef.current} + PaperProps={{ + style: { + maxHeight: ITEM_HEIGHT * 4.5, + width: '25ch', + }, + }} > - {dropdown.map(([key, value]) => ( - <MenuItem - component={Link} - to={to(key)} - className={itemActive(key) ? classes.itemActive : undefined} - onClick={() => setOpen(false)} - key={key} - > - {value} - </MenuItem> - ))} + {hasFilter && ( + <CustomTextField + onChange={(e) => { + const { value } = e.target; + setFilter(value); + }} + onKeyDown={(e) => e.stopPropagation()} + value={filter} + label={`Filter ${children}`} + /> + )} + {dropdown + .filter((d) => d[1].toLowerCase().includes(filter.toLowerCase())) + .map(([key, value]) => ( + <MenuItem + component={Link} + to={to(key)} + className={itemActive(key) ? classes.itemActive : undefined} + onClick={() => setOpen(false)} + key={key} + > + {value} + </MenuItem> + ))} </Menu> </> ); @@ -158,6 +205,7 @@ export type FilterProps = { icon?: React.ComponentType<SvgIconProps>; children: React.ReactNode; }; + function Filter({ active, to, children, icon: Icon }: FilterProps) { const classes = useStyles(); |