Skip to content

Commit

Permalink
Merge pull request #183 from m2ms/master
Browse files Browse the repository at this point in the history
right side filter
  • Loading branch information
reskyner authored May 29, 2020
2 parents 509856f + d330ed2 commit 9d97988
Show file tree
Hide file tree
Showing 27 changed files with 2,375 additions and 1,047 deletions.
25 changes: 21 additions & 4 deletions js/components/common/Surfaces/Panel/index.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,13 @@
import React, { forwardRef, memo, useEffect, useState } from 'react';
import { Paper as MaterialPaper, makeStyles, Grid, IconButton, Typography, CircularProgress } from '@material-ui/core';
import {
Paper as MaterialPaper,
makeStyles,
Grid,
IconButton,
Typography,
CircularProgress,
Tooltip
} from '@material-ui/core';
import ExpandMore from '@material-ui/icons/ExpandMore';
import ExpandLess from '@material-ui/icons/ExpandLess';

Expand Down Expand Up @@ -57,6 +65,7 @@ export const Panel = memo(
hasHeader,
secondaryBackground,
title,
withTooltip,
headerActions,
hasExpansion,
defaultExpanded = false,
Expand Down Expand Up @@ -107,9 +116,17 @@ export const Panel = memo(
xs={hasExpansion || headerActions ? (headerActions && headerActions.length > 2 ? 4 : 6) : 12}
className={classes.headerTitle}
>
<Typography variant="h6" color="inherit" noWrap>
{title}
</Typography>
{withTooltip ? (
<Tooltip title={title}>
<Typography variant="h6" color="inherit" noWrap>
{title}
</Typography>
</Tooltip>
) : (
<Typography variant="h6" color="inherit" noWrap>
{title}
</Typography>
)}
</Grid>
)}
{(headerActions || hasExpansion) && (
Expand Down
90 changes: 25 additions & 65 deletions js/components/datasets/customDatasetList.js
Original file line number Diff line number Diff line change
@@ -1,34 +1,23 @@
/**
* Created by abradley on 14/03/2018.
*/
import React, { useState, useEffect, memo, useContext } from 'react';
import { Button } from '../common/Inputs/Button';
import { FilterList } from '@material-ui/icons';
import { connect, useDispatch } from 'react-redux';
import * as apiActions from '../../reducers/api/actions';
import { setFilterDialogOpen, setMoleculeListIsLoading } from './redux/actions';
import { loadCompoundScoresListOfDataSet, loadMoleculesOfDataSet } from './redux/dispatchActions';
import { setFilter } from './redux/actions';
import React, { useEffect, memo } from 'react';
import { useDispatch } from 'react-redux';
import { setMoleculeListIsLoading } from './redux/actions';
import {
clearDatasetSettings,
initializeDatasetFilter,
loadCompoundScoresListOfDataSet,
loadMoleculesOfDataSet
} from './redux/dispatchActions';
import { DatasetMoleculeList } from './datasetMoleculeList';

const CustomDatasetList = memo(
({
dataset,
object_selection,
height,
setFilterItemsHeight,
filterItemsHeight,
filter,
sortDialogOpen,
setFilterDialogOpen,
hideProjects
}) => {
export const CustomDatasetList = memo(
({ dataset, height, setFilterItemsHeight, filterItemsHeight, hideProjects, isActive }) => {
const dispatch = useDispatch();

const [sortDialogAnchorEl, setSortDialogAnchorEl] = useState(null);

useEffect(() => {
if (dataset && dataset.id) {
if (dataset && dataset.id && isActive) {
dispatch(setMoleculeListIsLoading(true));
Promise.all([
dispatch(loadMoleculesOfDataSet(dataset.id)),
Expand All @@ -39,60 +28,31 @@ const CustomDatasetList = memo(
})
.finally(() => {
dispatch(setMoleculeListIsLoading(false));
dispatch(initializeDatasetFilter(dataset && dataset.id));
});
} else if (dataset && dataset.id && !isActive) {
dispatch(clearDatasetSettings(dataset.id));
}
}, [dataset, dispatch]);

const actions = [
<Button
onClick={event => {
if (sortDialogOpen === false) {
setSortDialogAnchorEl(event.currentTarget);
setFilterDialogOpen(true);
} else {
setSortDialogAnchorEl(null);
setFilterDialogOpen(false);
}
}}
color={'inherit'}
disabled={!(object_selection || []).length}
variant="text"
startIcon={<FilterList />}
size="small"
>
sort/filter
</Button>
];
return () => {
if (dataset && dataset.id) {
dispatch(clearDatasetSettings(dataset.id));
}
};
}, [dataset, dispatch, isActive]);

const title = dataset && `${dataset.title} v.${dataset.version}`;

return (
<DatasetMoleculeList
title={dataset && dataset.title}
title={title}
url={dataset && dataset.url}
height={height}
setFilterItemsHeight={setFilterItemsHeight}
filterItemsHeight={filterItemsHeight}
hideProjects={hideProjects}
object_selection={object_selection}
filter={filter}
setFilter={setFilter}
actions={actions}
sortDialogAnchorEl={sortDialogAnchorEl}
datasetID={dataset && dataset.id}
/>
);
}
);
function mapStateToProps(state) {
return {
group_type: state.apiReducers.group_type,
object_selection: state.selectionReducers.mol_group_selection,
object_list: state.apiReducers.molecule_list,
filter: state.datasetsReducers.filter,
sortDialogOpen: state.datasetsReducers.filterDialogOpen
};
}
const mapDispatchToProps = {
setMoleculeList: apiActions.setMoleculeList,
setFilterDialogOpen
};
CustomDatasetList.displayName = 'CustomDatasetList';
export default connect(mapStateToProps, mapDispatchToProps)(CustomDatasetList);
205 changes: 205 additions & 0 deletions js/components/datasets/datasetFilter.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,205 @@
import React, { memo, useState } from 'react';
import { Paper, Popper, Button, Grid } from '@material-ui/core';
import { useDispatch, useSelector } from 'react-redux';
import { makeStyles } from '@material-ui/styles';
import WarningIcon from '@material-ui/icons/Warning';
import { Delete } from '@material-ui/icons';
import { setFilterProperties, setFilterSettings } from './redux/actions';
import {
getFilteredDatasetMoleculeList,
getInitialDatasetFilterProperties,
getInitialDatasetFilterSettings
} from './redux/selectors';
import { DatasetMoleculeListSortFilter } from './datasetMoleculeListSortFilterItem';
import { createFilterSettingsObject } from './redux/constants';

const useStyles = makeStyles(theme => ({
title: {
fontSize: 22
},
numberOfHits: {
flexGrow: 1
},
gridItemHeader: {
height: '32px',
fontSize: '12px',
lineHeight: 1,
color: '#7B7B7B',
fontWeight: 'bold'
},
centered: {
display: 'flex',
alignItems: 'center',
justifyContent: 'center'
},
property: {
fontSize: '10px',
color: '#000'
},
min: {
fontSize: '10px',
color: '#7B7B7B'
},
warningIcon: {
color: '#FFC107',
position: 'relative',
top: 2
},
paper: {
width: 700,
overflow: 'none',
padding: theme.spacing(1)
}
}));

const widthCheckbox = 70;
const widthPrio = 100;
const widthOrder = 60;
const widthProperty = 212;
const widthMin = 30;
const widthSlider = 170;

export const DatasetFilter = memo(
({ open, anchorEl, datasetID, active, predefined, priorityOrder, filterProperties }) => {
let classes = useStyles();
const dispatch = useDispatch();
const id = open ? 'simple-popover-datasets' : undefined;
const defaultFilterSettings = useSelector(state => getInitialDatasetFilterSettings(state, datasetID));
const defaultFilterProperties = useSelector(state => getInitialDatasetFilterProperties(state, datasetID));
const scoreDatasetList = useSelector(state => state.datasetsReducers.scoreDatasetMap[datasetID]);
const scoreCompoundMap = useSelector(state => state.datasetsReducers.scoreCompoundMap[datasetID]);
const filteredDatasetMoleculeList = useSelector(state => getFilteredDatasetMoleculeList(state, datasetID));

const [predefinedFilter, setPredefinedFilter] = useState(predefined);

const getAttributeName = attr => {
return scoreDatasetList.find(item => item.name === attr);
};

const handleFilterChange = (newFilterProperties, newFilterSettings) => {
scoreDatasetList.forEach(attr => {
if (newFilterProperties[attr.name].priority === undefined || newFilterProperties[attr.name].priority === '') {
newFilterProperties[attr.name].priority = 0;
}
});
dispatch(setFilterProperties(datasetID, newFilterProperties));
dispatch(setFilterSettings(datasetID, newFilterSettings));
};

const handleItemChange = key => setting => {
const newFilterSettings = createFilterSettingsObject({ active: true, predefined, priorityOrder });
const newFilterProperties = { ...filterProperties, [key]: setting };
handleFilterChange(newFilterProperties, newFilterSettings);
};

const handlePrioChange = key => inc => () => {
const maxPrio = scoreDatasetList.length - 1;
const minPrio = 0;
let localPriorityOrder = JSON.parse(JSON.stringify(priorityOrder));
const index = localPriorityOrder.indexOf(key);
if (index > -1 && index + inc >= minPrio && index <= maxPrio) {
localPriorityOrder.splice(index, 1);
localPriorityOrder.splice(index + inc, 0, key);
let newFilterSettings = createFilterSettingsObject({ active, predefined, priorityOrder: localPriorityOrder });
newFilterSettings.priorityOrder = localPriorityOrder;
newFilterSettings.active = true;

handleFilterChange(filterProperties, newFilterSettings);
}
};

const handleClear = () => {
setPredefinedFilter('none');
handleFilterChange(defaultFilterProperties, defaultFilterSettings);
};

// Check for multiple attributes with same sorting priority
let prioWarning = false;
let prioWarningTest = {};

if (scoreCompoundMap) {
for (const attr of scoreCompoundMap) {
const prioKey = filterProperties[attr.score.name].priority;
if (prioKey > 0) {
prioWarningTest[prioKey] = prioWarningTest[prioKey] ? prioWarningTest[prioKey] + 1 : 1;
if (prioWarningTest[prioKey] > 1) {
prioWarning = true;
}
}
}
}

return (
<Popper id={id} open={open} anchorEl={anchorEl} placement="left-start">
<Paper className={classes.paper} elevation={21}>
<Grid container justify="space-between" direction="row" alignItems="center">
<Grid item>
<div className={classes.numberOfHits}>
# of hits matching selection: <b>{(filteredDatasetMoleculeList || []).length}</b>
{prioWarning && (
<div>
<WarningIcon className={classes.warningIcon} /> multiple attributes with same sorting priority
</div>
)}
</div>
</Grid>
<Grid item>
<Button onClick={handleClear} color="secondary" variant="contained" startIcon={<Delete />}>
Clear
</Button>
</Grid>
</Grid>
<Grid container>
<Grid container item className={classes.gridItemHeader}>
<Grid item className={classes.centered} style={{ width: widthCheckbox }}>
Is showed
</Grid>
<Grid item className={classes.centered} style={{ width: widthPrio }}>
priority
</Grid>
<Grid item className={classes.centered} style={{ width: widthOrder }}>
<div style={{ textAlign: 'center' }}>
order
<br />
<span style={{ fontSize: 'smaller' }}>(up/down)</span>
</div>
</Grid>
<Grid item className={classes.centered} style={{ width: widthProperty }}>
property
</Grid>
<Grid item className={classes.centered} style={{ width: widthMin }}>
min
</Grid>
<Grid item className={classes.centered} style={{ width: widthSlider }} />
<Grid item className={classes.centered} style={{ width: widthMin }}>
max
</Grid>
</Grid>

{priorityOrder.map(attr => {
let attrDef = getAttributeName(attr);
return (
<DatasetMoleculeListSortFilter
key={attr}
datasetID={datasetID}
scoreName={attrDef.name}
scoreDescription={attrDef.description}
scoreID={attrDef.id}
order={filterProperties[attr].order}
minValue={filterProperties[attr].minValue}
maxValue={filterProperties[attr].maxValue}
min={defaultFilterProperties[attr].minValue}
max={defaultFilterProperties[attr].maxValue}
isFloat={defaultFilterProperties[attr].isFloat}
disabled={predefinedFilter !== 'none' || defaultFilterProperties[attr].disabled}
onChange={handleItemChange(attr)}
onChangePrio={handlePrioChange(attr)}
/>
);
})}
</Grid>
</Paper>
</Popper>
);
}
);
Loading

0 comments on commit 9d97988

Please sign in to comment.