diff options
author | Quentin Gliech <quentingliech@gmail.com> | 2020-01-25 11:40:08 +0100 |
---|---|---|
committer | Quentin Gliech <quentingliech@gmail.com> | 2020-01-25 11:40:08 +0100 |
commit | 4d97e3a19a96e2361b35a0ccc0be74e0ba887214 (patch) | |
tree | d226f1a4f7181ff5d106c8bb65f6048c66f38a40 /webui/src/list/Filter.js | |
parent | fa13550115144a6f39888960a80cc24890f83536 (diff) | |
download | git-bug-4d97e3a19a96e2361b35a0ccc0be74e0ba887214.tar.gz |
webui: implement filtering
Diffstat (limited to 'webui/src/list/Filter.js')
-rw-r--r-- | webui/src/list/Filter.js | 73 |
1 files changed, 70 insertions, 3 deletions
diff --git a/webui/src/list/Filter.js b/webui/src/list/Filter.js index ce457d03..c93b2d35 100644 --- a/webui/src/list/Filter.js +++ b/webui/src/list/Filter.js @@ -1,6 +1,58 @@ import React from 'react'; +import { Link } from 'react-router-dom'; import { makeStyles } from '@material-ui/styles'; +function parse(query) { + // TODO: extract the rest of the query? + const params = {}; + + // TODO: support escaping without quotes + const re = /(\w+):(\w+|(["'])(([^\3]|\\.)*)\3)+/g; + let matches; + while ((matches = re.exec(query)) !== null) { + if (!params[matches[1]]) { + params[matches[1]] = []; + } + + let value; + if (matches[4]) { + value = matches[4]; + } else { + value = matches[2]; + } + value = value.replace(/\\(.)/g, '$1'); + params[matches[1]].push(value); + } + return params; +} + +function quote(value) { + const hasSingle = value.includes("'"); + const hasDouble = value.includes('"'); + const hasSpaces = value.includes(' '); + if (!hasSingle && !hasDouble && !hasSpaces) { + return value; + } + + if (!hasDouble) { + return `"${value}"`; + } + + if (!hasSingle) { + return `'${value}'`; + } + + value = value.replace(/"/g, '\\"'); + return `"${value}"`; +} + +function stringify(params) { + const parts = Object.entries(params).map(([key, values]) => { + return values.map(value => `${key}:${quote(value)}`); + }); + return [].concat(...parts).join(' '); +} + const useStyles = makeStyles(theme => ({ element: { ...theme.typography.body2, @@ -18,15 +70,30 @@ const useStyles = makeStyles(theme => ({ }, })); -function Filter({ active, children, icon: Icon, end, ...props }) { +function Filter({ active, to, children, icon: Icon, end, ...props }) { const classes = useStyles({ active, end }); - return ( - <button {...props} className={classes.element}> + const content = ( + <> {Icon && <Icon fontSize="small" classes={{ root: classes.icon }} />} <div>{children}</div> + </> + ); + + if (to) { + return ( + <Link to={to} {...props} className={classes.element}> + {content} + </Link> + ); + } + + return ( + <button {...props} className={classes.element}> + {content} </button> ); } export default Filter; +export { parse, stringify, quote }; |