Skip to content

Commit 63c8bb1

Browse files
Merge pull request #446 from MetaCell/feature/417
New control panel implementation
2 parents bc5e0eb + 68087f6 commit 63c8bb1

32 files changed

+1600
-427
lines changed

package.json

Lines changed: 0 additions & 6 deletions
This file was deleted.

webapp/Main.js

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,24 +3,23 @@ import ReactDOM from 'react-dom';
33
import { Provider } from 'react-redux';
44
import { MuiThemeProvider } from '@material-ui/core/styles';
55
import * as Sentry from '@sentry/react';
6-
import { Integrations } from '@sentry/tracing';
6+
import { CaptureConsole } from '@sentry/integrations';
77
import { NetPyNE } from './components';
88
import theme from './theme';
99
import store from './redux/store';
1010
import '@metacell/geppetto-meta-ui/flex-layout/style/dark.scss';
11-
import { CaptureConsole } from '@sentry/integrations';
1211

1312
global.GEPPETTO_CONFIGURATION = require('./GeppettoConfiguration.json');
1413
const { initGeppetto } = require('@metacell/geppetto-meta-client/GEPPETTO');
1514

1615
Sentry.init({
17-
dsn: "https://[email protected]/6",
16+
dsn: 'https://[email protected]/6',
1817
integrations: [
1918
new CaptureConsole({
20-
levels: ['error']
21-
})
19+
levels: ['error'],
20+
}),
2221
],
23-
tracesSampleRate: 1.0
22+
tracesSampleRate: 1.0,
2423
});
2524

2625
initGeppetto();

webapp/components/definition/cellRules/SelectCellTemplate.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ export default class NetPyNENewPlot extends React.Component {
5252
style={{
5353
width: 40,
5454
height: 40,
55+
borderRadius: '50%',
5556
}}
5657
color={page == 'main' ? 'primary' : 'secondary'}
5758
onClick={(event) => handleButtonClick(event)}

webapp/components/definition/plots/NetPyNENewPlot.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ export default class NetPyNENewPlot extends React.Component {
2828
return (
2929
<div>
3030
<Tooltip title="Add new plot" placement="top">
31-
<Fab size="small" color="primary" onClick={this.handleButtonClick}>
31+
<Fab size="small" color="primary" onClick={this.handleButtonClick} style={{ borderRadius: '50%' }}>
3232
<ContentAdd style={{ color: 'white' }} />
3333
</Fab>
3434
</Tooltip>

webapp/components/definition/stimulationTargets/NetPyNEStimulationTargets.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -276,6 +276,7 @@ export default class NetPyNEStimulationTargets extends Component {
276276
overflow: 'visible',
277277
display: 'flex',
278278
justifyContent: 'center',
279+
borderRadius: '50%',
279280
}}
280281
>
281282
Target

webapp/components/drawer/Drawer.js

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,9 @@ const DrawerList = ({
9494
editMode,
9595
widgets,
9696
activateWidget,
97+
maximiseWidget,
9798
updateWidget,
99+
addWidget,
98100
classes,
99101
}) => {
100102
const [expand, setExpand] = useState(false);
@@ -110,10 +112,8 @@ const DrawerList = ({
110112
if (!widget) {
111113
const widgetConf = getNewWidgetConf(widgetId);
112114
newWidget({ path: widgetConf.id, ...widgetConf });
113-
} else if (widget.status === WidgetStatus.MINIMIZED) {
114-
updateBorderWidget(widgetId);
115115
} else {
116-
activateWidget(widgetId);
116+
updateBorderWidget(widgetId);
117117
}
118118
}
119119

@@ -170,13 +170,13 @@ const DrawerList = ({
170170
return (
171171
<Paper elevation={0} className={paperClasses}>
172172
<div className={drawerClasses.container}>
173-
<Box p={2}>
173+
<Box px={1} py={2}>
174174
{ expand && (
175175
<Box className="drawerListBox">
176176
<Typography variant="body2">{editMode ? SIDEBAR_HEADINGS.MODEL : SIDEBAR_HEADINGS.PLOTS}</Typography>
177177
</Box>
178178
)}
179-
<List dense disablePadding>
179+
<List disablePadding>
180180
{getMenu()[0]
181181
.map(mapItem)}
182182
</List>
@@ -186,7 +186,7 @@ const DrawerList = ({
186186
<Typography variant="body2">{SIDEBAR_HEADINGS.TOOLS}</Typography>
187187
)}
188188
</Box>
189-
<List dense disablePadding>
189+
<List disablePadding>
190190
{getMenu()[1]
191191
.map(mapItem)}
192192
</List>

webapp/components/drawer/useStyles.js

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,8 @@ export default makeStyles(({
2424

2525
closeDrawer: drawerCss(false, transitions, palette, spacing),
2626

27-
buttonContainerOpen: { textAlign: 'end' },
28-
buttonContainerClosed: { textAlign: 'center' },
27+
buttonContainerOpen: { textAlign: 'end', padding: '0' },
28+
buttonContainerClosed: { textAlign: 'center', padding: '0' },
2929
button: {
3030
color: 'white',
3131
fontSize: '1em',
@@ -37,6 +37,11 @@ export default makeStyles(({
3737
flexDirection: 'column',
3838
flex: 1,
3939
width: '100%',
40+
41+
'& > .MuiBox-root': {
42+
overflow: 'auto',
43+
maxHeight: 'calc(100vh - 96px)',
44+
},
4045
},
4146

4247
selected: { color: palette.primary.main },
Lines changed: 185 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,185 @@
1+
import * as React from 'react';
2+
import { makeStyles } from '@material-ui/core/styles';
3+
import Grid from '@material-ui/core/Grid';
4+
import Typography from '@material-ui/core/Typography';
5+
import Box from '@material-ui/core/Box';
6+
import IconButton from '@material-ui/core/IconButton';
7+
import TreeItem from '@material-ui/lab/TreeItem';
8+
import Visibility from '@material-ui/icons/Visibility';
9+
import VisibilityOff from '@material-ui/icons/VisibilityOff';
10+
import ColorLens from '@material-ui/icons/ColorLens';
11+
import Shuffle from '@material-ui/icons/Shuffle';
12+
import { ChromePicker } from 'react-color';
13+
import { useDispatch, useSelector } from 'react-redux';
14+
import { experimentLabelColor } from '../../theme';
15+
import { changeInstanceColor } from '../../redux/actions/general';
16+
17+
const useStyles = makeStyles((theme) => ({
18+
networkItem: {
19+
paddingTop: '2px',
20+
paddingBottom: '2px',
21+
},
22+
controls: {
23+
'& .MuiIconButton-root': {
24+
padding: 0,
25+
marginLeft: '0.5rem',
26+
color: experimentLabelColor,
27+
'&:hover': {
28+
color: 'white',
29+
},
30+
},
31+
},
32+
colorPicker: {
33+
position: 'absolute',
34+
zIndex: 1000,
35+
right: 0,
36+
},
37+
}));
38+
39+
const ControlPanelTreeItem = (props) => {
40+
const classes = useStyles();
41+
const dispatch = useDispatch();
42+
const [showColorPicker, setShowColorPicker] = React.useState(false);
43+
const [isHoveredOver, setIsHoveredOver] = React.useState(false);
44+
const [color, setColor] = React.useState({
45+
g: 0.50, b: 0.60, r: 1, a: 1,
46+
});
47+
const [visibility, setVisibility] = React.useState(true);
48+
const instances = useSelector((state) => state.general.instances);
49+
50+
const handleColorSelection = (_color, event, nodeId) => {
51+
const newInstances = instances.filter((instance) => !(instance.instancePath.startsWith(nodeId)));
52+
newInstances.push({
53+
instancePath: nodeId,
54+
color: {
55+
r: _color.rgb.r / 255,
56+
g: _color.rgb.g / 255,
57+
b: _color.rgb.b / 255,
58+
a: _color.rgb.a,
59+
},
60+
});
61+
dispatch(changeInstanceColor(newInstances));
62+
setColor(_color.rgb);
63+
};
64+
65+
const generateRandomColor = (event, nodeId) => {
66+
const newInstances = instances.filter((instance) => !(instance.instancePath.startsWith(nodeId)));
67+
const randomColor = {
68+
r: parseFloat((Math.random() * 255).toFixed(2)),
69+
g: parseFloat((Math.random() * 255).toFixed(2)),
70+
b: parseFloat((Math.random() * 255).toFixed(2)),
71+
a: 1,
72+
};
73+
74+
newInstances.push({
75+
instancePath: nodeId,
76+
color: {
77+
r: randomColor.r / 255,
78+
g: randomColor.g / 255,
79+
b: randomColor.b / 255,
80+
a: randomColor.a,
81+
},
82+
});
83+
dispatch(changeInstanceColor(newInstances));
84+
setColor(randomColor);
85+
};
86+
87+
const changeVisibility = (event, nodeId) => {
88+
const copiedInstances = instances.slice();
89+
let oldIndex = null;
90+
let oldInstance = copiedInstances.find((pInstance, index) => {
91+
if (pInstance.instancePath === nodeId) {
92+
oldIndex = index;
93+
return true;
94+
}
95+
return false;
96+
});
97+
if (!oldInstance) {
98+
oldInstance = {
99+
instancePath: nodeId,
100+
visibility: false,
101+
};
102+
} else {
103+
copiedInstances.splice(oldIndex, 1);
104+
oldInstance.visibility = (oldInstance?.visibility !== undefined) ? !oldInstance.visibility : false;
105+
}
106+
107+
const newInstances = instances.map((instance) => {
108+
if (!(instance.instancePath.startsWith(nodeId))) {
109+
return instance;
110+
}
111+
const newInstance = instance;
112+
newInstance.visibility = oldInstance.visibility;
113+
return newInstance;
114+
});
115+
116+
newInstances.push(oldInstance);
117+
118+
dispatch(changeInstanceColor(newInstances));
119+
setVisibility(oldInstance.visibility);
120+
};
121+
122+
const {
123+
label,
124+
type,
125+
nodeId,
126+
onNodeSelect,
127+
onVisibilityClick,
128+
children,
129+
...other
130+
} = props;
131+
132+
return (
133+
<TreeItem
134+
nodeId={nodeId}
135+
onLabelClick={(e) => { e.stopPropagation(); e.preventDefault(); }}
136+
label={(
137+
<Grid
138+
container
139+
className={classes.networkItem}
140+
onMouseEnter={() => setIsHoveredOver(true)}
141+
onMouseLeave={() => setIsHoveredOver(false)}
142+
display="flex"
143+
flexDirection="row"
144+
justifyContent="space-between"
145+
>
146+
<Grid item xs={4}><Typography onClick={() => onNodeSelect(nodeId)}>{label}</Typography></Grid>
147+
<Grid item xs={4} justifyContent="center"><Typography>{type}</Typography></Grid>
148+
<Grid item xs={4} justifyContent="flex-end" className={classes.controls}>
149+
{isHoveredOver
150+
? (
151+
<>
152+
153+
<IconButton onClick={(event) => changeVisibility(event, nodeId)}>
154+
{ visibility ? <Visibility /> : <VisibilityOff /> }
155+
</IconButton>
156+
<IconButton onClick={(event) => generateRandomColor(event, nodeId)}><Shuffle /></IconButton>
157+
<IconButton onClick={() => setShowColorPicker(true)}><ColorLens /></IconButton>
158+
{
159+
showColorPicker
160+
? (
161+
<Box
162+
onMouseLeave={() => setShowColorPicker(false)}
163+
>
164+
<ChromePicker
165+
className={classes.colorPicker}
166+
color={color}
167+
onChangeComplete={(color, event) => handleColorSelection(color, event, nodeId)}
168+
/>
169+
</Box>
170+
) : null
171+
}
172+
173+
</>
174+
)
175+
: null}
176+
</Grid>
177+
</Grid>
178+
)}
179+
>
180+
{children}
181+
</TreeItem>
182+
);
183+
};
184+
185+
export default ControlPanelTreeItem;

0 commit comments

Comments
 (0)