|
|
|
@ -203,20 +203,29 @@ export function EditFilterExpressionDialog(props) { |
|
|
|
|
control = <EditMatchingFilterExpression {...subprops} /> |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// If this is a "leaf" filter, we will allow changing the filter type in the dialog.
|
|
|
|
|
// But if it is a combination filter (i.e. AND / OR / NOT), we won't allow it because
|
|
|
|
|
// That throws away all its children.
|
|
|
|
|
const allowTypeChange = |
|
|
|
|
(filter instanceof ConstFilter) || |
|
|
|
|
(filter instanceof MatchingFilter); |
|
|
|
|
|
|
|
|
|
return ( |
|
|
|
|
<Dialog aria-labelledby={id} open={open}> |
|
|
|
|
<DialogTitle id={id}>Edit expression</DialogTitle> |
|
|
|
|
<FormControl className={classes.margined}> |
|
|
|
|
<Select |
|
|
|
|
labelId={selectLabelId} |
|
|
|
|
id={selectId} |
|
|
|
|
value={getFilterType(filter)} |
|
|
|
|
onChange={handleTypeChange} |
|
|
|
|
> |
|
|
|
|
<MenuItem value={FilterTypeEnum.CONST}>Constant</MenuItem> |
|
|
|
|
<MenuItem value={FilterTypeEnum.MATCHING}>Matching</MenuItem> |
|
|
|
|
</Select> |
|
|
|
|
</FormControl> |
|
|
|
|
{allowTypeChange && |
|
|
|
|
<FormControl className={classes.margined}> |
|
|
|
|
<Select |
|
|
|
|
labelId={selectLabelId} |
|
|
|
|
id={selectId} |
|
|
|
|
value={getFilterType(filter)} |
|
|
|
|
onChange={handleTypeChange} |
|
|
|
|
> |
|
|
|
|
<MenuItem value={FilterTypeEnum.CONST}>Constant</MenuItem> |
|
|
|
|
<MenuItem value={FilterTypeEnum.MATCHING}>Matching</MenuItem> |
|
|
|
|
</Select> |
|
|
|
|
</FormControl> |
|
|
|
|
} |
|
|
|
|
{control} |
|
|
|
|
<Button onClick={handleClose} className={classes.margined}> |
|
|
|
|
Done |
|
|
|
@ -394,6 +403,40 @@ export function ConstFilterExpressionControl(props) { |
|
|
|
|
); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
export function NegationExpressionControl(props) { |
|
|
|
|
const classes = useStyles(); |
|
|
|
|
const { expr, onClick, onChange } = props; |
|
|
|
|
|
|
|
|
|
var _ = require('lodash'); |
|
|
|
|
function handleBodyChanged(body) { |
|
|
|
|
var new_filter = _.cloneDeep(expr); |
|
|
|
|
new_filter.body = body; |
|
|
|
|
onChange(new_filter); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
return ( |
|
|
|
|
<> |
|
|
|
|
<Box className={classes.logic_op_outer}> |
|
|
|
|
<Box className={classes.logic_op_sbs}> |
|
|
|
|
<Box> |
|
|
|
|
<Box className={classes.logic_op_subexpr}> |
|
|
|
|
<FilterExpressionControl expr={expr.body} onChange={handleBodyChanged} isRoot={false} /> |
|
|
|
|
</Box> |
|
|
|
|
</Box> |
|
|
|
|
</Box> |
|
|
|
|
<Button |
|
|
|
|
variant="outlined" |
|
|
|
|
className={classes.filterexpcontrol + " " + classes.logic_op_sbs} |
|
|
|
|
aria-controls="simple-menu" aria-haspopup="true" |
|
|
|
|
onClick={onClick} |
|
|
|
|
> |
|
|
|
|
NOT |
|
|
|
|
</Button> |
|
|
|
|
</Box> |
|
|
|
|
</> |
|
|
|
|
) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
export function FilterExpressionControl(props) { |
|
|
|
|
const { expr, onChange, isRoot } = props; |
|
|
|
|
const [anchorEl, setAnchorEl] = React.useState(null); |
|
|
|
@ -428,6 +471,12 @@ export function FilterExpressionControl(props) { |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
const handleRemove = () => { |
|
|
|
|
// For negation filters, removal means replacing the negation node by its child.
|
|
|
|
|
// In all other cases, removal means complete deletion of the subtree.
|
|
|
|
|
if (expr instanceof NegationFilter) { |
|
|
|
|
onChange(expr.body); |
|
|
|
|
return; |
|
|
|
|
} |
|
|
|
|
onChange(null); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
@ -458,6 +507,12 @@ export function FilterExpressionControl(props) { |
|
|
|
|
setCombineDialogOpen(true); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
const handleNegation = () => { |
|
|
|
|
handleCloseMenu(); |
|
|
|
|
var new_filter = new NegationFilter(expr.result_type, expr); |
|
|
|
|
onChange(new_filter.simplify()); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
var filter_elem = false; |
|
|
|
|
if (expr instanceof ConstFilter) { |
|
|
|
|
filter_elem = <ConstFilterExpressionControl {...props} onClick={handleClick} /> |
|
|
|
@ -465,10 +520,17 @@ export function FilterExpressionControl(props) { |
|
|
|
|
filter_elem = <LogicalOperatorFilterExpressionControl {...props} onClick={handleClick} onChange={onChange} /> |
|
|
|
|
} else if (expr instanceof MatchingFilter) { |
|
|
|
|
filter_elem = <MatchingFilterExpressionControl {...props} onClick={handleClick} /> |
|
|
|
|
} else if (expr instanceof NegationFilter) { |
|
|
|
|
filter_elem = <NegationExpressionControl {...props} onClick={handleClick} onChange={onChange} /> |
|
|
|
|
} else { |
|
|
|
|
throw new Error('Unsupported filter expression'); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// If this is the root node, removing it is not allowed.
|
|
|
|
|
// Other nodes may be removed.
|
|
|
|
|
// The only exception is a negation node: removing that will replace it by its child.
|
|
|
|
|
var allowRemove = !isRoot || (expr instanceof NegationFilter); |
|
|
|
|
|
|
|
|
|
return ( |
|
|
|
|
<> |
|
|
|
|
{filter_elem} |
|
|
|
@ -482,7 +544,8 @@ export function FilterExpressionControl(props) { |
|
|
|
|
<MenuItem onClick={handleOpenEditDialog}>Edit...</MenuItem> |
|
|
|
|
<MenuItem onClick={handleAnd}>And...</MenuItem> |
|
|
|
|
<MenuItem onClick={handleOr}>Or...</MenuItem> |
|
|
|
|
{!isRoot && <MenuItem onClick={handleRemove}>Remove</MenuItem>} |
|
|
|
|
<MenuItem onClick={handleNegation}>Negate</MenuItem> |
|
|
|
|
{allowRemove && <MenuItem onClick={handleRemove}>Remove</MenuItem>} |
|
|
|
|
</Menu> |
|
|
|
|
<EditFilterExpressionDialog |
|
|
|
|
onClose={handleCloseEditFilterDialog} |
|
|
|
|