diff --git a/.gitignore b/.gitignore index 188fe286..429e5678 100644 --- a/.gitignore +++ b/.gitignore @@ -18,3 +18,5 @@ Dockerfile_mini npm* .vscode app.log +utilities/x86_64 +.idea diff --git a/k8s/cf.yaml b/k8s/cf.yaml index 7bb61876..3d5a2e34 100644 --- a/k8s/cf.yaml +++ b/k8s/cf.yaml @@ -60,8 +60,8 @@ steps: resources: requests: - cpu: 200m - memory: 512Mi + cpu: 100m + memory: 128Mi allowNamedServers: true namedServerLimitPerUser: 2 @@ -123,7 +123,7 @@ steps: environment: - CHART_NAME=jupyterhub - RELEASE_NAME=${{RELEASE_NAME}} - - KUBE_CONTEXT=geppetto-cluster@metacellllc + - KUBE_CONTEXT=${{CLUSTER_NAME}} - NAMESPACE=${{NAMESPACE}} - TILLER_NAMESPACE=kube-system - CHART_VERSION=0.9.0-beta.4 diff --git a/netpyne_ui/netpyne_geppetto.py b/netpyne_ui/netpyne_geppetto.py index 544d34bb..cf300d59 100644 --- a/netpyne_ui/netpyne_geppetto.py +++ b/netpyne_ui/netpyne_geppetto.py @@ -512,6 +512,8 @@ def deleteParam(self, model, label): return False def validateFunction(self, functionString): + if isinstance(functionString, (float, int)): + return True return validateFunction(functionString, self.netParams.__dict__) def exportHLS(self, args): diff --git a/netpyne_ui/netpyne_model_interpreter.py b/netpyne_ui/netpyne_model_interpreter.py index 9526b494..5d1e3990 100644 --- a/netpyne_ui/netpyne_model_interpreter.py +++ b/netpyne_ui/netpyne_model_interpreter.py @@ -6,9 +6,9 @@ import pygeppetto.model as pygeppetto from pygeppetto.model.model_factory import GeppettoModelFactory from pygeppetto.model.values import Point, ArrayElement, ArrayValue +from pygeppetto.services.model_interpreter import ModelInterpreter - -class NetPyNEModelInterpreter(): +class NetPyNEModelInterpreter(ModelInterpreter): def __init__(self): self.factory = GeppettoModelFactory() diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 00000000..48e341a0 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,3 @@ +{ + "lockfileVersion": 1 +} diff --git a/src/jupyter-geppetto b/src/jupyter-geppetto new file mode 160000 index 00000000..840c78f5 --- /dev/null +++ b/src/jupyter-geppetto @@ -0,0 +1 @@ +Subproject commit 840c78f5ff64a291af5af87332f749df1ee84d48 diff --git a/webapp/Main.js b/webapp/Main.js index 770fdf7d..0b5200e0 100644 --- a/webapp/Main.js +++ b/webapp/Main.js @@ -3,18 +3,12 @@ global.GEPPETTO_CONFIGURATION = require('./GeppettoConfiguration.json'); jQuery(function () { require('geppetto-client-initialization'); - var ReactDOM = require('react-dom'); - var React = require('react'); - var getMuiTheme = require('@material-ui/core/styles/createMuiTheme').default; - var MuiThemeProvider = require('@material-ui/core/styles').MuiThemeProvider; - var NetPyNE = require('./NetPyNE').default; + const ReactDOM = require('react-dom'); + const React = require('react'); + const MuiThemeProvider = require('@material-ui/core/styles').MuiThemeProvider; + const NetPyNE = require('./components').NetPyNE; - - var Utils = require('./Utils').default; - var Console = require('geppetto-client/js/components/interface/console/Console'); - var TabbedDrawer = require('geppetto-client/js/components/interface/drawer/TabbedDrawer'); - var PythonConsole = require('geppetto-client/js/components/interface/pythonConsole/PythonConsole'); - var theme = require('./Theme').default + const theme = require('./Theme').default const Provider = require("react-redux").Provider; const configureStore = require('./redux/store').default; @@ -22,45 +16,25 @@ jQuery(function () { require('./css/netpyne.less'); require('./css/material.less'); require('./css/traceback.less'); + require('./css/flexlayout.less'); + require('./css/tree.less'); const store = configureStore(); - function App (data = {}) { - return ( -
- - - - - + ReactDOM.render( +
+ + + + + - -
- ); - } - ReactDOM.render(, document.querySelector('#mainContainer')); +
, + document.querySelector('#mainContainer') + ); GEPPETTO.G.setIdleTimeOut(-1); - GEPPETTO.G.debug(false); // Change this to true to see messages on the Geppetto console while loading GEPPETTO.Resources.COLORS.DEFAULT = "#6f54aa"; GEPPETTO.trigger(GEPPETTO.Events.Show_spinner, "Initialising NetPyNE"); - - GEPPETTO.on('jupyter_geppetto_extension_ready', data => { - let project = { id: 1, name: 'Project', experiments: [{ "id": 1, "name": 'Experiment', "status": 'DESIGN' }] } - GEPPETTO.Manager.loadProject(project, false); - GEPPETTO.Manager.loadExperiment(1, [], []); - Utils.execPythonMessage('from netpyne_ui.netpyne_geppetto import netpyne_geppetto'); - Utils.evalPythonMessage('netpyne_geppetto.getData',[]).then(response => { - var data = Utils.convertToJSON(response) - ReactDOM.render(, document.querySelector('#mainContainer')); - GEPPETTO.trigger("spinner:hide"); - }) - }); + }); \ No newline at end of file diff --git a/webapp/NetPyNE.js b/webapp/NetPyNE.js deleted file mode 100644 index cff02742..00000000 --- a/webapp/NetPyNE.js +++ /dev/null @@ -1,189 +0,0 @@ -import React from 'react'; -import Toolbar from '@material-ui/core/Toolbar'; -import Transition from './components/transition/Transition'; -import PythonControlledNetPyNEPopulations from './redux/reduxconnect/NetPyNEPopulationsConnection'; -import NetPyNECellRules from './components/definition/cellRules/NetPyNECellRules'; -import PythonControlledNetPyNESynapses from './redux/reduxconnect/NetPyNESynapsesConnection'; -import NetPyNEConnectivityRules from './components/definition/connectivity/NetPyNEConnectivityRules'; -import PythonControlledNetPyNEStimulationSources from './redux/reduxconnect/NetPyNEStimulationSourcesConnection'; -import NetPyNEStimulationTargets from './components/definition/stimulationTargets/NetPyNEStimulationTargets'; -import NetPyNEPlots from './components/definition/plots/NetPyNEPlots'; -import NetPyNESimConfig from './components/definition/configuration/NetPyNESimConfig'; -import NetPyNEInstantiated from './components/instantiation/NetPyNEInstantiated'; -import NetPyNEToolBar from './components/settings/NetPyNEToolBar'; -import NetPyNETabs from './components/settings/NetPyNETabs'; - -var PythonControlledCapability = require('geppetto-client/js/communication/geppettoJupyter/PythonControlledCapability'); - -var PythonControlledNetPyNECellRules = PythonControlledCapability.createPythonControlledComponent(NetPyNECellRules); - -var PythonControlledNetPyNEConnectivity = PythonControlledCapability.createPythonControlledComponent(NetPyNEConnectivityRules); - -var PythonControlledNetPyNEStimulationTargets = PythonControlledCapability.createPythonControlledComponent(NetPyNEStimulationTargets); -var PythonControlledNetPyNEPlots = PythonControlledCapability.createPythonControlledComponent(NetPyNEPlots); - -export default class NetPyNE extends React.Component { - - constructor (props) { - super(props); - this.widgets = {}; - this.state = { - value: 'define', - prevValue: 'define', - model: null, - tabClicked: false, - freezeInstance: false, - freezeSimulation: false, - fastForwardInstantiation: true, - fastForwardSimulation: false - }; - this.handleDeactivateInstanceUpdate = this.handleDeactivateInstanceUpdate.bind(this); - this.handleDeactivateSimulationUpdate = this.handleDeactivateSimulationUpdate.bind(this); - this.handleTabChangedByToolBar = this.handleTabChangedByToolBar.bind(this) - } - - UNSAFE_componentWillReceiveProps (nextProps) { - if (this.props.data != nextProps.data) { - console.log("Initialising NetPyNE Tabs") - - window.metadata = nextProps.data.metadata; - window.currentFolder = nextProps.data.currentFolder; - window.isDocker = nextProps.data.isDocker; - - this.setState({ model: nextProps.data }) - } - } - - hideWidgetsFor = value => { - if (value != "define") { - var page = this.refs[value]; - if (page) { - var widgets = page.getOpenedWidgets(); - if (this.widgets[value]) { - widgets = widgets.concat(this.widgets[value]); - } - for (var w in widgets) { - if (!widgets[w].destroyed){ - widgets[w].hide(); - } else { - delete widgets[w]; - } - } - this.widgets[value] = widgets; - } - } - } - - restoreWidgetsFor = (value, rename = false) => { - if (value != "define") { - if (this.widgets[value]) { - let widgets = this.widgets[value] - for (var w in widgets) { - if (rename && !widgets[w].getName().endsWith('(OLD)')) { - widgets[w].setName(widgets[w].getName() + ' (OLD)') - } - widgets[w].show(); - } - } - } - } - - handleChange = tab => { - this.hideWidgetsFor(this.state.value); - this.restoreWidgetsFor(tab, true); - this.setState( ({ value: pv, prevValue: xx, freezeInstance:fi, freezeSimulation:fs, tabClicked:tc, ...others }) => ({ - value: tab, - prevValue: pv, - freezeInstance: pv == 'define' ? false : fi, - freezeSimulation: pv == 'define' ? false : fs, - tabClicked: !tc, - })) - }; - - handleTransitionOptionsChange = value => { - var state = { fastForwardInstantiation: false, fastForwardSimulation: false } - if (value == 'Create and Simulate Network') { - state = { fastForwardInstantiation: true, fastForwardSimulation: true } - } else if (value == 'Create Network') { - state = { fastForwardInstantiation: true, fastForwardSimulation: false } - } - this.setState(state) - } - - handleDeactivateInstanceUpdate = netInstanceWasUpdated => { - if (netInstanceWasUpdated) { - if (!this.state.freezeInstance) { - this.setState({ freezeInstance: true }) - } - } - } - - handleDeactivateSimulationUpdate = netSimulationWasUpdated => { - if (netSimulationWasUpdated) { - if (!this.state.freezeSimulation) { - this.setState({ freezeSimulation: true, freezeInstance: true }) - } - } - } - - handleTabChangedByToolBar = (tab, args) => { - this.setState(({ value: x, prevValue: xx, freezeInstance: fi, freezeSimulation: fs, ...others }) => ({ - value: tab, - prevValue: tab, - freezeInstance: args.freezeInstance != undefined ? args.freezeInstance : fi, - freezeSimulation: args.freezeSimulation != undefined ? args.freezeSimulation : fs, - })); - } - - render () { - if (this.state.model == null) { - return
- } else { - if (this.state.value == 'define'){ - var content =
- - - - - - - - -
- } else { - var content = - } - - return ( -
-
- -
- -
-
- -
-
-
- - - - {content} -
- ) - } - } -} diff --git a/webapp/Theme.js b/webapp/Theme.js index 147605d3..4167087d 100644 --- a/webapp/Theme.js +++ b/webapp/Theme.js @@ -1,13 +1,15 @@ import createMuiTheme from '@material-ui/core/styles/createMuiTheme'; import purple from '@material-ui/core/colors/purple'; -import pink from '@material-ui/core/colors/pink'; +import orange from '@material-ui/core/colors/orange'; -// pink -const secondaryColor = pink['A200'] -// purple -const primaryColor = 'rgb(84, 58, 115)' +// orange +const primaryColor = '#f67700' +const secondaryColor = '#f67700' +const bgLight = '#616161'; +const bgRegular = '#424242'; +const bgDark = '#212121'; export default createMuiTheme({ typography: { @@ -21,8 +23,9 @@ export default createMuiTheme({ } }, palette: { + type: 'dark', primary: { main: primaryColor, }, - secondary: { main: secondaryColor, } + secondary: { main: bgLight, }, }, overrides: { MuiInputLabel: { formControl: { top: '-6px' } }, @@ -30,8 +33,10 @@ export default createMuiTheme({ input: { outline: 'none !important', border: 'none !important', - boxShadow: 'none !important' + boxShadow: 'none !important', }, + root:{ color: 'white' } + }, MuiSelect: { root: { @@ -39,7 +44,24 @@ export default createMuiTheme({ border: 'none !important', boxShadow: 'none !important' }, - select: { "&:focus" :{ background: "none" } }, - } + select: { "&:focus" :{ background: "none" }, paddingLeft: '4px' }, + }, + MuiCard: { root: { height: '100%', backgroundColor: bgRegular, overflowY: 'auto' } }, + MuiBottomNavigation: { root: { backgroundColor: bgRegular } }, + MuiPaper: { root: { color: 'inherit' } }, + MuiBottomNavigationAction: { root: { color: 'white' } }, + MuiFab:{ + secondary: { color: 'white' }, + primary: { color: 'white' } + }, + MuiButton: { + containedSecondary: { color: 'white' }, + containedPrimary: { color: 'white' }, + }, + MuiMenuItem: { root: { color: 'white' } }, + + MuiListItemText: { root: { color: 'white' } }, + MuiDialogTitle: { root: { color: 'white' } } + } }); \ No newline at end of file diff --git a/webapp/Utils.js b/webapp/Utils.js index 2e6bbbd9..e345780c 100644 --- a/webapp/Utils.js +++ b/webapp/Utils.js @@ -145,7 +145,7 @@ const Utils = { }, parsePythonException (exception){ - return

+    return 

   },
 
   handleUpdate (updateCondition, newValue, originalValue, context, componentName) {
@@ -185,4 +185,32 @@ const Utils = {
   evalPythonMessage: evalPythonMessage
 }
 
+/**
+ * Deep object comparison
+ * @param {*} a 
+ * @param {*} b 
+ */
+export function isEqual (a, b) {
+  if (a === b) {
+    return true;
+  }
+  if (a instanceof Date && b instanceof Date) {
+    return a.getTime() === b.getTime();
+  }
+  if (!a || !b || (typeof a !== 'object' && typeof b !== 'object')) {
+    return a === b;
+  }
+  if (a === null || a === undefined || b === null || b === undefined) {
+    return false;
+  }
+  if (a.prototype !== b.prototype) {
+    return false;
+  }
+  let keys = Object.keys(a);
+  if (keys.length !== Object.keys(b).length) {
+    return false;
+  }
+  return keys.every(k => isEqual(a[k], b[k]));
+}
+
 export default Utils
\ No newline at end of file
diff --git a/webapp/components/NetPyNE.js b/webapp/components/NetPyNE.js
new file mode 100644
index 00000000..2cf15f50
--- /dev/null
+++ b/webapp/components/NetPyNE.js
@@ -0,0 +1,78 @@
+import React from "react";
+import Toolbar from "@material-ui/core/Toolbar";
+
+import {
+  NetPyNEToolBar,
+  NetPyNETabs,
+  LayoutManager,
+  Drawer
+} from "netpyne/components";
+
+import { withStyles } from '@material-ui/core/styles'
+
+const styles = ({ zIndex, palette, spacing }) => ({
+  container: {
+    height: "100%",
+    width: "100%",
+    display: "flex",
+    flexDirection: "column"
+  },
+  toolbar: {
+    backgroundColor: palette.primary.main,
+    width: "100%",
+    boxShadow: "0 0px 4px 0 rgba(0, 0, 0, 0.2), 0 0px 8px 0 rgba(0, 0, 0, 0.19)",
+    position: "relative",
+    top: 0,
+    left: 0,
+    zIndex: zIndex.appBar
+  },
+  views: {
+    display: "flex",
+    flexFlow: "rows",
+    width: "100%",
+    marginRight: spacing(-1)
+  },
+  drawer: { marginLeft: spacing(-1) },
+  content: { position: "relative", zIndex: zIndex.drawer + 1 }
+})
+
+
+class NetPyNE extends React.Component {
+  
+  openPythonCallDialog (event) {
+    const payload = { errorMessage: event['evalue'], errorDetails: event['traceback'].join('\n') }
+    this.props.pythonCallErrorDialogBox(payload)
+  }
+
+  componentDidMount () {
+    GEPPETTO.on(GEPPETTO.Events.Error_while_exec_python_command, this.openPythonCallDialog, this)
+  }
+
+  componentWillUnmount () {
+    GEPPETTO.off(GEPPETTO.Events.Error_while_exec_python_command, this.openPythonCallDialog, this)
+  }
+
+  render () {
+    const { classes } = this.props
+
+    return (
+      
+
+ +
+ +
+
+ +
+
+
+
+ + +
+
+ ); + } +} +export default withStyles(styles)(NetPyNE) \ No newline at end of file diff --git a/webapp/components/definition/cellRules/NetPyNECellRule.js b/webapp/components/definition/cellRules/NetPyNECellRule.js index 94749559..25f5a2b4 100644 --- a/webapp/components/definition/cellRules/NetPyNECellRule.js +++ b/webapp/components/definition/cellRules/NetPyNECellRule.js @@ -3,10 +3,7 @@ import MenuItem from '@material-ui/core/MenuItem'; import TextField from '@material-ui/core/TextField'; import Button from '@material-ui/core/Button'; -import Utils from '../../../Utils'; -import NetPyNEField from '../../general/NetPyNEField'; -import NetPyNECoordsRange from '../../general/NetPyNECoordsRange'; import Dialog from '@material-ui/core/Dialog/Dialog'; import DialogActions from '@material-ui/core/DialogActions'; @@ -14,8 +11,8 @@ import DialogContent from '@material-ui/core/DialogContent'; import DialogContentText from '@material-ui/core/DialogContentText'; import DialogTitle from '@material-ui/core/DialogTitle'; -import PythonMethodControlledSelectFieldConnection from '../../../redux/reduxconnect/PythonMethodControlledSelectFieldConnection' - +import { NetPyNESelectField, NetPyNEField, NetPyNECoordsRange } from 'netpyne/components'; +import Utils from '../../../Utils'; export default class NetPyNECellRule extends React.Component { @@ -111,7 +108,7 @@ export default class NetPyNECellRule extends React.Component { - - - { @@ -447,7 +463,7 @@ export default class NetPyNECellRules extends React.Component { return selectedCellRule } } else { - return + return } case 'sections': @@ -463,7 +479,7 @@ export default class NetPyNECellRules extends React.Component { } } else { if (page == "sections" ) { - return + return } else { if (selectedCellRule) { if (!!model && !!model[selectedCellRule] && Object.keys(model[selectedCellRule]['secs']).length > 0){ @@ -513,7 +529,7 @@ export default class NetPyNECellRules extends React.Component { if (page == 'main') { if ( selectedCellRule !== undefined && model && Object.keys(model).indexOf(selectedCellRule) > -1) { selection = ( - ) } - } else if (page == "sections") { + } else if (page == "sections" && Object.keys(model).length > 0) { const sectionsModel = model[selectedCellRule].secs; if ( selectedSection !== undefined && Object.keys(sectionsModel).indexOf(selectedSection) > -1 ) { selection = ( @@ -555,7 +571,7 @@ export default class NetPyNECellRules extends React.Component { handleClick={this.selectSection} /> ) - } else if (page == "mechanisms") { + } else if (page == "mechanisms" && Object.keys(model).length > 0) { const mechanismsModel = model[selectedCellRule].secs[selectedSection].mechs; if ((selectedMechanism !== undefined) && Object.keys(mechanismsModel).indexOf(selectedMechanism) > -1) { selection = ( @@ -643,11 +659,6 @@ export default class NetPyNECellRules extends React.Component { return ( - {content} {dialogPop} diff --git a/webapp/components/definition/cellRules/sections/NetPyNESection.js b/webapp/components/definition/cellRules/sections/NetPyNESection.js index 51140d9b..da26bf07 100644 --- a/webapp/components/definition/cellRules/sections/NetPyNESection.js +++ b/webapp/components/definition/cellRules/sections/NetPyNESection.js @@ -5,19 +5,16 @@ import FontIcon from '@material-ui/core/Icon'; import CardContent from '@material-ui/core/CardContent'; import BottomNavigation from '@material-ui/core/BottomNavigation'; import BottomNavigationAction from '@material-ui/core/BottomNavigationAction'; -import Select from '../../../general/Select'; -import ListComponent from '../../../general/List'; -import NetPyNEField from '../../../general/NetPyNEField'; import Utils from '../../../../Utils'; -var PythonControlledCapability = require('geppetto-client/js/communication/geppettoJupyter/PythonControlledCapability'); -var PythonControlledTextField = PythonControlledCapability.createPythonControlledControl(TextField); -var PythonControlledListComponent = PythonControlledCapability.createPythonControlledControl(ListComponent); -var PythonMethodControlledSelectField = PythonControlledCapability.createPythonControlledControlWithPythonDataFetch(Select); +import { + NetPyNEField, + NetPyNETextField, + NetPyNESelectField, + ListComponent +} from 'netpyne/components'; -const hoverColor = '#66d2e2'; -const changeColor = 'rgb(0, 188, 212)'; export default class NetPyNESection extends React.Component { @@ -110,23 +107,23 @@ export default class NetPyNESection extends React.Component { content = (
- + - + - + - + - @@ -134,7 +131,7 @@ export default class NetPyNESection extends React.Component { } else if (this.state.sectionId == "Topology") { content = (
- -
- diff --git a/webapp/components/definition/cellRules/sections/NetPyNESectionThumbnail.js b/webapp/components/definition/cellRules/sections/NetPyNESectionThumbnail.js index 50292416..9e2d7c42 100644 --- a/webapp/components/definition/cellRules/sections/NetPyNESectionThumbnail.js +++ b/webapp/components/definition/cellRules/sections/NetPyNESectionThumbnail.js @@ -3,7 +3,7 @@ import FontIcon from '@material-ui/core/Icon'; import Button from '@material-ui/core/Button'; import DeleteDialogBox from '../../../general/DeleteDialogBox'; -const styles = { btn: { borderRadius: '25px', marginRight: '8px' } }; +const styles = { btn: { borderRadius: '25px', margin: '8px' } }; export default class NetPyNESectionThumbnail extends React.Component { @@ -46,7 +46,7 @@ export default class NetPyNESectionThumbnail extends React.Component {
@@ -121,73 +121,73 @@ export default class NetPyNESimConfig extends React.Component {
- + { !window.isDocker && - + } - + - + - +
- + - + - + - + - + - + {/* - + - + - + - + */} - +
@@ -197,30 +197,30 @@ export default class NetPyNESimConfig extends React.Component {
- + - + - + - +
- + - +
@@ -230,12 +230,12 @@ export default class NetPyNESimConfig extends React.Component {
- +
- +
@@ -245,54 +245,54 @@ export default class NetPyNESimConfig extends React.Component {
- + - + - + - + - + - +
- + - + - + - + - + - +
@@ -302,12 +302,6 @@ export default class NetPyNESimConfig extends React.Component { return ( - -
diff --git a/webapp/components/definition/connectivity/NetPyNEConnectivityRule.js b/webapp/components/definition/connectivity/NetPyNEConnectivityRule.js index 14a04752..e8aa256e 100644 --- a/webapp/components/definition/connectivity/NetPyNEConnectivityRule.js +++ b/webapp/components/definition/connectivity/NetPyNEConnectivityRule.js @@ -1,31 +1,26 @@ -import React from 'react'; -import MenuItem from '@material-ui/core/MenuItem'; +import React from "react"; import TextField from '@material-ui/core/TextField'; -import Utils from '../../../Utils'; -import FontIcon from '@material-ui/core/Icon'; -import CardContent from '@material-ui/core/CardContent'; -import { BottomNavigation, BottomNavigationAction } from '@material-ui/core'; -import NetPyNEField from '../../general/NetPyNEField'; -import ListComponent from '../../general/List'; -import Select from '../../general/Select'; -import NetPyNECoordsRange from '../../general/NetPyNECoordsRange'; -import Dialog from '@material-ui/core/Dialog/Dialog'; -import Button from '@material-ui/core/Button'; - - -import DialogActions from '@material-ui/core/DialogActions'; -import DialogContent from '@material-ui/core/DialogContent'; -import DialogContentText from '@material-ui/core/DialogContentText'; -import DialogTitle from '@material-ui/core/DialogTitle'; - -import PythonMethodControlledSelectFieldConnection from '../../../redux/reduxconnect/PythonMethodControlledSelectFieldConnection' -var PythonControlledCapability = require('geppetto-client/js/communication/geppettoJupyter/PythonControlledCapability'); -var PythonControlledTextField = PythonControlledCapability.createPythonControlledControl(TextField); -var PythonMethodControlledSelectField = PythonControlledCapability.createPythonControlledControlWithPythonDataFetch(Select); -var PythonControlledListComponent = PythonControlledCapability.createPythonControlledControl(ListComponent); +import FontIcon from "@material-ui/core/Icon"; +import CardContent from "@material-ui/core/CardContent"; +import { BottomNavigation, BottomNavigationAction } from "@material-ui/core"; +import Dialog from "@material-ui/core/Dialog/Dialog"; +import Button from "@material-ui/core/Button"; +import DialogActions from "@material-ui/core/DialogActions"; +import DialogContent from "@material-ui/core/DialogContent"; +import DialogContentText from "@material-ui/core/DialogContentText"; +import DialogTitle from "@material-ui/core/DialogTitle"; +import MenuItem from "@material-ui/core/MenuItem"; + +import { + NetPyNESelectField, + NetPyNEField, + NetPyNETextField, + ListComponent, + NetPyNECoordsRange +} from "netpyne/components"; +import Utils from "../../../Utils"; export default class NetPyNEConnectivityRule extends React.Component { - constructor (props) { super(props); this.state = { @@ -41,18 +36,29 @@ export default class NetPyNEConnectivityRule extends React.Component { var storedValue = this.props.name; var newValue = Utils.nameValidation(event.target.value); var updateCondition = this.props.renameHandler(newValue); - var triggerCondition = Utils.handleUpdate(updateCondition, newValue, event.target.value, this, "ConnectionRule"); + var triggerCondition = Utils.handleUpdate( + updateCondition, + newValue, + event.target.value, + this, + "ConnectionRule" + ); if (triggerCondition) { this.triggerUpdate(() => { // Rename the population in Python - Utils.renameKey('netParams.connParams', storedValue, newValue, (response, newValue) => { - this.renaming = false; - }); + Utils.renameKey( + "netParams.connParams", + storedValue, + newValue, + (response, newValue) => { + this.renaming = false; + } + ); this.renaming = true; }); } - } + }; triggerUpdate (updateMethod) { // common strategy when triggering processing of a value change, delay it, every time there is a change we reset @@ -62,19 +68,21 @@ export default class NetPyNEConnectivityRule extends React.Component { this.updateTimer = setTimeout(updateMethod, 1000); } - select = (index, sectionId) => this.setState({ selectedIndex: index, sectionId: sectionId }); + select = (index, sectionId) => + this.setState({ selectedIndex: index, sectionId: sectionId }); getBottomNavigationAction (index, sectionId, label, icon, id) { - return )} - onClick={() => this.select(index, sectionId)} - /> + return ( + } + onClick={() => this.select(index, sectionId)} + /> + ); } - postProcessMenuItems (pythonData, selected) { return pythonData.map(name => ( - {this.state.errorMessage} - - - {this.state.errorDetails} - - - - - - - ) - : undefined + var dialogPop + = this.state.errorMessage != undefined ? ( + + + {this.state.errorMessage} + + + + {this.state.errorDetails} + + + + + + + ) : ( + undefined + ); if (this.state.sectionId == "General") { var content = ( @@ -127,208 +144,243 @@ export default class NetPyNEConnectivityRule extends React.Component { /> - - - - + pythonData.map(name => ( - - {name} - - ))} + postProcessItems={(pythonData, selected) => + pythonData.map(name => ( + + {name} + + )) + } /> - - + + - - + + - - + + - - + + - - + + - - + + - - + + {dialogPop}
- ) + ); } else if (this.state.sectionId == "Pre Conditions") { - var content =
- - - - - - - - - - - - - - - + var content = ( +
+ + + + + + + + + -
- } else if (this.state.sectionId == "Post Conditions") { - var content =
- - - - - - - - - - + +
+ ); + } else if (this.state.sectionId == "Post Conditions") { + var content = ( +
+ + + + + + + + + - + - + -
+ +
+ ); } - // Generate Menu var index = 0; var bottomNavigationItems = []; - bottomNavigationItems.push(this.getBottomNavigationAction(index++, 'General', 'General', 'fa-bars', 'generalConnTab')); - bottomNavigationItems.push(this.getBottomNavigationAction(index++, 'Pre Conditions', 'Pre-synaptic cells conditions', 'fa-caret-square-o-left', "preCondsConnTab")); - bottomNavigationItems.push(this.getBottomNavigationAction(index++, 'Post Conditions', 'Post-synaptic cells conditions', 'fa-caret-square-o-right', 'postCondsConnTab')); + bottomNavigationItems.push( + this.getBottomNavigationAction( + index++, + "General", + "General", + "fa-bars", + "generalConnTab" + ) + ); + bottomNavigationItems.push( + this.getBottomNavigationAction( + index++, + "Pre Conditions", + "Pre-synaptic cells conditions", + "fa-caret-square-o-left", + "preCondsConnTab" + ) + ); + bottomNavigationItems.push( + this.getBottomNavigationAction( + index++, + "Post Conditions", + "Post-synaptic cells conditions", + "fa-caret-square-o-right", + "postCondsConnTab" + ) + ); return (
@@ -341,9 +393,7 @@ export default class NetPyNEConnectivityRule extends React.Component { {content}
); - } handleChange = (event, index, values) => this.setState({ values }); - } diff --git a/webapp/components/definition/connectivity/NetPyNEConnectivityRules.js b/webapp/components/definition/connectivity/NetPyNEConnectivityRules.js index d7ffa3ad..44b73d84 100644 --- a/webapp/components/definition/connectivity/NetPyNEConnectivityRules.js +++ b/webapp/components/definition/connectivity/NetPyNEConnectivityRules.js @@ -221,11 +221,6 @@ export default class NetPyNEConnectivityRules extends Component { return ( - {content} ); } diff --git a/webapp/components/definition/plots/NetPyNEInclude.js b/webapp/components/definition/plots/NetPyNEInclude.js index 59e19cef..a1ccdfb0 100644 --- a/webapp/components/definition/plots/NetPyNEInclude.js +++ b/webapp/components/definition/plots/NetPyNEInclude.js @@ -1,17 +1,19 @@ import React, { Component } from 'react'; -import Menu from '@material-ui/core/Menu'; + import Divider from '@material-ui/core/Divider'; import MenuItem from '@material-ui/core/MenuItem'; import TextField from '@material-ui/core/TextField'; -import Popover from '@material-ui/core/Popover/Popover'; +import Popover from '@material-ui/core/Popover'; import Utils from '../../../Utils'; -import NetPyNEField from '../../general/NetPyNEField'; import ListItem from '@material-ui/core/ListItem'; import ListItemIcon from '@material-ui/core/ListItemIcon'; import CheckIcon from '@material-ui/icons/Check'; import List from '@material-ui/core/List'; import ListItemText from '@material-ui/core/ListItemText'; + +import { NetPyNEField, } from 'netpyne/components'; + export default class NetPyNEInclude extends Component { constructor (props) { @@ -135,7 +137,7 @@ export default class NetPyNEInclude extends Component { collectInfo = async () => { const numberOfCellsByPopulation = await Utils.evalPythonMessage("netpyne_geppetto.getGIDs", []) - + console.log(numberOfCellsByPopulation) if (numberOfCellsByPopulation) { const dataInPythonFormat = await Utils.evalPythonMessage("netpyne_geppetto.getInclude", [this.props.model.split("'")[1]]) let included @@ -266,7 +268,7 @@ export default class NetPyNEInclude extends Component { onClose={() => this.closeSecondPopover()} onMouseLeave={() => this.closeSecondPopover()} > - + {menuItems} diff --git a/webapp/components/definition/plots/NetPyNENewPlot.js b/webapp/components/definition/plots/NetPyNENewPlot.js index 1e332120..c1632331 100644 --- a/webapp/components/definition/plots/NetPyNENewPlot.js +++ b/webapp/components/definition/plots/NetPyNENewPlot.js @@ -27,8 +27,8 @@ export default class NetPyNENewPlot extends React.Component { render () { return
- - + + -
diff --git a/webapp/components/definition/plots/TimeRange.js b/webapp/components/definition/plots/TimeRange.js index 398dbedb..4cd3c1bf 100644 --- a/webapp/components/definition/plots/TimeRange.js +++ b/webapp/components/definition/plots/TimeRange.js @@ -1,10 +1,9 @@ import React, { Component } from 'react'; import TextField from '@material-ui/core/TextField'; -import AdapterComponent from '../../general/AdapterComponent'; -var PythonControlledCapability = require('geppetto-client/js/communication/geppettoJupyter/PythonControlledCapability'); -var PythonControlledAdapterComponent = PythonControlledCapability.createPythonControlledControl(AdapterComponent); - +import { AdapterComponent } from 'netpyne/components'; + + export default class TimeRange extends Component { constructor (props) { @@ -14,7 +13,7 @@ export default class TimeRange extends Component { render () { return (
- { if (state[state.lastUpdated].toString().endsWith(".")) { @@ -32,7 +31,7 @@ export default class TimeRange extends Component { > - +
); } diff --git a/webapp/components/definition/plots/plotTypes/Plot2Dnet.js b/webapp/components/definition/plots/plotTypes/Plot2Dnet.js index b7228883..af68353e 100644 --- a/webapp/components/definition/plots/plotTypes/Plot2Dnet.js +++ b/webapp/components/definition/plots/plotTypes/Plot2Dnet.js @@ -1,12 +1,12 @@ import React, { Component } from 'react'; -import Checkbox from '../../../general/Checkbox'; -import Select from '../../../general/Select'; -import NetPyNEIncludeConnection from '../../../../redux/reduxconnect/NetPyNEIncludeConnection'; -import NetPyNEField from '../../../general/NetPyNEField'; -var PythonControlledCapability = require('geppetto-client/js/communication/geppettoJupyter/PythonControlledCapability'); -var PythonControlledCheckbox = PythonControlledCapability.createPythonControlledControl(Checkbox); -var PythonControlledSelectField = PythonControlledCapability.createPythonControlledControl(Select); +import { + NetPyNEInclude, + NetPyNEField, + NetPyNESelectField, + NetPyNECheckbox +} from 'netpyne/components'; + export default class Plot2Dnet extends React.Component { @@ -18,7 +18,7 @@ export default class Plot2Dnet extends React.Component { render () { var tag = "simConfig.analysis['plot2Dnet']" return
- - + - +
} diff --git a/webapp/components/definition/plots/plotTypes/PlotConn.js b/webapp/components/definition/plots/plotTypes/PlotConn.js index d70743bd..2afeebd7 100644 --- a/webapp/components/definition/plots/plotTypes/PlotConn.js +++ b/webapp/components/definition/plots/plotTypes/PlotConn.js @@ -1,10 +1,11 @@ import React, { Component } from 'react'; -import Select from '../../../general/Select'; -import NetPyNEIncludeConnection from '../../../../redux/reduxconnect/NetPyNEIncludeConnection'; -import NetPyNEField from '../../../general/NetPyNEField'; -var PythonControlledCapability = require('geppetto-client/js/communication/geppettoJupyter/PythonControlledCapability'); -var PythonControlledSelectField = PythonControlledCapability.createPythonControlledControl(Select); +import { + NetPyNEInclude, + NetPyNEField, + NetPyNESelectField +} from 'netpyne/components'; + export default class plotConn extends React.Component { @@ -16,7 +17,7 @@ export default class plotConn extends React.Component { render () { var tag = "simConfig.analysis['plotConn']" return
- - + - + - +
} diff --git a/webapp/components/definition/plots/plotTypes/PlotGranger.js b/webapp/components/definition/plots/plotTypes/PlotGranger.js index ccf9f436..bde57413 100644 --- a/webapp/components/definition/plots/plotTypes/PlotGranger.js +++ b/webapp/components/definition/plots/plotTypes/PlotGranger.js @@ -1,12 +1,11 @@ import React, { Component } from 'react'; -import TextField from '@material-ui/core/TextField'; import TimeRange from '../TimeRange' -import ListComponent from '../../../general/List'; -import NetPyNEField from '../../../general/NetPyNEField'; -var PythonControlledCapability = require('geppetto-client/js/communication/geppettoJupyter/PythonControlledCapability'); -var PythonControlledTextField = PythonControlledCapability.createPythonControlledControl(TextField); -var PythonControlledListComponent = PythonControlledCapability.createPythonControlledControl(ListComponent); +import { + NetPyNEField, + NetPyNETextField, + ListComponent +} from 'netpyne/components'; export default class PlotGranger extends React.Component { @@ -20,27 +19,27 @@ export default class PlotGranger extends React.Component { var content = (
- + - + - + - + - + - + @@ -48,7 +47,7 @@ export default class PlotGranger extends React.Component { - +
); diff --git a/webapp/components/definition/plots/plotTypes/PlotLFP.js b/webapp/components/definition/plots/plotTypes/PlotLFP.js index 6d6c5234..87d85832 100644 --- a/webapp/components/definition/plots/plotTypes/PlotLFP.js +++ b/webapp/components/definition/plots/plotTypes/PlotLFP.js @@ -1,16 +1,12 @@ import React from 'react'; -import Checkbox from '../../../general/Checkbox'; -import TextField from '@material-ui/core/TextField'; -import Select from '../../../general/Select'; import TimeRange from '../TimeRange' -import ListComponent from '../../../general/List'; -import NetPyNEField from '../../../general/NetPyNEField'; - -var PythonControlledCapability = require('geppetto-client/js/communication/geppettoJupyter/PythonControlledCapability'); -var PythonControlledCheckbox = PythonControlledCapability.createPythonControlledControl(Checkbox); -var PythonControlledTextField = PythonControlledCapability.createPythonControlledControl(TextField); -var PythonControlledListComponent = PythonControlledCapability.createPythonControlledControl(ListComponent); -var PythonControlledSelectField = PythonControlledCapability.createPythonControlledControl(Select); +import { + NetPyNEField, + NetPyNECheckbox, + NetPyNETextField, + NetPyNESelectField, + ListComponent, +} from 'netpyne/components'; export default class PlotLFP extends React.Component { @@ -23,11 +19,11 @@ export default class PlotLFP extends React.Component { var tag = "simConfig.analysis['plotLFP']" return
- + - + @@ -35,31 +31,31 @@ export default class PlotLFP extends React.Component { - + - + - + - + - + - + - +
} diff --git a/webapp/components/definition/plots/plotTypes/PlotRaster.js b/webapp/components/definition/plots/plotTypes/PlotRaster.js index e78c158a..64af9978 100644 --- a/webapp/components/definition/plots/plotTypes/PlotRaster.js +++ b/webapp/components/definition/plots/plotTypes/PlotRaster.js @@ -1,15 +1,14 @@ import React, { Component } from 'react'; -import Checkbox from '../../../general/Checkbox'; -import TextField from '@material-ui/core/TextField'; -import Select from '../../../general/Select'; -import TimeRange from '../TimeRange' -import NetPyNEIncludeConnection from '../../../../redux/reduxconnect/NetPyNEIncludeConnection'; -import NetPyNEField from '../../../general/NetPyNEField'; +import TimeRange from '../TimeRange'; + +import { + NetPyNEInclude, + NetPyNEField, + NetPyNESelectField, + NetPyNECheckbox, + NetPyNETextField +} from 'netpyne/components'; -var PythonControlledCapability = require('geppetto-client/js/communication/geppettoJupyter/PythonControlledCapability'); -var PythonControlledCheckbox = PythonControlledCapability.createPythonControlledControl(Checkbox); -var PythonControlledTextField = PythonControlledCapability.createPythonControlledControl(TextField); -var PythonControlledSelectField = PythonControlledCapability.createPythonControlledControl(Select); export default class PlotRaster extends React.Component { @@ -26,7 +25,7 @@ export default class PlotRaster extends React.Component { render () { var tag = "simConfig.analysis['plotRaster']" return
- - + - + - + - + - + - + - +
} diff --git a/webapp/components/definition/plots/plotTypes/PlotRatePSD.js b/webapp/components/definition/plots/plotTypes/PlotRatePSD.js index 7dd132a5..87314892 100644 --- a/webapp/components/definition/plots/plotTypes/PlotRatePSD.js +++ b/webapp/components/definition/plots/plotTypes/PlotRatePSD.js @@ -1,13 +1,12 @@ import React, { Component } from 'react'; -import Checkbox from '../../../general/Checkbox'; -import TextField from '@material-ui/core/TextField'; -import TimeRange from '../TimeRange' -import NetPyNEIncludeConnection from '../../../../redux/reduxconnect/NetPyNEIncludeConnection'; -import NetPyNEField from '../../../general/NetPyNEField'; -var PythonControlledCapability = require('geppetto-client/js/communication/geppettoJupyter/PythonControlledCapability'); -var PythonControlledCheckbox = PythonControlledCapability.createPythonControlledControl(Checkbox); -var PythonControlledTextField = PythonControlledCapability.createPythonControlledControl(TextField); +import TimeRange from '../TimeRange' +import { + NetPyNEInclude, + NetPyNEField, + NetPyNECheckbox, + NetPyNETextField +} from 'netpyne/components'; export default class PlotRatePSD extends React.Component { @@ -19,7 +18,7 @@ export default class PlotRatePSD extends React.Component { render () { var tag = "simConfig.analysis['plotRatePSD']" return
- - + - + - + - + - + - +
} diff --git a/webapp/components/definition/plots/plotTypes/PlotShape.js b/webapp/components/definition/plots/plotTypes/PlotShape.js index c720e682..4d2f8fdc 100644 --- a/webapp/components/definition/plots/plotTypes/PlotShape.js +++ b/webapp/components/definition/plots/plotTypes/PlotShape.js @@ -1,15 +1,12 @@ import React, { Component } from 'react'; -import Checkbox from '../../../general/Checkbox'; -import TextField from '@material-ui/core/TextField'; -import Select from '../../../general/Select'; -import ListComponent from '../../../general/List'; -import NetPyNEField from '../../../general/NetPyNEField'; -var PythonControlledCapability = require('geppetto-client/js/communication/geppettoJupyter/PythonControlledCapability'); -var PythonControlledCheckbox = PythonControlledCapability.createPythonControlledControl(Checkbox); -var PythonControlledTextField = PythonControlledCapability.createPythonControlledControl(TextField); -var PythonControlledSelectField = PythonControlledCapability.createPythonControlledControl(Select); -var PythonControlledListComponent = PythonControlledCapability.createPythonControlledControl(ListComponent); +import { + NetPyNEField, + NetPyNECheckbox, + NetPyNETextField, + NetPyNESelectField, + ListComponent +} from 'netpyne/components'; export default class PlotShape extends React.Component { @@ -22,55 +19,55 @@ export default class PlotShape extends React.Component { var tag = "simConfig.analysis['plotShape']" return
- + - + - + - + - + - + - + - + - + - + - + - + - +
} diff --git a/webapp/components/definition/plots/plotTypes/PlotSpikeHist.js b/webapp/components/definition/plots/plotTypes/PlotSpikeHist.js index e36b4cbb..2e67b2d9 100644 --- a/webapp/components/definition/plots/plotTypes/PlotSpikeHist.js +++ b/webapp/components/definition/plots/plotTypes/PlotSpikeHist.js @@ -1,15 +1,13 @@ import React, { Component } from 'react'; -import Checkbox from '../../../general/Checkbox'; -import TextField from '@material-ui/core/TextField'; -import Select from '../../../general/Select'; import TimeRange from '../TimeRange' -import NetPyNEIncludeConnection from '../../../../redux/reduxconnect/NetPyNEIncludeConnection'; -import NetPyNEField from '../../../general/NetPyNEField'; -var PythonControlledCapability = require('geppetto-client/js/communication/geppettoJupyter/PythonControlledCapability'); -var PythonControlledCheckbox = PythonControlledCapability.createPythonControlledControl(Checkbox); -var PythonControlledTextField = PythonControlledCapability.createPythonControlledControl(TextField); -var PythonControlledSelectField = PythonControlledCapability.createPythonControlledControl(Select); +import { + NetPyNEInclude, + NetPyNEField, + NetPyNECheckbox, + NetPyNETextField, + NetPyNESelectField +} from 'netpyne/components'; export default class PlotSpikeHist extends React.Component { @@ -21,7 +19,7 @@ export default class PlotSpikeHist extends React.Component { render () { var tag = "simConfig.analysis['plotSpikeHist']" return
- - + - + - + - +
} diff --git a/webapp/components/definition/plots/plotTypes/PlotSpikeStats.js b/webapp/components/definition/plots/plotTypes/PlotSpikeStats.js index 218dcdc5..46a2f818 100644 --- a/webapp/components/definition/plots/plotTypes/PlotSpikeStats.js +++ b/webapp/components/definition/plots/plotTypes/PlotSpikeStats.js @@ -1,15 +1,12 @@ import React, { Component } from 'react'; -import TextField from '@material-ui/core/TextField'; -import Select from '../../../general/Select'; -import TimeRange from '../TimeRange' -import NetPyNEIncludeConnection from '../../../../redux/reduxconnect/NetPyNEIncludeConnection'; -import ListComponent from '../../../general/List'; -import NetPyNEField from '../../../general/NetPyNEField'; -var PythonControlledCapability = require('geppetto-client/js/communication/geppettoJupyter/PythonControlledCapability'); -var PythonControlledTextField = PythonControlledCapability.createPythonControlledControl(TextField); -var PythonControlledSelectField = PythonControlledCapability.createPythonControlledControl(Select); -var PythonControlledListComponent = PythonControlledCapability.createPythonControlledControl(ListComponent); +import TimeRange from '../TimeRange' +import { + NetPyNEInclude, + NetPyNEField, + NetPyNESelectField, + ListComponent +} from 'netpyne/components'; export default class PlotSpikeStats extends React.Component { @@ -21,7 +18,7 @@ export default class PlotSpikeStats extends React.Component { render () { var tag = "simConfig.analysis['plotSpikeStats']" return
- - + - + - +
} diff --git a/webapp/components/definition/plots/plotTypes/PlotTraces.js b/webapp/components/definition/plots/plotTypes/PlotTraces.js index 98bede95..bdc1158e 100644 --- a/webapp/components/definition/plots/plotTypes/PlotTraces.js +++ b/webapp/components/definition/plots/plotTypes/PlotTraces.js @@ -1,15 +1,12 @@ import React, { Component } from 'react'; -import Checkbox from '../../../general/Checkbox'; -import TextField from '@material-ui/core/TextField'; -import Select from '../../../general/Select'; import TimeRange from '../TimeRange' -import NetPyNEIncludeConnection from '../../../../redux/reduxconnect/NetPyNEIncludeConnection'; -import NetPyNEField from '../../../general/NetPyNEField'; -var PythonControlledCapability = require('geppetto-client/js/communication/geppettoJupyter/PythonControlledCapability'); -var PythonControlledCheckbox = PythonControlledCapability.createPythonControlledControl(Checkbox); -var PythonControlledTextField = PythonControlledCapability.createPythonControlledControl(TextField); -var PythonControlledSelectField = PythonControlledCapability.createPythonControlledControl(Select); +import { + NetPyNEInclude, + NetPyNEField, + NetPyNESelectField, + NetPyNECheckbox +} from 'netpyne/components'; export default class PlotTraces extends React.Component { @@ -21,7 +18,7 @@ export default class PlotTraces extends React.Component { render () { var tag = "simConfig.analysis['plotTraces']" return
- - + - + - +
} diff --git a/webapp/components/definition/populations/Dimensions.js b/webapp/components/definition/populations/Dimensions.js index d7b59440..2953d985 100644 --- a/webapp/components/definition/populations/Dimensions.js +++ b/webapp/components/definition/populations/Dimensions.js @@ -5,10 +5,10 @@ import MenuItem from '@material-ui/core/MenuItem'; import Utils from '../../../Utils'; import { withStyles } from '@material-ui/core/styles'; -import NetPyNEField from '../../general/NetPyNEField'; -var PythonControlledCapability = require('geppetto-client/js/communication/geppettoJupyter/PythonControlledCapability'); - -var PythonControlledTextField = PythonControlledCapability.createPythonControlledControl(TextField); +import { + NetPyNEField, + NetPyNETextField, +} from 'netpyne/components'; const styles = ({ spacing }) => ({ selectField: { @@ -112,7 +112,7 @@ class DimensionsComponent extends Component { { this.state.dimension != undefined && this.state.dimension != "" ? - ({ fields: { @@ -61,7 +62,7 @@ class NetPyNEPopulation extends React.Component { cellModelFields = []; // Get Fields for new metadata cellModelFields = Utils.getFieldsFromMetadataTree(response, key => ( - )); @@ -156,7 +157,7 @@ class NetPyNEPopulation extends React.Component { /> - { Utils.evalPythonMessage("netpyne_geppetto.propagate_field_rename", ['cellType', newValue, oldValue]) this.props.updateCards() @@ -166,7 +167,7 @@ class NetPyNEPopulation extends React.Component { - { Utils.evalPythonMessage("netpyne_geppetto.propagate_field_rename", ['cellModel', newValue, oldValue]) this.props.updateCards() @@ -175,7 +176,7 @@ class NetPyNEPopulation extends React.Component { /> - + {dialogPop}
) diff --git a/webapp/components/definition/populations/NetPyNEPopulations.js b/webapp/components/definition/populations/NetPyNEPopulations.js index cc90fbb0..889fbc54 100644 --- a/webapp/components/definition/populations/NetPyNEPopulations.js +++ b/webapp/components/definition/populations/NetPyNEPopulations.js @@ -1,10 +1,15 @@ import React, { Component } from 'react'; import { Card, CardHeader, CardContent } from '@material-ui/core'; import Utils from '../../../Utils'; -import NetPyNEHome from '../../general/NetPyNEHome'; -import NetPyNEAddNew from '../../general/NetPyNEAddNew'; -import NetPyNEThumbnail from '../../general/NetPyNEThumbnail'; -import NetPyNEPopulationConnection from '../../../redux/reduxconnect/NetPyNEPopulationConnection'; + +import { + NetPyNEHome, + NetPyNEAddNew, + NetPyNEThumbnail, + NetPyNEPopulation, +} from 'netpyne/components'; + + import Dialog from '@material-ui/core/Dialog/Dialog'; import Button from '@material-ui/core/Button'; @@ -189,17 +194,12 @@ export default class NetPyNEPopulations extends React.Component { } var selectedPopulation = undefined; if ((this.state.selectedPopulation !== undefined) && Object.keys(model).indexOf(this.state.selectedPopulation) > -1) { - selectedPopulation = ; + selectedPopulation = ; } } return ( -
{selectedPopulation} diff --git a/webapp/components/definition/stimulationSources/NetPyNEStimulationSource.js b/webapp/components/definition/stimulationSources/NetPyNEStimulationSource.js index 711efa89..a9ad4537 100644 --- a/webapp/components/definition/stimulationSources/NetPyNEStimulationSource.js +++ b/webapp/components/definition/stimulationSources/NetPyNEStimulationSource.js @@ -3,8 +3,7 @@ import MenuItem from '@material-ui/core/MenuItem'; import TextField from '@material-ui/core/TextField'; import Utils from '../../../Utils'; import Select from '../../general/Select'; -import ListComponent from '../../general/List'; -import NetPyNEField from '../../general/NetPyNEField'; + import Dialog from '@material-ui/core/Dialog/Dialog'; import Button from '@material-ui/core/Button'; import { withStyles } from '@material-ui/core/styles'; @@ -14,9 +13,11 @@ import DialogContent from '@material-ui/core/DialogContent'; import DialogContentText from '@material-ui/core/DialogContentText'; import DialogTitle from '@material-ui/core/DialogTitle'; -var PythonControlledCapability = require('geppetto-client/js/communication/geppettoJupyter/PythonControlledCapability'); -var PythonControlledTextField = PythonControlledCapability.createPythonControlledControl(TextField); -var PythonControlledListComponent = PythonControlledCapability.createPythonControlledControl(ListComponent); +import { + NetPyNEField, + NetPyNETextField, + ListComponent +} from 'netpyne/components'; const styles = ({ spacing }) => ({ selectField: { @@ -142,19 +143,19 @@ class NetPyNEStimulationSource extends React.Component { var variableContent = (
- - - @@ -165,37 +166,37 @@ class NetPyNEStimulationSource extends React.Component { var variableContent = (
- - - - - - @@ -205,25 +206,25 @@ class NetPyNEStimulationSource extends React.Component { var variableContent = (
- - - - @@ -234,31 +235,31 @@ class NetPyNEStimulationSource extends React.Component { var variableContent = (
- - - - - @@ -268,19 +269,19 @@ class NetPyNEStimulationSource extends React.Component { var variableContent = (
- - - diff --git a/webapp/components/definition/stimulationSources/NetPyNEStimulationSources.js b/webapp/components/definition/stimulationSources/NetPyNEStimulationSources.js index 934ecf32..2d96c46d 100644 --- a/webapp/components/definition/stimulationSources/NetPyNEStimulationSources.js +++ b/webapp/components/definition/stimulationSources/NetPyNEStimulationSources.js @@ -2,13 +2,16 @@ import React, { Component } from 'react'; import { Card, CardHeader, CardContent } from '@material-ui/core'; import Utils from '../../../Utils'; -import NetPyNEHome from '../../general/NetPyNEHome'; -import NetPyNEAddNew from '../../general/NetPyNEAddNew'; -import NetPyNEThumbnail from '../../general/NetPyNEThumbnail'; -import NetPyNEStimulationSourceConnection from '../../../redux/reduxconnect/NetPyNEStimulationSourceConnection'; import Dialog from '@material-ui/core/Dialog/Dialog'; import Button from '@material-ui/core/Button'; +import { + NetPyNEHome, + NetPyNEAddNew, + NetPyNEThumbnail, + NetPyNEStimulationSource, +} from 'netpyne/components'; + export default class NetPyNEStimulationSources extends Component { constructor (props) { @@ -165,7 +168,7 @@ export default class NetPyNEStimulationSources extends Component { var selectedStimulationSource = undefined; if ((this.state.selectedStimulationSource !== undefined) && Object.keys(model).indexOf(this.state.selectedStimulationSource) > -1) { - selectedStimulationSource = ; + selectedStimulationSource = ; } var content = ( @@ -192,11 +195,6 @@ export default class NetPyNEStimulationSources extends Component { return ( - {content} {dialogPop} diff --git a/webapp/components/definition/stimulationTargets/NetPyNEStimulationTarget.js b/webapp/components/definition/stimulationTargets/NetPyNEStimulationTarget.js index 606bae25..b84c5eed 100644 --- a/webapp/components/definition/stimulationTargets/NetPyNEStimulationTarget.js +++ b/webapp/components/definition/stimulationTargets/NetPyNEStimulationTarget.js @@ -5,10 +5,7 @@ import TextField from '@material-ui/core/TextField'; import CondsIcon from '@material-ui/icons/LocalOffer'; import StimTargetIcon from '@material-ui/icons/Reorder'; import { BottomNavigation, BottomNavigationAction } from '@material-ui/core'; -import Utils from '../../../Utils'; -import Select from '../../general/Select'; -import NetPyNEField from '../../general/NetPyNEField'; -import StimulationConditions from './StimulationConditions'; + import Dialog from '@material-ui/core/Dialog/Dialog'; import Button from '@material-ui/core/Button'; @@ -17,10 +14,15 @@ import DialogContent from '@material-ui/core/DialogContent'; import DialogContentText from '@material-ui/core/DialogContentText'; import DialogTitle from '@material-ui/core/DialogTitle'; -import PythonMethodControlledSelectFieldConnection from '../../../redux/reduxconnect/PythonMethodControlledSelectFieldConnection' -var PythonControlledCapability = require('geppetto-client/js/communication/geppettoJupyter/PythonControlledCapability'); -var PythonControlledTextField = PythonControlledCapability.createPythonControlledControl(TextField); -var PythonMethodControlledSelectField = PythonControlledCapability.createPythonControlledControlWithPythonDataFetch(Select); +import Utils from '../../../Utils'; +import StimulationConditions from './StimulationConditions'; + +import { + NetPyNEField, + NetPyNETextField, + NetPyNESelectField +} from 'netpyne/components'; + export default class NetPyNEStimulationTarget extends React.Component { @@ -188,7 +190,7 @@ export default class NetPyNEStimulationTarget extends React.Component {
- - - @@ -212,7 +214,7 @@ export default class NetPyNEStimulationTarget extends React.Component { var extraContent = (
- - - - diff --git a/webapp/components/definition/stimulationTargets/NetPyNEStimulationTargets.js b/webapp/components/definition/stimulationTargets/NetPyNEStimulationTargets.js index d4fcee8a..ccac7aa1 100644 --- a/webapp/components/definition/stimulationTargets/NetPyNEStimulationTargets.js +++ b/webapp/components/definition/stimulationTargets/NetPyNEStimulationTargets.js @@ -1,14 +1,17 @@ import React, { Component } from 'react'; import { Card, CardHeader, CardContent } from '@material-ui/core'; - -import Utils from '../../../Utils'; -import NetPyNEHome from '../../general/NetPyNEHome'; -import NetPyNEAddNew from '../../general/NetPyNEAddNew'; -import NetPyNEThumbnail from '../../general/NetPyNEThumbnail'; -import NetPyNEStimulationTargetConnection from '../../../redux/reduxconnect/NetPyNEStimulationTargetConnection'; import Dialog from '@material-ui/core/Dialog/Dialog'; import Button from '@material-ui/core/Button'; +import Utils from '../../../Utils'; + +import { + NetPyNEHome, + NetPyNEAddNew, + NetPyNEThumbnail, + NetPyNEStimulationTarget +} from 'netpyne/components'; + export default class NetPyNEStimulationTargets extends Component { @@ -165,7 +168,7 @@ export default class NetPyNEStimulationTargets extends Component { } var selectedStimulationTarget = undefined; if ((this.state.selectedStimulationTarget !== undefined) && Object.keys(model).indexOf(this.state.selectedStimulationTarget) > -1) { - selectedStimulationTarget = ; + selectedStimulationTarget = ; } var content = ( @@ -191,11 +194,6 @@ export default class NetPyNEStimulationTargets extends Component { return ( - {content} {dialogPop} diff --git a/webapp/components/definition/stimulationTargets/StimulationConditions.js b/webapp/components/definition/stimulationTargets/StimulationConditions.js index cb3ae43f..938bd758 100644 --- a/webapp/components/definition/stimulationTargets/StimulationConditions.js +++ b/webapp/components/definition/stimulationTargets/StimulationConditions.js @@ -1,22 +1,20 @@ -import React, { Component } from 'react'; -import MenuItem from '@material-ui/core/MenuItem'; -import Select from '../../general/Select'; -import ListComponent from '../../general/List'; -import NetPyNEField from '../../general/NetPyNEField'; -import NetPyNECoordsRange from '../../general/NetPyNECoordsRange'; +import React, { Component } from "react"; +import MenuItem from "@material-ui/core/MenuItem"; -var PythonControlledCapability = require('geppetto-client/js/communication/geppettoJupyter/PythonControlledCapability'); -var PythonControlledListComponent = PythonControlledCapability.createPythonControlledControl(ListComponent); -var PythonMethodControlledSelectField = PythonControlledCapability.createPythonControlledControlWithPythonDataFetch(Select); +import { + NetPyNEField, + NetPyNESelectField, + NetPyNECoordsRange, + ListComponent +} from "netpyne/components"; export default class StimulationConditions extends React.Component { - constructor (props) { super(props); this.state = {}; this.postProcessMenuItems = this.postProcessMenuItems.bind(this); } - + postProcessMenuItems (pythonData, selected) { return pythonData.map(name => ( )); } - - render () { + + render () { var content = (
- - + - - - + - - - + - - - - - - - +
); - + return content; } } diff --git a/webapp/components/definition/synapses/NetPyNESynapse.js b/webapp/components/definition/synapses/NetPyNESynapse.js index 963d3402..3122f98c 100644 --- a/webapp/components/definition/synapses/NetPyNESynapse.js +++ b/webapp/components/definition/synapses/NetPyNESynapse.js @@ -2,7 +2,7 @@ import React, { Component } from 'react'; import TextField from '@material-ui/core/TextField'; import Select from '../../general/Select'; import Utils from '../../../Utils'; -import NetPyNEField from '../../general/NetPyNEField'; + import Dialog from '@material-ui/core/Dialog/Dialog'; import Button from '@material-ui/core/Button'; import { withStyles } from '@material-ui/core/styles' @@ -12,9 +12,10 @@ import DialogContent from '@material-ui/core/DialogContent'; import DialogContentText from '@material-ui/core/DialogContentText'; import DialogTitle from '@material-ui/core/DialogTitle'; -var PythonControlledCapability = require('geppetto-client/js/communication/geppettoJupyter/PythonControlledCapability'); -var PythonControlledTextField = PythonControlledCapability.createPythonControlledControl(TextField); - +import { + NetPyNEField, + NetPyNETextField, +} from "netpyne/components"; const styles = ({ spacing }) => ({ selectField: { marginTop: spacing(3) } }) class NetPyNESynapse extends React.Component { @@ -123,21 +124,21 @@ class NetPyNESynapse extends React.Component { var content = (
- {(this.state.synMechMod == "Exp2Syn") ?
-
: null} - diff --git a/webapp/components/definition/synapses/NetPyNESynapses.js b/webapp/components/definition/synapses/NetPyNESynapses.js index cd3491e3..ee364f8e 100644 --- a/webapp/components/definition/synapses/NetPyNESynapses.js +++ b/webapp/components/definition/synapses/NetPyNESynapses.js @@ -2,13 +2,17 @@ import React,{ Component } from 'react'; import { Card, CardHeader, CardContent } from '@material-ui/core'; import Utils from '../../../Utils'; -import NetPyNESynapseConnection from '../../../redux/reduxconnect/NetPyNESynapseConnection'; -import NetPyNEHome from '../../general/NetPyNEHome'; -import NetPyNEAddNew from '../../general/NetPyNEAddNew'; -import NetPyNEThumbnail from '../../general/NetPyNEThumbnail'; import Dialog from '@material-ui/core/Dialog/Dialog'; import Button from '@material-ui/core/Button'; + +import { + NetPyNEHome, + NetPyNEAddNew, + NetPyNEThumbnail, + NetPyNESynapse +} from 'netpyne/components'; + export default class NetPyNESynapses extends Component { constructor (props) { @@ -165,16 +169,11 @@ export default class NetPyNESynapses extends Component { } var selectedSynapse = undefined; if ((this.state.selectedSynapse !== undefined) && Object.keys(model).indexOf(this.state.selectedSynapse) > -1) { - selectedSynapse = ; + selectedSynapse = ; } return ( -
{selectedSynapse} diff --git a/webapp/components/general/Checkbox.js b/webapp/components/general/Checkbox.js index 965547c9..d5dcb506 100644 --- a/webapp/components/general/Checkbox.js +++ b/webapp/components/general/Checkbox.js @@ -13,7 +13,8 @@ export default class Checkbox extends Component { control={ } diff --git a/webapp/components/general/FileBrowser.js b/webapp/components/general/FileBrowser.js index 06c4f2e3..2fd672b6 100644 --- a/webapp/components/general/FileBrowser.js +++ b/webapp/components/general/FileBrowser.js @@ -1,5 +1,5 @@ import React from 'react'; -import Tree from 'geppetto-client/js/components/interface/tree/Tree' +import Tree from '@geppettoengine/geppetto-client/js/components/interface/tree/Tree' import Utils from '../../Utils'; import Button from '@material-ui/core/Button'; import { changeNodeAtPath } from 'react-sortable-tree'; @@ -142,7 +142,7 @@ export default class FileBrowser extends React.Component { onClose={() => this.props.onRequestClose()} > -
+
{selectMessage} These paths are relative to:
diff --git a/webapp/components/general/HTMLViewer.js b/webapp/components/general/HTMLViewer.js new file mode 100644 index 00000000..a0b3fde3 --- /dev/null +++ b/webapp/components/general/HTMLViewer.js @@ -0,0 +1,89 @@ +import React, { Component, createRef } from 'react' + +import HTMLViewer from '@geppettoengine/geppetto-client/js/components/interface/htmlViewer/HTMLViewer' + +import { withStyles } from '@material-ui/core/styles' + +const style = ({ palette }) => ({ + container: { + display: 'flex', + alignItems: 'center', + justifyContent: 'center', + backgroundColor: palette.common.white + } +}) + + +class CustomHTMLViewer extends Component { + containerRef = createRef() + + dimensions = { width: 200, height: 200 } + + componentDidMount () { + window.addEventListener('resize', this.delayedResize.bind(this)) + this.resizeIfNeeded() + } + + componentWillUnmount () { + clearTimeout(this.timer) + window.removeEventListener('resize', this.delayedResize) + } + + componentDidUpdate (){ + this.resizeIfNeeded() + } + + wasParentResized (dimensions) { + return dimensions.width !== this.dimensions.width || dimensions.height !== this.dimensions.height + } + + getParentSize () { + if (this.containerRef.current === null) { + return false + } + return this.containerRef.current.parentNode.getBoundingClientRect() + } + + getSvgComponent () { + // svg element + return this.containerRef.current.children[0].children[0].children[0] + } + + adjustSVGSize () { + const svg = this.getSvgComponent() + if (svg) { + const width = (this.dimensions.width - 20) > 0 ? `${this.dimensions.width - 20}px` : '0px' + const height = (this.dimensions.height - 20) > 0 ? `${this.dimensions.height - 20}px` : '0px' + svg.removeAttribute('width'); + svg.removeAttribute('height'); + svg.setAttribute('width', width); + svg.setAttribute('height', height); + } + + } + + resizeIfNeeded (){ + const dimensions = this.getParentSize() + + if (dimensions !== false && this.wasParentResized(dimensions)) { + this.dimensions = dimensions + this.adjustSVGSize() + } + } + + delayedResize () { + this.timer = setTimeout(() => this.resizeIfNeeded(), 100) + } + + render () { + const { classes } = this.props + return ( +
+ +
+ ) + } +} + + +export default withStyles(style)(CustomHTMLViewer) \ No newline at end of file diff --git a/webapp/components/general/NetPyNEAddNew.js b/webapp/components/general/NetPyNEAddNew.js index 03056fd9..bb4bc1ef 100644 --- a/webapp/components/general/NetPyNEAddNew.js +++ b/webapp/components/general/NetPyNEAddNew.js @@ -3,11 +3,12 @@ import ContentAdd from '@material-ui/icons/Add'; import Fab from '@material-ui/core/Fab'; import { withStyles } from '@material-ui/core/styles'; -const styles = ({ spacing }) => ({ +const styles = ({ spacing, palette }) => ({ root : { marginLeft: spacing(1), minWidth: 56 - } + }, + plus:{ color: palette.common.white } }) class NetPyNEAddNew extends React.Component { @@ -34,7 +35,7 @@ class NetPyNEAddNew extends React.Component { color='primary' className={classes.root} > - + ); } diff --git a/webapp/components/general/NetPyNECoordsRange.js b/webapp/components/general/NetPyNECoordsRange.js index 9a768848..68958c53 100644 --- a/webapp/components/general/NetPyNECoordsRange.js +++ b/webapp/components/general/NetPyNECoordsRange.js @@ -1,14 +1,12 @@ import React, { Component } from 'react'; import MenuItem from '@material-ui/core/MenuItem'; import TextField from '@material-ui/core/TextField'; + import SelectField from './Select'; import Utils from '../../Utils'; -import NetPyNEField from './NetPyNEField'; -import AdapterComponent from './AdapterComponent'; -var PythonControlledCapability = require('geppetto-client/js/communication/geppettoJupyter/PythonControlledCapability'); -var PythonControlledAdapterComponent = PythonControlledCapability.createPythonControlledControl(AdapterComponent); - +import { AdapterComponent, NetPyNEField } from "netpyne/components"; + export default class NetPyNECoordsRange extends Component { constructor (props) { @@ -105,7 +103,7 @@ export default class NetPyNECoordsRange extends Component { {(this.state.rangeType != undefined) ?
- { if (!state[state.lastUpdated].toString().endsWith(".") @@ -126,7 +124,7 @@ export default class NetPyNECoordsRange extends Component { > - +
: null}
diff --git a/webapp/components/general/NetPyNEHome.js b/webapp/components/general/NetPyNEHome.js index bfd913f9..aec3aadd 100644 --- a/webapp/components/general/NetPyNEHome.js +++ b/webapp/components/general/NetPyNEHome.js @@ -9,7 +9,8 @@ const useStyles = makeStyles(({ spacing, palette }) => ({ root: { display: 'flex', alignItems: 'center' - } + }, + icon: { color: palette.common.white, fontSize: 50 } })) @@ -23,10 +24,9 @@ export default ({ handleClick, selection }) => { onClick={ () => handleClick()} >
diff --git a/webapp/components/general/NetPyNEPythonConsole.js b/webapp/components/general/NetPyNEPythonConsole.js new file mode 100644 index 00000000..f31ad8ce --- /dev/null +++ b/webapp/components/general/NetPyNEPythonConsole.js @@ -0,0 +1,40 @@ +import React, { Component } from 'react' +import Utils from '../../Utils' + +import PythonConsole from '@geppettoengine/geppetto-client/js/components/interface/pythonConsole/PythonConsole'; + +export default class NetPyNEPythonConsole extends Component { + + addMetadataToWindow (data) { + console.log("Initialising NetPyNE Tabs"); + window.metadata = data.metadata; + window.currentFolder = data.currentFolder; + window.isDocker = data.isDocker; + window.pythonConsoleLoaded = true + } + + componentWillUnmount () { + console.log("unmounting python console") + } + + componentDidMount () { + if (!window.pythonConsoleLoaded) { + GEPPETTO.on('jupyter_geppetto_extension_ready', data => { + let project = { id: 1, name: 'Project', experiments: [{ "id": 1, "name": 'Experiment', "status": 'DESIGN' }] } + GEPPETTO.Manager.loadProject(project, false); + GEPPETTO.Manager.loadExperiment(1, [], []); + Utils.execPythonMessage('from netpyne_ui.netpyne_geppetto import netpyne_geppetto'); + Utils.evalPythonMessage('netpyne_geppetto.getData',[]).then(response => { + const data = Utils.convertToJSON(response); + this.addMetadataToWindow(data) + this.props.modelLoaded(); + GEPPETTO.trigger("spinner:hide"); + }) + }); + } + + } + render () { + return + } +} diff --git a/webapp/components/general/NetPyNEThumbnail.js b/webapp/components/general/NetPyNEThumbnail.js index be8b0615..daf1a982 100644 --- a/webapp/components/general/NetPyNEThumbnail.js +++ b/webapp/components/general/NetPyNEThumbnail.js @@ -55,7 +55,7 @@ export default class NetPyNEThumbnail extends React.Component { data-tooltip={isHovered && name.length > 14 ? name : undefined} className={"actionButton " + (selected ? "selectedActionButton" : "")} onClick={() => this.handleClick()} - color={selected ? "secondary" : "primary"} + color={selected ? "primary" : "secondary"} > {(this.state.isHovered && selected) ? : label} diff --git a/webapp/components/general/Select.js b/webapp/components/general/Select.js index c96b6ceb..c6dd3d80 100644 --- a/webapp/components/general/Select.js +++ b/webapp/components/general/Select.js @@ -5,12 +5,20 @@ import MuiSelect from '@material-ui/core/Select'; export default class Select extends Component { render () { + var value = this.props.value + if (this.props.multiple && this.props.value.constructor.name != 'Array') { + // when loading values from a script, we can't allow strings if *multiple* is enabled + value = [this.props.value] + } else if (!this.props.multiple && this.props.value.length === 0 && this.props.value.constructor.name === 'Array' ) { + // when *multiple* is disabled, we can't allow arrays + value = '' + } return ( {this.props.label} diff --git a/webapp/components/general/Tooltip.js b/webapp/components/general/Tooltip.js new file mode 100644 index 00000000..746ee8d7 --- /dev/null +++ b/webapp/components/general/Tooltip.js @@ -0,0 +1,14 @@ +import React from 'react' +import Tooltip from '@material-ui/core/Tooltip' +import { makeStyles } from '@material-ui/core/styles'; + +const tooltipStyle = makeStyles(({ palette, typography }) => ({ + arrow: { color: palette.common.black, }, + tooltip: { backgroundColor: palette.common.black, fontSize: typography.subtitle1.fontSize }, +})); + +export default function CustomTooltip (props) { + const classes = tooltipStyle(); + + return ; +} \ No newline at end of file diff --git a/webapp/components/index.js b/webapp/components/index.js new file mode 100644 index 00000000..ff5cd359 --- /dev/null +++ b/webapp/components/index.js @@ -0,0 +1,259 @@ +import React from 'react' +import { connect } from "react-redux"; +import PythonControlledCapability from "geppetto-client/js/communication/geppettoJupyter/PythonControlledCapability"; + +import { + activateWidget, + destroyWidget, + minimizeWidget, + maximizeWidget, + newWidget +} from "../redux/actions/flexlayout"; +import { openBackendErrorDialog, closeBackendErrorDialog } from '../redux/actions/errors'; +import { + updateCards, editModel, simulateNetwork, createNetwork, + createAndSimulateNetwork, showNetwork, pythonCall, modelLoaded +} from "../redux/actions/general"; +import { closeDrawerDialogBox, openDrawerDialogBox } from '../redux/actions/drawer'; + +const updateCardsDispatch = dispatch => ({ updateCards: () => dispatch(updateCards) }); +const pythonCallErrorDispatch = dispatch => ({ pythonCallErrorDialogBox: payload => dispatch(openBackendErrorDialog(payload)) }); + +/** **** COMPONENT PROXIES ******/ + +// Python controlled + +import TextField from "@material-ui/core/TextField"; +export const NetPyNETextField = PythonControlledCapability.createPythonControlledControl( + TextField +); + +import _NetPyNECellRules from "./definition/cellRules/NetPyNECellRules"; +export const NetPyNECellRules = PythonControlledCapability.createPythonControlledComponent( + _NetPyNECellRules +); + +import _NetPyNEConnectivityRules from "./definition/connectivity/NetPyNEConnectivityRules"; +export const NetPyNEConnectivityRules = PythonControlledCapability.createPythonControlledComponent( + _NetPyNEConnectivityRules +); + +import _NetPyNEPlots from "./definition/plots/NetPyNEPlots"; +export const NetPyNEPlots = PythonControlledCapability.createPythonControlledComponent( + _NetPyNEPlots +); + +import _ListComponent from "./general/List"; +export const ListComponent = PythonControlledCapability.createPythonControlledControl( + _ListComponent +); + +import _AdapterComponent from "./general/AdapterComponent"; +export const AdapterComponent = PythonControlledCapability.createPythonControlledControl( + _AdapterComponent +); + +import Checkbox from "./general/Checkbox"; +export const NetPyNECheckbox = PythonControlledCapability.createPythonControlledControl( + Checkbox +); + +import _NetPyNEStimulationTargets from "./definition/stimulationTargets/NetPyNEStimulationTargets"; +export const NetPyNEStimulationTargets = PythonControlledCapability.createPythonControlledComponent( + _NetPyNEStimulationTargets +); + +// ---------------------------------------------------------------------------------------- // + +// CONNECT + +import _Dimensions from "./definition/populations/Dimensions"; +export const Dimensions = connect( + (state, ownProps) => ({ ...ownProps }), + updateCardsDispatch +)(_Dimensions); + + +import _NetPyNE from "./NetPyNE"; +export const NetPyNE = connect( + state => ({ editMode: state.general.editMode, }), + pythonCallErrorDispatch +)(_NetPyNE); + +import _NetPyNECellRule from "./definition/cellRules/NetPyNECellRule"; +export const NetPyNECellRule = connect((state, ownProps) => ({ + ...ownProps, + updates: state.general.updates +}))(_NetPyNECellRule); + + +import _LayoutManager from "./layout/LayoutManager"; +export const LayoutManager = connect( + state => ({ ...state.flexlayout, editMode: state.general.editMode }), + dispatch => ({ + minimizeWidget: id => dispatch(minimizeWidget(id)), + destroyWidget: id => dispatch(destroyWidget(id)), + maximizeWidget: id => dispatch(maximizeWidget(id)), + activateWidget: id => dispatch(activateWidget(id)) + }) +)(_LayoutManager); + + +import _NetPyNEPopulation from "./definition/populations/NetPyNEPopulation"; +export const NetPyNEPopulation = connect( + null, + updateCardsDispatch +)(_NetPyNEPopulation); + +import _NetPyNEPopulations from "./definition/populations/NetPyNEPopulations"; +export const NetPyNEPopulations = connect( + null, + updateCardsDispatch +)( + PythonControlledCapability.createPythonControlledComponent( + _NetPyNEPopulations + ) +); + +import _NetPyNEStimulationSource from "./definition/stimulationSources/NetPyNEStimulationSource"; +export const NetPyNEStimulationSource = connect( + null, + updateCardsDispatch +)(_NetPyNEStimulationSource); + +import _NetPyNEStimulationSources from "./definition/stimulationSources/NetPyNEStimulationSources"; +export const NetPyNEStimulationSources = connect( + null, + updateCardsDispatch +)( + PythonControlledCapability.createPythonControlledComponent( + _NetPyNEStimulationSources + ) +); + +import _NetPyNEStimulationTarget from "./definition/stimulationTargets/NetPyNEStimulationTarget"; +export const NetPyNEStimulationTarget = connect( + (state, ownProps) => ({ + ...ownProps, + updates: state.general.updates + }), + updateCardsDispatch +)(_NetPyNEStimulationTarget); + + +import _NetPyNESynapse from "./definition/synapses/NetPyNESynapse"; +export const NetPyNESynapse = connect( + null, + updateCardsDispatch +)(_NetPyNESynapse); + +import _NetPyNESynapses from "./definition/synapses/NetPyNESynapses"; +export const NetPyNESynapses = connect( + null, + updateCardsDispatch +)(PythonControlledCapability.createPythonControlledComponent(_NetPyNESynapses)); + +import _NetPyNETabs from "./settings/NetPyNETabs"; +export const NetPyNETabs = connect( + state => state.general, + dispatch => ({ + editModel: () => dispatch(editModel), + createNetwork: () => dispatch(createNetwork), + createAndSimulateNetwork: () => dispatch(createAndSimulateNetwork), + showNetwork: () => dispatch(showNetwork) + }) +)(_NetPyNETabs); + +import _NetPyNEToolbar from "./settings/NetPyNEToolBar"; +export const NetPyNEToolBar = connect( + state => ({ ...state.general, ...state.drawer }), + dispatch => ({ + closeDrawerDialogBox: () => dispatch(closeDrawerDialogBox), + openDrawerDialogBox: () => dispatch(openDrawerDialogBox), + }) +)(_NetPyNEToolbar); + +import SelectField from "./general/Select"; +export const NetPyNESelectField = connect((state, ownProps) => ({ + ...ownProps, + updates: String(state.general.updates) +}))(PythonControlledCapability.createPythonControlledControlWithPythonDataFetch( + SelectField +)); + + +import _NetPyNEInclude from './definition/plots/NetPyNEInclude'; +export const NetPyNEInclude = connect( + (state, ownProps) => ({ ...ownProps, updates: state.general.updates }), + null +)(_NetPyNEInclude); + +import _NetPyNEInstantiated from "./instantiation/NetPyNEInstantiated" +export const NetPyNEInstantiated = connect( + state => ({ modelState: state.general.modelState }), + null +)(_NetPyNEInstantiated) + +import _NetWorkControlButtons from './instantiation/NetWorkControlButtons' +export const NetWorkControlButtons = connect( + state => ({ modelState: state.general.modelState }), + dispatch => ({ + createAndSimulateNetwork: () => dispatch(createAndSimulateNetwork), + simulateNetwork: () => dispatch(simulateNetwork), + }) +)(_NetWorkControlButtons) + +import _ActionDialog from './settings/actions/ActionDialog' +export const ActionDialog = connect( + state => ({ ...state.errors, openErrorDialogBox: state.errors.openDialog }), + dispatch => ({ + pythonCall: (cmd, args) => dispatch(pythonCall(cmd, args)), + closeBackendErrorDialog: () => dispatch(closeBackendErrorDialog) + }) +)(_ActionDialog) + + +import _PlotButton from './instantiation/PlotButtons' +export const PlotButtons = connect( + state => ({ ...state.errors, openErrorDialogBox: state.errors.openDialog }), + dispatch => ({ + newWidget: conf => dispatch(newWidget(conf)), + closeBackendErrorDialog: () => dispatch(closeBackendErrorDialog), + pythonCallErrorDialogBox: payload => dispatch(openBackendErrorDialog(payload)) + }) +)(_PlotButton) + + +import _NetPyNEPythonConsole from './general/NetPyNEPythonConsole' +export const NetPyNEPythonConsole = connect( + null, + dispatch => ({ + modelLoaded: () => dispatch(modelLoaded), + newWidget: widget => dispatch(newWidget(widget)), + activateWidget: widgetId => dispatch(activateWidget(widgetId)) + }) +)(_NetPyNEPythonConsole) + +import _Drawer from './settings/Drawer' +export const Drawer = connect( + state => ({ + widgets: state.flexlayout.widgets, + editMode: state.general.editMode + }), + dispatch => ({ + newWidget: widget => dispatch(newWidget(widget)), + activateWidget: widgetId => dispatch(activateWidget(widgetId)) + }) +)(_Drawer) + +// ---------------------------------------------------------------------------------------- // + +// DEFAULTS +export { default as NetPyNEHome } from "./general/NetPyNEHome"; +export { default as NetPyNEField } from "./general/NetPyNEField"; +export { default as NetPyNEAddNew } from "./general/NetPyNEAddNew"; +export { default as NetPyNEThumbnail } from "./general/NetPyNEThumbnail"; +export { default as NetPyNECoordsRange } from "./general/NetPyNECoordsRange"; +export { default as NetPyNESimConfig } from "./definition/configuration/NetPyNESimConfig"; +export { default as HTMLViewer } from './general/HTMLViewer' +export { default as Tooltip } from './general/Tooltip' \ No newline at end of file diff --git a/webapp/components/instantiation/NetPyNEInstantiated.js b/webapp/components/instantiation/NetPyNEInstantiated.js index 146b5af1..e61afdab 100644 --- a/webapp/components/instantiation/NetPyNEInstantiated.js +++ b/webapp/components/instantiation/NetPyNEInstantiated.js @@ -1,71 +1,26 @@ -import React from 'react'; -import Dialog from '@material-ui/core/Dialog'; -import Button from '@material-ui/core/Button'; -import Canvas from 'geppetto-client/js/components/interface/3dCanvas/Canvas'; +import React, { createRef } from 'react'; +import ReactDOM from 'react-dom' +import Canvas from '@geppettoengine/geppetto-client/js/components/interface/3dCanvas/Canvas'; import ControlPanel from 'geppetto-client/js/components/interface/controlPanel/controlpanel'; -import IconButton from 'geppetto-client/js/components/controls/iconButton/IconButton'; -import Menu from '@material-ui/core/Menu'; -import MenuItem from '@material-ui/core/MenuItem'; -import Utils from '../../Utils'; +import IconButton from '@geppettoengine/geppetto-client/js/components/controls/iconButton/IconButton'; +import { NetWorkControlButtons } from 'netpyne/components' -import DialogActions from '@material-ui/core/DialogActions'; -import DialogContent from '@material-ui/core/DialogContent'; -import DialogContentText from '@material-ui/core/DialogContentText'; -import DialogTitle from '@material-ui/core/DialogTitle'; - const styles = { modal: { position: 'absolute !important', backgroundColor: 'rgba(0, 0, 0, 0.6)', zIndex: '999', height: '100%', - width: '100%', - top: 0 - }, - - menuItemDiv: { - fontSize: '12px', - lineHeight: '28px' + width: '98%', + overflow: 'hidden' }, - menuItem: { - lineHeight: '28px', - minHeight: '28px' - }, instantiatedContainer: { height: '100%', width: '100%', - position: 'fixed' }, - controlpanelBtn: { - position: 'absolute', - left: 34, - top: 280 - }, - plotBtn: { - position: 'absolute', - left: 34, - top: 317 - } - }; -const plots = [ - { id: 'connectionPlot', primaryText: 'Connectivity', plotName: 'Connections Plot', plotMethod: 'plotConn', plotType: false }, - { id: '2dNetPlot', primaryText: '2D network', plotName: '2D Net Plot', plotMethod: 'plot2Dnet', plotType: false }, - { id: 'shapePlot', primaryText: 'Cell shape', plotName: 'Shape Plot', plotMethod: 'plotShape', plotType: false }, - { id: 'tracesPlot', primaryText: 'Cell traces', plotName: 'Traces Plot', plotMethod: 'plotTraces', plotType: false }, - { id: 'rasterPlot', primaryText: 'Raster plot', plotName: 'Raster Plot', plotMethod: 'plotRaster', plotType: false }, - { id: 'spikePlot', primaryText: 'Spike histogram', plotName: 'Spike Hist Plot', plotMethod: 'plotSpikeHist', plotType: false }, - { id: 'spikeStatsPlot', primaryText: 'Spike stats', plotName: 'Spike Stats Plot', plotMethod: 'plotSpikeStats', plotType: false }, - { id: 'ratePSDPlot', primaryText: 'Rate PSD', plotName: 'Rate PSD Plot', plotMethod: 'plotRatePSD', plotType: false }, - { id: 'LFPTimeSeriesPlot', primaryText: 'LFP time-series', plotName: 'LFP Time Series Plot', plotMethod: 'plotLFP', plotType: 'timeSeries' }, - { id: 'LFPLocationsPlot', primaryText: 'LFP PSD', plotName: 'LFP PSD Plot', plotMethod: 'plotLFP', plotType: 'PSD' }, - { id: 'LFPSpectrogramPlot', primaryText: 'LFP spectrogram', plotName: 'LFP Spectrogram Plot', plotMethod: 'plotLFP', plotType: 'spectrogram' }, - { id: 'LFPLocationsPlot', primaryText: 'LFP locations', plotName: 'LFP Locations Plot', plotMethod: 'plotLFP', plotType: 'locations' }, - { id: 'grangerPlot', primaryText: 'Granger causality plot', plotName: 'Granger Plot', plotMethod: 'granger', plotType: false }, - { id: 'rxdConcentrationPlot',primaryText: 'RxD concentration plot', plotName: 'RxD concentration plot', plotMethod: 'plotRxDConcentration', plotType: false } -]; export default class NetPyNEInstantiated extends React.Component { @@ -74,200 +29,98 @@ export default class NetPyNEInstantiated extends React.Component { this.state = { model: props.model, controlPanelHidden: true, - plotButtonOpen: false, - openDialog: false, - bringItToFront: 0 + bringItToFront: 0, + update: 0 }; - - this.widgets = []; - this.plotFigure = this.plotFigure.bind(this); - this.newPlotWidget = this.newPlotWidget.bind(this); - this.getOpenedWidgets = this.getOpenedWidgets.bind(this); - this.handleClick = this.handleClick.bind(this); - this.handleRequestClose = this.handleRequestClose.bind(this); + this.dimensions = { width: 200, height: 200 } + this.canvasRef = createRef(); } - - handleCloseDialog = () => { - this.setState({ openDialog: false }); - }; - newPlotWidget (name, svgResponse, data, i, total) { - if (svgResponse === '') { - return - } - var s = svgResponse; - var that = this; - G.addWidget(1).then(w => { - if (total == 0) { - w.setName(name); - } else { - w.setName(name + " " + i); - } - w.$el.append(s); - var svg = $(w.$el).find("svg")[0]; - svg.removeAttribute('width'); - svg.removeAttribute('height'); - svg.setAttribute('width', '100%'); - svg.setAttribute('height', '98%'); - that.widgets.push(w); - if (i < total) { - that.newPlotWidget(name, data[i++], data, i++, total) - } - w.showHistoryIcon(false); - w.showHelpIcon(false); - }); - } + componentDidUpdate (){ + this.resizeIfNeeded() + } - processError (response, plotName) { - var parsedResponse = Utils.getErrorResponse(response); - if (parsedResponse) { - this.setState({ - dialogTitle: "NetPyNE returned an error plotting " + plotName, - dialogMessage: parsedResponse['message'] + "\n " + parsedResponse['details'], - openDialog: true - }); - return true; - } - return false; - } - plotFigure (plotName, plotMethod, plotType = false) { - Utils.evalPythonMessage('netpyne_geppetto.getPlot', [plotMethod, plotType], false) - .then(response => { - // TODO Fix this, use just JSON - if (typeof response === 'string'){ - if (response.startsWith("{") && response.endsWith("}")) { - if (this.processError(response, plotName)){ - return; - } - } - if (response.startsWith("[") && response.endsWith("]")) { - response = eval(response); - } - } - if ($.isArray(response)) { - this.newPlotWidget(plotName, response[0], response, 0, response.length - 1); - } else if (response == -1) { - this.processError(response, plotName) - } else { - this.newPlotWidget(plotName, response, response, 0, 0); - } - }); - } + componentDidMount () { + this.canvasRef.current.engine.setLinesThreshold(10000); + this.canvasRef.current.displayAllInstances(); + this.canvasRef.current.setBackgroundColor('#191919') - getOpenedWidgets () { - return this.widgets; - } + window.addEventListener('resize', this.delayedResize.bind(this)) + this.resizeIfNeeded() + this.updateInstances() - showWidgets (visible) { - GEPPETTO.WidgetFactory.getController(GEPPETTO.Widgets.POPUP).then(controller => { - controller.widgets.forEach(widget => { - if (visible){ - widget.show() - } else { - widget.hide() - } - }) - }) - } + GEPPETTO.on(GEPPETTO.Events.Control_panel_close, () => { + this.setState({ bringItToFront: 0 }) + }); + } - componentDidMount () { - this.refs.canvas.engine.setLinesThreshold(10000); - this.refs.canvas.displayAllInstances(); - GEPPETTO.on(GEPPETTO.Events.Control_panel_close, () => { - this.setState({ bringItToFront: 0 }) - this.showWidgets(true) - }); - } + componentWillUnmount (){ + GEPPETTO.off(GEPPETTO.Events.Control_panel_close) + clearTimeout(this.timer) + window.removeEventListener('resize', this.delayedResize) + } - componentWillUnmount (){ - GEPPETTO.off(GEPPETTO.Events.Control_panel_close) + updateInstances () { + if (Instances.network) { + // update canvas only if there are instances to show + this.canvasRef.current.engine.updateSceneWithNewInstances(window.Instances); } + } - handleClick (event) { - // This prevents ghost click. - event.preventDefault(); + resizeCanvas () { + this.setState({ update: this.state.update++ }) + } - this.setState({ - plotButtonOpen: true, - anchorEl: event.currentTarget, - }); + resizeIfNeeded (){ + const dimensions = this.getParentSize() + if (dimensions !== false && this.wasParentResized(dimensions)) { + this.dimensions = dimensions + this.resizeCanvas() } + } + + wasParentResized (dimensions) { + return dimensions.width !== this.dimensions.width || dimensions.height !== this.dimensions.height + } - handleRequestClose () { - this.setState({ plotButtonOpen: false, }); - } + delayedResize () { + this.timer = setTimeout(() => this.resizeIfNeeded(), 100) + } - render () { - return ( -
- -
- - -
- { - $('#controlpanel').show(); this.showWidgets(false); this.setState({ bringItToFront: 1 }) - }} - icon={"fa-list"} - id={"ControlPanelButton"} /> -
- - - {plots.map((plot, index) => ( - this.plotFigure(plot.plotName, plot.plotMethod, plot.plotType)} - > - {plot.primaryText} - - ))} - -
+ getParentSize () { + if (this.canvasRef.current === null) { + return false + } + const node = ReactDOM.findDOMNode(this) + return node.parentNode.getBoundingClientRect() + } - + + +
+ - {this.state.dialogTitle} - - - {this.state.dialogMessage} - - - - - -
+
- ); - } + + +
+ + ); + } } \ No newline at end of file diff --git a/webapp/components/instantiation/NetWorkControlButtons.js b/webapp/components/instantiation/NetWorkControlButtons.js new file mode 100644 index 00000000..413774b0 --- /dev/null +++ b/webapp/components/instantiation/NetWorkControlButtons.js @@ -0,0 +1,90 @@ +import React from 'react'; +import IconButton from '@geppettoengine/geppetto-client/js/components/controls/iconButton/IconButton'; +import { Tooltip } from 'netpyne/components'; + +import { MODEL_STATE } from '../../constants' +import { withStyles } from '@material-ui/core/styles'; +import { PlotButtons } from 'netpyne/components' + +const styles = ({ spacing }) => ({ + container: { + position: 'absolute', + top: spacing(2), + right: spacing(2) + }, + innerContainer: { position: 'relative' }, + buttons: { marginBottom: spacing(1) } +}) + + +class NetWorkControlButtons extends React.Component { + render () { + const { classes, modelState } = this.props + const disableSimulate = modelState === MODEL_STATE.SIMULATED + const disableRefreshInstance = modelState === MODEL_STATE.INSTANTIATED || modelState === MODEL_STATE.SIMULATED + + return ( +
+
+ + +
+ $('#controlpanel').show()} + icon={"fa-list"} + id="ControlPanelButton" + /> +
+
+ + +
+ this.props.createNetwork()} + disabled={disableRefreshInstance} + /> +
+ +
+ + + +
+ disableRefreshInstance ? this.props.simulateNetwork() : this.props.createAndSimulateNetwork()} + disabled={disableSimulate} + /> +
+ +
+ + + + +
+
+ + ); + } +} + +export default withStyles(styles)(NetWorkControlButtons) \ No newline at end of file diff --git a/webapp/components/instantiation/PlotButtons.js b/webapp/components/instantiation/PlotButtons.js new file mode 100644 index 00000000..0f57fa32 --- /dev/null +++ b/webapp/components/instantiation/PlotButtons.js @@ -0,0 +1,193 @@ +import React from 'react'; + +import Dialog from '@material-ui/core/Dialog'; +import Button from '@material-ui/core/Button'; +import IconButton from '@geppettoengine/geppetto-client/js/components/controls/iconButton/IconButton'; +import Menu from '@material-ui/core/Menu'; +import MenuItem from '@material-ui/core/MenuItem'; +import Utils from '../../Utils'; +import { Tooltip } from 'netpyne/components'; + +import DialogActions from '@material-ui/core/DialogActions'; +import DialogContent from '@material-ui/core/DialogContent'; +import DialogTitle from '@material-ui/core/DialogTitle'; +import { withStyles } from '@material-ui/core/styles'; + +const plots = [ + { id: 'connectionPlot', primaryText: 'Connectivity', plotName: 'Connections Plot', plotMethod: 'plotConn', plotType: false }, + { id: '2dNetPlot', primaryText: '2D network', plotName: '2D Net Plot', plotMethod: 'plot2Dnet', plotType: false }, + { id: 'shapePlot', primaryText: 'Cell shape', plotName: 'Shape Plot', plotMethod: 'plotShape', plotType: false }, + { id: 'tracesPlot', primaryText: 'Cell traces', plotName: 'Traces Plot', plotMethod: 'plotTraces', plotType: false }, + { id: 'rasterPlot', primaryText: 'Raster plot', plotName: 'Raster Plot', plotMethod: 'plotRaster', plotType: false }, + { id: 'spikePlot', primaryText: 'Spike histogram', plotName: 'Spike Hist Plot', plotMethod: 'plotSpikeHist', plotType: false }, + { id: 'spikeStatsPlot', primaryText: 'Spike stats', plotName: 'Spike Stats Plot', plotMethod: 'plotSpikeStats', plotType: false }, + { id: 'ratePSDPlot', primaryText: 'Rate PSD', plotName: 'Rate PSD Plot', plotMethod: 'plotRatePSD', plotType: false }, + { id: 'LFPTimeSeriesPlot', primaryText: 'LFP time-series', plotName: 'LFP Time Series Plot', plotMethod: 'plotLFP', plotType: 'timeSeries' }, + { id: 'LFPLocationsPlot', primaryText: 'LFP PSD', plotName: 'LFP PSD Plot', plotMethod: 'plotLFP', plotType: 'PSD' }, + { id: 'LFPSpectrogramPlot', primaryText: 'LFP spectrogram', plotName: 'LFP Spectrogram Plot', plotMethod: 'plotLFP', plotType: 'spectrogram' }, + { id: 'LFPLocationsPlot', primaryText: 'LFP locations', plotName: 'LFP Locations Plot', plotMethod: 'plotLFP', plotType: 'locations' }, + { id: 'grangerPlot', primaryText: 'Granger causality plot', plotName: 'Granger Plot', plotMethod: 'granger', plotType: false }, + { id: 'rxdConcentrationPlot',primaryText: 'RxD concentration plot', plotName: 'RxD concentration plot', plotMethod: 'plotRxDConcentration', plotType: false } +]; + + +const styles = ({ spacing }) => ({}) + + +class PlotButtons extends React.Component { + + constructor (props) { + super(props); + this.state = { + plotButtonOpen: false, + openDialog: false, + }; + + this.plotFigure = this.plotFigure.bind(this); + this.newPlotWidget = this.newPlotWidget.bind(this); + this.handleClick = this.handleClick.bind(this); + this.handleRequestClose = this.handleRequestClose.bind(this); + } + + newPlotWidget (name, svgResponse, data, i, total) { + if (svgResponse === '') { + return + } + const pathName = `network.${name.replace(/ /g, '')}_${i}` + if (!window.plotSvgImages) { + window.plotSvgImages = { [pathName]: svgResponse } + } else { + window.plotSvgImages[pathName] = svgResponse + } + + this.props.newWidget({ + path: pathName, + component: 'Plot', + panelName: 'plotPanel' + }) + + if (i < total) { + this.newPlotWidget(name, data[i++], data, i++, total) + } + + if (i === total) { + this.handleRequestClose() + } + } + + processError (response, plotName) { + var parsedResponse = Utils.getErrorResponse(response); + if (parsedResponse) { + const payload = { + errorMessage: parsedResponse['message'], + errorDetails: parsedResponse['details'], + } + this.props.pythonCallErrorDialogBox(payload) + return true; + } + return false; + } + + plotFigure (plotName, plotMethod, plotType = false) { + Utils.evalPythonMessage('netpyne_geppetto.getPlot', [plotMethod, plotType], false) + .then(response => { + // TODO Fix this, use just JSON + if (typeof response === 'string'){ + if (response.startsWith("{") && response.endsWith("}")) { + if (this.processError(response, plotName)){ + return; + } + } + if (response.startsWith("[") && response.endsWith("]")) { + response = eval(response); + } + } + if ($.isArray(response)) { + this.newPlotWidget(plotName, response[0], response, 0, response.length - 1); + } else if (response == -1) { + this.processError(response, plotName) + } else { + this.newPlotWidget(plotName, response, response, 0, 0); + } + }); + } + + + handleClick (event) { + // This prevents ghost click. + event.preventDefault(); + + this.setState({ + plotButtonOpen: true, + anchorEl: event.currentTarget, + }); + } + + handleRequestClose () { + this.setState({ plotButtonOpen: false, }); + } + + handleCloseDialog () { + this.props.closeBackendErrorDialog() + } + + + render () { + const { classes } = this.props + return ( +
+ + + + + + {plots.map((plot, index) => ( + { + this.plotFigure(plot.plotName, plot.plotMethod, plot.plotType) + }} + > + {plot.primaryText} + + ))} + + + this.handleCloseDialog()} + style={{ whiteSpace: "pre-wrap" }} + > + {this.props.errorMessage} + + {Utils.parsePythonException(this.props.errorDetails)} + + + + + +
+ + ); + } +} + +export default withStyles(styles)(PlotButtons) \ No newline at end of file diff --git a/webapp/components/layout/LayoutManager.js b/webapp/components/layout/LayoutManager.js new file mode 100644 index 00000000..5f7ae021 --- /dev/null +++ b/webapp/components/layout/LayoutManager.js @@ -0,0 +1,303 @@ +import React, { Component } from 'react'; +import * as FlexLayout from 'geppetto-client/js/components/interface/flexLayout2/src/index'; +import Actions from 'geppetto-client/js/components/interface/flexLayout2/src/model/Actions'; + + +import { WidgetStatus } from '../../constants'; +import { isEqual } from '../../Utils'; +import WidgetFactory from './WidgetFactory'; +import TabsetIconFactory from './TabsetIconFactory' +import defaultLayoutConfiguration from './layoutConf.json'; +import simulateLayoutConfiguration from './simulateLayoutConf.json'; +import Icon from '@material-ui/core/Icon'; + +import { withStyles } from '@material-ui/core/styles' + + +/** + * Transforms a widget configutation into a flexlayout node descriptor + */ +function widget2Node (configuration) { + const { id, name, component, instancePath, status, panelName, enableClose = true, ...others } = configuration; + return { + id, + name, + status, + component, + type: "tab", + enableRename: false, + enableClose: enableClose, + // attr defined inside config, will also be available from within flexlayout nodes. For example: node.getNodeById(id).getConfig() + config: configuration , + ...others + }; +} + +const styles = ({ spacing }) => ({ + container: { + position: 'relative', + flexGrow: 1, + display: 'flex', + flexDirection: 'row', + alignItems: 'stretch' + }, + spacer: { width: spacing(1) }, + flexlayout: { position: 'relative', flexGrow: 1 } +}) + + +class LayoutManager extends Component { + + constructor (props) { + super(props); + const layout = this.props.layout ? this.props.layout : defaultLayoutConfiguration; + this.destroyWidget = this.props.destroyWidget ? this.props.destroyWidget : () => console.debug('destroyWidget not defined'); + this.activateWidget = this.props.activateWidget ? this.props.activateWidget : () => console.debug('activateWidget not defined'); + this.maximizeWidget = this.props.maximizeWidget ? this.props.maximizeWidget : () => console.debug('maximizeWidget not defined'); + this.minimizeWidget = this.props.minimizeWidget ? this.props.minimizeWidget : () => console.debug('minimizeWidget not defined'); + + + this.widgetFactory = this.props.widgetFactory ? this.props.widgetFactory : new WidgetFactory(); + this.tabsetIconFactory = this.props.TabsetIconFactory ? this.props.TabsetIconFactory : new TabsetIconFactory(); + + this.cacheModels = { + edit: FlexLayout.Model.fromJson(layout), + simulate: FlexLayout.Model.fromJson(simulateLayoutConfiguration) + } + } + + + componentDidMount () { + const { widgets } = this.props; + this.addWidgets(Object.values(widgets)); + } + + componentDidUpdate (prevProps, prevState) { + if (prevProps.editMode !== this.props.editMode) { + return + } + + const { widgets } = this.props; + const oldWidgets = prevProps.widgets; + const newWidgets = this.findNewWidgets(widgets, oldWidgets); + if (newWidgets.length > 0) { + this.addWidgets(newWidgets); + } + + if (!this.props.editMode && !this.cacheModels.simulate.getNodeById(widgets.python.id)) { + this.addWidgets([widgets.python]); + } + + const updatedWidgets = this.findUpdatedWidgets(widgets, oldWidgets); + if (updatedWidgets) { + this.updateWidgets(updatedWidgets); + } + + const deletedWidgets = this.findDeletedWidgets(widgets, oldWidgets); + + if (deletedWidgets) { + this.deleteWidgets(deletedWidgets); + } + } + + getModel () { + if (this.props.editMode) { + return this.cacheModels.edit + } + return this.cacheModels.simulate + } + + addWidgets (widgets) { + const model = this.getModel() + for (let newWidgetDescriptor of widgets) { + + if (!model.getNodeById(newWidgetDescriptor.id)) { + this.addWidget(newWidgetDescriptor); + } else { + console.warn('Should not be here in addWidgets...'); + } + + } + for (let widget of widgets) { + + if (widget.status == WidgetStatus.ACTIVE) { + model.doAction(FlexLayout.Actions.selectTab(widget.id)); + } + + } + // window.dispatchEvent(new Event('resize')); + } + + deleteWidgets (widgets) { + const model = this.getModel() + for (let widget of widgets) { + model.doAction(FlexLayout.Actions.deleteTab(widget.id)); + } + } + + addWidget (widgetConfiguration) { + this.refs.layout.addTabToTabSet(widgetConfiguration.panelName, widget2Node(widgetConfiguration)); + } + + updateWidgets (widgets) { + const model = this.getModel() + for (let widget of widgets) { + + this.updateWidget(widget); + + // This updates plotly.js plots to new panel sizes + if (widget.status == WidgetStatus.ACTIVE) { + model.doAction(FlexLayout.Actions.selectTab(widget.id)); + } + + } + // window.dispatchEvent(new Event('resize')); + } + + updateWidget (widget) { + const model = this.getModel() + if (widget) { + this.widgetFactory.updateWidget(widget); + model.doAction(Actions.updateNodeAttributes(widget.id, widget2Node(widget))); + } + + } + + + factory (node) { + return this.widgetFactory.factory(node.getConfig()); + } + + iconFactory (node) { + // TODO move to newest flexlayout-react to add this functionality + return this.tabsetIconFactory.factory(node.getConfig()); + } + + + findNewWidgets (widgets, oldWidgets) { + if (oldWidgets) { + return Object.values(widgets).filter(widget => widget && !oldWidgets[widget.id]) + } + return Object.values(widgets) + } + + findUpdatedWidgets (widgets, oldWidgets) { + return oldWidgets + ? Object.values(widgets) + .filter(widget => widget && oldWidgets[widget.id] && !isEqual(widget, oldWidgets[widget.id])) + : Object.values(widgets); + } + + findDeletedWidgets (widgets, oldWidgets) { + return oldWidgets ? Object.values(oldWidgets).filter(widget => widget && !widgets[widget.id]) : Object.values(widgets); + } + + + onAction (action) { + const model = this.getModel() + switch (action.type){ + case Actions.SET_ACTIVE_TABSET: + break; + case Actions.SELECT_TAB: + this.activateWidget(action.data.tabNode); + window.dispatchEvent(new Event('resize')); + break; + case Actions.DELETE_TAB: + this.onActionDeleteWidget(action); + window.dispatchEvent(new Event('resize')); + break; + case Actions.MAXIMIZE_TOGGLE: + this.onActionMaximizeWidget(action); + window.dispatchEvent(new Event('resize')); + break; + case Actions.ADJUST_SPLIT: + case Actions.MOVE_NODE : + window.dispatchEvent(new Event('resize')); + break; + } + + model.doAction(action); + } + + onActionMaximizeWidget (action) { + const model = this.getModel() + const { widgets } = this.props; + const { maximizeWidget, activateWidget } = this; + const panel2maximize = model.getNodeById(action.data.node); + + if (panel2maximize.getChildren().length > 0) { + const widgetId2maximize = panel2maximize.getSelectedNode().getId(); + const maximizedWidget = this.findMaximizedWidget(widgets); + if (maximizedWidget) { + if (maximizedWidget.id !== widgetId2maximize) { + maximizeWidget(widgetId2maximize); + } + activateWidget(maximizedWidget.id); + + } else { + maximizeWidget(widgetId2maximize); + } + } + + } + + findMaximizedWidget (widgets) { + return Object.values(widgets).find(widget => widget && widget.status == WidgetStatus.MAXIMIZED); + } + + onActionDeleteWidget (action) { + const model = this.getModel() + const { widgets } = this.props; + const maximizedWidget = this.findMaximizedWidget(widgets); + // change widget status + this.destroyWidget(action.data.node); + // check if the current maximized widget is the same than in the action dispatched + if (maximizedWidget && maximizedWidget.id == action.data.node) { + // find if there exists another widget in the maximized panel that could take its place + const panelChildren = model.getActiveTabset().getChildren(); + const index = panelChildren.findIndex(child => child.getId() == action.data.node); + // Understand if the tab to the left or right of the destroyed tab will be the next one to be maximized + if (index != -1 && panelChildren.length > 1) { + if (index == 0) { + this.onActionMaximizeWidget(panelChildren[1].getId()); + } else { + this.onActionMaximizeWidget(panelChildren[index - 1].getId()); + } + } + } + } + + + clickOnBordersAction (node) { + const model = this.getModel() + model.doAction(FlexLayout.Actions.moveNode(node.getId(), 'bottomPanel', FlexLayout.DockLocation.CENTER, 0)); + } + + onRenderTab (node,renderValues) { + renderValues.leading = + } + render () { + const { classes } = this.props + return ( +
+
+
+ this.onAction(action)} + clickOnBordersAction={node => this.clickOnBordersAction(node)} + onRenderTab={(node,renderValues) => this.onRenderTab(node,renderValues)} + /> +
+ +
+
+ ) + } +} + + +export default withStyles(styles)(LayoutManager) \ No newline at end of file diff --git a/webapp/components/layout/TabsetIconFactory.js b/webapp/components/layout/TabsetIconFactory.js new file mode 100644 index 00000000..01ab2281 --- /dev/null +++ b/webapp/components/layout/TabsetIconFactory.js @@ -0,0 +1,14 @@ +import React from 'react'; + +import Icon from '@material-ui/core/Icon' + +export default class TabsetIconFactory{ + + factory (widgetConfig) { + return this.createIcon(widgetConfig.icon) + } + + createIcon (iconName) { + return + } +} \ No newline at end of file diff --git a/webapp/components/layout/WidgetFactory.js b/webapp/components/layout/WidgetFactory.js new file mode 100644 index 00000000..2c4c3331 --- /dev/null +++ b/webapp/components/layout/WidgetFactory.js @@ -0,0 +1,90 @@ +import React, { lazy, Suspense } from 'react'; + +import PythonConsole from '@geppettoengine/geppetto-client/js/components/interface/pythonConsole/PythonConsole'; + +import { NetPyNEInstantiated, HTMLViewer } from 'netpyne/components'; + +import { + NetPyNESynapses, + NetPyNEConnectivityRules, + NetPyNECellRules, + NetPyNEStimulationSources, + NetPyNEStimulationTargets, + NetPyNESimConfig, + NetPyNEPopulations, + NetPyNEPlots, + NetPyNEPythonConsole +} from "netpyne/components"; + +export default class WidgetFactory{ + + constructor () { + this.widgets = {}; + } + + /** + * Widget configuration is the same we are using in the flexlayout actions + * + * @param { id, name, component, panelName, [instancePath], * } widgetConfig + */ + factory (widgetConfig) { + + // With this lazy construction we avoidto trigger an update on every layout event. + if (!this.widgets[widgetConfig.id]) { + this.widgets[widgetConfig.id] = this.newWidget(widgetConfig); + } + + return this.widgets[widgetConfig.id]; + } + + updateWidget (widgetConfig) { + this.widgets[widgetConfig.id] = this.newWidget(widgetConfig); + return this.widgets[widgetConfig.id]; + } + + newWidget (widgetConfig) { + const component = widgetConfig.component; + switch (component) { + case "PythonConsole": { + return ; + } + case "D3Canvas": + return + case "Plot": { + const data = window.plotSvgImages[widgetConfig.id] + return ( + + ) + } + + case "popParams":{ + return + } + case "cellParams":{ + return + } + case "synMechParams":{ + return + } + case "connParams":{ + return + } + case "stimSourceParams":{ + return + } + case "stimTargetParams":{ + return + } + case "simConfig":{ + return + } + case "analysis":{ + return + } + } + } + +} \ No newline at end of file diff --git a/webapp/components/layout/layoutConf.json b/webapp/components/layout/layoutConf.json new file mode 100644 index 00000000..bfb193ca --- /dev/null +++ b/webapp/components/layout/layoutConf.json @@ -0,0 +1,51 @@ +{ + "global": { + "sideBorders": 8, + "tabSetHeaderHeight": 26, + "tabSetTabStripHeight": 26, + "enableEdgeDock": false + }, + "layout": { + "type": "tabset", + "weight": 100, + "id": "root", + "children": [ + { + "type": "row", + "weight": 100, + "children": [ + { + "type": "row", + "weight": 65, + "children": [ + { + "type": "tabset", + "weight": 100, + "id": "hlsPanel", + "enableDeleteWhenEmpty": false, + "children": [ + ] + } + ] + }, + { + "type": "row", + "weight": 35, + "children": [ + { + "type": "tabset", + "weight": 100, + "id": "consolePanel", + "enableDeleteWhenEmpty": false, + "children": [ + ] + } + ] + } + + ] + } + ] + } + } + \ No newline at end of file diff --git a/webapp/components/layout/simulateLayoutConf.json b/webapp/components/layout/simulateLayoutConf.json new file mode 100644 index 00000000..b0f32009 --- /dev/null +++ b/webapp/components/layout/simulateLayoutConf.json @@ -0,0 +1,64 @@ +{ + "global": { + "sideBorders": 8, + "tabSetHeaderHeight": 26, + "tabSetTabStripHeight": 26, + "enableEdgeDock": false + }, + "layout": { + "type": "tabset", + "weight": 100, + "id": "root", + "children": [ + { + "type": "row", + "weight": 50, + "children": [ + { + "type": "tabset", + "weight": 100, + "id": "morphoPanel", + "enableDeleteWhenEmpty": false, + "children": [] + } + ] + }, + { + "type": "row", + "weight": 50, + "children": [ + { + "type": "row", + "weight": 65, + "children": [ + { + "type": "tabset", + "weight": 100, + "id": "plotPanel", + "enableDeleteWhenEmpty": false, + "children": [ + ] + } + ] + }, + { + "type": "row", + "weight": 35, + "children": [ + { + "type": "tabset", + "weight": 100, + "id": "consolePanel", + "enableDeleteWhenEmpty": false, + "children": [ + ] + } + ] + } + + ] + } + ] + } + } + \ No newline at end of file diff --git a/webapp/components/settings/Drawer.js b/webapp/components/settings/Drawer.js new file mode 100644 index 00000000..419b8c11 --- /dev/null +++ b/webapp/components/settings/Drawer.js @@ -0,0 +1,144 @@ +import React, { useState } from 'react' +import { makeStyles } from '@material-ui/core/styles'; +import Drawer from '@material-ui/core/Drawer'; + +import IconButton from '@material-ui/core/IconButton' +import Toolbar from '@material-ui/core/Toolbar'; +import List from '@material-ui/core/List'; + +import ListItem from '@material-ui/core/ListItem'; +import ListItemIcon from '@material-ui/core/ListItemIcon'; +import ListItemText from '@material-ui/core/ListItemText'; + +import ChevronRightIcon from '@material-ui/icons/ChevronRight'; +import ChevronLeftIcon from '@material-ui/icons/ChevronLeft'; +import Typography from '@material-ui/core/Typography'; + +import AdjustIcon from '@material-ui/icons/Adjust'; +import { + HLS_WIDGETS, PYTHON_CONSOLE_WIDGET, + FLEXLAYOUT_DEFAULT_STATE, MORPHOLOGY_WIDGET +} from '../../redux/reducers/flexlayout' + + +const drawerOpenWidth = 200; +const drawerCloseWidth = 54; + +const drawerCss = (entering, transitions, palette) => ({ + overflow: 'hidden', + width: props => props.width, + flexShrink: 0, + borderRight: 'none', + transition: transitions.create('width', { + easing: transitions.easing.sharp, + duration: entering ? transitions.duration.enteringScreen : transitions.duration.leavingScreen, + }) +}) + +const useStyles = makeStyles(({ transitions, palette }) => ({ + openDrawer: drawerCss(true, transitions, palette), + + closeDrawer: drawerCss(false, transitions, palette), + + colapse: { + color: 'white', + position: 'absolute', + bottom: 0, + right: 2 + }, + paper: { backgroundColor: palette.grey['900'] }, + + selected: { height: 60, color: palette.primary.main }, + unselected: { height: 60, color: palette.common.white }, + noColor: { color: 'inherit' } +})) + +export default ({ widgets, newWidget, editMode }) => { + const [expand, setExpand] = useState({ dark: !editMode }) + + const classes = useStyles({ width: expand ? drawerOpenWidth : drawerCloseWidth }); + + function createWidget (widgetId) { + if (!widgets[widgetId]) { + let widget = HLS_WIDGETS[widgetId] + if (!editMode) { + widget = MORPHOLOGY_WIDGET + } + + newWidget({ path: widget.id, ...widget }) + } + } + + function getMenu () { + if (editMode) { + return Object.values({ ...HLS_WIDGETS, python: PYTHON_CONSOLE_WIDGET }) + } else { + return Object.values({ ...FLEXLAYOUT_DEFAULT_STATE.widgetsBackground, python: PYTHON_CONSOLE_WIDGET }) + } + + } + + return ( +
+ +
+ + + {getMenu().map(({ name, id }) => ( + createWidget(id)} + > + + + + + {name} + + + ))} + + + + { + setExpand(!expand) + setTimeout(() => window.dispatchEvent(new Event('resize')), 400) + // pepe() + }} + > + {expand ? : } + + + +
+ +
+
+ ) + +} + +const pepe = () => { + window.expansionTransit = { + timer: setInterval(() => { + window.dispatchEvent(new Event('resize')) + console.log(window.expansionTransit.count) + if (window.expansionTransit.count > 0) { + window.expansionTransit.count -= 1 + } else { + clearInterval(window.expansionTransit.timer) + console.log('clear') + } + + }, 150), + count: 1 + } +} \ No newline at end of file diff --git a/webapp/components/settings/NetPyNETabs.js b/webapp/components/settings/NetPyNETabs.js index b6a0ed59..a9e7e4b7 100644 --- a/webapp/components/settings/NetPyNETabs.js +++ b/webapp/components/settings/NetPyNETabs.js @@ -1,50 +1,81 @@ -import React from 'react'; -import MenuItem from '@material-ui/core/MenuItem'; -import Menu from '@material-ui/core/Menu'; -import IconButton from '@material-ui/core/IconButton'; -import Button from '@material-ui/core/Button'; -import NavigationExpandMoreIcon from '@material-ui/icons/ExpandMore'; +import React from "react"; +import MenuItem from "@material-ui/core/MenuItem"; +import Menu from "@material-ui/core/Menu"; +import IconButton from "@material-ui/core/IconButton"; +import NavigationExpandMoreIcon from "@material-ui/icons/ExpandMore"; +import ToggleButton from "@material-ui/lab/ToggleButton"; +import ToggleButtonGroup from "@material-ui/lab/ToggleButtonGroup"; +import { withStyles } from '@material-ui/core/styles' -export default class NetPyNETabs extends React.Component { +const style = ({ palette, shape, spacing, typography }) => ({ + container: { + backgroundColor: palette.primary.main, + flexGrow: 1 + }, + toggleButton: { + flex: 1, + borderRadius: shape.borderRadius, + marginLeft: spacing(1), + background: 'transparent', + border: 'none', + color: palette.common.white + '!important' + }, + toggleButtonSelected: { + fontWeight: typography.fontWeightBold, + color: palette.common.white + }, + menu: { position: "absolute", top: "6px", right: "28px" } +}) + +class NetPyNETabs extends React.Component { constructor (props) { super(props); this.state = { - simulateTabLabel: 'Create network', - label: 'define', + simulateTabLabel: "Create network", transitionOptionsHovered: false, - anchorEl: null - } - + anchorEl: null, + editTab: true + }; + this.rightTabAction = this.props.createNetwork; this.handleTransitionOptionsChange = this.handleTransitionOptionsChange.bind(this); } componentDidUpdate (prevProps, prevState) { if (this.props.label != prevProps.label) { - this.setState({ label:this.props.label }); + this.setState({ label: this.props.label }); } } - - handleTransitionOptionsChange (e, v) { - this.props.handleTransitionOptionsChange(e.target.innerText) - this.setState({ simulateTabLabel: e.target.innerText, anchorEl: null }); - } - - getLabelStyle (label) { - var style = { color: 'white', fontWeight: 400 } - if (label == this.state.label) { - style['fontWeight'] = 600; + handleChange = tab => { + if (tab == "define") { + if (!this.props.editMode) { + this.props.editModel(); + this.setState({ editTab: true }) + } + + } else { + if (this.props.editMode) { + this.rightTabAction(); + this.setState({ editTab: false }) + } } - return style; - } + }; - getBackgroundStyle (label) { - var color = 'primary'; - if (label == this.state.label || (label == 'simulate' && this.state.transitionOptionsHovered)) { - color = 'secondary'; + handleTransitionOptionsChange = e => { + const value = e.target.innerText; + switch (value) { + case "Create and Simulate Network": + this.rightTabAction = this.props.createAndSimulateNetwork; + break; + case "Create Network": + this.rightTabAction = this.props.createNetwork; + break; + default: + this.rightTabAction = this.props.showNetwork; } - return color; - } + + this.setState({ simulateTabLabel: value, anchorEl: null }); + }; handleClick = event => { this.setState({ anchorEl: event.currentTarget }); @@ -55,63 +86,83 @@ export default class NetPyNETabs extends React.Component { }; render () { - - return
- - - - this.setState({ transitionOptionsHovered: true })} - onMouseLeave={() => this.setState({ transitionOptionsHovered: false })} - style={{ color: 'white' }} - > - - - - - + this.handleChange(e.currentTarget.value)} + aria-label="Choose mode" > - Create Network - - + {"Define your Network"} + + + + {this.state.simulateTabLabel} + + + this.setState({ transitionOptionsHovered: true })} + onMouseLeave={() => + this.setState({ transitionOptionsHovered: false }) + } + style={{ color: "white" }} > - Create and Simulate Network - - + + + - Explore Existing Network - - -
+ + Create Network + + + Create and Simulate Network + + + Explore Existing Network + +
+
+ ); } } + + +export default withStyles(style)(NetPyNETabs) \ No newline at end of file diff --git a/webapp/components/settings/NetPyNEToolBar.js b/webapp/components/settings/NetPyNEToolBar.js index 082669b8..b09210f9 100644 --- a/webapp/components/settings/NetPyNEToolBar.js +++ b/webapp/components/settings/NetPyNEToolBar.js @@ -22,11 +22,33 @@ const ExportIcon = props => ; const CellTemplateIcon = props => ; +const OPTIONS = { + Load: { component: LoadFile, }, + Save: { component: SaveFile }, + ImportHLS: { + component: ImportExportHLS, + mode: 'IMPORT' + }, + ExportHLS: { + component: ImportExportHLS, + mode: 'EXPORT' + }, + ImportCellTemplate: { component: ImportCellParams, }, + NewModel: { component: NewModel, }, + UploadFiles: { + component: UploadDownloadFiles, + mode: 'UPLOAD' + }, + DownloadFiles: { + component: UploadDownloadFiles, + mode: 'DOWNLOAD' + }, +} + export default class NetPyNEToolBar extends React.Component { constructor (props) { super(props); this.state = { - openDialogBox: false, open: false, openSnackBar: false, action: null @@ -35,64 +57,64 @@ export default class NetPyNEToolBar extends React.Component { } handleMenuItemClick = action => { - this.setState({ action:action, openDialogBox:true, open: false }) + this.props.openDrawerDialogBox() + this.setState({ action:action, open: false }) } handleOpenSnackBar (message) { this.snackBarMessage = message this.setState({ openSnackBar: true }) } - + + handleClose () { + this.setState({ open: false }) + this.props.closeDrawerDialogBox() + } render () { - if (this.state.openDialogBox){ + if (this.props.dialogBoxOpen){ switch (this.state.action){ case 'Load': var content = this.setState({ openDialogBox: false })} - changeTab={this.props.changeTab} + open={this.props.dialogBoxOpen} + onRequestClose={() => this.handleClose()} /> break; case 'Save': var content = this.setState({ openDialogBox: false })} - changeTab={this.props.changeTab} + open={this.props.dialogBoxOpen} + onRequestClose={() => this.handleClose()} /> break; case 'ImportHLS': var content = this.setState({ openDialogBox: false })} - changeTab={this.props.changeTab} + open={this.props.dialogBoxOpen} + onRequestClose={() => this.handleClose()} mode ={"IMPORT"}/> break; case 'ExportHLS': var content = this.setState({ openDialogBox: false })} - changeTab={this.props.changeTab} + open={this.props.dialogBoxOpen} + onRequestClose={() => this.handleClose()} mode ={"EXPORT"} /> break; case 'ImportCellTemplate': var content = this.setState({ openDialogBox: false })} + open={this.props.dialogBoxOpen} + onRequestClose={() => this.handleClose()} /> break; case 'NewModel': var content = this.setState({ openDialogBox: false })} - changeTab={this.props.changeTab} + open={this.props.dialogBoxOpen} + onRequestClose={() => this.handleClose()} /> break; case 'UploadFiles': var content = this.setState({ openDialogBox: false })} + open={this.props.dialogBoxOpen} + onRequestClose={() => this.handleClose()} openSnackBar={message => { this.handleOpenSnackBar(message) } } @@ -100,8 +122,8 @@ export default class NetPyNEToolBar extends React.Component { break; case 'DownloadFiles': var content = this.setState({ openDialogBox: false })} + open={this.props.dialogBoxOpen} + onRequestClose={() => this.handleClose()} openSnackBar={message => { this.handleOpenSnackBar(message) } } diff --git a/webapp/components/settings/actions/ActionDialog.js b/webapp/components/settings/actions/ActionDialog.js index 5243ce4e..c161be24 100644 --- a/webapp/components/settings/actions/ActionDialog.js +++ b/webapp/components/settings/actions/ActionDialog.js @@ -10,132 +10,44 @@ import DialogTitle from '@material-ui/core/DialogTitle'; const styles = { cancel: { marginRight: 10 } } export default class ActionDialog extends React.Component { - constructor (props) { - super(props); - this.state = { - open: this.props.open, - errorMessage: undefined, - errorDetails: undefined - } - } - - componentDidUpdate = (prevProps, prevState) => { - if (this.props.open != prevProps.open) { - this.setState({ open: this.props.open }); - } - } + + state = { hide: false } performAction = () => { if (this.props.isFormValid === undefined || this.props.isFormValid()){ GEPPETTO.trigger(GEPPETTO.Events.Show_spinner, this.props.message); - this.closeDialog(); - Utils - .evalPythonMessage(this.props.command, [this.props.args]) - .then(response => { - if (!this.processError(response)) { - if (this.props.command == "netpyne_geppetto.exportModel") { - this.downloadJsonResponse(response) - } else if (this.props.command == 'netpyne_geppetto.exportHLS') { - this.downloadPythonResponse(response) - } - if (this.props.args.tab != undefined) { - this.props.changeTab(this.props.args.tab, this.props.args); - } - if (this.props.args.tab == 'simulate') { - GEPPETTO.trigger(GEPPETTO.Events.Show_spinner, GEPPETTO.Resources.PARSING_MODEL); - GEPPETTO.Manager.loadModel(response); - GEPPETTO.CommandController.log("The NetPyNE model " + this.props.args.tab + " was completed"); - } - if (this.props.args.action == "deleteModel") { - GEPPETTO.WidgetFactory.getController(GEPPETTO.Widgets.POPUP).then(controller => { - controller.widgets.forEach(widget => { - widget.destroy() - }) - }) - } - GEPPETTO.trigger(GEPPETTO.Events.Hide_spinner); - this.props.onRequestClose(); - } - }); + + this.props.pythonCall(this.props.command, this.props.args) + this.setState({ hide: true }) } } - - closeDialog = () => { - this.setState({ open: false, errorMessage: undefined, errorDetails: undefined }) - } - cancelDialog = () => { - this.setState({ open: false, errorMessage: undefined, errorDetails: undefined }) - this.props.onRequestClose(); - } + /* + * componentDidUpdate (prevProps) { + * if (this.props.openErrorDialogBox) { + * this.setState({ hide: false }) + * } + * } + */ - processError = response => { - var parsedResponse = Utils.getErrorResponse(response); - if (parsedResponse) { - GEPPETTO.trigger(GEPPETTO.Events.Hide_spinner); - this.setState({ open: true, errorMessage: parsedResponse['message'], errorDetails: parsedResponse['details'] }) - return true; - } - return false; + clearErrorDialogBox () { + this.props.closeBackendErrorDialog() } - - downloadJsonResponse (jsonData) { - var filename = this.createFileName('NetPyNE_Model_') - - if (jsonData.simConfig && jsonData.simConfig.filename) { - filename = this.createFileName(jsonData.simConfig.filename + '_') - } - - filename += '.json' - - const blob = new Blob([JSON.stringify(jsonData, null, 2)], { type : 'application/json' }); - this.forceBlobDownload(blob, filename) - - } - - unescapeText (text) { - text = text.replace(/\\\\/g, '\\').replace(/\\\'/g, "'").replace(/\\\"/g, '"').split('\\n').join('\n').substring(1) - return text.substring(0, text.length - 1) - } - - downloadPythonResponse (textData) { - var filename = this.createFileName('NetPyNE_init_') + '.py' - const blob = new Blob([this.unescapeText(textData)], { type : 'text/plain;charset=utf-8' }); - this.forceBlobDownload(blob, filename) - } - - createFileName (name) { - return name + this.getTimeStamp() - } - - getTimeStamp () { - return new Date().toGMTString().replace(",", '').replace(/[ ,:]/g, '_') + + cancelDialog = () => { + this.clearErrorDialogBox() + this.props.onRequestClose(); } - forceBlobDownload (blob, filename) { - const url = window.URL.createObjectURL(blob); - const link = document.createElement('a'); - link.href = url; - link.setAttribute('download', filename); - document.body.appendChild(link); - link.click(); - link.parentNode.removeChild(link); + handleClickGoBack () { + this.setState({ hide: false }) + this.clearErrorDialogBox() } - render () { - if (this.state.open) { - var cancelAction = ( - - ); - if (this.state.errorMessage == undefined) { + if (this.props.open || this.props.openErrorDialogBox) { + if (this.props.errorMessage === '') { var title = this.props.title - var actions = [ - cancelAction, + var action = ( - ]; + ) + var content = this.props.children; } else { - var actions = [ - cancelAction, + var action = ( - ]; - var title = this.state.errorMessage; - var content = Utils.parsePythonException(this.state.errorDetails); + ) + + var title = this.props.errorMessage; + var content = Utils.parsePythonException(this.props.errorDetails); } return ( this.closeDialog()} + maxWidth={this.props.openErrorDialogBox ? 'md' : 'sm'} + open={!this.state.hide || this.props.openErrorDialogBox} + onClose={() => this.cancelDialog()} > {title} - {content} + {content} - {actions} + + {action} ); diff --git a/webapp/components/settings/actions/ImportCellParams.js b/webapp/components/settings/actions/ImportCellParams.js index af486a97..592be7e3 100644 --- a/webapp/components/settings/actions/ImportCellParams.js +++ b/webapp/components/settings/actions/ImportCellParams.js @@ -1,14 +1,13 @@ import React from 'react'; import Checkbox from '../../general/Checkbox'; import TextField from '@material-ui/core/TextField'; -import ActionDialog from './ActionDialog'; import ListComponent from '../../general/List'; import FileBrowser from '../../general/FileBrowser'; import NetPyNEField from '../../general/NetPyNEField'; - import IconButton from '@material-ui/core/IconButton'; import Icon from '@material-ui/core/Icon'; import { withStyles } from '@material-ui/core/styles'; +import { ActionDialog } from 'netpyne/components' const styles = ({ spacing, typography, zIndex }) => ({ container: { diff --git a/webapp/components/settings/actions/ImportExportHLS.js b/webapp/components/settings/actions/ImportExportHLS.js index 36b995d1..cc36325f 100644 --- a/webapp/components/settings/actions/ImportExportHLS.js +++ b/webapp/components/settings/actions/ImportExportHLS.js @@ -5,7 +5,6 @@ import TextField from '@material-ui/core/TextField'; import Select from '@material-ui/core/Select'; import { orange , grey } from '@material-ui/core/colors'; import FileBrowser from '../../general/FileBrowser'; -import ActionDialog from './ActionDialog'; import InputLabel from '@material-ui/core/InputLabel'; import FormControl from '@material-ui/core/FormControl'; import FormHelperText from '@material-ui/core/FormHelperText' @@ -17,6 +16,9 @@ import Icon from '@material-ui/core/Icon'; import { withStyles } from '@material-ui/core/styles'; +import { NETPYNE_COMMANDS } from '../../../constants' +import { ActionDialog } from 'netpyne/components' + const styles = ({ spacing, typography, zIndex }) => ({ container: { marginTop: spacing(2), @@ -236,13 +238,13 @@ class ImportExportHLS extends React.Component { - {this.state.loadMod === undefined ? "This field is required." : ''} + {this.state.loadMod === undefined ? "This field is required." : ''} @@ -263,7 +265,7 @@ class ImportExportHLS extends React.Component {
) - var command = 'netpyne_geppetto.importModel'; + var command = NETPYNE_COMMANDS.importModel; var message = 'IMPORTING MODEL'; var buttonLabel = 'Import' var title = 'Import from Python scripts' @@ -279,7 +281,7 @@ class ImportExportHLS extends React.Component { }} /> ) - var command = 'netpyne_geppetto.exportHLS'; + var command = NETPYNE_COMMANDS.exportHLS; var message = 'EXPORTING MODEL'; var buttonLabel = 'Export' var title = 'Export as Python script' diff --git a/webapp/components/settings/actions/LoadFile.js b/webapp/components/settings/actions/LoadFile.js index beac86d0..d11892a4 100644 --- a/webapp/components/settings/actions/LoadFile.js +++ b/webapp/components/settings/actions/LoadFile.js @@ -7,7 +7,6 @@ import MenuItem from '@material-ui/core/MenuItem'; import TextField from '@material-ui/core/TextField'; import Select from '@material-ui/core/Select'; import FileBrowser from '../../general/FileBrowser'; -import ActionDialog from './ActionDialog'; import ListItemIcon from '@material-ui/core/ListItemIcon'; import ListItemText from '@material-ui/core/ListItemText'; import InputLabel from '@material-ui/core/InputLabel'; @@ -19,6 +18,7 @@ import IconButton from '@material-ui/core/IconButton'; import Icon from '@material-ui/core/Icon'; import { withStyles } from '@material-ui/core/styles'; +import { ActionDialog } from 'netpyne/components' const styles = ({ spacing, typography, zIndex }) => ({ container: { diff --git a/webapp/components/settings/actions/NewModel.js b/webapp/components/settings/actions/NewModel.js index 74ea6621..c241bb76 100644 --- a/webapp/components/settings/actions/NewModel.js +++ b/webapp/components/settings/actions/NewModel.js @@ -1,6 +1,6 @@ import React from 'react'; -import ActionDialog from './ActionDialog'; +import { ActionDialog } from 'netpyne/components' export default class NewModel extends React.Component { constructor (props) { super(props); @@ -17,7 +17,7 @@ export default class NewModel extends React.Component { title={"Create new model"} {...this.props} > -

The current model will be deleted

+

The current model will be deleted

) } diff --git a/webapp/components/settings/actions/SaveFile.js b/webapp/components/settings/actions/SaveFile.js index f4be342a..300e20f8 100644 --- a/webapp/components/settings/actions/SaveFile.js +++ b/webapp/components/settings/actions/SaveFile.js @@ -3,13 +3,14 @@ import Checkbox from '../../general/Checkbox'; import TextField from '@material-ui/core/TextField'; import { List, ListItem } from '@material-ui/core'; import Utils from '../../../Utils'; -import ActionDialog from './ActionDialog'; import ListItemIcon from '@material-ui/core/ListItemIcon'; import ListItemText from '@material-ui/core/ListItemText'; import { withStyles } from '@material-ui/core/styles'; +import { ActionDialog } from 'netpyne/components' +import { NETPYNE_COMMANDS } from '../../../constants' const styles = ({ spacing }) => ({ container: { marginTop: spacing(10), }, }) const saveOptions = [ @@ -29,7 +30,6 @@ class SaveFile extends React.Component { simData: true, netCells: true } - } componentDidMount () { @@ -44,8 +44,8 @@ class SaveFile extends React.Component { const { classes } = this.props return ( ({ +const styles = ({ spacing, typography, zIndex, palette }) => ({ + root: { color: palette.common.white }, container: { marginTop: spacing(2), display: 'flex', @@ -200,7 +201,7 @@ class UploadDownloadFile extends React.Component { switch (mode) { case 'UPLOAD': var content = ( -
+
+
(); - -export default class Transition extends React.Component { - - constructor (props) { - super(props); - this.state = { - openDialog: false, - haveInstData: false, - parallelSimulation: false, - instantiateButtonHovered: false, - simulateButtonHovered: false - }; - } - - componentDidUpdate (prevProps, prevState) { - if (this.props.clickOnTab != prevProps.clickOnTab) { - if (this.props.tab != prevProps.tab && this.props.tab == 'simulate') { - if (this.props.fastForwardSimulation) { // re instantiate and re simulate network - this.setState({ openDialog: true }) - } else if (this.props.fastForwardInstantiation) { // re instantiate network but do not simulate - this.instantiate({ usePrevInst: false }) - } else { - if (!this.state.haveInstData) { // if there is no previous instance data - Utils.evalPythonMessage('netpyne_geppetto.doIhaveInstOrSimData', []) - .then(response => { - // FIXME: Checking if the contructor name is an array is clearly the wrong approach - if (response.constructor.name == 'Array'){ - this.instantiate({ usePrevInst: this.props.fastForwardInstantiation ? false : response[0] }) - } else if (response.haveInstance){ - this.instantiate({ usePrevInst: true, haveInstData: true }) - } - }) - } else { // if has prev instance data - this.instantiate({ usePrevInst: true }) - } - } - } - } - } - - instantiate = args => { - GEPPETTO.CommandController.log("The NetPyNE model is getting instantiated..."); - GEPPETTO.trigger(GEPPETTO.Events.Show_spinner, GEPPETTO.Resources.INSTANTIATING_MODEL); - this.closeTransition(); - Utils.evalPythonMessage('netpyne_geppetto.instantiateNetPyNEModelInGeppetto', [args]) - .then(response => { - if (!this.processError(response)) { - GEPPETTO.trigger(GEPPETTO.Events.Show_spinner, GEPPETTO.Resources.PARSING_MODEL); - if (!args.usePrevInst) { - this.props.handleDeactivateInstanceUpdate(true) - } - GEPPETTO.Manager.loadModel(response); - GEPPETTO.CommandController.log("The NetPyNE model instantiation was completed"); - GEPPETTO.trigger(GEPPETTO.Events.Hide_spinner); - this.setState({ haveInstData: true }) - } - }); - } - - simulate = () => { - this.setState({ openDialog: false }) - GEPPETTO.CommandController.log("The NetPyNE model is getting simulated..."); - GEPPETTO.trigger(GEPPETTO.Events.Show_spinner, GEPPETTO.Resources.RUNNING_SIMULATION); - this.closeTransition(); - Utils.evalPythonMessage('netpyne_geppetto.simulateNetPyNEModelInGeppetto ', [{ usePrevInst: this.props.freezeInstance, parallelSimulation: this.state.parallelSimulation, cores:this.state.cores }]) - .then(response => { - if (!this.processError(response)) { - GEPPETTO.trigger(GEPPETTO.Events.Show_spinner, GEPPETTO.Resources.PARSING_MODEL); - this.props.handleDeactivateSimulationUpdate(true) - if (!this.props.freezeInstance) { - this.props.handleDeactivateInstanceUpdate(true) - } - GEPPETTO.Manager.loadModel(response); - GEPPETTO.CommandController.log("The NetPyNE model simulation was completed"); - GEPPETTO.trigger(GEPPETTO.Events.Hide_spinner); - this.setState({ haveInstData: true }) - } - }); - } - - processError (response) { - var parsedResponse = Utils.getErrorResponse(response); - if (parsedResponse) { - GEPPETTO.trigger(GEPPETTO.Events.Hide_spinner); - this.setState({ openDialog: true, errorMessage: parsedResponse['message'], errorDetails: parsedResponse['details'] }) - return true; - } - return false; - } - - closeTransition = () => { - this.setState({ openDialog: false }); - } - - render () { - var children = this.state.errorDetails ? Utils.parsePythonException(this.state.errorDetails) :
- var title = this.state.errorMessage ? this.state.errorMessage : "NetPyNE"; - - if (this.state.openDialog) { - if (this.state.errorMessage == undefined) { - children = ( -
-
We are about to instantiate and simulate your network, this could take some time.
- this.setState(oldState => ({ parallelSimulation: !oldState.parallelSimulation }))} style={{ marginTop: '35px' }} id="runParallelSimulation" /> - this.setState({ cores: event.target.value })} className="netpyneRightField" type="number"/> -
- ) - var actions = [ - , - - ]; - } else { - var actions = ( - - ) - } - } - - if (this.props.tab == 'simulate' ) { - var refreshInstanceButton = ( - this.instantiate({ usePrevInst: false })} - style={{ position: 'absolute', right: 30, top: 80, zIndex: 1 }} - tooltip-data={this.props.freezeInstance ? "Your network is in sync" : "Synchronise network"} - disabled={this.props.freezeInstance} - /> - ) - var refreshSimulationButton = ( - this.setState({ openDialog: true })} - style={{ position: 'absolute', right: 30, top: 120, zIndex: 1 }} - tooltip-data={this.props.freezeSimulation ? "You have already simulated your network" : "Simulate your network"} - disabled={this.props.freezeSimulation} - /> - ) - } - - return ( -
- {refreshInstanceButton} - {refreshSimulationButton} - - - {title} - - {children} - - - {actions} - - -
- ) - } -} diff --git a/webapp/constants.js b/webapp/constants.js new file mode 100644 index 00000000..4ed15129 --- /dev/null +++ b/webapp/constants.js @@ -0,0 +1,31 @@ +export const MODULE_NOT_FOUND_ERROR = 'ModuleNotFoundError'; +export const NAME_ERROR = "NameError"; +export const FILEVARIABLE_LENGTH = 'network.'.length; +/* + * status can be one of: + * - ACTIVE: the user can see the tab content. + * - MINIMIZED: the tab is minimized. + * - HIDDEN: other tab in the node is currently selected + * - MAXIMIZED: the tab is maximized (only one tab can be maximized simultaneously) + */ +export const WidgetStatus = { + HIDDEN: 'HIDDEN', + ACTIVE: 'ACTIVE', + MAXIMIZED: 'MAXIMIZED', + MINIMIZED: 'MINIMIZED' +}; + +export const MODEL_STATE = { + NOT_INSTANTIATED: 'NOT_INSTANTIATED', + INSTANTIATED: 'INSTANTIATED', + SIMULATED: 'SIMULATED' + +} +export const NETPYNE_COMMANDS = { + instantiateModel: 'netpyne_geppetto.instantiateNetPyNEModelInGeppetto', + simulateModel: 'netpyne_geppetto.simulateNetPyNEModelInGeppetto', + importModel: 'netpyne_geppetto.importModel', + exportModel: 'netpyne_geppetto.exportModel', + exportHLS: 'netpyne_geppetto.exportHLS' +} + diff --git a/webapp/css/colors.less b/webapp/css/colors.less index 4eac2be3..0c345389 100644 --- a/webapp/css/colors.less +++ b/webapp/css/colors.less @@ -1,8 +1,16 @@ -@primary_color: rgb(242, 61, 122); -@secondary_color: rgb(204, 33, 90); +@primary_color: #f67700d2; +@secondary_color: #f67700; @complementary_color:rgb(84, 58, 115); @background_color_body_0: rgb(195, 195, 195); @background_color_body_50: rgb(195, 195, 195); @background_color_body_73: rgb(195, 195, 195); @background_color_body_100: rgb(195, 195, 195); @background_color_widget: rgb(255,255,255); + +@box_color: #323232; +@color-link: #b9c680; + +@color-light: #616161; +@color-regular: #424242; +@color-dark: #212121; +@color-white: white; \ No newline at end of file diff --git a/webapp/css/flexlayout.less b/webapp/css/flexlayout.less new file mode 100644 index 00000000..ed7e8354 --- /dev/null +++ b/webapp/css/flexlayout.less @@ -0,0 +1,222 @@ +@import "colors"; + +@panelradius: 5px; +@gutter: 16px; +@icon-padding: 12px; +@font-size-panels: 16px; +@font: "Source Sans Pro", sans-serif; +@inputradius: 30px; +@font-weight-content: 300; +@font-weight-label: 400; + +@font-size: 1rem; +@font-size-sm: 0.8rem; +@border-radius: @panelradius; +@tab-spacing: 8px; + +@spacing: 8px; + +@inner-layout-padding: @gutter / 2; + +@explorer-tooltip-z-index: 3; +@explorer-tooltip-top: -50px !important; +@explorer-tooltip-margin: 10px; + +@explorer-tooltip-title-font-size: 0.9em; + +@explorer-tooltip-subtitle-font-size: 12px; +@explorer-tooltip-subtitle-font-weight: 100; + +@list-padding: 5px; + +@logo-size: 20px; +@logo-margin: 3px; +@bring-front: 1000; + +.flexlayout__layout { + border-left: none!important; + border-right: none!important; + background-color: @color-dark;//color-light + top: 0px; + margin-top: @gutter / 2; + margin-bottom: @gutter / 2; + border-top: none; + + a { + color: @color-link; + } + + .flexlayout__tab { + padding: @inner-layout-padding; + background-color: @color-regular;//color-dark + border-top-right-radius: @panelradius; + border-bottom-right-radius: @panelradius; + border-bottom-left-radius: @panelradius; + + .svg-container, + .main-svg, + .plot-container { + width: 100% !important; + height: 100% !important; + // position: static; + } + + .scrollbar; + * { + .scrollbar; + } + + input { + width: 100%; + } + #pythonConsoleOutput { + padding: 0; + display: flex; + } + } + + .flexlayout__tabset { + background-color: @color-dark;//color-light + overflow: visible; + border-radius: 0px @panelradius @panelradius @panelradius; + } + .flexlayout__tabset-selected { + background-color: @color-dark;//color-light + } + .flexlayout__border_inner_bottom { + position: fixed; + bottom: 0; + left: 0; + padding-left: @gutter / 2; + border-top-right-radius: @panelradius; + border-top-left-radius: @panelradius; + } + + .flexlayout__tab_button--selected { + background-color: @color-regular;//was color-dark + border-top-right-radius: @panelradius; + border-top-left-radius: @panelradius; + color: @color-white; + display: flex; + align-items: flex-end; + margin-right: @tab-spacing; + .flexlayout__tab_button_content { + color: @secondary_color; + } + } + + .flexlayout__tab_button--unselected { + margin-right: @tab-spacing; + background-color: @color-regular; + border-top-right-radius: @panelradius; + border-top-left-radius: @panelradius; + display: flex; + align-items: flex-end; + } + + .flexlayout__border_bottom { + overflow: visible; + background-color: @color-light; + border: none; + border-top-right-radius: @panelradius; + border-top-left-radius: @panelradius; + } + + .flexlayout__border_button { + .flexlayout__border_button_content { + font-family: @font; + // font-weight: bold; + color: @secondary_color; + + font-size: @font-size-panels; + } + border: none; + margin: 0; + margin-right: @tab-spacing; + border-top-right-radius: @panelradius; + border-top-left-radius: @panelradius; + background-color: @box_color; + display: flex; + flex-direction: row; + align-items: flex-end; + } + + .flexlayout__tab_button_content { + color: @secondary_color; + font-family: @font; + font-size: @font-size-panels; + margin-top: -3px; + padding: 0; + font-weight: normal; + color: #b9b9b9; + margin-left: @spacing; + } + + .flexlayout__tab_button_trailing { + display: flex; + align-items: center; + } + + // Thin white tab icons + .flexlayout__border_button_trailing, + .flexlayout__tab_button_trailing, + .flexlayout__tab_toolbar_button-max, + .customIconFlexLayout, + .flexlayout__tab_toolbar_button-min, + .flexlayout__tab_button_overflow { + color: @color-white; + + &:hover { + color: @color-white; + } + } + + .flexlayout__border_button_trailing, + .flexlayout__tab_button_trailing { + &:before { + content: "\00d7"; + } + height: inherit; + } + .flexlayout__splitter { + background-color: inherit; + } + .flexlayout__splitter:hover { + background-color: @secondary_color; + } + + .flexlayout__splitter { + border-radius: @panelradius; + } + + .flexlayout__splitter_drag { + border: 1px solid @color-white; + } + + .flexlayout__popup_menu_item { + color: @color-white; + background-color: @box_color; + } + +} + +.flexlayout__tab_header_outer { + background-color: @color-dark;//color-light +} + +.scrollbar { + &::-webkit-scrollbar-thumb { + background: @secondary_color; + border-radius: @panelradius; + } + &::-webkit-scrollbar { + width: @gutter / 2; + } +} + + +.flexlayout__tab:hover { + .node-legend { + visibility: visible; + } +} \ No newline at end of file diff --git a/webapp/css/material.less b/webapp/css/material.less index 009efce0..0f77e23c 100644 --- a/webapp/css/material.less +++ b/webapp/css/material.less @@ -7,6 +7,8 @@ label { input[type="text"], input[type="number"]{ box-shadow: none!important; border-radius:0px!important; + color: white!important; + font-size: inherit; } div.MuiExpansionPanelSummary-root { @@ -18,11 +20,7 @@ div.MuiExpansionPanelSummary-root { font-size: 1em; color : grey; } -} - -div.MuiPaper-root { - color: black; -} +} div.MuiCollapse-container .MuiCollapse-wrapper, div.MuiExpansionPanelSummary-root { @@ -33,8 +31,9 @@ div.MuiExpansionPanel-root.Mui-expanded { } .MuiExpansionPanel-root { margin: 2px; -} +} +label.MuiInputLabel-root .MuiInput-root { color : #fff; cursor : text; @@ -53,7 +52,7 @@ div.MuiExpansionPanel-root.Mui-expanded { border : none; outline : none; background-color : rgba(0, 0, 0, 0); - color : rgba(0, 0, 0, 0.87); + color : rgba(255, 255, 255, 0.87); cursor : inherit; font : inherit; opacity : 1; @@ -75,7 +74,7 @@ div.MuiExpansionPanel-root.Mui-expanded { label.MuiInputLabel-root, label.MuiFormLabel-root{ font-weight: bold; - color: rgba(0, 0, 0, 0.3); + color: rgba(255, 255, 255, 0.7); padding: 4px } diff --git a/webapp/css/netpyne.less b/webapp/css/netpyne.less index c92f84d6..79eb12bd 100644 --- a/webapp/css/netpyne.less +++ b/webapp/css/netpyne.less @@ -3,7 +3,7 @@ html, body { height: 100%; - background: white !important; + background: @color-dark !important; font-size: 16px; } @@ -14,6 +14,7 @@ body { --breadcrumb-min-width: 450px; } +@flexlayout-widget-border: 8px; #footer { z-index: 2 !important; } @@ -27,6 +28,14 @@ body { height: 100% !important; } +.position-toolbar { + top: 0px; +} +#controlpanel { + margin-top: @flexlayout-widget-border; + top: 0%!important; + width: 97%!important; +} button.actionButton { font-size: 15px; margin: 10px; @@ -96,7 +105,7 @@ button.actionButton { overflow: visible; } .gearThumbButton .gpt-fullgear { - color: @complementary_color!important; + color: @color-light!important; font-size: 100px !important; height: 100px !important; width: 100px !important; @@ -135,7 +144,7 @@ button.actionButton { } .breadcrumb { - background-color: #eeeeee; + background-color: @color-light; height: 100px; display: flex; align-items: center; @@ -254,17 +263,17 @@ button.actionButton { margin-top: 43px; margin-right: 5px; font-size: 11px; - color: @complementary_color; + color: @secondary_color; } .spinner-container { - color: @complementary_color!important; + color: @secondary_color!important; p { - color: @complementary_color !important; + color: @secondary_color !important; } } .listIcon { - color: @complementary_color!important; + color: @secondary_color!important; } .listButtonLarge { @@ -299,7 +308,7 @@ button.actionButton { .rst__rowContents { background: transparent; - color: @complementary_color; + color: @secondary_color; cursor: pointer; box-shadow: none; @@ -512,4 +521,8 @@ button.actionButton { .MuiListItem-root.Mui-selected { color: @primary_color; +} + +div.flexlayout__layout{ + top: 0; } \ No newline at end of file diff --git a/webapp/css/tree.less b/webapp/css/tree.less new file mode 100644 index 00000000..e7037432 --- /dev/null +++ b/webapp/css/tree.less @@ -0,0 +1,829 @@ +@import "colors"; + +// Tree CSS file copied + +/** + * Extra class applied to VirtualScroll through className prop + */ + .rst__virtualScrollOverride { + overflow: auto !important; + } + + .rst__virtualScrollOverride * { + box-sizing: border-box; + } + + .ReactVirtualized__Grid__innerScrollContainer { + overflow: visible !important; + } + + .rst__rtl .ReactVirtualized__Grid__innerScrollContainer { + direction: rtl; + } + + .ReactVirtualized__Grid { + outline: none; + } + + .rst__node { + min-width: 100%; + white-space: nowrap; + position: relative; + text-align: left; + } + + .rst__node.rst__rtl { + text-align: right; + } + + .rst__nodeContent { + position: absolute; + top: 0; + bottom: 0; + } + + /* ========================================================================== + Scaffold + Line-overlaid blocks used for showing the tree structure + ========================================================================== */ + .rst__lineBlock, + .rst__absoluteLineBlock { + height: 100%; + position: relative; + display: inline-block; + } + + .rst__absoluteLineBlock { + position: absolute; + top: 0; + } + + .rst__lineHalfHorizontalRight::before, + .rst__lineFullVertical::after, + .rst__lineHalfVerticalTop::after, + .rst__lineHalfVerticalBottom::after { + position: absolute; + content: ''; + background-color: black; + } + + /** + * +-----+ + * | | + * | +--+ + * | | + * +-----+ + */ + .rst__lineHalfHorizontalRight::before { + height: 1px; + top: 50%; + right: 0; + width: 50%; + } + + .rst__rtl.rst__lineHalfHorizontalRight::before { + left: 0; + right: initial; + } + + /** + * +--+--+ + * | | | + * | | | + * | | | + * +--+--+ + */ + .rst__lineFullVertical::after, + .rst__lineHalfVerticalTop::after, + .rst__lineHalfVerticalBottom::after { + width: 1px; + left: 50%; + top: 0; + height: 100%; + } + + /** + * +--+--+ + * | | | + * | | | + * | | | + * +--+--+ + */ + .rst__rtl.rst__lineFullVertical::after, + .rst__rtl.rst__lineHalfVerticalTop::after, + .rst__rtl.rst__lineHalfVerticalBottom::after { + right: 50%; + left: initial; + } + + /** + * +-----+ + * | | | + * | + | + * | | + * +-----+ + */ + .rst__lineHalfVerticalTop::after { + height: 50%; + } + + /** + * +-----+ + * | | + * | + | + * | | | + * +-----+ + */ + .rst__lineHalfVerticalBottom::after { + top: auto; + bottom: 0; + height: 50%; + } + + /* Highlight line for pointing to dragged row destination + ========================================================================== */ + /** + * +--+--+ + * | | | + * | | | + * | | | + * +--+--+ + */ + .rst__highlightLineVertical { + z-index: 3; + } + .rst__highlightLineVertical::before { + position: absolute; + content: ''; + background-color: #36c2f6; + width: 8px; + margin-left: -4px; + left: 50%; + top: 0; + height: 100%; + } + + .rst__rtl.rst__highlightLineVertical::before { + margin-left: initial; + margin-right: -4px; + left: initial; + right: 50%; + } + + @keyframes arrow-pulse { + 0% { + transform: translate(0, 0); + opacity: 0; + } + 30% { + transform: translate(0, 300%); + opacity: 1; + } + 70% { + transform: translate(0, 700%); + opacity: 1; + } + 100% { + transform: translate(0, 1000%); + opacity: 0; + } + } + .rst__highlightLineVertical::after { + content: ''; + position: absolute; + height: 0; + margin-left: -4px; + left: 50%; + top: 0; + border-left: 4px solid transparent; + border-right: 4px solid transparent; + border-top: 4px solid white; + animation: arrow-pulse 1s infinite linear both; + } + + .rst__rtl.rst__highlightLineVertical::after { + margin-left: initial; + margin-right: -4px; + right: 50%; + left: initial; + } + + /** + * +-----+ + * | | + * | +--+ + * | | | + * +--+--+ + */ + .rst__highlightTopLeftCorner::before { + z-index: 3; + content: ''; + position: absolute; + border-top: solid 8px #36c2f6; + border-left: solid 8px #36c2f6; + box-sizing: border-box; + height: calc(50% + 4px); + top: 50%; + margin-top: -4px; + right: 0; + width: calc(50% + 4px); + } + + .rst__rtl.rst__highlightTopLeftCorner::before { + border-right: solid 8px #36c2f6; + border-left: none; + left: 0; + right: initial; + } + + /** + * +--+--+ + * | | | + * | | | + * | +->| + * +-----+ + */ + .rst__highlightBottomLeftCorner { + z-index: 3; + } + .rst__highlightBottomLeftCorner::before { + content: ''; + position: absolute; + border-bottom: solid 8px #36c2f6; + border-left: solid 8px #36c2f6; + box-sizing: border-box; + height: calc(100% + 4px); + top: 0; + right: 12px; + width: calc(50% - 8px); + } + + .rst__rtl.rst__highlightBottomLeftCorner::before { + border-right: solid 8px #36c2f6; + border-left: none; + left: 12px; + right: initial; + } + + .rst__highlightBottomLeftCorner::after { + content: ''; + position: absolute; + height: 0; + right: 0; + top: 100%; + margin-top: -12px; + border-top: 12px solid transparent; + border-bottom: 12px solid transparent; + border-left: 12px solid #36c2f6; + } + + .rst__rtl.rst__highlightBottomLeftCorner::after { + left: 0; + right: initial; + border-right: 12px solid #36c2f6; + border-left: none; + } + + .rst__rowWrapper { + padding: 3px 3px 3px 0; + height: 100%; + box-sizing: border-box; + } + + .rst__rtl.rst__rowWrapper { + padding: 3px 0 3px 3px; + } + + .rst__row { + height: 100%; + white-space: nowrap; + display: flex; + } + .rst__row > * { + box-sizing: border-box; + } + + /** + * The outline of where the element will go if dropped, displayed while dragging + */ + .rst__rowLandingPad, + .rst__rowCancelPad { + border: none !important; + box-shadow: none !important; + outline: none !important; + } + .rst__rowLandingPad > *, + .rst__rowCancelPad > * { + opacity: 0 !important; + } + .rst__rowLandingPad::before, + .rst__rowCancelPad::before { + background-color: lightblue; + border: 3px dashed white; + content: ''; + position: absolute; + top: 0; + right: 0; + bottom: 0; + left: 0; + z-index: -1; + } + + /** + * Alternate appearance of the landing pad when the dragged location is invalid + */ + .rst__rowCancelPad::before { + background-color: #e6a8ad; + } + + /** + * Nodes matching the search conditions are highlighted + */ + .rst__rowSearchMatch { + } + + /** + * The node that matches the search conditions and is currently focused + */ + .rst__rowSearchFocus { + outline: solid 3px #fc6421; + } + + .rst__rowContents, + .rst__rowLabel, + .rst__rowToolbar, + .rst__moveHandle, + .rst__toolbarButton { + display: inline-block; + vertical-align: middle; + } + + .rst__rowContents { + position: relative; + height: 100%; + border-left: none; + box-shadow: 0 2px 2px -2px; + padding: 0 5px 0 10px; + border-radius: 2px; + min-width: 80px; + flex: 1 0 auto; + display: flex; + align-items: center; + justify-content: space-between; + background-color: white; + } + + .rst__rtl.rst__rowContents { + border-right: none; + padding: 0 10px 0 5px; + } + + .rst__rowContentsDragDisabled { + } + + .rst__rtl.rst__rowContentsDragDisabled { + } + + .rst__rowLabel { + flex: 0 1 auto; + padding-right: 20px; + } + .rst__rtl.rst__rowLabel { + padding-left: 20px; + padding-right: inherit; + } + + .rst__rowToolbar { + flex: 0 1 auto; + display: flex; + } + + .rst__moveHandle, + .rst__loadingHandle { + height: 100%; + width: 44px; + background: #d9d9d9 + url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI0MiIgaGVpZ2h0PSI0MiI+PGcgc3Ryb2tlPSIjRkZGIiBzdHJva2Utd2lkdGg9IjIuOSIgPjxwYXRoIGQ9Ik0xNCAxNS43aDE0LjQiLz48cGF0aCBkPSJNMTQgMjEuNGgxNC40Ii8+PHBhdGggZD0iTTE0IDI3LjFoMTQuNCIvPjwvZz4KPC9zdmc+') + no-repeat center; + border: solid #aaa 1px; + box-shadow: 0 2px 2px -2px; + cursor: move; + border-radius: 1px; + z-index: 1; + } + + .rst__loadingHandle { + cursor: default; + background: #d9d9d9; + } + + @keyframes pointFade { + 0%, + 19.999%, + 100% { + opacity: 0; + } + 20% { + opacity: 1; + } + } + + .rst__loadingCircle { + width: 80%; + height: 80%; + margin: 10%; + position: relative; + } + + .rst__loadingCirclePoint { + width: 100%; + height: 100%; + position: absolute; + left: 0; + top: 0; + } + + .rst__rtl.rst__loadingCirclePoint { + right: 0; + left: initial; + } + + .rst__loadingCirclePoint::before { + content: ''; + display: block; + margin: 0 auto; + width: 11%; + height: 30%; + background-color: #fff; + border-radius: 30%; + animation: pointFade 800ms infinite ease-in-out both; + } + .rst__loadingCirclePoint:nth-of-type(1) { + transform: rotate(0deg); + } + .rst__loadingCirclePoint:nth-of-type(7) { + transform: rotate(180deg); + } + .rst__loadingCirclePoint:nth-of-type(1)::before, + .rst__loadingCirclePoint:nth-of-type(7)::before { + animation-delay: -800ms; + } + .rst__loadingCirclePoint:nth-of-type(2) { + transform: rotate(30deg); + } + .rst__loadingCirclePoint:nth-of-type(8) { + transform: rotate(210deg); + } + .rst__loadingCirclePoint:nth-of-type(2)::before, + .rst__loadingCirclePoint:nth-of-type(8)::before { + animation-delay: -666ms; + } + .rst__loadingCirclePoint:nth-of-type(3) { + transform: rotate(60deg); + } + .rst__loadingCirclePoint:nth-of-type(9) { + transform: rotate(240deg); + } + .rst__loadingCirclePoint:nth-of-type(3)::before, + .rst__loadingCirclePoint:nth-of-type(9)::before { + animation-delay: -533ms; + } + .rst__loadingCirclePoint:nth-of-type(4) { + transform: rotate(90deg); + } + .rst__loadingCirclePoint:nth-of-type(10) { + transform: rotate(270deg); + } + .rst__loadingCirclePoint:nth-of-type(4)::before, + .rst__loadingCirclePoint:nth-of-type(10)::before { + animation-delay: -400ms; + } + .rst__loadingCirclePoint:nth-of-type(5) { + transform: rotate(120deg); + } + .rst__loadingCirclePoint:nth-of-type(11) { + transform: rotate(300deg); + } + .rst__loadingCirclePoint:nth-of-type(5)::before, + .rst__loadingCirclePoint:nth-of-type(11)::before { + animation-delay: -266ms; + } + .rst__loadingCirclePoint:nth-of-type(6) { + transform: rotate(150deg); + } + .rst__loadingCirclePoint:nth-of-type(12) { + transform: rotate(330deg); + } + .rst__loadingCirclePoint:nth-of-type(6)::before, + .rst__loadingCirclePoint:nth-of-type(12)::before { + animation-delay: -133ms; + } + .rst__loadingCirclePoint:nth-of-type(7) { + transform: rotate(180deg); + } + .rst__loadingCirclePoint:nth-of-type(13) { + transform: rotate(360deg); + } + .rst__loadingCirclePoint:nth-of-type(7)::before, + .rst__loadingCirclePoint:nth-of-type(13)::before { + animation-delay: 0ms; + } + + .rst__rowTitle { + font-weight: bold; + } + + .rst__rowTitleWithSubtitle { + font-size: 85%; + display: block; + padding-top: 8px; + } + + .rst__rowSubtitle { + font-size: 70%; + line-height: 3; + } + + .rst__collapseButton, + .rst__expandButton { + appearance: none; + border: none; + position: absolute; + border-radius: 100%; + box-shadow: 0 0 0 1px #000; + width: 14px; + height: 14px; + padding: 0; + top: 50%; + transform: translate(-50%, -50%); + cursor: pointer; + } + .rst__rtl.rst__collapseButton, + .rst__rtl.rst__expandButton { + transform: translate(50%, -50%); + } + .rst__collapseButton:focus, + .rst__expandButton:focus { + outline: none; + box-shadow: none; + } + .rst__collapseButton:hover:not(:active), + .rst__expandButton:hover:not(:active) { + background-size: 24px; + height: 20px; + width: 20px; + } + + .rst__collapseButton { + background: #fff + url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxOCIgaGVpZ2h0PSIxOCI+PGNpcmNsZSBjeD0iOSIgY3k9IjkiIHI9IjgiIGZpbGw9IiNGRkYiLz48ZyBzdHJva2U9IiM5ODk4OTgiIHN0cm9rZS13aWR0aD0iMS45IiA+PHBhdGggZD0iTTQuNSA5aDkiLz48L2c+Cjwvc3ZnPg==') + no-repeat center; + } + + .rst__expandButton { + background: #fff + url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxOCIgaGVpZ2h0PSIxOCI+PGNpcmNsZSBjeD0iOSIgY3k9IjkiIHI9IjgiIGZpbGw9IiNGRkYiLz48ZyBzdHJva2U9IiM5ODk4OTgiIHN0cm9rZS13aWR0aD0iMS45IiA+PHBhdGggZD0iTTQuNSA5aDkiLz48cGF0aCBkPSJNOSA0LjV2OSIvPjwvZz4KPC9zdmc+') + no-repeat center; + } + + /** + * Line for under a node with children + */ + .rst__lineChildren { + height: 100%; + display: inline-block; + position: absolute; + } + .rst__lineChildren::after { + content: ''; + position: absolute; + background-color: unset; + width: 1px; + left: 50%; + bottom: 0; + height: 10px; + } + + .rst__rtl.rst__lineChildren::after { + right: 50%; + left: initial; + } + + .rst__placeholder { + position: relative; + height: 68px; + max-width: 300px; + padding: 10px; + } + .rst__placeholder, + .rst__placeholder > * { + box-sizing: border-box; + } + .rst__placeholder::before { + border: 3px dashed @primary_color; + content: ''; + position: absolute; + top: 5px; + right: 5px; + bottom: 5px; + left: 5px; + z-index: -1; + } + + /** + * The outline of where the element will go if dropped, displayed while dragging + */ + .rst__placeholderLandingPad, + .rst__placeholderCancelPad { + border: none !important; + box-shadow: none !important; + outline: none !important; + } + .rst__placeholderLandingPad *, + .rst__placeholderCancelPad * { + opacity: 0 !important; + } + .rst__placeholderLandingPad::before, + .rst__placeholderCancelPad::before { + background-color: lightblue; + border-color: white; + } + + /** + * Alternate appearance of the landing pad when the dragged location is invalid + */ + .rst__placeholderCancelPad::before { + background-color: #e6a8ad; + } + + /* Collection default theme */ + + .ReactVirtualized__Collection { + } + + .ReactVirtualized__Collection__innerScrollContainer { + } + + /* Grid default theme */ + + .ReactVirtualized__Grid { + } + + .ReactVirtualized__Grid__innerScrollContainer { + } + + /* Table default theme */ + + .ReactVirtualized__Table { + } + + .ReactVirtualized__Table__Grid { + } + + .ReactVirtualized__Table__headerRow { + font-weight: 700; + text-transform: uppercase; + display: -webkit-box; + display: -ms-flexbox; + display: flex; + -webkit-box-orient: horizontal; + -webkit-box-direction: normal; + -ms-flex-direction: row; + flex-direction: row; + -webkit-box-align: center; + -ms-flex-align: center; + align-items: center; + } + .ReactVirtualized__Table__row { + display: -webkit-box; + display: -ms-flexbox; + display: flex; + -webkit-box-orient: horizontal; + -webkit-box-direction: normal; + -ms-flex-direction: row; + flex-direction: row; + -webkit-box-align: center; + -ms-flex-align: center; + align-items: center; + } + + .ReactVirtualized__Table__headerTruncatedText { + display: inline-block; + max-width: 100%; + white-space: nowrap; + text-overflow: ellipsis; + overflow: hidden; + } + + .ReactVirtualized__Table__headerColumn, + .ReactVirtualized__Table__rowColumn { + margin-right: 10px; + min-width: 0px; + } + .ReactVirtualized__Table__rowColumn { + text-overflow: ellipsis; + white-space: nowrap; + } + + .ReactVirtualized__Table__headerColumn:first-of-type, + .ReactVirtualized__Table__rowColumn:first-of-type { + margin-left: 10px; + } + .ReactVirtualized__Table__sortableHeaderColumn { + cursor: pointer; + } + + .ReactVirtualized__Table__sortableHeaderIconContainer { + display: -webkit-box; + display: -ms-flexbox; + display: flex; + -webkit-box-align: center; + -ms-flex-align: center; + align-items: center; + } + .ReactVirtualized__Table__sortableHeaderIcon { + -webkit-box-flex: 0; + -ms-flex: 0 0 24px; + flex: 0 0 24px; + height: 1em; + width: 1em; + fill: currentColor; + } + + /* List default theme */ + + .ReactVirtualized__List { + } + + + +// Tree component styling +.rst__rowContents { + background: transparent; + color: #ffffff; + cursor: pointer; + border-radius: 0; + box-shadow:none; +} + +.rst__lineBlock:before, .rst__lineBlock:after { + background-color: transparent; +} + +.rst__virtualScrollOverride { + overflow-x: hidden!important; +} +.rst__rowSubtitle { + display: block; + white-space: pre-wrap; + font-size:0px; + font-family: 'Khand', sans-serif; + padding-top: 8px; +} + +.rst__rowContents:hover { + color: @secondary_color !important; +} + +.rst__rowTitle { + font-weight: 500; +} + +.rst__rowSubtitle{ + font-weight:400; +} + +// Copy-pasted the css from the library + +.rst__expandButton { + // background: black + // url("images/collapse.png") + // no-repeat center; + border-color: black; + padding-right: 2px; +} + +.rst__collapseButton { + // background: black + // url('images/expand.png') + // no-repeat center; + border-color: black; + padding-right: 2px; + } + +.nodeSelected { + color: #ffffff; + font-size: 14px; + font-family: 'Khand', sans-serif; + border: 0px solid; + background: transparent; +} + +.nodeSelected:hover { + color: @primary_color; +} + +.nodeFound { + color: @secondary_color; + +} \ No newline at end of file diff --git a/webapp/package-lock.json b/webapp/package-lock.json index c4ac13ce..9b2ff207 100644 --- a/webapp/package-lock.json +++ b/webapp/package-lock.json @@ -1,5 +1,5 @@ { - "name": "geppetto-application", + "name": "geppetto-netpyne", "version": "1.0.0", "lockfileVersion": 1, "requires": true, @@ -42,53 +42,6 @@ "@babel/highlight": "^7.0.0" } }, - "@babel/compat-data": { - "version": "7.8.5", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.8.5.tgz", - "integrity": "sha512-jWYUqQX/ObOhG1UiEkbH5SANsE/8oKXiQWjj7p7xgj9Zmnt//aUvyz4dBkK0HNsS8/cbyC5NmmH87VekW+mXFg==", - "requires": { - "browserslist": "^4.8.5", - "invariant": "^2.2.4", - "semver": "^5.5.0" - }, - "dependencies": { - "browserslist": { - "version": "4.8.7", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.8.7.tgz", - "integrity": "sha512-gFOnZNYBHrEyUML0xr5NJ6edFaaKbTFX9S9kQHlYfCP0Rit/boRIz4G+Avq6/4haEKJXdGGUnoolx+5MWW2BoA==", - "requires": { - "caniuse-lite": "^1.0.30001027", - "electron-to-chromium": "^1.3.349", - "node-releases": "^1.1.49" - } - }, - "caniuse-lite": { - "version": "1.0.30001027", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001027.tgz", - "integrity": "sha512-7xvKeErvXZFtUItTHgNtLgS9RJpVnwBlWX8jSo/BO8VsF6deszemZSkJJJA1KOKrXuzZH4WALpAJdq5EyfgMLg==" - }, - "electron-to-chromium": { - "version": "1.3.353", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.353.tgz", - "integrity": "sha512-CkG24biyy9qQTQs8U2vGQaiyWSFDxAXP/UGHBveXZ1TGoWOAw+eYZXryrX0UeIMKnQjcaHx33hzYuydv98kqGQ==" - }, - "node-releases": { - "version": "1.1.49", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.49.tgz", - "integrity": "sha512-xH8t0LS0disN0mtRCh+eByxFPie+msJUBL/lJDBuap53QGiYPa9joh83K4pCZgWJ+2L4b9h88vCVdXQ60NO2bg==", - "requires": { - "semver": "^6.3.0" - }, - "dependencies": { - "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==" - } - } - } - } - }, "@babel/core": { "version": "7.5.5", "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.5.5.tgz", @@ -161,55 +114,6 @@ "@babel/types": "^7.4.4" } }, - "@babel/helper-compilation-targets": { - "version": "7.8.4", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.8.4.tgz", - "integrity": "sha512-3k3BsKMvPp5bjxgMdrFyq0UaEO48HciVrOVF0+lon8pp95cyJ2ujAh0TrBHNMnJGT2rr0iKOJPFFbSqjDyf/Pg==", - "requires": { - "@babel/compat-data": "^7.8.4", - "browserslist": "^4.8.5", - "invariant": "^2.2.4", - "levenary": "^1.1.1", - "semver": "^5.5.0" - }, - "dependencies": { - "browserslist": { - "version": "4.8.7", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.8.7.tgz", - "integrity": "sha512-gFOnZNYBHrEyUML0xr5NJ6edFaaKbTFX9S9kQHlYfCP0Rit/boRIz4G+Avq6/4haEKJXdGGUnoolx+5MWW2BoA==", - "requires": { - "caniuse-lite": "^1.0.30001027", - "electron-to-chromium": "^1.3.349", - "node-releases": "^1.1.49" - } - }, - "caniuse-lite": { - "version": "1.0.30001027", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001027.tgz", - "integrity": "sha512-7xvKeErvXZFtUItTHgNtLgS9RJpVnwBlWX8jSo/BO8VsF6deszemZSkJJJA1KOKrXuzZH4WALpAJdq5EyfgMLg==" - }, - "electron-to-chromium": { - "version": "1.3.353", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.353.tgz", - "integrity": "sha512-CkG24biyy9qQTQs8U2vGQaiyWSFDxAXP/UGHBveXZ1TGoWOAw+eYZXryrX0UeIMKnQjcaHx33hzYuydv98kqGQ==" - }, - "node-releases": { - "version": "1.1.49", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.49.tgz", - "integrity": "sha512-xH8t0LS0disN0mtRCh+eByxFPie+msJUBL/lJDBuap53QGiYPa9joh83K4pCZgWJ+2L4b9h88vCVdXQ60NO2bg==", - "requires": { - "semver": "^6.3.0" - }, - "dependencies": { - "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==" - } - } - } - } - }, "@babel/helper-create-class-features-plugin": { "version": "7.8.3", "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.8.3.tgz", @@ -353,38 +257,6 @@ } } }, - "@babel/helper-create-regexp-features-plugin": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.8.3.tgz", - "integrity": "sha512-Gcsm1OHCUr9o9TcJln57xhWHtdXbA2pgQ58S0Lxlks0WMGNXuki4+GLfX0p+L2ZkINUGZvfkz8rzoqJQSthI+Q==", - "requires": { - "@babel/helper-regex": "^7.8.3", - "regexpu-core": "^4.6.0" - }, - "dependencies": { - "@babel/helper-regex": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/helper-regex/-/helper-regex-7.8.3.tgz", - "integrity": "sha512-BWt0QtYv/cg/NecOAZMdcn/waj/5P26DR4mVLXfFtDokSR6fyuG0Pj+e2FqtSME+MqED1khnSMulkmGl8qWiUQ==", - "requires": { - "lodash": "^4.17.13" - } - }, - "regexpu-core": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-4.6.0.tgz", - "integrity": "sha512-YlVaefl8P5BnFYOITTNzDvan1ulLOiXJzCNZxduTIosN17b87h3bvG9yHMoHaRuo88H4mQ06Aodj5VtYGGGiTg==", - "requires": { - "regenerate": "^1.4.0", - "regenerate-unicode-properties": "^8.1.0", - "regjsgen": "^0.5.0", - "regjsparser": "^0.6.0", - "unicode-match-property-ecmascript": "^1.0.4", - "unicode-match-property-value-ecmascript": "^1.1.0" - } - } - } - }, "@babel/helper-define-map": { "version": "7.5.5", "resolved": "https://registry.npmjs.org/@babel/helper-define-map/-/helper-define-map-7.5.5.tgz", @@ -1390,18 +1262,28 @@ "@babel/highlight": "^7.8.3" } }, + "@babel/compat-data": { + "version": "7.8.6", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.8.6.tgz", + "integrity": "sha512-CurCIKPTkS25Mb8mz267vU95vy+TyUpnctEX2lV33xWNmHAfjruztgiPBbXZRh3xZZy1CYvGx6XfxyTVS+sk7Q==", + "requires": { + "browserslist": "^4.8.5", + "invariant": "^2.2.4", + "semver": "^5.5.0" + } + }, "@babel/core": { - "version": "7.8.4", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.8.4.tgz", - "integrity": "sha512-0LiLrB2PwrVI+a2/IEskBopDYSd8BCb3rOvH7D5tzoWd696TBEduBvuLVm4Nx6rltrLZqvI3MCalB2K2aVzQjA==", + "version": "7.8.7", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.8.7.tgz", + "integrity": "sha512-rBlqF3Yko9cynC5CCFy6+K/w2N+Sq/ff2BPy+Krp7rHlABIr5epbA7OxVeKoMHB39LZOp1UY5SuLjy6uWi35yA==", "requires": { "@babel/code-frame": "^7.8.3", - "@babel/generator": "^7.8.4", + "@babel/generator": "^7.8.7", "@babel/helpers": "^7.8.4", - "@babel/parser": "^7.8.4", - "@babel/template": "^7.8.3", - "@babel/traverse": "^7.8.4", - "@babel/types": "^7.8.3", + "@babel/parser": "^7.8.7", + "@babel/template": "^7.8.6", + "@babel/traverse": "^7.8.6", + "@babel/types": "^7.8.7", "convert-source-map": "^1.7.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.1", @@ -1413,11 +1295,11 @@ } }, "@babel/generator": { - "version": "7.8.4", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.8.4.tgz", - "integrity": "sha512-PwhclGdRpNAf3IxZb0YVuITPZmmrXz9zf6fH8lT4XbrmfQKr6ryBzhv593P5C6poJRciFCL/eHGW2NuGrgEyxA==", + "version": "7.8.8", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.8.8.tgz", + "integrity": "sha512-HKyUVu69cZoclptr8t8U5b6sx6zoWjh8jiUhnuj3MpZuKT2dJ8zPTuiy31luq32swhI0SpwItCIlU8XW7BZeJg==", "requires": { - "@babel/types": "^7.8.3", + "@babel/types": "^7.8.7", "jsesc": "^2.5.1", "lodash": "^4.17.13", "source-map": "^0.5.0" @@ -1441,13 +1323,35 @@ } }, "@babel/helper-call-delegate": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/helper-call-delegate/-/helper-call-delegate-7.8.3.tgz", - "integrity": "sha512-6Q05px0Eb+N4/GTyKPPvnkig7Lylw+QzihMpws9iiZQv7ZImf84ZsZpQH7QoWN4n4tm81SnSzPgHw2qtO0Zf3A==", + "version": "7.8.7", + "resolved": "https://registry.npmjs.org/@babel/helper-call-delegate/-/helper-call-delegate-7.8.7.tgz", + "integrity": "sha512-doAA5LAKhsFCR0LAFIf+r2RSMmC+m8f/oQ+URnUET/rWeEzC0yTRmAGyWkD4sSu3xwbS7MYQ2u+xlt1V5R56KQ==", "requires": { "@babel/helper-hoist-variables": "^7.8.3", "@babel/traverse": "^7.8.3", - "@babel/types": "^7.8.3" + "@babel/types": "^7.8.7" + } + }, + "@babel/helper-compilation-targets": { + "version": "7.8.7", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.8.7.tgz", + "integrity": "sha512-4mWm8DCK2LugIS+p1yArqvG1Pf162upsIsjE7cNBjez+NjliQpVhj20obE520nao0o14DaTnFJv+Fw5a0JpoUw==", + "requires": { + "@babel/compat-data": "^7.8.6", + "browserslist": "^4.9.1", + "invariant": "^2.2.4", + "levenary": "^1.1.1", + "semver": "^5.5.0" + } + }, + "@babel/helper-create-regexp-features-plugin": { + "version": "7.8.8", + "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.8.8.tgz", + "integrity": "sha512-LYVPdwkrQEiX9+1R29Ld/wTrmQu1SSKYnuOk3g0CkcZMA1p0gsNxJFj/3gBdaJ7Cg0Fnek5z0DsMULePP7Lrqg==", + "requires": { + "@babel/helper-annotate-as-pure": "^7.8.3", + "@babel/helper-regex": "^7.8.3", + "regexpu-core": "^4.7.0" } }, "@babel/helper-define-map": { @@ -1512,15 +1416,16 @@ } }, "@babel/helper-module-transforms": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.8.3.tgz", - "integrity": "sha512-C7NG6B7vfBa/pwCOshpMbOYUmrYQDfCpVL/JCRu0ek8B5p8kue1+BCXpg2vOYs7w5ACB9GTOBYQ5U6NwrMg+3Q==", + "version": "7.8.6", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.8.6.tgz", + "integrity": "sha512-RDnGJSR5EFBJjG3deY0NiL0K9TO8SXxS9n/MPsbPK/s9LbQymuLNtlzvDiNS7IpecuL45cMeLVkA+HfmlrnkRg==", "requires": { "@babel/helper-module-imports": "^7.8.3", + "@babel/helper-replace-supers": "^7.8.6", "@babel/helper-simple-access": "^7.8.3", "@babel/helper-split-export-declaration": "^7.8.3", - "@babel/template": "^7.8.3", - "@babel/types": "^7.8.3", + "@babel/template": "^7.8.6", + "@babel/types": "^7.8.6", "lodash": "^4.17.13" } }, @@ -1558,14 +1463,14 @@ } }, "@babel/helper-replace-supers": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.8.3.tgz", - "integrity": "sha512-xOUssL6ho41U81etpLoT2RTdvdus4VfHamCuAm4AHxGr+0it5fnwoVdwUJ7GFEqCsQYzJUhcbsN9wB9apcYKFA==", + "version": "7.8.6", + "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.8.6.tgz", + "integrity": "sha512-PeMArdA4Sv/Wf4zXwBKPqVj7n9UF/xg6slNRtZW84FM7JpE1CbG8B612FyM4cxrf4fMAMGO0kR7voy1ForHHFA==", "requires": { "@babel/helper-member-expression-to-functions": "^7.8.3", "@babel/helper-optimise-call-expression": "^7.8.3", - "@babel/traverse": "^7.8.3", - "@babel/types": "^7.8.3" + "@babel/traverse": "^7.8.6", + "@babel/types": "^7.8.6" } }, "@babel/helper-simple-access": { @@ -1617,9 +1522,9 @@ } }, "@babel/parser": { - "version": "7.8.4", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.8.4.tgz", - "integrity": "sha512-0fKu/QqildpXmPVaRBoXOlyBb3MC+J0A66x97qEfLOMkn3u6nfY5esWogQwi/K0BjASYy4DbnsEWnpNL6qT5Mw==" + "version": "7.8.8", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.8.8.tgz", + "integrity": "sha512-mO5GWzBPsPf6865iIbzNE0AvkKF3NE+2S3eRUpE+FE07BOAkXh6G+GW/Pj01hhXjve1WScbaIO4UlY1JKeqCcA==" }, "@babel/plugin-proposal-async-generator-functions": { "version": "7.8.3", @@ -1668,11 +1573,11 @@ } }, "@babel/plugin-proposal-unicode-property-regex": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.8.3.tgz", - "integrity": "sha512-1/1/rEZv2XGweRwwSkLpY+s60za9OZ1hJs4YDqFHCw0kYWYwL5IFljVY1MYBL+weT1l9pokDO2uhSTLVxzoHkQ==", + "version": "7.8.8", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.8.8.tgz", + "integrity": "sha512-EVhjVsMpbhLw9ZfHWSx2iy13Q8Z/eg8e8ccVWt23sWQK5l1UdkoLJPN5w69UA4uITGBnEZD2JOe4QOHycYKv8A==", "requires": { - "@babel/helper-create-regexp-features-plugin": "^7.8.3", + "@babel/helper-create-regexp-features-plugin": "^7.8.8", "@babel/helper-plugin-utils": "^7.8.3" } }, @@ -1752,16 +1657,16 @@ } }, "@babel/plugin-transform-classes": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.8.3.tgz", - "integrity": "sha512-SjT0cwFJ+7Rbr1vQsvphAHwUHvSUPmMjMU/0P59G8U2HLFqSa082JO7zkbDNWs9kH/IUqpHI6xWNesGf8haF1w==", + "version": "7.8.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.8.6.tgz", + "integrity": "sha512-k9r8qRay/R6v5aWZkrEclEhKO6mc1CCQr2dLsVHBmOQiMpN6I2bpjX3vgnldUWeEI1GHVNByULVxZ4BdP4Hmdg==", "requires": { "@babel/helper-annotate-as-pure": "^7.8.3", "@babel/helper-define-map": "^7.8.3", "@babel/helper-function-name": "^7.8.3", "@babel/helper-optimise-call-expression": "^7.8.3", "@babel/helper-plugin-utils": "^7.8.3", - "@babel/helper-replace-supers": "^7.8.3", + "@babel/helper-replace-supers": "^7.8.6", "@babel/helper-split-export-declaration": "^7.8.3", "globals": "^11.1.0" } @@ -1775,9 +1680,9 @@ } }, "@babel/plugin-transform-destructuring": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.8.3.tgz", - "integrity": "sha512-H4X646nCkiEcHZUZaRkhE2XVsoz0J/1x3VVujnn96pSoGCtKPA99ZZA+va+gK+92Zycd6OBKCD8tDb/731bhgQ==", + "version": "7.8.8", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.8.8.tgz", + "integrity": "sha512-eRJu4Vs2rmttFCdhPUM3bV0Yo/xPSdPw6ML9KHs/bjB4bLA5HXlbvYXPOD5yASodGod+krjYx21xm1QmL8dCJQ==", "requires": { "@babel/helper-plugin-utils": "^7.8.3" } @@ -1809,9 +1714,9 @@ } }, "@babel/plugin-transform-for-of": { - "version": "7.8.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.8.4.tgz", - "integrity": "sha512-iAXNlOWvcYUYoV8YIxwS7TxGRJcxyl8eQCfT+A5j8sKUzRFvJdcyjp97jL2IghWSRDaL2PU2O2tX8Cu9dTBq5A==", + "version": "7.8.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.8.6.tgz", + "integrity": "sha512-M0pw4/1/KI5WAxPsdcUL/w2LJ7o89YHN3yLkzNjg7Yl15GlVGgzHyCU+FMeAxevHGsLVmUqbirlUIKTafPmzdw==", "requires": { "@babel/helper-plugin-utils": "^7.8.3" } @@ -1908,11 +1813,11 @@ } }, "@babel/plugin-transform-parameters": { - "version": "7.8.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.8.4.tgz", - "integrity": "sha512-IsS3oTxeTsZlE5KqzTbcC2sV0P9pXdec53SU+Yxv7o/6dvGM5AkTotQKhoSffhNgZ/dftsSiOoxy7evCYJXzVA==", + "version": "7.8.8", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.8.8.tgz", + "integrity": "sha512-hC4Ld/Ulpf1psQciWWwdnUspQoQco2bMzSrwU6TmzRlvoYQe4rQFy9vnCZDTlVeCQj0JPfL+1RX0V8hCJvkgBA==", "requires": { - "@babel/helper-call-delegate": "^7.8.3", + "@babel/helper-call-delegate": "^7.8.7", "@babel/helper-get-function-arity": "^7.8.3", "@babel/helper-plugin-utils": "^7.8.3" } @@ -1926,11 +1831,11 @@ } }, "@babel/plugin-transform-regenerator": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.8.3.tgz", - "integrity": "sha512-qt/kcur/FxrQrzFR432FGZznkVAjiyFtCOANjkAKwCbt465L6ZCiUQh2oMYGU3Wo8LRFJxNDFwWn106S5wVUNA==", + "version": "7.8.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.8.7.tgz", + "integrity": "sha512-TIg+gAl4Z0a3WmD3mbYSk+J9ZUH6n/Yc57rtKRnlA/7rcCvpekHXe0CMZHP1gYp7/KLe9GHTuIba0vXmls6drA==", "requires": { - "regenerator-transform": "^0.14.0" + "regenerator-transform": "^0.14.2" } }, "@babel/plugin-transform-reserved-words": { @@ -1993,12 +1898,12 @@ } }, "@babel/preset-env": { - "version": "7.8.4", - "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.8.4.tgz", - "integrity": "sha512-HihCgpr45AnSOHRbS5cWNTINs0TwaR8BS8xIIH+QwiW8cKL0llV91njQMpeMReEPVs+1Ao0x3RLEBLtt1hOq4w==", + "version": "7.8.7", + "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.8.7.tgz", + "integrity": "sha512-BYftCVOdAYJk5ASsznKAUl53EMhfBbr8CJ1X+AJLfGPscQkwJFiaV/Wn9DPH/7fzm2v6iRYJKYHSqyynTGw0nw==", "requires": { - "@babel/compat-data": "^7.8.4", - "@babel/helper-compilation-targets": "^7.8.4", + "@babel/compat-data": "^7.8.6", + "@babel/helper-compilation-targets": "^7.8.7", "@babel/helper-module-imports": "^7.8.3", "@babel/helper-plugin-utils": "^7.8.3", "@babel/plugin-proposal-async-generator-functions": "^7.8.3", @@ -2021,13 +1926,13 @@ "@babel/plugin-transform-async-to-generator": "^7.8.3", "@babel/plugin-transform-block-scoped-functions": "^7.8.3", "@babel/plugin-transform-block-scoping": "^7.8.3", - "@babel/plugin-transform-classes": "^7.8.3", + "@babel/plugin-transform-classes": "^7.8.6", "@babel/plugin-transform-computed-properties": "^7.8.3", "@babel/plugin-transform-destructuring": "^7.8.3", "@babel/plugin-transform-dotall-regex": "^7.8.3", "@babel/plugin-transform-duplicate-keys": "^7.8.3", "@babel/plugin-transform-exponentiation-operator": "^7.8.3", - "@babel/plugin-transform-for-of": "^7.8.4", + "@babel/plugin-transform-for-of": "^7.8.6", "@babel/plugin-transform-function-name": "^7.8.3", "@babel/plugin-transform-literals": "^7.8.3", "@babel/plugin-transform-member-expression-literals": "^7.8.3", @@ -2038,9 +1943,9 @@ "@babel/plugin-transform-named-capturing-groups-regex": "^7.8.3", "@babel/plugin-transform-new-target": "^7.8.3", "@babel/plugin-transform-object-super": "^7.8.3", - "@babel/plugin-transform-parameters": "^7.8.4", + "@babel/plugin-transform-parameters": "^7.8.7", "@babel/plugin-transform-property-literals": "^7.8.3", - "@babel/plugin-transform-regenerator": "^7.8.3", + "@babel/plugin-transform-regenerator": "^7.8.7", "@babel/plugin-transform-reserved-words": "^7.8.3", "@babel/plugin-transform-shorthand-properties": "^7.8.3", "@babel/plugin-transform-spread": "^7.8.3", @@ -2048,7 +1953,7 @@ "@babel/plugin-transform-template-literals": "^7.8.3", "@babel/plugin-transform-typeof-symbol": "^7.8.4", "@babel/plugin-transform-unicode-regex": "^7.8.3", - "@babel/types": "^7.8.3", + "@babel/types": "^7.8.7", "browserslist": "^4.8.5", "core-js-compat": "^3.6.2", "invariant": "^2.2.2", @@ -2056,36 +1961,44 @@ "semver": "^5.5.0" } }, + "@babel/runtime": { + "version": "7.8.7", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.8.7.tgz", + "integrity": "sha512-+AATMUFppJDw6aiR5NVPHqIQBlV/Pj8wY/EZH+lmvRdUo9xBaz/rF3alAwFJQavvKfeOlPE7oaaDHVbcySbCsg==", + "requires": { + "regenerator-runtime": "^0.13.4" + } + }, "@babel/template": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.8.3.tgz", - "integrity": "sha512-04m87AcQgAFdvuoyiQ2kgELr2tV8B4fP/xJAVUL3Yb3bkNdMedD3d0rlSQr3PegP0cms3eHjl1F7PWlvWbU8FQ==", + "version": "7.8.6", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.8.6.tgz", + "integrity": "sha512-zbMsPMy/v0PWFZEhQJ66bqjhH+z0JgMoBWuikXybgG3Gkd/3t5oQ1Rw2WQhnSrsOmsKXnZOx15tkC4qON/+JPg==", "requires": { "@babel/code-frame": "^7.8.3", - "@babel/parser": "^7.8.3", - "@babel/types": "^7.8.3" + "@babel/parser": "^7.8.6", + "@babel/types": "^7.8.6" } }, "@babel/traverse": { - "version": "7.8.4", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.8.4.tgz", - "integrity": "sha512-NGLJPZwnVEyBPLI+bl9y9aSnxMhsKz42so7ApAv9D+b4vAFPpY013FTS9LdKxcABoIYFU52HcYga1pPlx454mg==", + "version": "7.8.6", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.8.6.tgz", + "integrity": "sha512-2B8l0db/DPi8iinITKuo7cbPznLCEk0kCxDoB9/N6gGNg/gxOXiR/IcymAFPiBwk5w6TtQ27w4wpElgp9btR9A==", "requires": { "@babel/code-frame": "^7.8.3", - "@babel/generator": "^7.8.4", + "@babel/generator": "^7.8.6", "@babel/helper-function-name": "^7.8.3", "@babel/helper-split-export-declaration": "^7.8.3", - "@babel/parser": "^7.8.4", - "@babel/types": "^7.8.3", + "@babel/parser": "^7.8.6", + "@babel/types": "^7.8.6", "debug": "^4.1.0", "globals": "^11.1.0", "lodash": "^4.17.13" } }, "@babel/types": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.8.3.tgz", - "integrity": "sha512-jBD+G8+LWpMBBWvVcdr4QysjUE4mU/syrhN17o1u3gx0/WzJB1kwiVZAXRtWbsIPOwW8pF/YJV5+nmetPzepXg==", + "version": "7.8.7", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.8.7.tgz", + "integrity": "sha512-k2TreEHxFA4CjGkL+GYjRyx35W0Mr7DP5+9q6WMkyKXB+904bYmG40syjMFV0oLlhhFCwWl0vA0DyzTDkwAiJw==", "requires": { "esutils": "^2.0.2", "lodash": "^4.17.13", @@ -2093,19 +2006,25 @@ } }, "browserslist": { - "version": "4.8.7", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.8.7.tgz", - "integrity": "sha512-gFOnZNYBHrEyUML0xr5NJ6edFaaKbTFX9S9kQHlYfCP0Rit/boRIz4G+Avq6/4haEKJXdGGUnoolx+5MWW2BoA==", + "version": "4.10.0", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.10.0.tgz", + "integrity": "sha512-TpfK0TDgv71dzuTsEAlQiHeWQ/tiPqgNZVdv046fvNtBZrjbv2O3TsWCDU0AWGJJKCF/KsjNdLzR9hXOsh/CfA==", "requires": { - "caniuse-lite": "^1.0.30001027", - "electron-to-chromium": "^1.3.349", - "node-releases": "^1.1.49" + "caniuse-lite": "^1.0.30001035", + "electron-to-chromium": "^1.3.378", + "node-releases": "^1.1.52", + "pkg-up": "^3.1.0" } }, "caniuse-lite": { - "version": "1.0.30001027", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001027.tgz", - "integrity": "sha512-7xvKeErvXZFtUItTHgNtLgS9RJpVnwBlWX8jSo/BO8VsF6deszemZSkJJJA1KOKrXuzZH4WALpAJdq5EyfgMLg==" + "version": "1.0.30001035", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001035.tgz", + "integrity": "sha512-C1ZxgkuA4/bUEdMbU5WrGY4+UhMFFiXrgNAfxiMIqWgFTWfv/xsZCS2xEHT2LMq7xAZfuAnu6mcqyDl0ZR6wLQ==" + }, + "classnames": { + "version": "2.2.6", + "resolved": "https://registry.npmjs.org/classnames/-/classnames-2.2.6.tgz", + "integrity": "sha512-JR/iSQOSt+LQIWwrwEzJ9uk0xfN3mTVYMwt1Ir5mUcSN6pU+V4zQFFaJsclJbPuAUQH+yfWef6tm7l1quW3C8Q==" }, "convert-source-map": { "version": "1.7.0", @@ -2132,14 +2051,14 @@ } }, "electron-to-chromium": { - "version": "1.3.353", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.353.tgz", - "integrity": "sha512-CkG24biyy9qQTQs8U2vGQaiyWSFDxAXP/UGHBveXZ1TGoWOAw+eYZXryrX0UeIMKnQjcaHx33hzYuydv98kqGQ==" + "version": "1.3.379", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.379.tgz", + "integrity": "sha512-NK9DBBYEBb5f9D7zXI0hiE941gq3wkBeQmXs1ingigA/jnTg5mhwY2Z5egwA+ZI8OLGKCx0h1Cl8/xeuIBuLlg==" }, "node-releases": { - "version": "1.1.49", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.49.tgz", - "integrity": "sha512-xH8t0LS0disN0mtRCh+eByxFPie+msJUBL/lJDBuap53QGiYPa9joh83K4pCZgWJ+2L4b9h88vCVdXQ60NO2bg==", + "version": "1.1.52", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.52.tgz", + "integrity": "sha512-snSiT1UypkgGt2wxPqS6ImEUICbNCMb31yaxWrOLXjhlt2z2/IBpaOxzONExqSm4y5oLnAqjjRWu+wsDzK5yNQ==", "requires": { "semver": "^6.3.0" }, @@ -2150,6 +2069,76 @@ "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==" } } + }, + "prop-types": { + "version": "15.7.2", + "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.7.2.tgz", + "integrity": "sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ==", + "requires": { + "loose-envify": "^1.4.0", + "object-assign": "^4.1.1", + "react-is": "^16.8.1" + } + }, + "regenerate-unicode-properties": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-8.2.0.tgz", + "integrity": "sha512-F9DjY1vKLo/tPePDycuH3dn9H1OTPIkVD9Kz4LODu+F2C75mgjAJ7x/gwy6ZcSNRAAkhNlJSOHRe8k3p+K9WhA==", + "requires": { + "regenerate": "^1.4.0" + } + }, + "regenerator-runtime": { + "version": "0.13.5", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.5.tgz", + "integrity": "sha512-ZS5w8CpKFinUzOwW3c83oPeVXoNsrLsaCoLtJvAClH135j/R77RuymhiSErhm2lKcwSCIpmvIWSbDkIfAqKQlA==" + }, + "regenerator-transform": { + "version": "0.14.4", + "resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.14.4.tgz", + "integrity": "sha512-EaJaKPBI9GvKpvUz2mz4fhx7WPgvwRLY9v3hlNHWmAuJHI13T4nwKnNvm5RWJzEdnI5g5UwtOww+S8IdoUC2bw==", + "requires": { + "@babel/runtime": "^7.8.4", + "private": "^0.1.8" + } + }, + "regexpu-core": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-4.7.0.tgz", + "integrity": "sha512-TQ4KXRnIn6tz6tjnrXEkD/sshygKH/j5KzK86X8MkeHyZ8qst/LZ89j3X4/8HEIfHANTFIP/AbXakeRhWIl5YQ==", + "requires": { + "regenerate": "^1.4.0", + "regenerate-unicode-properties": "^8.2.0", + "regjsgen": "^0.5.1", + "regjsparser": "^0.6.4", + "unicode-match-property-ecmascript": "^1.0.4", + "unicode-match-property-value-ecmascript": "^1.2.0" + } + }, + "regjsgen": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.5.1.tgz", + "integrity": "sha512-5qxzGZjDs9w4tzT3TPhCJqWdCc3RLYwy9J2NB0nm5Lz+S273lvWcpjaTGHsT1dc6Hhfq41uSEOw8wBmxrKOuyg==" + }, + "regjsparser": { + "version": "0.6.4", + "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.6.4.tgz", + "integrity": "sha512-64O87/dPDgfk8/RQqC4gkZoGyyWFIEUTTh80CU6CWuK5vkCGyekIx+oKcEIYtP/RAxSQltCZHCNu/mdd7fqlJw==", + "requires": { + "jsesc": "~0.5.0" + }, + "dependencies": { + "jsesc": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz", + "integrity": "sha1-597mbjXW/Bb3EP6R1c9p9w8IkR0=" + } + } + }, + "unicode-match-property-value-ecmascript": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-1.2.0.tgz", + "integrity": "sha512-wjuQHGQVofmSJv1uVISKLE5zO2rNGzM/KCYZch/QQvez7C1hUhBIuZ701fYXExuufJFMPhv2SyL8CyoIfMLbIQ==" } } }, @@ -2514,6 +2503,30 @@ "@babel/runtime": "^7.4.4" } }, + "@material-ui/lab": { + "version": "4.0.0-alpha.46", + "resolved": "https://registry.npmjs.org/@material-ui/lab/-/lab-4.0.0-alpha.46.tgz", + "integrity": "sha512-JGgZmj1UNP8bbYNAGvndipjXRK3x2+9mFBzbX7MyCj+WpfnJbeqTmJK2No9MXvPj/EZJ1piaKif46FdDc4U93A==", + "requires": { + "@babel/runtime": "^7.4.4", + "@material-ui/utils": "^4.9.6", + "clsx": "^1.0.4", + "prop-types": "^15.7.2", + "react-is": "^16.8.0" + }, + "dependencies": { + "@material-ui/utils": { + "version": "4.9.6", + "resolved": "https://registry.npmjs.org/@material-ui/utils/-/utils-4.9.6.tgz", + "integrity": "sha512-gqlBn0JPPTUZeAktn1rgMcy9Iczrr74ecx31tyZLVGdBGGzsxzM6PP6zeS7FuoLS6vG4hoZP7hWnOoHtkR0Kvw==", + "requires": { + "@babel/runtime": "^7.4.4", + "prop-types": "^15.7.2", + "react-is": "^16.8.0" + } + } + } + }, "@material-ui/pickers": { "version": "3.2.6", "resolved": "https://registry.npmjs.org/@material-ui/pickers/-/pickers-3.2.6.tgz", @@ -3517,18 +3530,60 @@ } }, "awesome-typescript-loader": { - "version": "3.5.0", - "resolved": "https://registry.npmjs.org/awesome-typescript-loader/-/awesome-typescript-loader-3.5.0.tgz", - "integrity": "sha512-qzgm9SEvodVkSi9QY7Me1/rujg+YBNMjayNSAyzNghwTEez++gXoPCwMvpbHRG7wrOkDCiF6dquvv9ESmUBAuw==", + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/awesome-typescript-loader/-/awesome-typescript-loader-5.2.1.tgz", + "integrity": "sha512-slv66OAJB8orL+UUaTI3pKlLorwIvS4ARZzYR9iJJyGsEgOqueMfOMdKySWzZ73vIkEe3fcwFgsKMg4d8zyb1g==", "dev": true, "requires": { - "chalk": "^2.3.1", - "enhanced-resolve": "3.3.0", + "chalk": "^2.4.1", + "enhanced-resolve": "^4.0.0", "loader-utils": "^1.1.0", - "lodash": "^4.17.4", - "micromatch": "^3.0.3", + "lodash": "^4.17.5", + "micromatch": "^3.1.9", "mkdirp": "^0.5.1", - "source-map-support": "^0.5.3" + "source-map-support": "^0.5.3", + "webpack-log": "^1.2.0" + }, + "dependencies": { + "enhanced-resolve": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-4.1.1.tgz", + "integrity": "sha512-98p2zE+rL7/g/DzMHMTF4zZlCgeVdJ7yr6xzEpJRYwFYrGi9ANdn5DnJURg6RpBkyk60XYDnWIv51VfIhfNGuA==", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "memory-fs": "^0.5.0", + "tapable": "^1.0.0" + } + }, + "memory-fs": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/memory-fs/-/memory-fs-0.5.0.tgz", + "integrity": "sha512-jA0rdU5KoQMC0e6ppoNRtpp6vjFq6+NY7r8hywnC7V+1Xj/MtHwGIbB1QaK/dunyjWteJzmkpd7ooeWg10T7GA==", + "dev": true, + "requires": { + "errno": "^0.1.3", + "readable-stream": "^2.0.1" + } + }, + "tapable": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-1.1.3.tgz", + "integrity": "sha512-4WK/bYZmj8xLr+HUCODHGF1ZFzsYffasLUgEiMBY4fgtltdO6B4WJtlSbPaDTLpYTcGVwM2qLnFTICEcNxs3kA==", + "dev": true + }, + "webpack-log": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/webpack-log/-/webpack-log-1.2.0.tgz", + "integrity": "sha512-U9AnICnu50HXtiqiDxuli5gLB5PGBo7VvcHx36jRZHwK4vzOYLbImqT4lwWwoMHdQWwEKw736fCHEekokTEKHA==", + "dev": true, + "requires": { + "chalk": "^2.1.0", + "log-symbols": "^2.1.0", + "loglevelnext": "^1.0.1", + "uuid": "^3.1.0" + } + } } }, "aws-sign2": { @@ -3986,6 +4041,15 @@ "resolved": "https://registry.npmjs.org/binary-search-bounds/-/binary-search-bounds-2.0.4.tgz", "integrity": "sha512-2hg5kgdKql5ClF2ErBcSx0U5bnl5hgS4v7wMnLFodyR47yMtj2w+UAZB+0CiqyHct2q543i7Bi4/aMIegorCCg==" }, + "bindings": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz", + "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==", + "optional": true, + "requires": { + "file-uri-to-path": "1.0.0" + } + }, "bit-twiddle": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/bit-twiddle/-/bit-twiddle-1.0.2.tgz", @@ -6411,18 +6475,6 @@ "once": "^1.4.0" } }, - "enhanced-resolve": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-3.3.0.tgz", - "integrity": "sha512-2qbxE7ek3YxPJ1ML6V+satHkzHpJQKWkRHmRx6mfAoW59yP8YH8BFplbegSP+u2hBd6B6KCOpvJQ3dZAP+hkpg==", - "dev": true, - "requires": { - "graceful-fs": "^4.1.2", - "memory-fs": "^0.4.0", - "object-assign": "^4.0.1", - "tapable": "^0.2.5" - } - }, "enquire.js": { "version": "2.1.6", "resolved": "https://registry.npmjs.org/enquire.js/-/enquire.js-2.1.6.tgz", @@ -7253,6 +7305,12 @@ "tslib": "^1.9.0" } }, + "file-uri-to-path": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", + "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==", + "optional": true + }, "fill-range": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", @@ -7729,13 +7787,14 @@ "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" }, "fsevents": { - "version": "1.2.9", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.9.tgz", - "integrity": "sha512-oeyj2H3EjjonWcFjD5NvZNE9Rqe4UW+nQBU2HNeKw0koVLEFIhtyETyAakeAM3de7Z/SW5kcA+fZUait9EApnw==", + "version": "1.2.12", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.12.tgz", + "integrity": "sha512-Ggd/Ktt7E7I8pxZRbGIs7vwqAPscSESMrCSkx2FtWeqmheJgCo2R74fTsZFCifr0VTPwqRpPv17+6b8Zp7th0Q==", "optional": true, "requires": { + "bindings": "^1.5.0", "nan": "^2.12.1", - "node-pre-gyp": "^0.12.0" + "node-pre-gyp": "*" }, "dependencies": { "abbrev": { @@ -7777,7 +7836,7 @@ } }, "chownr": { - "version": "1.1.1", + "version": "1.1.4", "bundled": true, "optional": true }, @@ -7802,7 +7861,7 @@ "optional": true }, "debug": { - "version": "4.1.1", + "version": "3.2.6", "bundled": true, "optional": true, "requires": { @@ -7825,11 +7884,11 @@ "optional": true }, "fs-minipass": { - "version": "1.2.5", + "version": "1.2.7", "bundled": true, "optional": true, "requires": { - "minipass": "^2.2.1" + "minipass": "^2.6.0" } }, "fs.realpath": { @@ -7853,7 +7912,7 @@ } }, "glob": { - "version": "7.1.3", + "version": "7.1.6", "bundled": true, "optional": true, "requires": { @@ -7879,7 +7938,7 @@ } }, "ignore-walk": { - "version": "3.0.1", + "version": "3.0.3", "bundled": true, "optional": true, "requires": { @@ -7896,7 +7955,7 @@ } }, "inherits": { - "version": "2.0.3", + "version": "2.0.4", "bundled": true, "optional": true }, @@ -7927,12 +7986,12 @@ } }, "minimist": { - "version": "0.0.8", + "version": "1.2.5", "bundled": true, "optional": true }, "minipass": { - "version": "2.3.5", + "version": "2.9.0", "bundled": true, "optional": true, "requires": { @@ -7941,38 +8000,38 @@ } }, "minizlib": { - "version": "1.2.1", + "version": "1.3.3", "bundled": true, "optional": true, "requires": { - "minipass": "^2.2.1" + "minipass": "^2.9.0" } }, "mkdirp": { - "version": "0.5.1", + "version": "0.5.3", "bundled": true, "optional": true, "requires": { - "minimist": "0.0.8" + "minimist": "^1.2.5" } }, "ms": { - "version": "2.1.1", + "version": "2.1.2", "bundled": true, "optional": true }, "needle": { - "version": "2.3.0", + "version": "2.3.3", "bundled": true, "optional": true, "requires": { - "debug": "^4.1.0", + "debug": "^3.2.6", "iconv-lite": "^0.4.4", "sax": "^1.2.4" } }, "node-pre-gyp": { - "version": "0.12.0", + "version": "0.14.0", "bundled": true, "optional": true, "requires": { @@ -7985,11 +8044,11 @@ "rc": "^1.2.7", "rimraf": "^2.6.1", "semver": "^5.3.0", - "tar": "^4" + "tar": "^4.4.2" } }, "nopt": { - "version": "4.0.1", + "version": "4.0.3", "bundled": true, "optional": true, "requires": { @@ -7998,17 +8057,26 @@ } }, "npm-bundled": { - "version": "1.0.6", + "version": "1.1.1", + "bundled": true, + "optional": true, + "requires": { + "npm-normalize-package-bin": "^1.0.1" + } + }, + "npm-normalize-package-bin": { + "version": "1.0.1", "bundled": true, "optional": true }, "npm-packlist": { - "version": "1.4.1", + "version": "1.4.8", "bundled": true, "optional": true, "requires": { "ignore-walk": "^3.0.1", - "npm-bundled": "^1.0.1" + "npm-bundled": "^1.0.1", + "npm-normalize-package-bin": "^1.0.1" } }, "npmlog": { @@ -8065,7 +8133,7 @@ "optional": true }, "process-nextick-args": { - "version": "2.0.0", + "version": "2.0.1", "bundled": true, "optional": true }, @@ -8078,17 +8146,10 @@ "ini": "~1.3.0", "minimist": "^1.2.0", "strip-json-comments": "~2.0.1" - }, - "dependencies": { - "minimist": { - "version": "1.2.0", - "bundled": true, - "optional": true - } } }, "readable-stream": { - "version": "2.3.6", + "version": "2.3.7", "bundled": true, "optional": true, "requires": { @@ -8102,7 +8163,7 @@ } }, "rimraf": { - "version": "2.6.3", + "version": "2.7.1", "bundled": true, "optional": true, "requires": { @@ -8125,7 +8186,7 @@ "optional": true }, "semver": { - "version": "5.7.0", + "version": "5.7.1", "bundled": true, "optional": true }, @@ -8171,17 +8232,17 @@ "optional": true }, "tar": { - "version": "4.4.8", + "version": "4.4.13", "bundled": true, "optional": true, "requires": { "chownr": "^1.1.1", "fs-minipass": "^1.2.5", - "minipass": "^2.3.4", - "minizlib": "^1.1.1", + "minipass": "^2.8.6", + "minizlib": "^1.2.1", "mkdirp": "^0.5.0", "safe-buffer": "^5.1.2", - "yallist": "^3.0.2" + "yallist": "^3.0.3" } }, "util-deprecate": { @@ -8203,7 +8264,7 @@ "optional": true }, "yallist": { - "version": "3.0.3", + "version": "3.1.1", "bundled": true, "optional": true } @@ -11437,12 +11498,31 @@ "integrity": "sha1-vyJJiGzlFM2hEvrpIYzcBlIR/Jw=", "dev": true }, + "log-symbols": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-2.2.0.tgz", + "integrity": "sha512-VeIAFslyIerEJLXHziedo2basKbMKtTw3vfn5IzG0XTjhAVEJyNHnL2p7vc+wBDSdQuUpNw3M2u6xb9QsAY5Eg==", + "dev": true, + "requires": { + "chalk": "^2.0.1" + } + }, "loglevel": { "version": "1.6.3", "resolved": "https://registry.npmjs.org/loglevel/-/loglevel-1.6.3.tgz", "integrity": "sha512-LoEDv5pgpvWgPF4kNYuIp0qqSJVWak/dML0RY74xlzMZiT9w77teNAwKYKWBTYjlokMirg+o3jBwp+vlLrcfAA==", "dev": true }, + "loglevelnext": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/loglevelnext/-/loglevelnext-1.0.5.tgz", + "integrity": "sha512-V/73qkPuJmx4BcBF19xPBr+0ZRVBhc4POxvZTZdMeXpJ4NItXSJ/MSwuFT0kQJlCbXvdlZoQQ/418bS1y9Jh6A==", + "dev": true, + "requires": { + "es6-symbol": "^3.1.1", + "object.assign": "^4.1.0" + } + }, "longest": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/longest/-/longest-1.0.1.tgz", @@ -13338,6 +13418,14 @@ "find-up": "^3.0.0" } }, + "pkg-up": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/pkg-up/-/pkg-up-3.1.0.tgz", + "integrity": "sha512-nDywThFk1i4BQK4twPQ6TA4RT8bDY96yeuCVBWL3ePARCiEKDRSrNGbFIgUJpLp+XeIR65v8ra7WuJOFUBtkMA==", + "requires": { + "find-up": "^3.0.0" + } + }, "planar-dual": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/planar-dual/-/planar-dual-1.0.2.tgz", @@ -14653,6 +14741,7 @@ "version": "0.14.1", "resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.14.1.tgz", "integrity": "sha512-flVuee02C3FKRISbxhXl9mGzdbWUVHubl1SMaknjxkFB1/iqpJhArQUvRxOOPEc/9tAiX0BaQ28FJH10E4isSQ==", + "dev": true, "requires": { "private": "^0.1.6" } @@ -16644,12 +16733,6 @@ } } }, - "tapable": { - "version": "0.2.9", - "resolved": "https://registry.npmjs.org/tapable/-/tapable-0.2.9.tgz", - "integrity": "sha512-2wsvQ+4GwBvLPLWsNfLCDYGsW6xb7aeC6utq2Qh0PFwgEy7K7dsma9Jsmb2zSQj7GvYAyUGSntLtsv++GmgL1A==", - "dev": true - }, "tape": { "version": "4.11.0", "resolved": "https://registry.npmjs.org/tape/-/tape-4.11.0.tgz", @@ -17288,9 +17371,9 @@ "integrity": "sha512-sOFA1FXgP0gOgBYlS6irwq6hHYA370KE3dPlgYEJHL3PJd5X8gQE0RmL79ONif6fL5JZuGDj+rtOrFeOqz5IZQ==" }, "typescript": { - "version": "2.9.2", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-2.9.2.tgz", - "integrity": "sha512-Gr4p6nFNaoufRIY4NMdpQRNmgxVIGMs4Fcu/ujdYk3nAZqk7supzBE9idmvfZIlH/Cuj//dvi+019qEue9lV0w==", + "version": "3.8.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.8.3.tgz", + "integrity": "sha512-MYlEfn5VrLNsgudQTVJeNaQFUAI7DkhnOjdpAp4T+ku1TfQClewlbSuTVHiA+8skNBgaf02TL/kLOvig4y3G8w==", "dev": true }, "ua-parser-js": { diff --git a/webapp/package.json b/webapp/package.json index b6c1d0a7..8cedb663 100644 --- a/webapp/package.json +++ b/webapp/package.json @@ -1,8 +1,8 @@ { - "name": "geppetto-application", + "name": "geppetto-netpyne", "version": "1.0.0", - "description": "Geppetto Sample Application. Geppetto is an open-source platform to build web-based tools to visualize and simulate neuroscience data and models.", - "repository": "http://git.geppetto.org", + "description": "Geppetto NetPyNE-UI frontend.", + "repository": "https://github.com/MetaCell/NetPyNE-UI", "license": "MIT", "scripts": { "test": "jest --verbose", @@ -10,9 +10,9 @@ "build": "webpack --mode none -p --progress", "prebuild-dev": "eslint . --color", "build-dev": "webpack --mode none --devtool eval", - "prebuild-dev-noTest": "eslint . --color", + "prebuild-dev-noTest": "eslint . --color --fix", "build-dev-noTest": "webpack --mode none --devtool source-map --env.noTest=true", - "prebuild-dev-noTest:watch": "eslint . --color", + "prebuild-dev-noTest:watch": "eslint . --color --fix", "build-dev-noTest:watch": "webpack --mode none --devtool source-map --env.noTest=true --progress --watch", "prestart": "eslint . --color", "start": "node --max_old_space_size=2048 node_modules/webpack-dev-server/bin/webpack-dev-server.js --progress --config webpack.config.dev.js" @@ -21,6 +21,7 @@ "@babel/plugin-proposal-class-properties": "^7.8.3", "@geppettoengine/geppetto-client": "file:./geppetto-client", "@material-ui/icons": "^4.9.1", + "@material-ui/lab": "^4.0.0-alpha.46", "react-redux": "^7.2.0" }, "devDependencies": { @@ -32,40 +33,40 @@ "@types/material-ui": "^0.21.5", "@types/react": "^16.4.9", "@types/react-dom": "^16.0.7", - "awesome-typescript-loader": "^3.5.0", + "awesome-typescript-loader": "^5.0.0-1", "babel-eslint": "^10.0.1", + "babel-loader": "^8.0.6", "babel-plugin-transform-decorators-legacy": "^1.3.5", "babel-plugin-transform-object-assign": "^6.8.0", "babel-plugin-transform-runtime": "^6.15.0", "casperjs": "^1.1.4", - "eslint": "^6.0.1", - "eslint-plugin-jest": "^22.17.0", - "jasmine": "^3.2.0", - "json-loader": "^0.5.4", - "phantomjs": "^2.1.7", - "raw-loader": "^0.5.1", - "slimerjs": "^1.0.0", - "source-map-loader": "^0.2.3", - "typedoc": "^0.11.1", - "typescript": "^2.0.0", - "webpack-dev-server": "^3.7.2", - "babel-loader": "^8.0.6", "copy-webpack-plugin": "^4.6.0", "css-loader": "^3.0.0", + "eslint": "^6.0.1", + "eslint-plugin-jest": "^22.17.0", "exports-loader": "^0.6.3", "html-webpack-plugin": "^3.2.0", "ignore-loader": "^0.1.2", "imports-loader": "^0.7.1", + "jasmine": "^3.2.0", "jest": "^24.8.0", "jest-puppeteer": "^4.3.0", + "json-loader": "^0.5.4", "less-loader": "^5.0.0", "mini-css-extract-plugin": "^0.7.0", + "phantomjs": "^2.1.7", "puppeteer": "^1.17.0", + "raw-loader": "^0.5.1", + "slimerjs": "^1.0.0", + "source-map-loader": "^0.2.3", "style-loader": "^0.13.2", + "typedoc": "^0.11.1", + "typescript": "^3.8.3", "url-join": "^4.0.0", "url-loader": "^0.5.8", "webpack": "^4.35.0", - "webpack-cli": "^3.3.5" + "webpack-cli": "^3.3.5", + "webpack-dev-server": "^3.7.2" }, "buildOptions": { "emitEntryPoint": true, diff --git a/webapp/redux/actions/drawer.js b/webapp/redux/actions/drawer.js new file mode 100644 index 00000000..c699f999 --- /dev/null +++ b/webapp/redux/actions/drawer.js @@ -0,0 +1,7 @@ +// Action Types +export const OPEN_DRAWER_DIALOG_BOX = 'OPEN_DRAWER_DIALOG_BOX'; +export const CLOSE_DRAWER_DIALOG_BOX = 'CLOSE_DRAWER_DIALOG_BOX'; + +// Actions +export const closeDrawerDialogBox = { type: CLOSE_DRAWER_DIALOG_BOX } +export const openDrawerDialogBox = { type: OPEN_DRAWER_DIALOG_BOX } diff --git a/webapp/redux/actions/errors.js b/webapp/redux/actions/errors.js new file mode 100644 index 00000000..845915a2 --- /dev/null +++ b/webapp/redux/actions/errors.js @@ -0,0 +1,7 @@ +// Action Types +export const CLOSE_BACKEND_ERROR_DIALOG = 'CLOSE_BACKEND_ERROR_DIALOG'; +export const OPEN_BACKEND_ERROR_DIALOG = 'OPEN_BACKEND_ERROR_DIALOG'; + +// Actions +export const closeBackendErrorDialog = { type: CLOSE_BACKEND_ERROR_DIALOG } +export const openBackendErrorDialog = payload => ({ type: OPEN_BACKEND_ERROR_DIALOG, payload }); diff --git a/webapp/redux/actions/flexlayout.js b/webapp/redux/actions/flexlayout.js new file mode 100644 index 00000000..9cebb1f1 --- /dev/null +++ b/webapp/redux/actions/flexlayout.js @@ -0,0 +1,62 @@ +import { WidgetStatus, FILEVARIABLE_LENGTH } from '../../constants'; + +export const UPDATE_WIDGET = 'UPDATE_WIDGET'; +export const ACTIVATE_WIDGET = 'ACTIVATE_WIDGET'; +export const ADD_WIDGET = 'ADD_WIDGET'; +export const RESET_LAYOUT = 'RESET_LAYOUT'; +export const DESTROY_WIDGET = 'DESTROY_WIDGET'; +export const ADD_PLOT_TO_EXISTING_WIDGET = 'ADD_PLOT_TO_EXISTING_WIDGET' + +export const SWITCH_LAYOUT = 'SWITCH_LAYOUT' + + +export const newWidget = ({ path, component, panelName, ...others }) => ({ + type: ADD_WIDGET, + data: { + id: path, + instancePath: path, + component: component, + name: path.slice(FILEVARIABLE_LENGTH), + status: WidgetStatus.ACTIVE, + panelName: panelName, + ...others + } +}); + +export const updateWidget = (newConf => ({ + type: UPDATE_WIDGET, + data: newConf +})) + + +export const minimizeWidget = id => ({ + type: UPDATE_WIDGET, + data: { + id, + status: WidgetStatus.MINIMIZED + } + +}); + +export const maximizeWidget = id => ({ + type: UPDATE_WIDGET, + data: { + id, + status: WidgetStatus.MAXIMIZED + } +}); +export const activateWidget = id => ({ + type: ACTIVATE_WIDGET, + data: { id } + +}); + +export const destroyWidget = id => ({ + type: DESTROY_WIDGET, + data: { id } + +}); + +export const resetLayout = { type: RESET_LAYOUT, }; + +export const switchLayout = { type: SWITCH_LAYOUT, }; \ No newline at end of file diff --git a/webapp/redux/actions/general.js b/webapp/redux/actions/general.js index a834f394..a94274c9 100644 --- a/webapp/redux/actions/general.js +++ b/webapp/redux/actions/general.js @@ -1,5 +1,23 @@ // Action Types export const UPDATE_CARDS = 'UPDATE_CARDS'; +export const MODEL_LOADED = 'MODEL_LOADED'; +export const SHOW_NETWORK = 'SHOW_NETWORK'; +export const CREATE_NETWORK = 'CREATE_NETWORK'; +export const CREATE_SIMULATE_NETWORK = 'CREATE_SIMULATE_NETWORK'; +export const SIMULATE_NETWORK = 'SIMULATE_NETWORK'; +export const EDIT_MODEL = 'EDIT_MODEL'; +export const PYTHON_CALL = 'PYTHON_CALL' // Actions export const updateCards = { type: UPDATE_CARDS }; + +export const modelLoaded = { type: MODEL_LOADED }; + +export const createNetwork = { type: CREATE_NETWORK }; +export const createAndSimulateNetwork = { type: CREATE_SIMULATE_NETWORK }; +export const simulateNetwork = { type: SIMULATE_NETWORK }; +export const showNetwork = { type: SHOW_NETWORK }; + +export const editModel = { type: EDIT_MODEL }; + +export const pythonCall = (cmd, args) => ({ type: PYTHON_CALL, cmd, args }) \ No newline at end of file diff --git a/webapp/redux/actions/notebook.js b/webapp/redux/actions/notebook.js new file mode 100644 index 00000000..5c2b7b79 --- /dev/null +++ b/webapp/redux/actions/notebook.js @@ -0,0 +1,10 @@ +export const LOAD_NOTEBOOK = 'LOAD_NOTEBOOK'; +export const UNLOAD_NOTEBOOK = 'UNLOAD_NOTEBOOK'; +export const NOTEBOOK_READY = 'NOTEBOOK_READY'; + + +export const loadNotebook = { type: LOAD_NOTEBOOK }; + +export const unloadNotebook = { type: UNLOAD_NOTEBOOK }; + +export const notebookReady = { type: NOTEBOOK_READY }; diff --git a/webapp/redux/middleware/middleware.js b/webapp/redux/middleware/middleware.js index 00f40948..e050a816 100644 --- a/webapp/redux/middleware/middleware.js +++ b/webapp/redux/middleware/middleware.js @@ -1,4 +1,13 @@ -import { UPDATE_CARDS } from '../actions/general'; +import { + UPDATE_CARDS, CREATE_NETWORK, CREATE_SIMULATE_NETWORK, PYTHON_CALL, SIMULATE_NETWORK, + showNetwork, createNetwork, createAndSimulateNetwork, editModel, EDIT_MODEL +} from '../actions/general'; +import { switchLayout } from '../actions/flexlayout' +import { openBackendErrorDialog } from '../actions/errors'; +import { closeDrawerDialogBox } from '../actions/drawer'; +import Utils from '../../Utils'; +import { NETPYNE_COMMANDS } from '../../constants'; +import { downloadJsonResponse, downloadPythonResponse } from './utils' export default store => next => action => { switch (action.type) { @@ -7,10 +16,105 @@ export default store => next => action => { console.log("Triggered card update") next(action); break; - + case EDIT_MODEL:{ + next(action) + next(switchLayout) + break + } + case CREATE_NETWORK:{ + instantiateNetwork({}, next, createNetwork, switchLayout, true) + break; + } + case CREATE_SIMULATE_NETWORK:{ + simulateNetwork({ parallelSimulation: false }, next, action, true) + break; + } + + case SIMULATE_NETWORK: + simulateNetwork({ parallelSimulation: false, usePrevInst: true }, next, action, false) + break + case PYTHON_CALL: + pythonCall(next, action) + break; default: { next(action); } + } +} + + +const instantiateNetwork = async (payload, next, action, shouldSwitchLayout) => { + createSimulateBackendCall( + NETPYNE_COMMANDS.instantiateModel, + payload, next, action, + "The NetPyNE model is getting instantiated...", + GEPPETTO.Resources.INSTANTIATING_MODEL, + shouldSwitchLayout + ) +} + +const simulateNetwork = async (payload, next, action, shouldSwitchLayout) => { + createSimulateBackendCall( + NETPYNE_COMMANDS.simulateModel, + payload, next, action, + "The NetPyNE model is getting simulated...", + GEPPETTO.Resources.RUNNING_SIMULATION, + shouldSwitchLayout + ) +} + +const createSimulateBackendCall = async (cmd, payload, next, action, consoleMessage, spinnerType, shouldSwitchLayout) => { + GEPPETTO.CommandController.log(consoleMessage); + GEPPETTO.trigger(GEPPETTO.Events.Show_spinner, spinnerType); + const response = await Utils.evalPythonMessage(cmd, [payload]) + + const errorPayload = await processError(response) + if (errorPayload) { + GEPPETTO.trigger(GEPPETTO.Events.Hide_spinner); + next(openBackendErrorDialog(errorPayload)) + } else { + GEPPETTO.trigger(GEPPETTO.Events.Show_spinner, GEPPETTO.Resources.PARSING_MODEL); + GEPPETTO.Manager.loadModel(response); + GEPPETTO.CommandController.log('Instantiation / Simulation completed.'); + GEPPETTO.trigger(GEPPETTO.Events.Hide_spinner); + + next(action) + if (shouldSwitchLayout) { + next(switchLayout) + } + } +} + +const processError = response => { + var parsedResponse = Utils.getErrorResponse(response); + if (parsedResponse) { + return { errorMessage: parsedResponse['message'], errorDetails: parsedResponse['details'] } + } + return false +} + +const pythonCall = async (next, action) => { + const response = await Utils.evalPythonMessage(action.cmd, [action.args]) + const errorPayload = await processError(response) + if (errorPayload) { + GEPPETTO.trigger(GEPPETTO.Events.Hide_spinner); + next(openBackendErrorDialog(errorPayload)) + } else { + switch (action.cmd) { + case NETPYNE_COMMANDS.exportModel: + downloadJsonResponse(response) + break; + case NETPYNE_COMMANDS.exportHLS: + downloadPythonResponse(response) + break; + case NETPYNE_COMMANDS.deleteModel: + next(editModel) + break; + default: + break; + } + GEPPETTO.trigger(GEPPETTO.Events.Hide_spinner); + next(closeDrawerDialogBox) } -} \ No newline at end of file +} diff --git a/webapp/redux/middleware/utils.js b/webapp/redux/middleware/utils.js new file mode 100644 index 00000000..e4d93e2a --- /dev/null +++ b/webapp/redux/middleware/utils.js @@ -0,0 +1,39 @@ +const createFileName = name => name + getTimeStamp() + +const getTimeStamp = () => new Date().toGMTString().replace(",", '').replace(/[ ,:]/g, '_') + +const unescapeText = text => { + text = text.replace(/\\\\/g, '\\').replace(/\\\'/g, "'").replace(/\\\"/g, '"').split('\\n').join('\n').substring(1) + return text.substring(0, text.length - 1) +} + +const forceBlobDownload = (blob, filename) => { + const url = window.URL.createObjectURL(blob); + const link = document.createElement('a'); + link.href = url; + link.setAttribute('download', filename); + document.body.appendChild(link); + link.click(); + link.parentNode.removeChild(link); +} + +export const downloadJsonResponse = jsonData => { + var filename = createFileName('NetPyNE_Model_') + + if (jsonData.simConfig && jsonData.simConfig.filename) { + filename = createFileName(jsonData.simConfig.filename + '_') + } + + filename += '.json' + + const blob = new Blob([JSON.stringify(jsonData, null, 2)], { type : 'application/json' }); + forceBlobDownload(blob, filename) + +} + +export const downloadPythonResponse = textData => { + var filename = createFileName('NetPyNE_init_') + '.py' + const blob = new Blob([unescapeText(textData)], { type : 'text/plain;charset=utf-8' }); + forceBlobDownload(blob, filename) +} + diff --git a/webapp/redux/reducers/all.js b/webapp/redux/reducers/all.js index 361cb603..735a8115 100644 --- a/webapp/redux/reducers/all.js +++ b/webapp/redux/reducers/all.js @@ -1,5 +1,9 @@ import { combineReducers } from 'redux'; import general from './general'; +import notebook from './notebook'; +import flexlayout from './flexlayout'; +import errors from './errors'; +import drawer from './drawer'; -export default combineReducers({ general, }); \ No newline at end of file +export default combineReducers({ general, notebook, flexlayout, errors, drawer }); \ No newline at end of file diff --git a/webapp/redux/reducers/drawer.js b/webapp/redux/reducers/drawer.js new file mode 100644 index 00000000..5f7a5278 --- /dev/null +++ b/webapp/redux/reducers/drawer.js @@ -0,0 +1,25 @@ +// import action types +import { OPEN_DRAWER_DIALOG_BOX, CLOSE_DRAWER_DIALOG_BOX } from '../actions/drawer'; + +// Default state for general +export const DRAWER_DEFAULT_STATE = { dialogBoxOpen: false, }; + +// reducer +export default ( state = DRAWER_DEFAULT_STATE, action ) => ({ + ...state, + ...reduceError(state, action) +}); + + +// reducer function +function reduceError (state, action) { + switch (action.type) { + case OPEN_DRAWER_DIALOG_BOX: + return { dialogBoxOpen: true } + case CLOSE_DRAWER_DIALOG_BOX: + return { ...DRAWER_DEFAULT_STATE } + default: { + return { ...state }; + } + } +} diff --git a/webapp/redux/reducers/errors.js b/webapp/redux/reducers/errors.js new file mode 100644 index 00000000..d7102421 --- /dev/null +++ b/webapp/redux/reducers/errors.js @@ -0,0 +1,33 @@ +// import action types +import { OPEN_BACKEND_ERROR_DIALOG, CLOSE_BACKEND_ERROR_DIALOG } from '../actions/errors'; + +// Default state for general +export const ERROR_DEFAULT_STATE = { + openDialog: false, + errorMessage: '', + errorDetails: '' +}; + +// reducer +export default ( state = ERROR_DEFAULT_STATE, action ) => ({ + ...state, + ...reduceError(state, action) +}); + + +// reducer function +function reduceError (state, action) { + switch (action.type) { + case OPEN_BACKEND_ERROR_DIALOG: + return { + openDialog: true, + errorMessage: action.payload.errorMessage, + errorDetails: action.payload.errorDetails + } + case CLOSE_BACKEND_ERROR_DIALOG: + return { ...ERROR_DEFAULT_STATE } + default: { + return { ...state }; + } + } +} diff --git a/webapp/redux/reducers/flexlayout.js b/webapp/redux/reducers/flexlayout.js new file mode 100644 index 00000000..a439ba71 --- /dev/null +++ b/webapp/redux/reducers/flexlayout.js @@ -0,0 +1,202 @@ +import { + ADD_WIDGET, + UPDATE_WIDGET, + RESET_LAYOUT, + DESTROY_WIDGET, + ACTIVATE_WIDGET, + SWITCH_LAYOUT +} from '../actions/flexlayout'; + +import { MODEL_LOADED } from '../actions/general'; +import { WidgetStatus } from '../../constants'; + + +function removeUndefined (obj) { + return Object.keys(obj).forEach(key => obj[key] === undefined ? delete obj[key] : ''); +} + +export const PYTHON_CONSOLE_WIDGET = { + id: 'python', + name: 'Python', + status: WidgetStatus.ACTIVE, + icon: 'fa fa-code', + component: 'PythonConsole', + panelName: "consolePanel", + enableClose: false, + enableDrag: false, + enableRename: false +}; + +export const MORPHOLOGY_WIDGET = { + id: 'D3Canvas', + name: 'Morphology', + status: WidgetStatus.ACTIVE, + icon: 'fa fa-dot-circle-o', + component: 'D3Canvas', + panelName: "morphoPanel", + enableRename: false +} + +export const HLS_WIDGETS = { + 'popParams': { + id: 'popParams', + name: 'Populations', + status: WidgetStatus.ACTIVE, + icon: 'fa fa-dot-circle-o', + component: 'popParams', + panelName: "hlsPanel", + enableRename: false + }, + 'cellParams': { + id: 'cellParams', + name: 'Cell rules', + status: WidgetStatus.HIDDEN, + icon: 'fa fa-dot-circle-o', + component: 'cellParams', + panelName: "hlsPanel", + enableRename: false + }, + 'synMechParams': { + id: 'synMechParams', + name: 'Synapses', + status: WidgetStatus.HIDDEN, + icon: 'fa fa-dot-circle-o', + component: 'synMechParams', + panelName: "hlsPanel", + enableRename: false + }, + 'connParams': { + id: 'connParams', + name: 'Connections', + status: WidgetStatus.HIDDEN, + icon: 'fa fa-dot-circle-o', + component: 'connParams', + panelName: "hlsPanel", + enableRename: false + }, + 'stimSourceParams': { + id: 'stimSourceParams', + name: 'Stim. sources', + status: WidgetStatus.HIDDEN, + icon: 'fa fa-dot-circle-o', + component: 'stimSourceParams', + panelName: "hlsPanel", + enableRename: false + }, + 'stimTargetParams': { + id: 'stimTargetParams', + name: 'Stim. targets', + status: WidgetStatus.HIDDEN, + icon: 'fa fa-dot-circle-o', + component: 'stimTargetParams', + panelName: "hlsPanel", + enableRename: false + }, + 'simConfig': { + id: 'simConfig', + name: 'Settings', + status: WidgetStatus.HIDDEN, + icon: 'fa fa-dot-circle-o', + component: 'simConfig', + panelName: "hlsPanel", + enableRename: false + }, + 'analysis': { + id: 'analysis', + name: 'Anaylysis', + status: WidgetStatus.HIDDEN, + icon: 'fa fa-dot-circle-o', + component: 'analysis', + panelName: "hlsPanel", + enableRename: false + } + + +} + +export const FLEXLAYOUT_DEFAULT_STATE = { + widgets: { 'python': PYTHON_CONSOLE_WIDGET }, + widgetsBackground: { + 'D3Canvas': MORPHOLOGY_WIDGET, + 'python': PYTHON_CONSOLE_WIDGET, + } +}; + + +export default (state = FLEXLAYOUT_DEFAULT_STATE, action) => { + if (action.data) { + removeUndefined(action.data); // Prevent deletion in case of unpolished update action + } + + switch (action.type) { + + case ADD_WIDGET: + case UPDATE_WIDGET: { + const newWidget = { ...state.widgets[action.data.id], panelName: extractPanelName(action), ...action.data }; + return { + ...state, widgets: { + ...updateWidgetState(state.widgets, newWidget), + [action.data.id]: newWidget + } + } ; + } + + case DESTROY_WIDGET:{ + const newWidgets = { ...state.widgets }; + delete newWidgets[action.data.id]; + return { ...state, widgets: newWidgets }; + } + + case ACTIVATE_WIDGET: { + const activatedWidget = state.widgets[action.data.id]; + + return { + ...state, widgets: { + ...updateWidgetState(state.widgets, { panelName: state.widgets[action.data.id], status: WidgetStatus.ACTIVE }), + [action.data.id]: { ...activatedWidget, status: WidgetStatus.ACTIVE } + } + } + } + + case RESET_LAYOUT: + return FLEXLAYOUT_DEFAULT_STATE; + + case MODEL_LOADED: + return { ...state, widgets: { ...HLS_WIDGETS, ...state.widgets } } + + case SWITCH_LAYOUT: { + const { widgets, widgetsBackground, ...others } = state + return { ...others, widgets: { ...widgetsBackground }, widgetsBackground: { ...widgets } } + } + + + default: + return state + } +} + +function filterWidgets (widgets, filterFn) { + return Object.fromEntries(Object.values(widgets).filter(filterFn)); +} + +/** + * Ensure there is one only active widget in the same panel + * @param {*} widgets + * @param {*} param1 + */ +function updateWidgetState (widgets, { status, panelName }) { + if (status != WidgetStatus.ACTIVE) { + return widgets; + } + return Object.fromEntries(Object.values(widgets).filter(widget => widget).map(widget => [ + widget.id, + { + ...widget, + status: widget.panelName == panelName ? WidgetStatus.HIDDEN : widget.status + } + ])); +} + +function extractPanelName (action) { + return action.data.component == "Plot" ? "bottomPanel" : "leftPanel"; +} diff --git a/webapp/redux/reducers/general.js b/webapp/redux/reducers/general.js index a07b5a0f..1379364f 100644 --- a/webapp/redux/reducers/general.js +++ b/webapp/redux/reducers/general.js @@ -1,8 +1,14 @@ // import action types -import { UPDATE_CARDS, } from '../actions/general'; +import { UPDATE_CARDS, MODEL_LOADED, SHOW_NETWORK, EDIT_MODEL, SIMULATE_NETWORK, CREATE_NETWORK, CREATE_SIMULATE_NETWORK } from '../actions/general'; +import { MODEL_STATE } from '../../constants'; // Default state for general -export const GENERAL_DEFAULT_STATE = { updates: 0 }; +export const GENERAL_DEFAULT_STATE = { + updates: 0, + modelLoaded: false, + editMode: true, + modelState: MODEL_STATE.NOT_INSTANTIATED +}; // reducer export default ( state = GENERAL_DEFAULT_STATE, action ) => ({ @@ -14,13 +20,22 @@ export default ( state = GENERAL_DEFAULT_STATE, action ) => ({ // reducer function function reduceGeneral (state, action) { switch (action.type) { - case UPDATE_CARDS: return { updates: state.updates + 1 } - + case MODEL_LOADED: + return { modelLoaded: true } + case SHOW_NETWORK: + return { editMode: false } + case CREATE_NETWORK: + return { editMode: false, modelState: MODEL_STATE.INSTANTIATED } + case CREATE_SIMULATE_NETWORK: + return { editMode: false, modelState: MODEL_STATE.SIMULATED } + case SIMULATE_NETWORK: + return { editMode: false, modelState: MODEL_STATE.SIMULATED } + case EDIT_MODEL: + return { editMode: true, modelState: MODEL_STATE.NOT_INSTANTIATED } default: { return { ...state }; } - } } diff --git a/webapp/redux/reducers/notebook.js b/webapp/redux/reducers/notebook.js new file mode 100644 index 00000000..7745f5bc --- /dev/null +++ b/webapp/redux/reducers/notebook.js @@ -0,0 +1,24 @@ +import * as TYPES from '../actions/notebook'; + + +export const NOTEBOOK_DEFAULT_STATE = { + showNotebook: false, + isNotebookReady: false, +}; + +export default ( state = {}, action ) => ({ ...state, ...reduceNotebook(state, action) }); + +function reduceNotebook (state = {}, action) { + switch (action.type) { + + case TYPES.LOAD_NOTEBOOK: + return { showNotebook: true, isNotebookReady: false } + case TYPES.UNLOAD_NOTEBOOK: { + return { showNotebook: false, isNotebookReady: false } + } + case TYPES.NOTEBOOK_READY: + return { isLoadingInNotebook: true, isNotebookReady: true } + default: + return state; + } +} diff --git a/webapp/redux/reduxconnect/DimensionsConnection.js b/webapp/redux/reduxconnect/DimensionsConnection.js deleted file mode 100644 index b519aaf2..00000000 --- a/webapp/redux/reduxconnect/DimensionsConnection.js +++ /dev/null @@ -1,9 +0,0 @@ -import { connect } from 'react-redux'; -import { updateCards } from '../actions/general'; -import Dimensions from '../../components/definition/populations/Dimensions'; - -const mapStateToProps = (state, ownProps) => ({ ...ownProps }); - -const mapDispatchToProps = dispatch => ({ updateCards: () => dispatch(updateCards) }); - -export default connect(mapStateToProps, mapDispatchToProps)(Dimensions); \ No newline at end of file diff --git a/webapp/redux/reduxconnect/NetPyNECellRuleConnection.js b/webapp/redux/reduxconnect/NetPyNECellRuleConnection.js deleted file mode 100644 index 75e368b5..00000000 --- a/webapp/redux/reduxconnect/NetPyNECellRuleConnection.js +++ /dev/null @@ -1,12 +0,0 @@ -import { connect } from 'react-redux'; -import { updateCards } from '../actions/general'; -import NetPyNECellRule from '../../components/definition/cellRules/NetPyNECellRule'; - -const mapStateToProps = (state, ownProps) => ({ - ...ownProps, - updates: state.general.updates -}); - -const mapDispatchToProps = dispatch => ({}); - -export default connect(mapStateToProps, mapDispatchToProps)(NetPyNECellRule); \ No newline at end of file diff --git a/webapp/redux/reduxconnect/NetPyNEIncludeConnection.js b/webapp/redux/reduxconnect/NetPyNEIncludeConnection.js deleted file mode 100644 index fc7ca72a..00000000 --- a/webapp/redux/reduxconnect/NetPyNEIncludeConnection.js +++ /dev/null @@ -1,11 +0,0 @@ -import { connect } from 'react-redux'; -import NetPyNEInclude from '../../components/definition/plots/NetPyNEInclude'; - -const mapStateToProps = (state, ownProps) => ({ - ...ownProps, - updates: state.general.updates -}); - -const mapDispatchToProps = dispatch => ({}); - -export default connect(mapStateToProps, mapDispatchToProps)(NetPyNEInclude); \ No newline at end of file diff --git a/webapp/redux/reduxconnect/NetPyNEPopulationConnection.js b/webapp/redux/reduxconnect/NetPyNEPopulationConnection.js deleted file mode 100644 index 27d7b298..00000000 --- a/webapp/redux/reduxconnect/NetPyNEPopulationConnection.js +++ /dev/null @@ -1,9 +0,0 @@ -import { connect } from 'react-redux'; -import { updateCards } from '../actions/general'; -import NetPyNEPopulation from '../../components/definition/populations/NetPyNEPopulation'; - -const mapStateToProps = (state, ownProps) => ({ ...ownProps }); - -const mapDispatchToProps = dispatch => ({ updateCards: () => dispatch(updateCards) }); - -export default connect(mapStateToProps, mapDispatchToProps)(NetPyNEPopulation); \ No newline at end of file diff --git a/webapp/redux/reduxconnect/NetPyNEPopulationsConnection.js b/webapp/redux/reduxconnect/NetPyNEPopulationsConnection.js deleted file mode 100644 index 1ae9d515..00000000 --- a/webapp/redux/reduxconnect/NetPyNEPopulationsConnection.js +++ /dev/null @@ -1,12 +0,0 @@ -import { connect } from 'react-redux'; -import { updateCards } from '../actions/general'; -import NetPyNEPopulations from '../../components/definition/populations/NetPyNEPopulations'; - -const PythonControlledCapability = require('geppetto-client/js/communication/geppettoJupyter/PythonControlledCapability'); -const PythonControlledNetPyNEPopulations = PythonControlledCapability.createPythonControlledComponent(NetPyNEPopulations); - -const mapStateToProps = (state, ownProps) => ({ ...ownProps }); - -const mapDispatchToProps = dispatch => ({ updateCards: () => dispatch(updateCards), }); - -export default connect(mapStateToProps, mapDispatchToProps)(PythonControlledNetPyNEPopulations); \ No newline at end of file diff --git a/webapp/redux/reduxconnect/NetPyNEStimulationSourceConnection.js b/webapp/redux/reduxconnect/NetPyNEStimulationSourceConnection.js deleted file mode 100644 index e636f546..00000000 --- a/webapp/redux/reduxconnect/NetPyNEStimulationSourceConnection.js +++ /dev/null @@ -1,9 +0,0 @@ -import { connect } from 'react-redux'; -import { updateCards } from '../actions/general'; -import NetPyNEStimulationSource from '../../components/definition/stimulationSources/NetPyNEStimulationSource'; - -const mapStateToProps = (state, ownProps) => ({ ...ownProps }); - -const mapDispatchToProps = dispatch => ({ updateCards: () => dispatch(updateCards) }); - -export default connect(mapStateToProps, mapDispatchToProps)(NetPyNEStimulationSource); \ No newline at end of file diff --git a/webapp/redux/reduxconnect/NetPyNEStimulationSourcesConnection.js b/webapp/redux/reduxconnect/NetPyNEStimulationSourcesConnection.js deleted file mode 100644 index 447d18f7..00000000 --- a/webapp/redux/reduxconnect/NetPyNEStimulationSourcesConnection.js +++ /dev/null @@ -1,12 +0,0 @@ -import { connect } from 'react-redux'; -import { updateCards } from '../actions/general'; -import NetPyNEStimulationSources from '../../components/definition/stimulationSources/NetPyNEStimulationSources'; - -const PythonControlledCapability = require('geppetto-client/js/communication/geppettoJupyter/PythonControlledCapability'); -const PythonControlledNetPyNEStimulationSources = PythonControlledCapability.createPythonControlledComponent(NetPyNEStimulationSources); - -const mapStateToProps = (state, ownProps) => ({ ...ownProps }); - -const mapDispatchToProps = dispatch => ({ updateCards: () => dispatch(updateCards) }); - -export default connect(mapStateToProps, mapDispatchToProps)(PythonControlledNetPyNEStimulationSources); \ No newline at end of file diff --git a/webapp/redux/reduxconnect/NetPyNEStimulationTargetConnection.js b/webapp/redux/reduxconnect/NetPyNEStimulationTargetConnection.js deleted file mode 100644 index 0bcd9046..00000000 --- a/webapp/redux/reduxconnect/NetPyNEStimulationTargetConnection.js +++ /dev/null @@ -1,12 +0,0 @@ -import { connect } from 'react-redux'; -import { updateCards } from '../actions/general'; -import NetPyNEStimulationTarget from '../../components/definition/stimulationTargets/NetPyNEStimulationTarget'; - -const mapStateToProps = (state, ownProps) => ({ - ...ownProps, - updates: state.general.updates -}); - -const mapDispatchToProps = dispatch => ({ updateCards: () => dispatch(updateCards) }); - -export default connect(mapStateToProps, mapDispatchToProps)(NetPyNEStimulationTarget); \ No newline at end of file diff --git a/webapp/redux/reduxconnect/NetPyNESynapseConnection.js b/webapp/redux/reduxconnect/NetPyNESynapseConnection.js deleted file mode 100644 index f529628e..00000000 --- a/webapp/redux/reduxconnect/NetPyNESynapseConnection.js +++ /dev/null @@ -1,9 +0,0 @@ -import { connect } from 'react-redux'; -import { updateCards } from '../actions/general'; -import NetPyNESynapse from '../../components/definition/synapses/NetPyNESynapse'; - -const mapStateToProps = (state, ownProps) => ({ ...ownProps }); - -const mapDispatchToProps = dispatch => ({ updateCards: () => dispatch(updateCards) }); - -export default connect(mapStateToProps, mapDispatchToProps)(NetPyNESynapse); \ No newline at end of file diff --git a/webapp/redux/reduxconnect/NetPyNESynapsesConnection.js b/webapp/redux/reduxconnect/NetPyNESynapsesConnection.js deleted file mode 100644 index f89e43ec..00000000 --- a/webapp/redux/reduxconnect/NetPyNESynapsesConnection.js +++ /dev/null @@ -1,12 +0,0 @@ -import { connect } from 'react-redux'; -import { updateCards } from '../actions/general'; -import NetPyNESynapses from '../../components/definition/synapses/NetPyNESynapses'; - -const PythonControlledCapability = require('geppetto-client/js/communication/geppettoJupyter/PythonControlledCapability'); -const PythonControlledNetPyNESynapses = PythonControlledCapability.createPythonControlledComponent(NetPyNESynapses); - -const mapStateToProps = (state, ownProps) => ({ ...ownProps }); - -const mapDispatchToProps = dispatch => ({ updateCards: () => dispatch(updateCards), }); - -export default connect(mapStateToProps, mapDispatchToProps)(PythonControlledNetPyNESynapses); \ No newline at end of file diff --git a/webapp/redux/reduxconnect/PythonMethodControlledSelectFieldConnection.js b/webapp/redux/reduxconnect/PythonMethodControlledSelectFieldConnection.js deleted file mode 100644 index 937bc7b4..00000000 --- a/webapp/redux/reduxconnect/PythonMethodControlledSelectFieldConnection.js +++ /dev/null @@ -1,14 +0,0 @@ -import { connect } from 'react-redux'; -import SelectField from '../../components/general/Select'; - -const PythonControlledCapability = require('geppetto-client/js/communication/geppettoJupyter/PythonControlledCapability'); -const PythonMethodControlledSelectField = PythonControlledCapability.createPythonControlledControlWithPythonDataFetch(SelectField); - -const mapStateToProps = (state, ownProps) => ({ - ...ownProps, - updates: String(state.general.updates) -}); - -const mapDispatchToProps = dispatch => ({}); - -export default connect(mapStateToProps, mapDispatchToProps)(PythonMethodControlledSelectField); \ No newline at end of file diff --git a/webapp/redux/store.js b/webapp/redux/store.js index c200adae..9b31bd31 100644 --- a/webapp/redux/store.js +++ b/webapp/redux/store.js @@ -1,9 +1,15 @@ import { createStore, applyMiddleware, compose } from "redux"; import all from "./reducers/all"; import { GENERAL_DEFAULT_STATE } from "./reducers/general"; +import { NOTEBOOK_DEFAULT_STATE } from "./reducers/notebook"; +import { FLEXLAYOUT_DEFAULT_STATE } from "./reducers/flexlayout"; import middleware from './middleware/middleware'; -const INIT_STATE = { general: GENERAL_DEFAULT_STATE, }; +const INIT_STATE = { + general: GENERAL_DEFAULT_STATE, + notebook: NOTEBOOK_DEFAULT_STATE, + flexlayout: FLEXLAYOUT_DEFAULT_STATE +}; const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose; diff --git a/webapp/webpack.config.js b/webapp/webpack.config.js index c5f1b794..da481c25 100644 --- a/webapp/webpack.config.js +++ b/webapp/webpack.config.js @@ -33,6 +33,7 @@ if ( isWin ) { const availableExtensions = [ { from: path.resolve(__dirname, geppetto_client_path, "static/*"), to: 'static', flatten: true }, + { from: path.resolve(__dirname, "images/*"), to: '', flatten: true }, ]; module.exports = function (env){ @@ -140,7 +141,8 @@ module.exports = function (env){ 'geppetto-client': path.resolve(__dirname, geppetto_client_path), geppetto: path.resolve(__dirname, geppetto_client_path, 'js/pages/geppetto/GEPPETTO.js'), 'geppetto-client-initialization': path.resolve(__dirname, geppetto_client_path, 'js/pages/geppetto/main'), - handlebars: 'handlebars/dist/handlebars.js' + handlebars: 'handlebars/dist/handlebars.js', + netpyne: path.resolve(__dirname) }, extensions: ['*', '.js', '.json', '.ts', '.tsx', '.jsx'], },