From 02ae343d8de2daad60f0a1323a4ff1c23c699baa Mon Sep 17 00:00:00 2001 From: Filippo Ledda Date: Thu, 19 Mar 2020 12:18:09 +0100 Subject: [PATCH 01/14] #135 Base flexlayou scaffolding --- .gitignore | 2 + netpyne_ui/geppetto | 1 + package-lock.json | 3 + src/jupyter-geppetto | 1 + webapp/Main.js | 1 + webapp/NetPyNE.js | 15 +- webapp/Utils.js | 28 + .../instantiation/NetPyNEInstantiated.js | 4 + webapp/components/layout/LayoutManager.js | 290 ++++++++ webapp/components/layout/WidgetFactory.js | 44 ++ webapp/constants.js | 16 + webapp/css/netpyne.less | 4 + webapp/package-lock.json | 639 ++++++++++-------- webapp/package.json | 34 +- webapp/redux/actions/flexlayout.js | 58 ++ webapp/redux/actions/general.js | 3 + webapp/redux/actions/notebook.js | 10 + webapp/redux/reducers/all.js | 4 +- webapp/redux/reducers/flexlayout.js | 126 ++++ webapp/redux/reducers/general.js | 12 +- webapp/redux/reducers/notebook.js | 24 + .../reduxconnect/LayoutManagerContainer.js | 19 + .../NetPyNEInstantiatedConnection.js | 12 + webapp/redux/store.js | 8 +- 24 files changed, 1035 insertions(+), 323 deletions(-) create mode 160000 netpyne_ui/geppetto create mode 100644 package-lock.json create mode 160000 src/jupyter-geppetto create mode 100644 webapp/components/layout/LayoutManager.js create mode 100644 webapp/components/layout/WidgetFactory.js create mode 100644 webapp/constants.js create mode 100644 webapp/redux/actions/flexlayout.js create mode 100644 webapp/redux/actions/notebook.js create mode 100644 webapp/redux/reducers/flexlayout.js create mode 100644 webapp/redux/reducers/notebook.js create mode 100644 webapp/redux/reduxconnect/LayoutManagerContainer.js create mode 100644 webapp/redux/reduxconnect/NetPyNEInstantiatedConnection.js 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/netpyne_ui/geppetto b/netpyne_ui/geppetto new file mode 160000 index 00000000..bebcc0dd --- /dev/null +++ b/netpyne_ui/geppetto @@ -0,0 +1 @@ +Subproject commit bebcc0ddcfd986f4ff00f62d308f3054d314f194 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..2633c7f1 100644 --- a/webapp/Main.js +++ b/webapp/Main.js @@ -51,6 +51,7 @@ jQuery(function () { 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' }] } diff --git a/webapp/NetPyNE.js b/webapp/NetPyNE.js index cff02742..8087c0d1 100644 --- a/webapp/NetPyNE.js +++ b/webapp/NetPyNE.js @@ -9,9 +9,9 @@ import PythonControlledNetPyNEStimulationSources from './redux/reduxconnect/NetP 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'; +import LayoutManager from './redux/reduxconnect/LayoutManagerContainer'; var PythonControlledCapability = require('geppetto-client/js/communication/geppettoJupyter/PythonControlledCapability'); @@ -30,7 +30,6 @@ export default class NetPyNE extends React.Component { this.state = { value: 'define', prevValue: 'define', - model: null, tabClicked: false, freezeInstance: false, freezeSimulation: false, @@ -49,8 +48,6 @@ export default class NetPyNE extends React.Component { window.metadata = nextProps.data.metadata; window.currentFolder = nextProps.data.currentFolder; window.isDocker = nextProps.data.isDocker; - - this.setState({ model: nextProps.data }) } } @@ -136,7 +133,7 @@ export default class NetPyNE extends React.Component { } render () { - if (this.state.model == null) { + if (!this.props.data) { return
} else { if (this.state.value == 'define'){ @@ -147,15 +144,15 @@ export default class NetPyNE extends React.Component { - + } else { - var content = + var content = } return ( -
+
@@ -180,7 +177,7 @@ export default class NetPyNE extends React.Component { fastForwardInstantiation={this.state.fastForwardInstantiation} fastForwardSimulation={this.state.fastForwardSimulation} /> - + {content}
) diff --git a/webapp/Utils.js b/webapp/Utils.js index 2e6bbbd9..0410a7ce 100644 --- a/webapp/Utils.js +++ b/webapp/Utils.js @@ -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/instantiation/NetPyNEInstantiated.js b/webapp/components/instantiation/NetPyNEInstantiated.js index 146b5af1..57413048 100644 --- a/webapp/components/instantiation/NetPyNEInstantiated.js +++ b/webapp/components/instantiation/NetPyNEInstantiated.js @@ -178,6 +178,10 @@ export default class NetPyNEInstantiated extends React.Component { this.setState({ bringItToFront: 0 }) this.showWidgets(true) }); + + GEPPETTO.on(GEPPETTO.Events.Model_loaded, () => { + this.props.modelLoaded(); + }); } componentWillUnmount (){ diff --git a/webapp/components/layout/LayoutManager.js b/webapp/components/layout/LayoutManager.js new file mode 100644 index 00000000..fcb1bb8f --- /dev/null +++ b/webapp/components/layout/LayoutManager.js @@ -0,0 +1,290 @@ +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'; + + +const defaultLayoutConfiguration = { + "global": { sideBorders: 8 }, + "layout": { + "type": "row", + "weight": 100, + "id": "root", + "children": [ + + { + "type": "row", + "weight": 100, + "children": [ + { + "type": "tabset", + "weight": 70, + "id": "topPanel", + "enableDeleteWhenEmpty": false, + "children": [ + ] + }, + { + "type": "tabset", + "weight": 30, + "id": "bottomPanel", + "enableDeleteWhenEmpty": false, + "children": [ + ] + } + ] + } + ] + }, + "borders": [ + { + "type": "border", + "location": "bottom", + "size": 100, + "children": [], + "barSize": 10 + } + ] +}; + +/** + * Transforms a widget configutation into a flexlayout node descriptor + */ +function widget2Node (configuration) { + const { id, name, component, instancePath, status, panelName, enableClose = true } = 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 , + }; +} + +export default class LayoutManager extends Component { + + constructor (props) { + super(props); + const layout = this.props.layout ? this.props.layout : defaultLayoutConfiguration; + this.model = FlexLayout.Model.fromJson(layout); + 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(); + } + componentDidMount () { + const { widgets } = this.props; + this.addWidgets(Object.values(widgets)); + } + + componentDidUpdate (prevProps, prevState) { + const { widgets } = this.props; + const oldWidgets = prevProps.widgets; + const newWidgets = this.findNewWidgets(widgets, oldWidgets); + if (newWidgets) { + this.addWidgets(newWidgets); + } + + const updatedWidgets = this.findUpdatedWidgets(widgets, oldWidgets); + if (updatedWidgets) { + this.updateWidgets(updatedWidgets); + } + + const deletedWidgets = this.findDeletedWidgets(widgets, oldWidgets); + + if (deletedWidgets) { + this.deleteWidgets(deletedWidgets); + } + } + + addWidgets (widgets) { + const { model } = this; + 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) { + this.model.doAction(FlexLayout.Actions.selectTab(widget.id)); + } + + } + // window.dispatchEvent(new Event('resize')); + } + + deleteWidgets (widgets) { + for (let widget of widgets) { + this.model.doAction(FlexLayout.Actions.deleteTab(widget.id)); + } + } + + addWidget (widgetConfiguration) { + this.refs.layout.addTabToTabSet(widgetConfiguration.panelName, widget2Node(widgetConfiguration)); + } + + updateWidgets (widgets) { + + for (let widget of widgets) { + + this.updateWidget(widget); + + // This updates plotly.js plots to new panel sizes + if (widget.status == WidgetStatus.ACTIVE) { + this.model.doAction(FlexLayout.Actions.selectTab(widget.id)); + } + + } + // window.dispatchEvent(new Event('resize')); + } + + updateWidget (widget) { + if (widget) { + this.widgetFactory.updateWidget(widget); + this.model.doAction(Actions.updateNodeAttributes(widget.id, widget2Node(widget))); + } + + } + + + factory (node) { + return this.widgetFactory.factory(node.getConfig()); + } + + + findNewWidgets (widgets, oldWidgets) { + return oldWidgets ? Object.values(widgets).filter(widget => widget && !oldWidgets[widget.id]) : 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) { + 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; + } + + this.model.doAction(action); + } + + onActionMaximizeWidget (action) { + const { model } = this; + 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; + 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) { + this.model.doAction(FlexLayout.Actions.moveNode(node.getId(), 'bottomPanel', FlexLayout.DockLocation.CENTER, 0)); + } + + onRenderTabSet (panel, renderValues) { + if (panel.getType() === "tabset") { + if (panel.getId() != 'leftPanel' && panel.getChildren().length > 0){ + renderValues.buttons.push(
{ + this.model.doAction(FlexLayout.Actions.moveNode(panel.getSelectedNode().getId(), "border_bottom", FlexLayout.DockLocation.CENTER, 0)); + }} />); + } + } + } + render () { + + return ( +
+ this.onAction(action)} + clickOnBordersAction={node => this.clickOnBordersAction(node)} + onRenderTabSet={(node, renderValues) => this.onRenderTabSet(node, renderValues)} + /> + +
+ ) + } +} \ 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..e14da8bc --- /dev/null +++ b/webapp/components/layout/WidgetFactory.js @@ -0,0 +1,44 @@ +import React, { lazy, Suspense } from 'react'; + +import PythonConsole from 'geppetto-client/js/components/interface/pythonConsole/PythonConsole'; + + +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 "3DCanvas": + return + } + } + +} \ No newline at end of file diff --git a/webapp/constants.js b/webapp/constants.js new file mode 100644 index 00000000..8683e75c --- /dev/null +++ b/webapp/constants.js @@ -0,0 +1,16 @@ +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' +}; \ No newline at end of file diff --git a/webapp/css/netpyne.less b/webapp/css/netpyne.less index c92f84d6..f264b34a 100644 --- a/webapp/css/netpyne.less +++ b/webapp/css/netpyne.less @@ -512,4 +512,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/package-lock.json b/webapp/package-lock.json index c4ac13ce..0d9e42ed 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==" } } }, @@ -3517,18 +3506,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 +4017,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 +6451,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 +7281,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 +7763,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 +7812,7 @@ } }, "chownr": { - "version": "1.1.1", + "version": "1.1.4", "bundled": true, "optional": true }, @@ -7802,7 +7837,7 @@ "optional": true }, "debug": { - "version": "4.1.1", + "version": "3.2.6", "bundled": true, "optional": true, "requires": { @@ -7825,11 +7860,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 +7888,7 @@ } }, "glob": { - "version": "7.1.3", + "version": "7.1.6", "bundled": true, "optional": true, "requires": { @@ -7879,7 +7914,7 @@ } }, "ignore-walk": { - "version": "3.0.1", + "version": "3.0.3", "bundled": true, "optional": true, "requires": { @@ -7896,7 +7931,7 @@ } }, "inherits": { - "version": "2.0.3", + "version": "2.0.4", "bundled": true, "optional": true }, @@ -7927,12 +7962,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 +7976,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 +8020,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 +8033,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 +8109,7 @@ "optional": true }, "process-nextick-args": { - "version": "2.0.0", + "version": "2.0.1", "bundled": true, "optional": true }, @@ -8078,17 +8122,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 +8139,7 @@ } }, "rimraf": { - "version": "2.6.3", + "version": "2.7.1", "bundled": true, "optional": true, "requires": { @@ -8125,7 +8162,7 @@ "optional": true }, "semver": { - "version": "5.7.0", + "version": "5.7.1", "bundled": true, "optional": true }, @@ -8171,17 +8208,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 +8240,7 @@ "optional": true }, "yallist": { - "version": "3.0.3", + "version": "3.1.1", "bundled": true, "optional": true } @@ -11437,12 +11474,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 +13394,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 +14717,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 +16709,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 +17347,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..6b89cf0c 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", @@ -32,40 +32,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/flexlayout.js b/webapp/redux/actions/flexlayout.js new file mode 100644 index 00000000..98130769 --- /dev/null +++ b/webapp/redux/actions/flexlayout.js @@ -0,0 +1,58 @@ +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 newWidget = ({ path, component, panelName }) => ({ + type: ADD_WIDGET, + data: { + id: path, + instancePath: path, + component: component, + name: path.slice(FILEVARIABLE_LENGTH), + status: WidgetStatus.ACTIVE, + panelName: panelName + } +}); + +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, }; + diff --git a/webapp/redux/actions/general.js b/webapp/redux/actions/general.js index a834f394..f593b3a3 100644 --- a/webapp/redux/actions/general.js +++ b/webapp/redux/actions/general.js @@ -1,5 +1,8 @@ // Action Types export const UPDATE_CARDS = 'UPDATE_CARDS'; +export const MODEL_LOADED = 'MODEL_LOADED'; // Actions export const updateCards = { type: UPDATE_CARDS }; + +export const modelLoaded = { type: MODEL_LOADED }; 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/reducers/all.js b/webapp/redux/reducers/all.js index 361cb603..a59c61ad 100644 --- a/webapp/redux/reducers/all.js +++ b/webapp/redux/reducers/all.js @@ -1,5 +1,7 @@ import { combineReducers } from 'redux'; import general from './general'; +import notebook from './notebook'; +import flexlayout from './flexlayout'; -export default combineReducers({ general, }); \ No newline at end of file +export default combineReducers({ general, notebook, flexlayout }); \ No newline at end of file diff --git a/webapp/redux/reducers/flexlayout.js b/webapp/redux/reducers/flexlayout.js new file mode 100644 index 00000000..56ecc916 --- /dev/null +++ b/webapp/redux/reducers/flexlayout.js @@ -0,0 +1,126 @@ +import { + ADD_WIDGET, + UPDATE_WIDGET, + RESET_LAYOUT, + DESTROY_WIDGET, + ACTIVATE_WIDGET, +} 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 FLEXLAYOUT_DEFAULT_STATE = { + widgets: { + + 'python': { + id: 'python', + name: 'Python', + status: WidgetStatus.MINIMIZED, + icon: 'fa-python', + component: 'PythonConsole', + panelName: "bottomPanel", + enableClose: false + }, + + }, + +}; + + +const MODEL_LOADED_STATE_WIDGETS = { + '3dcanvas': { + id: '3DCanvas', + name: '3DCanvas', + status: WidgetStatus.MINIMIZED, + icon: 'fa-python', + component: '3DCanvas', + panelName: "topPanel", + enableClose: false + } +} + + +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: { + ...state.widgets, + ...MODEL_LOADED_STATE_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..71f90805 100644 --- a/webapp/redux/reducers/general.js +++ b/webapp/redux/reducers/general.js @@ -1,8 +1,11 @@ // import action types -import { UPDATE_CARDS, } from '../actions/general'; +import { UPDATE_CARDS, MODEL_LOADED, modelLoaded } from '../actions/general'; // Default state for general -export const GENERAL_DEFAULT_STATE = { updates: 0 }; +export const GENERAL_DEFAULT_STATE = { + updates: 0, + modelLoaded: false +}; // reducer export default ( state = GENERAL_DEFAULT_STATE, action ) => ({ @@ -14,13 +17,12 @@ 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 } 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/LayoutManagerContainer.js b/webapp/redux/reduxconnect/LayoutManagerContainer.js new file mode 100644 index 00000000..e2e8d104 --- /dev/null +++ b/webapp/redux/reduxconnect/LayoutManagerContainer.js @@ -0,0 +1,19 @@ +import { connect } from 'react-redux'; +import LayoutManager from '../../components/layout/LayoutManager'; +import { + activateWidget, + destroyWidget, + minimizeWidget, + maximizeWidget, +} from '../actions/flexlayout'; + +const mapStateToProps = state => state.flexlayout; + +const mapDispatchToProps = dispatch => ({ + minimizeWidget: id => dispatch(minimizeWidget(id)), + destroyWidget: id => dispatch(destroyWidget(id)), + maximizeWidget: id => dispatch(maximizeWidget(id)), + activateWidget: id => dispatch(activateWidget(id)), +}); +console.log(LayoutManager); +export default connect(mapStateToProps, mapDispatchToProps)(LayoutManager); \ No newline at end of file diff --git a/webapp/redux/reduxconnect/NetPyNEInstantiatedConnection.js b/webapp/redux/reduxconnect/NetPyNEInstantiatedConnection.js new file mode 100644 index 00000000..5ca4cec1 --- /dev/null +++ b/webapp/redux/reduxconnect/NetPyNEInstantiatedConnection.js @@ -0,0 +1,12 @@ +import { connect } from 'react-redux'; +import { modelLoaded } from '../actions/general'; +import NetPyNEInstantiated from '../../components/instantiation/NetPyNEInstantiated'; + +const mapStateToProps = (state, ownProps) => ({ + ...ownProps, + updates: state.general.updates +}); + +const mapDispatchToProps = dispatch => ({ modelLoaded: dispatch(modelLoaded) }); + +export default connect(mapStateToProps, mapDispatchToProps)(NetPyNEInstantiated); \ 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; From a07c04bcbd1d4f82b840599374a6292ace5b0859 Mon Sep 17 00:00:00 2001 From: Filippo Ledda Date: Fri, 20 Mar 2020 12:45:45 +0100 Subject: [PATCH 02/14] #135 Refactor component wrappers and imports As well as main state on tab switching (to be completed) --- netpyne_ui/geppetto | 1 - webapp/Main.js | 28 +- webapp/NetPyNE.js | 186 ------- webapp/components/NetPyNE.js | 115 ++++ .../definition/cellRules/NetPyNECellRule.js | 13 +- .../definition/cellRules/NetPyNECellRules.js | 13 +- .../cellRules/sections/NetPyNESection.js | 31 +- .../sections/mechanisms/NetPyNEMechanism.js | 8 +- .../configuration/NetPyNESimConfig.js | 124 ++--- .../connectivity/NetPyNEConnectivityRule.js | 509 ++++++++++-------- .../components/definition/plots/TimeRange.js | 11 +- .../definition/plots/plotTypes/Plot2Dnet.js | 19 +- .../definition/plots/plotTypes/PlotConn.js | 19 +- .../definition/plots/plotTypes/PlotGranger.js | 25 +- .../definition/plots/plotTypes/PlotLFP.js | 36 +- .../definition/plots/plotTypes/PlotRaster.js | 35 +- .../definition/plots/plotTypes/PlotRatePSD.js | 29 +- .../definition/plots/plotTypes/PlotShape.js | 43 +- .../plots/plotTypes/PlotSpikeHist.js | 25 +- .../plots/plotTypes/PlotSpikeStats.js | 25 +- .../definition/plots/plotTypes/PlotTraces.js | 22 +- .../definition/populations/Dimensions.js | 10 +- .../populations/NetPyNEPopulation.js | 21 +- .../populations/NetPyNEPopulations.js | 15 +- .../NetPyNEStimulationSource.js | 53 +- .../NetPyNEStimulationSources.js | 13 +- .../NetPyNEStimulationTarget.js | 32 +- .../NetPyNEStimulationTargets.js | 17 +- .../StimulationConditions.js | 107 ++-- .../definition/synapses/NetPyNESynapse.js | 15 +- .../definition/synapses/NetPyNESynapses.js | 14 +- .../components/general/NetPyNECoordsRange.js | 12 +- webapp/components/index.js | 186 +++++++ .../instantiation/NetPyNEInstantiated.js | 5 +- webapp/components/layout/LayoutManager.js | 2 +- webapp/components/layout/WidgetFactory.js | 2 +- webapp/components/settings/NetPyNETabs.js | 188 ++++--- webapp/components/settings/NetPyNEToolBar.js | 6 +- webapp/package-lock.json | 24 + webapp/package.json | 5 +- webapp/redux/actions/general.js | 10 + webapp/redux/middleware/middleware.js | 9 +- webapp/redux/reducers/flexlayout.js | 2 +- webapp/redux/reducers/general.js | 9 +- .../reduxconnect/DimensionsConnection.js | 9 - .../reduxconnect/LayoutManagerContainer.js | 19 - .../reduxconnect/NetPyNECellRuleConnection.js | 12 - .../reduxconnect/NetPyNEIncludeConnection.js | 11 - .../NetPyNEInstantiatedConnection.js | 12 - .../NetPyNEPopulationConnection.js | 9 - .../NetPyNEPopulationsConnection.js | 12 - .../NetPyNEStimulationSourceConnection.js | 9 - .../NetPyNEStimulationSourcesConnection.js | 12 - .../NetPyNEStimulationTargetConnection.js | 12 - .../reduxconnect/NetPyNESynapseConnection.js | 9 - .../reduxconnect/NetPyNESynapsesConnection.js | 12 - ...onMethodControlledSelectFieldConnection.js | 14 - webapp/webpack.config.js | 3 +- 58 files changed, 1174 insertions(+), 1055 deletions(-) delete mode 160000 netpyne_ui/geppetto delete mode 100644 webapp/NetPyNE.js create mode 100644 webapp/components/NetPyNE.js create mode 100644 webapp/components/index.js delete mode 100644 webapp/redux/reduxconnect/DimensionsConnection.js delete mode 100644 webapp/redux/reduxconnect/LayoutManagerContainer.js delete mode 100644 webapp/redux/reduxconnect/NetPyNECellRuleConnection.js delete mode 100644 webapp/redux/reduxconnect/NetPyNEIncludeConnection.js delete mode 100644 webapp/redux/reduxconnect/NetPyNEInstantiatedConnection.js delete mode 100644 webapp/redux/reduxconnect/NetPyNEPopulationConnection.js delete mode 100644 webapp/redux/reduxconnect/NetPyNEPopulationsConnection.js delete mode 100644 webapp/redux/reduxconnect/NetPyNEStimulationSourceConnection.js delete mode 100644 webapp/redux/reduxconnect/NetPyNEStimulationSourcesConnection.js delete mode 100644 webapp/redux/reduxconnect/NetPyNEStimulationTargetConnection.js delete mode 100644 webapp/redux/reduxconnect/NetPyNESynapseConnection.js delete mode 100644 webapp/redux/reduxconnect/NetPyNESynapsesConnection.js delete mode 100644 webapp/redux/reduxconnect/PythonMethodControlledSelectFieldConnection.js diff --git a/netpyne_ui/geppetto b/netpyne_ui/geppetto deleted file mode 160000 index bebcc0dd..00000000 --- a/netpyne_ui/geppetto +++ /dev/null @@ -1 +0,0 @@ -Subproject commit bebcc0ddcfd986f4ff00f62d308f3054d314f194 diff --git a/webapp/Main.js b/webapp/Main.js index 2633c7f1..8dd2cf34 100644 --- a/webapp/Main.js +++ b/webapp/Main.js @@ -3,22 +3,25 @@ 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 getMuiTheme = require('@material-ui/core/styles/createMuiTheme').default; + 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 Utils = require('./Utils').default; + const Console = require('geppetto-client/js/components/interface/console/Console'); + const TabbedDrawer = require('geppetto-client/js/components/interface/drawer/TabbedDrawer'); + const PythonConsole = require('geppetto-client/js/components/interface/pythonConsole/PythonConsole'); + + const theme = require('./Theme').default const Provider = require("react-redux").Provider; const configureStore = require('./redux/store').default; + const modelLoaded = require('./redux/actions/general').modelLoaded; + require('./css/netpyne.less'); require('./css/material.less'); require('./css/traceback.less'); @@ -59,7 +62,10 @@ jQuery(function () { 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) + const data = Utils.convertToJSON(response); + GEPPETTO.on(GEPPETTO.Events.Model_loaded, () => { + store.dispatch(modelLoaded); + }); ReactDOM.render(, document.querySelector('#mainContainer')); GEPPETTO.trigger("spinner:hide"); }) diff --git a/webapp/NetPyNE.js b/webapp/NetPyNE.js deleted file mode 100644 index 8087c0d1..00000000 --- a/webapp/NetPyNE.js +++ /dev/null @@ -1,186 +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 NetPyNEToolBar from './components/settings/NetPyNEToolBar'; -import NetPyNETabs from './components/settings/NetPyNETabs'; -import LayoutManager from './redux/reduxconnect/LayoutManagerContainer'; - -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', - 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; - } - } - - 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.props.data) { - return
- } else { - if (this.state.value == 'define'){ - var content =
- - - - - - - - -
- } else { - var content = - } - - return ( -
-
- -
- -
-
- -
-
-
- - - - {content} -
- ) - } - } -} diff --git a/webapp/components/NetPyNE.js b/webapp/components/NetPyNE.js new file mode 100644 index 00000000..43076996 --- /dev/null +++ b/webapp/components/NetPyNE.js @@ -0,0 +1,115 @@ +import React from "react"; +import Toolbar from "@material-ui/core/Toolbar"; +import Transition from "./transition/Transition"; +import { + NetPyNESynapses, + NetPyNEConnectivityRules, + NetPyNECellRules, + NetPyNEStimulationSources, + NetPyNEStimulationTargets, + NetPyNESimConfig, + NetPyNEToolBar, + NetPyNETabs, + NetPyNEPopulations, + LayoutManager, + NetPyNEPlots +} from "netpyne/components"; + +export default class NetPyNE extends React.Component { + constructor (props) { + super(props); + this.widgets = {}; + + } + + 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; + } + } + + + render () { + if (!this.props.data) { + return
; + } else { + if (this.props.editMode) { + var content = ( +
+ + + + + + + + +
+ ); + } else { + var content = ; + } + + return ( +
+
+ +
+ +
+
+ +
+
+
+ + {/** TODO Reengineer Transition using the middleware to handle simulation and instantiation. The transition should only show content related to what actually we want to do + */} + + {content} +
+ ); + } + } +} diff --git a/webapp/components/definition/cellRules/NetPyNECellRule.js b/webapp/components/definition/cellRules/NetPyNECellRule.js index 94749559..78c242f5 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 {
- - - -1) { selection = ( - - + - + - + - + - @@ -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/mechanisms/NetPyNEMechanism.js b/webapp/components/definition/cellRules/sections/mechanisms/NetPyNEMechanism.js index 3ed1911e..bc179895 100644 --- a/webapp/components/definition/cellRules/sections/mechanisms/NetPyNEMechanism.js +++ b/webapp/components/definition/cellRules/sections/mechanisms/NetPyNEMechanism.js @@ -2,10 +2,8 @@ import React from 'react'; import TextField from '@material-ui/core/TextField'; import Utils from '../../../../../Utils'; import { withStyles } from '@material-ui/core/styles'; - -var PythonControlledCapability = require('geppetto-client/js/communication/geppettoJupyter/PythonControlledCapability'); -var PythonControlledTextField = PythonControlledCapability.createPythonControlledControl(TextField); - +import { NetPyNETextField, } from 'netpyne/components'; + const styles = ({ spacing }) => ({ fields: { marginTop: spacing(3), @@ -32,7 +30,7 @@ class NetPyNEMechanism extends React.Component { } else { var tag = "netParams.cellParams['" + this.props.cellRule + "']['secs']['" + this.props.section + "']['mechs']['" + this.state.currentName + "']" return this.state.mechFields.map((name, i) => - + ) } }; diff --git a/webapp/components/definition/configuration/NetPyNESimConfig.js b/webapp/components/definition/configuration/NetPyNESimConfig.js index d3e96192..6e9a3121 100644 --- a/webapp/components/definition/configuration/NetPyNESimConfig.js +++ b/webapp/components/definition/configuration/NetPyNESimConfig.js @@ -4,15 +4,15 @@ import TextField from '@material-ui/core/TextField'; import Select from '@material-ui/core/Select'; import { Card, CardHeader, CardContent } from '@material-ui/core'; import { BottomNavigation, BottomNavigationAction } from '@material-ui/core'; -import ListComponent from '../../general/List'; -import NetPyNEField from '../../general/NetPyNEField'; -import Checkbox from '../../general/Checkbox'; -var PythonControlledCapability = require('geppetto-client/js/communication/geppettoJupyter/PythonControlledCapability'); -var PythonControlledSelectField = PythonControlledCapability.createPythonControlledControl(Select); -var PythonControlledTextField = PythonControlledCapability.createPythonControlledControl(TextField); -var PythonControlledCheckbox = PythonControlledCapability.createPythonControlledControl(Checkbox); -var PythonControlledListComponent = PythonControlledCapability.createPythonControlledControl(ListComponent); + +import { + NetPyNEField, + NetPyNECheckbox, + NetPyNETextField, + NetPyNESelectField, + ListComponent +} from 'netpyne/components'; export default class NetPyNESimConfig extends React.Component { @@ -39,23 +39,23 @@ export default class NetPyNESimConfig extends React.Component {
- + - + - + - + - + @@ -63,55 +63,55 @@ export default class NetPyNESimConfig 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 {
- + - + - + - + - + - +
- + - + - + - + - + - +
diff --git a/webapp/components/definition/connectivity/NetPyNEConnectivityRule.js b/webapp/components/definition/connectivity/NetPyNEConnectivityRule.js index 14a04752..dac6428d 100644 --- a/webapp/components/definition/connectivity/NetPyNEConnectivityRule.js +++ b/webapp/components/definition/connectivity/NetPyNEConnectivityRule.js @@ -1,31 +1,25 @@ -import React from 'react'; -import MenuItem from '@material-ui/core/MenuItem'; -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 React from "react"; + +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, + ListComponent, + NetPyNECoordsRange +} from "netpyne/components"; +import Utils from "../../../Utils"; export default class NetPyNEConnectivityRule extends React.Component { - constructor (props) { super(props); this.state = { @@ -41,18 +35,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 +67,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 +143,273 @@ 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 +422,7 @@ export default class NetPyNEConnectivityRule extends React.Component { {content}
); - } handleChange = (event, index, values) => this.setState({ values }); - } 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..8865f1c0 100644 --- a/webapp/components/definition/plots/plotTypes/Plot2Dnet.js +++ b/webapp/components/definition/plots/plotTypes/Plot2Dnet.js @@ -1,12 +1,11 @@ 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, + SelectField +} from 'netpyne/components'; + export default class Plot2Dnet extends React.Component { @@ -18,7 +17,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..9e8220f0 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, + SelectField +} 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..67e244e9 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..0bdf3fad 100644 --- a/webapp/components/definition/plots/plotTypes/PlotSpikeHist.js +++ b/webapp/components/definition/plots/plotTypes/PlotSpikeHist.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, + NetPyNECheckbox, + NetPyNETextField +} from 'netpyne/components'; export default class PlotSpikeHist extends React.Component { @@ -21,7 +18,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..6a5b2d0a 100644 --- a/webapp/components/definition/plots/plotTypes/PlotTraces.js +++ b/webapp/components/definition/plots/plotTypes/PlotTraces.js @@ -1,15 +1,11 @@ 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, +} from 'netpyne/components'; export default class PlotTraces extends React.Component { @@ -21,7 +17,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..c97d6a3f 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,7 +194,7 @@ 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 = ; } } 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..7ffbc52e 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 = ( 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..64e9ecfc 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 = ( 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..4e6053e4 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,7 +169,7 @@ 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 ( diff --git a/webapp/components/general/NetPyNECoordsRange.js b/webapp/components/general/NetPyNECoordsRange.js index 9a768848..2df0f036 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, } 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/index.js b/webapp/components/index.js new file mode 100644 index 00000000..893fc282 --- /dev/null +++ b/webapp/components/index.js @@ -0,0 +1,186 @@ +import { connect } from "react-redux"; +import PythonControlledCapability from "geppetto-client/js/communication/geppettoJupyter/PythonControlledCapability"; + +import { + activateWidget, + destroyWidget, + minimizeWidget, + maximizeWidget +} from "../redux/actions/flexlayout"; + +import { updateCards, editModel, createNetwork, createAndSimulateNetwork, showNetwork } from "../redux/actions/general"; + + +const updateCardsDispatch = dispatch => ({ updateCards: () => dispatch(updateCards) }); + + +/** **** COMPONENT PROXIES ******/ + +import _AdapterComponent from "./general/AdapterComponent"; +export const AdapterComponent = PythonControlledCapability.createPythonControlledControl( + _AdapterComponent +); + +import Checkbox from "./general/Checkbox"; +export const NetPyNECheckbox = PythonControlledCapability.createPythonControlledControl( + Checkbox +); + +import _Dimensions from "./definition/populations/Dimensions"; +export const Dimensions = connect( + (state, ownProps) => ({ ...ownProps }), + updateCardsDispatch +)(_Dimensions); + + +import _ListComponent from "./general/List"; +export const ListComponent = PythonControlledCapability.createPythonControlledControl( + _ListComponent +); + +import _NetPyNE from "./NetPyNE"; +export const NetPyNE = connect(state => state.general, null)(_NetPyNE); + +import NetPyNEAddNew from "./general/NetPyNEAddNew"; +export { NetPyNEAddNew }; + +import NetPyNEHome from "./general/NetPyNEHome"; +export { NetPyNEHome }; + +import _NetPyNECellRule from "./definition/cellRules/NetPyNECellRule"; +export const NetPyNECellRule = connect((state, ownProps) => ({ + ...ownProps, + updates: state.general.updates +}))(_NetPyNECellRule); + +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 NetPyNECoordsRange from "./general/NetPyNECoordsRange"; +export { NetPyNECoordsRange }; + +import NetPyNEField from "./general/NetPyNEField"; +export { NetPyNEField }; + +import NetPyNEInstantiated from "./instantiation/NetPyNEInstantiated"; +export { NetPyNEInstantiated }; + +import _LayoutManager from "./layout/LayoutManager"; +export const LayoutManager = connect( + state => state.flexlayout, + dispatch => ({ + minimizeWidget: id => dispatch(minimizeWidget(id)), + destroyWidget: id => dispatch(destroyWidget(id)), + maximizeWidget: id => dispatch(maximizeWidget(id)), + activateWidget: id => dispatch(activateWidget(id)) + }) +)(_LayoutManager); + +import _NetPyNEPlots from "./definition/plots/NetPyNEPlots"; +export const NetPyNEPlots = PythonControlledCapability.createPythonControlledComponent( + _NetPyNEPlots +); + +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 NetPyNESimConfig from "./definition/configuration/NetPyNESimConfig"; +export { NetPyNESimConfig }; + + +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 _NetPyNEStimulationTargets from "./definition/stimulationTargets/NetPyNEStimulationTargets"; +export const NetPyNEStimulationTargets = PythonControlledCapability.createPythonControlledComponent( + _NetPyNEStimulationTargets +); + +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 NetPyNEThumbnail from "./general/NetPyNEThumbnail"; +export { NetPyNEThumbnail }; + +import _NetPyNEToolbar from "./settings/NetPyNEToolBar"; +export const NetPyNEToolBar = connect( + state => state.general +)(_NetPyNEToolbar); + +import SelectField from "./general/Select"; +export const NetPyNESelectField = connect((state, ownProps) => ({ + ...ownProps, + updates: String(state.general.updates) +}))(PythonControlledCapability.createPythonControlledControlWithPythonDataFetch( + SelectField +)); + +import TextField from "@material-ui/core/TextField"; +export const NetPyNETextField = PythonControlledCapability.createPythonControlledControl( + TextField +); + + diff --git a/webapp/components/instantiation/NetPyNEInstantiated.js b/webapp/components/instantiation/NetPyNEInstantiated.js index 57413048..7bc3d91f 100644 --- a/webapp/components/instantiation/NetPyNEInstantiated.js +++ b/webapp/components/instantiation/NetPyNEInstantiated.js @@ -36,7 +36,6 @@ const styles = { instantiatedContainer: { height: '100%', width: '100%', - position: 'fixed' }, controlpanelBtn: { position: 'absolute', @@ -179,9 +178,7 @@ export default class NetPyNEInstantiated extends React.Component { this.showWidgets(true) }); - GEPPETTO.on(GEPPETTO.Events.Model_loaded, () => { - this.props.modelLoaded(); - }); + } componentWillUnmount (){ diff --git a/webapp/components/layout/LayoutManager.js b/webapp/components/layout/LayoutManager.js index fcb1bb8f..9c2b89ae 100644 --- a/webapp/components/layout/LayoutManager.js +++ b/webapp/components/layout/LayoutManager.js @@ -274,7 +274,7 @@ export default class LayoutManager extends Component { render () { return ( -
+
{ + if (tab == "define") { + this.props.editModel(); + } else { + this.rightTabAction(); } - 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 +59,79 @@ export default class NetPyNETabs extends React.Component { }; render () { + return ( +
+ this.handleChange(e.currentTarget.value)} + aria-label="Choose mode" - return
- - - - this.setState({ transitionOptionsHovered: true })} - onMouseLeave={() => this.setState({ transitionOptionsHovered: false })} - style={{ color: 'white' }} - > - - - - - - 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 + + +
+ ); } } diff --git a/webapp/components/settings/NetPyNEToolBar.js b/webapp/components/settings/NetPyNEToolBar.js index 082669b8..26d3c141 100644 --- a/webapp/components/settings/NetPyNEToolBar.js +++ b/webapp/components/settings/NetPyNEToolBar.js @@ -42,6 +42,7 @@ export default class NetPyNEToolBar extends React.Component { this.snackBarMessage = message this.setState({ openSnackBar: true }) } + render () { @@ -51,28 +52,24 @@ export default class NetPyNEToolBar extends React.Component { var content = this.setState({ openDialogBox: false })} - changeTab={this.props.changeTab} /> break; case 'Save': var content = this.setState({ openDialogBox: false })} - changeTab={this.props.changeTab} /> break; case 'ImportHLS': var content = this.setState({ openDialogBox: false })} - changeTab={this.props.changeTab} mode ={"IMPORT"}/> break; case 'ExportHLS': var content = this.setState({ openDialogBox: false })} - changeTab={this.props.changeTab} mode ={"EXPORT"} /> break; @@ -86,7 +83,6 @@ export default class NetPyNEToolBar extends React.Component { var content = this.setState({ openDialogBox: false })} - changeTab={this.props.changeTab} /> break; case 'UploadFiles': diff --git a/webapp/package-lock.json b/webapp/package-lock.json index 0d9e42ed..9b2ff207 100644 --- a/webapp/package-lock.json +++ b/webapp/package-lock.json @@ -2503,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", diff --git a/webapp/package.json b/webapp/package.json index 6b89cf0c..8cedb663 100644 --- a/webapp/package.json +++ b/webapp/package.json @@ -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": { diff --git a/webapp/redux/actions/general.js b/webapp/redux/actions/general.js index f593b3a3..061bf2fe 100644 --- a/webapp/redux/actions/general.js +++ b/webapp/redux/actions/general.js @@ -1,8 +1,18 @@ // Action Types export const UPDATE_CARDS = 'UPDATE_CARDS'; export const MODEL_LOADED = 'MODEL_LOADED'; +export const CREATE_NETWORK = 'CREATE_NETWORK'; +export const SHOW_NETWORK = 'SHOW_NETWORK'; +export const CREATE_SIMULATE_NETWORK = 'CREATE_SIMULATE_NETWORK'; +export const EDIT_MODEL = 'EDIT_MODEL'; // 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 showNetwork = { type: SHOW_NETWORK }; + +export const editModel = { type: EDIT_MODEL }; diff --git a/webapp/redux/middleware/middleware.js b/webapp/redux/middleware/middleware.js index 00f40948..f6063670 100644 --- a/webapp/redux/middleware/middleware.js +++ b/webapp/redux/middleware/middleware.js @@ -1,4 +1,4 @@ -import { UPDATE_CARDS } from '../actions/general'; +import { UPDATE_CARDS, showNetwork, CREATE_NETWORK, CREATE_SIMULATE_NETWORK } from '../actions/general'; export default store => next => action => { switch (action.type) { @@ -7,7 +7,12 @@ export default store => next => action => { console.log("Triggered card update") next(action); break; - + case CREATE_SIMULATE_NETWORK: + next(showNetwork); + break; + case CREATE_NETWORK: + next(showNetwork); + break; default: { next(action); } diff --git a/webapp/redux/reducers/flexlayout.js b/webapp/redux/reducers/flexlayout.js index 56ecc916..70331764 100644 --- a/webapp/redux/reducers/flexlayout.js +++ b/webapp/redux/reducers/flexlayout.js @@ -35,7 +35,7 @@ export const FLEXLAYOUT_DEFAULT_STATE = { const MODEL_LOADED_STATE_WIDGETS = { '3dcanvas': { id: '3DCanvas', - name: '3DCanvas', + name: 'Network', status: WidgetStatus.MINIMIZED, icon: 'fa-python', component: '3DCanvas', diff --git a/webapp/redux/reducers/general.js b/webapp/redux/reducers/general.js index 71f90805..9a8a741c 100644 --- a/webapp/redux/reducers/general.js +++ b/webapp/redux/reducers/general.js @@ -1,10 +1,11 @@ // import action types -import { UPDATE_CARDS, MODEL_LOADED, modelLoaded } from '../actions/general'; +import { UPDATE_CARDS, MODEL_LOADED, SHOW_NETWORK, EDIT_MODEL } from '../actions/general'; // Default state for general export const GENERAL_DEFAULT_STATE = { updates: 0, - modelLoaded: false + modelLoaded: false, + editMode: true }; // reducer @@ -21,6 +22,10 @@ function reduceGeneral (state, action) { return { updates: state.updates + 1 } case MODEL_LOADED: return { modelLoaded: true } + case SHOW_NETWORK: + return { editMode: false } + case EDIT_MODEL: + return { editMode: 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/LayoutManagerContainer.js b/webapp/redux/reduxconnect/LayoutManagerContainer.js deleted file mode 100644 index e2e8d104..00000000 --- a/webapp/redux/reduxconnect/LayoutManagerContainer.js +++ /dev/null @@ -1,19 +0,0 @@ -import { connect } from 'react-redux'; -import LayoutManager from '../../components/layout/LayoutManager'; -import { - activateWidget, - destroyWidget, - minimizeWidget, - maximizeWidget, -} from '../actions/flexlayout'; - -const mapStateToProps = state => state.flexlayout; - -const mapDispatchToProps = dispatch => ({ - minimizeWidget: id => dispatch(minimizeWidget(id)), - destroyWidget: id => dispatch(destroyWidget(id)), - maximizeWidget: id => dispatch(maximizeWidget(id)), - activateWidget: id => dispatch(activateWidget(id)), -}); -console.log(LayoutManager); -export default connect(mapStateToProps, mapDispatchToProps)(LayoutManager); \ 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/NetPyNEInstantiatedConnection.js b/webapp/redux/reduxconnect/NetPyNEInstantiatedConnection.js deleted file mode 100644 index 5ca4cec1..00000000 --- a/webapp/redux/reduxconnect/NetPyNEInstantiatedConnection.js +++ /dev/null @@ -1,12 +0,0 @@ -import { connect } from 'react-redux'; -import { modelLoaded } from '../actions/general'; -import NetPyNEInstantiated from '../../components/instantiation/NetPyNEInstantiated'; - -const mapStateToProps = (state, ownProps) => ({ - ...ownProps, - updates: state.general.updates -}); - -const mapDispatchToProps = dispatch => ({ modelLoaded: dispatch(modelLoaded) }); - -export default connect(mapStateToProps, mapDispatchToProps)(NetPyNEInstantiated); \ 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/webpack.config.js b/webapp/webpack.config.js index c5f1b794..b12df05e 100644 --- a/webapp/webpack.config.js +++ b/webapp/webpack.config.js @@ -140,7 +140,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'], }, From afd4f3cd508f8925f2f68c622fed1c19d70fa17c Mon Sep 17 00:00:00 2001 From: rodriguez-facundo Date: Sun, 22 Mar 2020 09:06:39 -0300 Subject: [PATCH 03/14] #135 Flexlaout functional --- netpyne_ui/netpyne_model_interpreter.py | 4 +- webapp/Main.js | 2 +- .../definition/cellRules/NetPyNECellRule.js | 6 +- .../connectivity/NetPyNEConnectivityRule.js | 63 +++++-------------- .../definition/plots/NetPyNEInclude.js | 10 +-- .../definition/plots/plotTypes/Plot2Dnet.js | 5 +- .../definition/plots/plotTypes/PlotConn.js | 8 +-- .../definition/plots/plotTypes/PlotLFP.js | 4 +- .../plots/plotTypes/PlotSpikeHist.js | 3 +- .../definition/plots/plotTypes/PlotTraces.js | 1 + .../components/general/NetPyNECoordsRange.js | 2 +- webapp/components/general/Select.js | 2 +- webapp/components/index.js | 13 +++- webapp/components/settings/NetPyNETabs.js | 46 +++++++++++--- 14 files changed, 91 insertions(+), 78 deletions(-) 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/webapp/Main.js b/webapp/Main.js index 8dd2cf34..39e6c0dc 100644 --- a/webapp/Main.js +++ b/webapp/Main.js @@ -66,7 +66,7 @@ jQuery(function () { GEPPETTO.on(GEPPETTO.Events.Model_loaded, () => { store.dispatch(modelLoaded); }); - ReactDOM.render(, document.querySelector('#mainContainer')); + ReactDOM.render(, document.querySelector('#mainContainer'), store.dispatch(modelLoaded)); GEPPETTO.trigger("spinner:hide"); }) }); diff --git a/webapp/components/definition/cellRules/NetPyNECellRule.js b/webapp/components/definition/cellRules/NetPyNECellRule.js index 78c242f5..25f5a2b4 100644 --- a/webapp/components/definition/cellRules/NetPyNECellRule.js +++ b/webapp/components/definition/cellRules/NetPyNECellRule.js @@ -108,7 +108,7 @@ export default class NetPyNECellRule extends React.Component {
- - - - - + - + - + - + - + - - + + - + {dialogPop}
@@ -234,7 +205,7 @@ export default class NetPyNEConnectivityRule extends React.Component { var content = (
- - - - - - { 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 diff --git a/webapp/components/definition/plots/plotTypes/Plot2Dnet.js b/webapp/components/definition/plots/plotTypes/Plot2Dnet.js index 8865f1c0..af68353e 100644 --- a/webapp/components/definition/plots/plotTypes/Plot2Dnet.js +++ b/webapp/components/definition/plots/plotTypes/Plot2Dnet.js @@ -3,7 +3,8 @@ import React, { Component } from 'react'; import { NetPyNEInclude, NetPyNEField, - SelectField + NetPyNESelectField, + NetPyNECheckbox } from 'netpyne/components'; @@ -25,7 +26,7 @@ export default class Plot2Dnet extends React.Component { /> - + diff --git a/webapp/components/definition/plots/plotTypes/PlotConn.js b/webapp/components/definition/plots/plotTypes/PlotConn.js index 9e8220f0..2afeebd7 100644 --- a/webapp/components/definition/plots/plotTypes/PlotConn.js +++ b/webapp/components/definition/plots/plotTypes/PlotConn.js @@ -3,7 +3,7 @@ import React, { Component } from 'react'; import { NetPyNEInclude, NetPyNEField, - SelectField + NetPyNESelectField } from 'netpyne/components'; @@ -25,15 +25,15 @@ export default class plotConn extends React.Component { /> - + - + - +
} diff --git a/webapp/components/definition/plots/plotTypes/PlotLFP.js b/webapp/components/definition/plots/plotTypes/PlotLFP.js index 67e244e9..87d85832 100644 --- a/webapp/components/definition/plots/plotTypes/PlotLFP.js +++ b/webapp/components/definition/plots/plotTypes/PlotLFP.js @@ -1,11 +1,11 @@ import React from 'react'; - +import TimeRange from '../TimeRange' import { NetPyNEField, NetPyNECheckbox, NetPyNETextField, NetPyNESelectField, - ListComponent + ListComponent, } from 'netpyne/components'; export default class PlotLFP extends React.Component { diff --git a/webapp/components/definition/plots/plotTypes/PlotSpikeHist.js b/webapp/components/definition/plots/plotTypes/PlotSpikeHist.js index 0bdf3fad..2e67b2d9 100644 --- a/webapp/components/definition/plots/plotTypes/PlotSpikeHist.js +++ b/webapp/components/definition/plots/plotTypes/PlotSpikeHist.js @@ -5,7 +5,8 @@ import { NetPyNEInclude, NetPyNEField, NetPyNECheckbox, - NetPyNETextField + NetPyNETextField, + NetPyNESelectField } from 'netpyne/components'; export default class PlotSpikeHist extends React.Component { diff --git a/webapp/components/definition/plots/plotTypes/PlotTraces.js b/webapp/components/definition/plots/plotTypes/PlotTraces.js index 6a5b2d0a..bdc1158e 100644 --- a/webapp/components/definition/plots/plotTypes/PlotTraces.js +++ b/webapp/components/definition/plots/plotTypes/PlotTraces.js @@ -5,6 +5,7 @@ import { NetPyNEInclude, NetPyNEField, NetPyNESelectField, + NetPyNECheckbox } from 'netpyne/components'; export default class PlotTraces extends React.Component { diff --git a/webapp/components/general/NetPyNECoordsRange.js b/webapp/components/general/NetPyNECoordsRange.js index 2df0f036..68958c53 100644 --- a/webapp/components/general/NetPyNECoordsRange.js +++ b/webapp/components/general/NetPyNECoordsRange.js @@ -5,7 +5,7 @@ import TextField from '@material-ui/core/TextField'; import SelectField from './Select'; import Utils from '../../Utils'; -import { AdapterComponent, } from "netpyne/components"; +import { AdapterComponent, NetPyNEField } from "netpyne/components"; export default class NetPyNECoordsRange extends Component { diff --git a/webapp/components/general/Select.js b/webapp/components/general/Select.js index c96b6ceb..b0d506cc 100644 --- a/webapp/components/general/Select.js +++ b/webapp/components/general/Select.js @@ -10,7 +10,7 @@ export default class Select extends Component { {this.props.label} diff --git a/webapp/components/index.js b/webapp/components/index.js index 893fc282..7b14683b 100644 --- a/webapp/components/index.js +++ b/webapp/components/index.js @@ -1,3 +1,4 @@ +import React from 'react' import { connect } from "react-redux"; import PythonControlledCapability from "geppetto-client/js/communication/geppettoJupyter/PythonControlledCapability"; @@ -12,6 +13,7 @@ import { updateCards, editModel, createNetwork, createAndSimulateNetwork, showNe const updateCardsDispatch = dispatch => ({ updateCards: () => dispatch(updateCards) }); +const editModelDispatch = dispatch => ({ editModel: () => dispatch(editModel) }); /** **** COMPONENT PROXIES ******/ @@ -39,7 +41,10 @@ export const ListComponent = PythonControlledCapability.createPythonControlledCo ); import _NetPyNE from "./NetPyNE"; -export const NetPyNE = connect(state => state.general, null)(_NetPyNE); +export const NetPyNE = connect( + state => ({ editMode: state.general.editMode, }), + editModelDispatch +)(_NetPyNE); import NetPyNEAddNew from "./general/NetPyNEAddNew"; export { NetPyNEAddNew }; @@ -183,4 +188,8 @@ export const NetPyNETextField = PythonControlledCapability.createPythonControlle TextField ); - +import _NetPyNEInclude from './definition/plots/NetPyNEInclude'; +export const NetPyNEInclude = connect( + (state, ownProps) => ({ ...ownProps, updates: state.general.updates }), + null +)(_NetPyNEInclude); \ No newline at end of file diff --git a/webapp/components/settings/NetPyNETabs.js b/webapp/components/settings/NetPyNETabs.js index 866f348b..bf7dd7a6 100644 --- a/webapp/components/settings/NetPyNETabs.js +++ b/webapp/components/settings/NetPyNETabs.js @@ -6,14 +6,36 @@ 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", transitionOptionsHovered: false, - anchorEl: null + anchorEl: null, + editTab: true }; this.rightTabAction = this.props.createNetwork; this.handleTransitionOptionsChange = this.handleTransitionOptionsChange.bind( @@ -29,8 +51,10 @@ export default class NetPyNETabs extends React.Component { handleChange = tab => { if (tab == "define") { this.props.editModel(); + this.setState({ editTab: true }) } else { this.rightTabAction(); + this.setState({ editTab: false }) } }; @@ -59,23 +83,23 @@ export default class NetPyNETabs extends React.Component { }; render () { + const { classes } = this.props + const { editTab } = this.state return (
this.handleChange(e.currentTarget.value)} aria-label="Choose mode" - - style={{flexGrow: 1}} > {"Define your Network"} @@ -84,7 +108,8 @@ export default class NetPyNETabs extends React.Component { id={"simulateNetwork"} value="simulate" color="primary" - style={{ flex: 1, borderRadius: 10, marginLeft: 5, background: 'transparent', border: 'none', color: 'white' }} + selected={!editTab} + classes={{ root: classes.toggleButton, selected: classes.toggleButtonSelected }} > {this.state.simulateTabLabel} @@ -106,7 +131,7 @@ export default class NetPyNETabs extends React.Component { value={this.state.simulateTabLabel} open={Boolean(this.state.anchorEl)} anchorEl={this.state.anchorEl} - style={{ position: "absolute", top: "6px", right: "28px" }} + className={classes.menu} onClose={this.handleClose} > Date: Mon, 23 Mar 2020 14:21:35 -0300 Subject: [PATCH 04/14] #135 Move svg plots to flexlayout --- webapp/components/general/HTMLViewer.js | 84 ++++ webapp/components/index.js | 106 ++--- .../instantiation/NetPyNEInstantiated.js | 391 ++++++++++-------- webapp/components/layout/LayoutManager.js | 5 +- webapp/components/layout/WidgetFactory.js | 17 +- webapp/constants.js | 9 +- webapp/redux/actions/errors.js | 7 + webapp/redux/middleware/middleware.js | 53 ++- webapp/redux/reducers/all.js | 3 +- webapp/redux/reducers/errors.js | 33 ++ webapp/redux/reducers/flexlayout.js | 30 +- 11 files changed, 497 insertions(+), 241 deletions(-) create mode 100644 webapp/components/general/HTMLViewer.js create mode 100644 webapp/redux/actions/errors.js create mode 100644 webapp/redux/reducers/errors.js diff --git a/webapp/components/general/HTMLViewer.js b/webapp/components/general/HTMLViewer.js new file mode 100644 index 00000000..c51c99ef --- /dev/null +++ b/webapp/components/general/HTMLViewer.js @@ -0,0 +1,84 @@ +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() + svg.removeAttribute('width'); + svg.removeAttribute('height'); + svg.setAttribute('width', `${this.dimensions.width}px`); + svg.setAttribute('height', `${this.dimensions.height}px`); + } + + 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/index.js b/webapp/components/index.js index 7b14683b..fc16a11b 100644 --- a/webapp/components/index.js +++ b/webapp/components/index.js @@ -10,7 +10,7 @@ import { } from "../redux/actions/flexlayout"; import { updateCards, editModel, createNetwork, createAndSimulateNetwork, showNetwork } from "../redux/actions/general"; - +import { newWidget } from "../redux/actions/flexlayout"; const updateCardsDispatch = dispatch => ({ updateCards: () => dispatch(updateCards) }); const editModelDispatch = dispatch => ({ editModel: () => dispatch(editModel) }); @@ -18,6 +18,33 @@ const editModelDispatch = dispatch => ({ editModel: () => dispatch(editModel) }) /** **** 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 @@ -28,6 +55,15 @@ export const NetPyNECheckbox = PythonControlledCapability.createPythonControlled 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 }), @@ -35,47 +71,18 @@ export const Dimensions = connect( )(_Dimensions); -import _ListComponent from "./general/List"; -export const ListComponent = PythonControlledCapability.createPythonControlledControl( - _ListComponent -); - import _NetPyNE from "./NetPyNE"; export const NetPyNE = connect( state => ({ editMode: state.general.editMode, }), editModelDispatch )(_NetPyNE); -import NetPyNEAddNew from "./general/NetPyNEAddNew"; -export { NetPyNEAddNew }; - -import NetPyNEHome from "./general/NetPyNEHome"; -export { NetPyNEHome }; - import _NetPyNECellRule from "./definition/cellRules/NetPyNECellRule"; export const NetPyNECellRule = connect((state, ownProps) => ({ ...ownProps, updates: state.general.updates }))(_NetPyNECellRule); -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 NetPyNECoordsRange from "./general/NetPyNECoordsRange"; -export { NetPyNECoordsRange }; - -import NetPyNEField from "./general/NetPyNEField"; -export { NetPyNEField }; - -import NetPyNEInstantiated from "./instantiation/NetPyNEInstantiated"; -export { NetPyNEInstantiated }; import _LayoutManager from "./layout/LayoutManager"; export const LayoutManager = connect( @@ -88,10 +95,6 @@ export const LayoutManager = connect( }) )(_LayoutManager); -import _NetPyNEPlots from "./definition/plots/NetPyNEPlots"; -export const NetPyNEPlots = PythonControlledCapability.createPythonControlledComponent( - _NetPyNEPlots -); import _NetPyNEPopulation from "./definition/populations/NetPyNEPopulation"; export const NetPyNEPopulation = connect( @@ -109,10 +112,6 @@ export const NetPyNEPopulations = connect( ) ); -import NetPyNESimConfig from "./definition/configuration/NetPyNESimConfig"; -export { NetPyNESimConfig }; - - import _NetPyNEStimulationSource from "./definition/stimulationSources/NetPyNEStimulationSource"; export const NetPyNEStimulationSource = connect( null, @@ -138,10 +137,6 @@ export const NetPyNEStimulationTarget = connect( updateCardsDispatch )(_NetPyNEStimulationTarget); -import _NetPyNEStimulationTargets from "./definition/stimulationTargets/NetPyNEStimulationTargets"; -export const NetPyNEStimulationTargets = PythonControlledCapability.createPythonControlledComponent( - _NetPyNEStimulationTargets -); import _NetPyNESynapse from "./definition/synapses/NetPyNESynapse"; export const NetPyNESynapse = connect( @@ -166,10 +161,6 @@ export const NetPyNETabs = connect( }) )(_NetPyNETabs); - -import NetPyNEThumbnail from "./general/NetPyNEThumbnail"; -export { NetPyNEThumbnail }; - import _NetPyNEToolbar from "./settings/NetPyNEToolBar"; export const NetPyNEToolBar = connect( state => state.general @@ -183,13 +174,26 @@ export const NetPyNESelectField = connect((state, ownProps) => ({ SelectField )); -import TextField from "@material-ui/core/TextField"; -export const NetPyNETextField = PythonControlledCapability.createPythonControlledControl( - TextField -); import _NetPyNEInclude from './definition/plots/NetPyNEInclude'; export const NetPyNEInclude = connect( (state, ownProps) => ({ ...ownProps, updates: state.general.updates }), null -)(_NetPyNEInclude); \ No newline at end of file +)(_NetPyNEInclude); + +import _NetPyNEInstantiated from "./instantiation/NetPyNEInstantiated" +export const NetPyNEInstantiated = connect( + null, + dispatch => ({ newWidget: conf => dispatch(newWidget(conf)), }) +)(_NetPyNEInstantiated) + +// ---------------------------------------------------------------------------------------- // + +// 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' \ No newline at end of file diff --git a/webapp/components/instantiation/NetPyNEInstantiated.js b/webapp/components/instantiation/NetPyNEInstantiated.js index 7bc3d91f..2ee531a5 100644 --- a/webapp/components/instantiation/NetPyNEInstantiated.js +++ b/webapp/components/instantiation/NetPyNEInstantiated.js @@ -1,9 +1,10 @@ -import React from 'react'; +import React, { createRef } from 'react'; +import ReactDOM from 'react-dom' 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 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 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'; @@ -75,200 +76,254 @@ export default class NetPyNEInstantiated extends React.Component { controlPanelHidden: true, plotButtonOpen: false, openDialog: false, - bringItToFront: 0 + bringItToFront: 0, + update: 0 }; + this.dimensions = { width: 200, height: 200 } + this.canvasRef = createRef(); - 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); } + + componentDidUpdate (){ + this.resizeIfNeeded() + } - handleCloseDialog = () => { - this.setState({ openDialog: false }); - }; + 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); - }); + newPlotWidget (name, svgResponse, data, i, total) { + if (svgResponse === '') { + return } - - 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; + const pathName = `network.${name.replace(/ /g, '')}_${i}` + if (!window.plotSvgImages) { + window.plotSvgImages = { [pathName]: svgResponse } + } else { + window.plotSvgImages[pathName] = svgResponse } - 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); - } - }); + this.props.newWidget({ + path: pathName, + component: 'Plot', + panelName: 'topPanel' + }) + + if (i < total) { + this.newPlotWidget(name, data[i++], data, i++, total) } - getOpenedWidgets () { - return this.widgets; + if (i === total) { + this.handleRequestClose() } + } - showWidgets (visible) { - GEPPETTO.WidgetFactory.getController(GEPPETTO.Widgets.POPUP).then(controller => { - controller.widgets.forEach(widget => { - if (visible){ - widget.show() - } else { - widget.hide() - } - }) - }) + 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; + } - 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) + 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); + } }); + } - - } - componentWillUnmount (){ - GEPPETTO.off(GEPPETTO.Events.Control_panel_close) - } + componentDidMount () { + this.canvasRef.current.engine.setLinesThreshold(10000); + this.canvasRef.current.displayAllInstances(); + this.canvasRef.current.engine.updateSceneWithNewInstances(window.Instances); + this.canvasRef.current.setBackgroundColor('#191919') + + window.addEventListener('resize', this.delayedResize.bind(this)) + this.resizeIfNeeded() - handleClick (event) { - // This prevents ghost click. - event.preventDefault(); - this.setState({ - plotButtonOpen: true, - anchorEl: event.currentTarget, - }); + GEPPETTO.on(GEPPETTO.Events.Control_panel_close, () => { + this.setState({ bringItToFront: 0 }) + }); + } + + componentWillUnmount (){ + GEPPETTO.off(GEPPETTO.Events.Control_panel_close) + clearTimeout(this.timer) + window.removeEventListener('resize', this.delayedResize) + } + + handleClick (event) { + // This prevents ghost click. + event.preventDefault(); + + this.setState({ + plotButtonOpen: true, + anchorEl: event.currentTarget, + }); + } + + handleRequestClose () { + this.setState({ plotButtonOpen: false, }); + } + + updateInstances () { + this.canvasRef.current.engine.updateSceneWithNewInstances(window.Instances); + } + + resizeCanvas () { + this.setState({ update: this.state.update++ }) + } + + 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 + } + + delayedResize () { + this.timer = setTimeout(() => this.resizeIfNeeded(), 100) + } - handleRequestClose () { - this.setState({ plotButtonOpen: false, }); + getParentSize () { + if (this.canvasRef.current === null) { + return false } + const node = ReactDOM.findDOMNode(this) + return node.parentNode.getBoundingClientRect() + } - render () { - return ( -
- + + +
+ + +
+ { + $('#controlpanel').show(); this.setState({ bringItToFront: 1 }) + }} + icon={"fa-list"} + id="ControlPanelButton" /> +
+ -
- - -
- { - $('#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} - - ))} - -
- - - {this.state.dialogTitle} - - - {this.state.dialogMessage} - - - - - - + {plots.map((plot, index) => ( + this.plotFigure(plot.plotName, plot.plotMethod, plot.plotType)} + > + {plot.primaryText} + + ))} +
- ); - } + + 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} + /> + + + 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} + /> + + + {this.state.dialogTitle} + + + {this.state.dialogMessage} + + + + + + +
+ + ); + } } \ No newline at end of file diff --git a/webapp/components/layout/LayoutManager.js b/webapp/components/layout/LayoutManager.js index 9c2b89ae..d1e445fb 100644 --- a/webapp/components/layout/LayoutManager.js +++ b/webapp/components/layout/LayoutManager.js @@ -169,7 +169,10 @@ export default class LayoutManager extends Component { findNewWidgets (widgets, oldWidgets) { - return oldWidgets ? Object.values(widgets).filter(widget => widget && !oldWidgets[widget.id]) : Object.values(widgets); + if (oldWidgets) { + return Object.values(widgets).filter(widget => widget && !oldWidgets[widget.id]) + } + return Object.values(widgets) } findUpdatedWidgets (widgets, oldWidgets) { diff --git a/webapp/components/layout/WidgetFactory.js b/webapp/components/layout/WidgetFactory.js index 79a30fec..e541bde7 100644 --- a/webapp/components/layout/WidgetFactory.js +++ b/webapp/components/layout/WidgetFactory.js @@ -1,8 +1,8 @@ import React, { lazy, Suspense } from 'react'; -import PythonConsole from 'geppetto-client/js/components/interface/pythonConsole/PythonConsole'; +import PythonConsole from '@geppettoengine/geppetto-client/js/components/interface/pythonConsole/PythonConsole'; -import { NetPyNEInstantiated } from 'netpyne/components'; +import { NetPyNEInstantiated, HTMLViewer } from 'netpyne/components'; export default class WidgetFactory{ @@ -36,8 +36,17 @@ export default class WidgetFactory{ case "PythonConsole": { return ; } - case "3DCanvas": - return + case "D3Canvas": + return + case "Plot": { + const data = window.plotSvgImages[widgetConfig.id] + return ( + + ) + } } } diff --git a/webapp/constants.js b/webapp/constants.js index 8683e75c..6704ea44 100644 --- a/webapp/constants.js +++ b/webapp/constants.js @@ -13,4 +13,11 @@ export const WidgetStatus = { ACTIVE: 'ACTIVE', MAXIMIZED: 'MAXIMIZED', MINIMIZED: 'MINIMIZED' -}; \ No newline at end of file +}; + + +export const NETPYNE_COMMANDS = { + instantiateModel: 'netpyne_geppetto.instantiateNetPyNEModelInGeppetto', + simulateModel: 'netpyne_geppetto.simulateNetPyNEModelInGeppetto' +} + 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/middleware/middleware.js b/webapp/redux/middleware/middleware.js index f6063670..9e9300a1 100644 --- a/webapp/redux/middleware/middleware.js +++ b/webapp/redux/middleware/middleware.js @@ -1,4 +1,7 @@ import { UPDATE_CARDS, showNetwork, CREATE_NETWORK, CREATE_SIMULATE_NETWORK } from '../actions/general'; +import { openBackendErrorDialog } from '../actions/errors'; +import Utils from '../../Utils'; +import { NETPYNE_COMMANDS } from '../../constants'; export default store => next => action => { switch (action.type) { @@ -8,14 +11,60 @@ export default store => next => action => { next(action); break; case CREATE_SIMULATE_NETWORK: - next(showNetwork); + simulateNetwork({ parallelSimulation: false }, next, showNetwork) break; case CREATE_NETWORK: - next(showNetwork); + instantiateNetwork({}, next, showNetwork) break; default: { next(action); } } +} + + +const instantiateNetwork = async (payload, next, action) => { + createSimulateBackendCall( + NETPYNE_COMMANDS.instantiateModel, + payload, next, action, + "The NetPyNE model is getting instantiated...", + GEPPETTO.Resources.INSTANTIATING_MODEL + ) +} + +const simulateNetwork = async (payload, next, action) => { + createSimulateBackendCall( + NETPYNE_COMMANDS.simulateModel, + payload, next, action, + "The NetPyNE model is getting simulated...", + GEPPETTO.Resources.RUNNING_SIMULATION + ) +} + +const createSimulateBackendCall = async (cmd, payload, next, action, consoleMessage, spinnerType) => { + 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(payload)) + } else { + GEPPETTO.trigger(GEPPETTO.Events.Show_spinner, GEPPETTO.Resources.PARSING_MODEL); + GEPPETTO.Manager.loadModel(response); + GEPPETTO.CommandController.log(consoleMessage + ' was completed.'); + GEPPETTO.trigger(GEPPETTO.Events.Hide_spinner); + next(action) + } +} + +const processError = response => { + var parsedResponse = Utils.getErrorResponse(response); + if (parsedResponse) { + return { errorMessage: parsedResponse['message'], errorDetails: parsedResponse['details'] } + } + return false } \ No newline at end of file diff --git a/webapp/redux/reducers/all.js b/webapp/redux/reducers/all.js index a59c61ad..df46d7f9 100644 --- a/webapp/redux/reducers/all.js +++ b/webapp/redux/reducers/all.js @@ -3,5 +3,6 @@ import { combineReducers } from 'redux'; import general from './general'; import notebook from './notebook'; import flexlayout from './flexlayout'; +import errors from './errors'; -export default combineReducers({ general, notebook, flexlayout }); \ No newline at end of file +export default combineReducers({ general, notebook, flexlayout, errors }); \ No newline at end of file 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 index 70331764..403a7ff2 100644 --- a/webapp/redux/reducers/flexlayout.js +++ b/webapp/redux/reducers/flexlayout.js @@ -17,15 +17,19 @@ function removeUndefined (obj) { export const FLEXLAYOUT_DEFAULT_STATE = { widgets: { - 'python': { - id: 'python', - name: 'Python', - status: WidgetStatus.MINIMIZED, - icon: 'fa-python', - component: 'PythonConsole', - panelName: "bottomPanel", - enableClose: false - }, + /* + * 'python': { + * id: 'python', + * name: 'Python', + * status: WidgetStatus.ACTIVE, + * icon: 'fa-python', + * component: 'PythonConsole', + * panelName: "bottomPanel", + * enableClose: false + * }, + */ + + ...MODEL_LOADED_STATE_WIDGETS }, @@ -33,12 +37,12 @@ export const FLEXLAYOUT_DEFAULT_STATE = { const MODEL_LOADED_STATE_WIDGETS = { - '3dcanvas': { - id: '3DCanvas', + 'D3Canvas': { + id: 'D3Canvas', name: 'Network', - status: WidgetStatus.MINIMIZED, + status: WidgetStatus.ACTIVE, icon: 'fa-python', - component: '3DCanvas', + component: 'D3Canvas', panelName: "topPanel", enableClose: false } From 1be0eef53f6de07af2cb5f2910e0c34d97aac277 Mon Sep 17 00:00:00 2001 From: rodriguez-facundo Date: Tue, 24 Mar 2020 08:46:58 -0300 Subject: [PATCH 05/14] #135 Moved drawer menu actions to redux --- webapp/Utils.js | 2 +- webapp/components/NetPyNE.js | 13 ++ .../definition/cellRules/NetPyNECellRules.js | 13 +- .../sections/NetPyNESectionThumbnail.js | 2 +- webapp/components/general/Checkbox.js | 2 +- webapp/components/general/Select.js | 10 +- webapp/components/index.js | 25 ++- .../instantiation/NetPyNEInstantiated.js | 6 +- webapp/components/settings/NetPyNEToolBar.js | 66 ++++--- .../settings/actions/ActionDialog.js | 169 +++++------------- .../settings/actions/ImportCellParams.js | 3 +- .../settings/actions/ImportExportHLS.js | 12 +- .../components/settings/actions/LoadFile.js | 2 +- .../components/settings/actions/NewModel.js | 2 +- .../components/settings/actions/SaveFile.js | 8 +- webapp/constants.js | 5 +- webapp/redux/actions/drawer.js | 7 + webapp/redux/actions/general.js | 3 + webapp/redux/middleware/middleware.js | 39 +++- webapp/redux/middleware/utils.js | 39 ++++ webapp/redux/reducers/all.js | 3 +- webapp/redux/reducers/drawer.js | 25 +++ 22 files changed, 279 insertions(+), 177 deletions(-) create mode 100644 webapp/redux/actions/drawer.js create mode 100644 webapp/redux/middleware/utils.js create mode 100644 webapp/redux/reducers/drawer.js diff --git a/webapp/Utils.js b/webapp/Utils.js index 0410a7ce..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) {
diff --git a/webapp/components/NetPyNE.js b/webapp/components/NetPyNE.js
index 43076996..bc6ca20f 100644
--- a/webapp/components/NetPyNE.js
+++ b/webapp/components/NetPyNE.js
@@ -22,6 +22,19 @@ export default 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)
+  }
+
   UNSAFE_componentWillReceiveProps (nextProps) {
     if (this.props.data != nextProps.data) {
       console.log("Initialising NetPyNE Tabs");
diff --git a/webapp/components/definition/cellRules/NetPyNECellRules.js b/webapp/components/definition/cellRules/NetPyNECellRules.js
index 2f45d28c..4d3ec7c3 100644
--- a/webapp/components/definition/cellRules/NetPyNECellRules.js
+++ b/webapp/components/definition/cellRules/NetPyNECellRules.js
@@ -307,6 +307,15 @@ export default class NetPyNECellRules extends React.Component {
     if (newMechanismName !== undefined) {
       this.setState({ selectedMechanism: newMechanismName });
     }
+
+    if (Object.keys(this.state.value).length === 0) {
+      this.setState({ 
+        selectedCellRule: undefined,
+        selectedSection: undefined,
+        selectedMechanism: undefined,
+        page: 'main'
+      })
+    }
   }
 
   handleHierarchyClick = nextPage => {
@@ -540,7 +549,7 @@ export default class NetPyNECellRules extends React.Component {
           />
         )
       }
-    } 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 = (
@@ -562,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 = (
diff --git a/webapp/components/definition/cellRules/sections/NetPyNESectionThumbnail.js b/webapp/components/definition/cellRules/sections/NetPyNESectionThumbnail.js
index 50292416..d8988316 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 {
 
diff --git a/webapp/components/general/Checkbox.js b/webapp/components/general/Checkbox.js
index 965547c9..58532dd1 100644
--- a/webapp/components/general/Checkbox.js
+++ b/webapp/components/general/Checkbox.js
@@ -13,7 +13,7 @@ export default class Checkbox extends Component {
           control={
             
           }
diff --git a/webapp/components/general/Select.js b/webapp/components/general/Select.js
index b0d506cc..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/index.js b/webapp/components/index.js
index fc16a11b..ed30903a 100644
--- a/webapp/components/index.js
+++ b/webapp/components/index.js
@@ -8,13 +8,13 @@ import {
   minimizeWidget,
   maximizeWidget
 } from "../redux/actions/flexlayout";
-
-import { updateCards, editModel, createNetwork, createAndSimulateNetwork, showNetwork } from "../redux/actions/general";
+import { openBackendErrorDialog, closeBackendErrorDialog } from '../redux/actions/errors';
+import { updateCards, editModel, createNetwork, createAndSimulateNetwork, showNetwork, pythonCall } from "../redux/actions/general";
+import { closeDrawerDialogBox, openDrawerDialogBox } from '../redux/actions/drawer';
 import { newWidget } from "../redux/actions/flexlayout";
 
 const updateCardsDispatch = dispatch => ({ updateCards: () => dispatch(updateCards) });
-const editModelDispatch = dispatch => ({ editModel: () => dispatch(editModel) });
-
+const pythonCallErrorDispatch = dispatch => ({ pythonCallErrorDialogBox: payload => dispatch(openBackendErrorDialog(payload)) });
 
 /** **** COMPONENT PROXIES ******/
 
@@ -74,7 +74,7 @@ export const Dimensions = connect(
 import _NetPyNE from "./NetPyNE";
 export const NetPyNE = connect(
   state => ({ editMode: state.general.editMode, }),
-  editModelDispatch
+  pythonCallErrorDispatch
 )(_NetPyNE);
 
 import _NetPyNECellRule from "./definition/cellRules/NetPyNECellRule";
@@ -163,7 +163,11 @@ export const NetPyNETabs = connect(
 
 import _NetPyNEToolbar from "./settings/NetPyNEToolBar";
 export const NetPyNEToolBar = connect(
-  state => state.general
+  state => ({ ...state.general, ...state.drawer }),
+  dispatch => ({
+    closeDrawerDialogBox: () => dispatch(closeDrawerDialogBox),
+    openDrawerDialogBox: () => dispatch(openDrawerDialogBox),
+  })
 )(_NetPyNEToolbar);
 
 import SelectField from "./general/Select";
@@ -187,6 +191,15 @@ export const NetPyNEInstantiated = connect(
   dispatch => ({ newWidget: conf => dispatch(newWidget(conf)), })
 )(_NetPyNEInstantiated)
 
+
+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)
 // ---------------------------------------------------------------------------------------- //
 
 // DEFAULTS
diff --git a/webapp/components/instantiation/NetPyNEInstantiated.js b/webapp/components/instantiation/NetPyNEInstantiated.js
index 2ee531a5..d891bfb2 100644
--- a/webapp/components/instantiation/NetPyNEInstantiated.js
+++ b/webapp/components/instantiation/NetPyNEInstantiated.js
@@ -164,7 +164,11 @@ export default class NetPyNEInstantiated extends React.Component {
   componentDidMount () {
     this.canvasRef.current.engine.setLinesThreshold(10000);
     this.canvasRef.current.displayAllInstances();
-    this.canvasRef.current.engine.updateSceneWithNewInstances(window.Instances);
+    if (Instances.length > 2) {
+      // update canvas only if there are instances to show
+      this.canvasRef.current.engine.updateSceneWithNewInstances(window.Instances);
+    }
+    
     this.canvasRef.current.setBackgroundColor('#191919')
     
     window.addEventListener('resize', this.delayedResize.bind(this))
diff --git a/webapp/components/settings/NetPyNEToolBar.js b/webapp/components/settings/NetPyNEToolBar.js
index 26d3c141..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,7 +57,8 @@ 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) {
@@ -43,52 +66,55 @@ export default class NetPyNEToolBar extends React.Component {
     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 })}
+          open={this.props.dialogBoxOpen}
+          onRequestClose={() => this.handleClose()}
         />
         break;
       case 'Save':
         var content =  this.setState({ openDialogBox: false })}
+          open={this.props.dialogBoxOpen}
+          onRequestClose={() => this.handleClose()}
         />
         break;
       case 'ImportHLS':
         var content =  this.setState({ openDialogBox: false })}
+          open={this.props.dialogBoxOpen}
+          onRequestClose={() => this.handleClose()}
           mode ={"IMPORT"}/>
         break;
       case 'ExportHLS':
         var content =  this.setState({ openDialogBox: false })}
+          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 })}
+          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)
           } }
@@ -96,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..5e519bc4 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); 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 ( ({ type: PYTHON_CALL, cmd, args }) \ No newline at end of file diff --git a/webapp/redux/middleware/middleware.js b/webapp/redux/middleware/middleware.js index 9e9300a1..f999ca97 100644 --- a/webapp/redux/middleware/middleware.js +++ b/webapp/redux/middleware/middleware.js @@ -1,7 +1,9 @@ -import { UPDATE_CARDS, showNetwork, CREATE_NETWORK, CREATE_SIMULATE_NETWORK } from '../actions/general'; +import { UPDATE_CARDS, showNetwork, editModel, CREATE_NETWORK, CREATE_SIMULATE_NETWORK, PYTHON_CALL } from '../actions/general'; 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) { @@ -16,10 +18,12 @@ export default store => next => action => { case CREATE_NETWORK: instantiateNetwork({}, next, showNetwork) break; + case PYTHON_CALL: + pythonCall(next, action) + break; default: { next(action); } - } } @@ -51,11 +55,11 @@ const createSimulateBackendCall = async (cmd, payload, next, action, consoleMess const errorPayload = await processError(response) if (errorPayload) { GEPPETTO.trigger(GEPPETTO.Events.Hide_spinner); - next(openBackendErrorDialog(payload)) + next(openBackendErrorDialog(errorPayload)) } else { GEPPETTO.trigger(GEPPETTO.Events.Show_spinner, GEPPETTO.Resources.PARSING_MODEL); GEPPETTO.Manager.loadModel(response); - GEPPETTO.CommandController.log(consoleMessage + ' was completed.'); + GEPPETTO.CommandController.log('Instantiation / Simulation completed.'); GEPPETTO.trigger(GEPPETTO.Events.Hide_spinner); next(action) } @@ -67,4 +71,29 @@ const processError = response => { return { errorMessage: parsedResponse['message'], errorDetails: parsedResponse['details'] } } return false -} \ No newline at end of file +} + +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) + } +} 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 df46d7f9..735a8115 100644 --- a/webapp/redux/reducers/all.js +++ b/webapp/redux/reducers/all.js @@ -4,5 +4,6 @@ import general from './general'; import notebook from './notebook'; import flexlayout from './flexlayout'; import errors from './errors'; +import drawer from './drawer'; -export default combineReducers({ general, notebook, flexlayout, errors }); \ 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 }; + } + } +} From 08c943880e8c711db2ba06c35357b101ca8602d0 Mon Sep 17 00:00:00 2001 From: rodriguez-facundo Date: Tue, 24 Mar 2020 15:16:29 -0300 Subject: [PATCH 06/14] #135 Split NetPyNEInstantiated component, add redux actions to simulate / instantiate --- webapp/components/general/Tooltip.js | 14 ++ webapp/components/index.js | 28 ++- .../instantiation/NetPyNEInstantiated.js | 225 ++---------------- .../instantiation/NetWorkControlButtons.js | 75 ++++++ .../components/instantiation/PlotButtons.js | 193 +++++++++++++++ webapp/components/settings/NetPyNETabs.js | 4 +- webapp/constants.js | 5 + webapp/redux/actions/general.js | 4 +- webapp/redux/middleware/middleware.js | 16 +- webapp/redux/reducers/general.js | 14 +- 10 files changed, 353 insertions(+), 225 deletions(-) create mode 100644 webapp/components/general/Tooltip.js create mode 100644 webapp/components/instantiation/NetWorkControlButtons.js create mode 100644 webapp/components/instantiation/PlotButtons.js 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 index ed30903a..a9a03a44 100644 --- a/webapp/components/index.js +++ b/webapp/components/index.js @@ -9,7 +9,7 @@ import { maximizeWidget } from "../redux/actions/flexlayout"; import { openBackendErrorDialog, closeBackendErrorDialog } from '../redux/actions/errors'; -import { updateCards, editModel, createNetwork, createAndSimulateNetwork, showNetwork, pythonCall } from "../redux/actions/general"; +import { updateCards, editModel, simulateNetwork, createNetwork, createAndSimulateNetwork, showNetwork, pythonCall } from "../redux/actions/general"; import { closeDrawerDialogBox, openDrawerDialogBox } from '../redux/actions/drawer'; import { newWidget } from "../redux/actions/flexlayout"; @@ -187,10 +187,18 @@ export const NetPyNEInclude = connect( import _NetPyNEInstantiated from "./instantiation/NetPyNEInstantiated" export const NetPyNEInstantiated = connect( - null, - dispatch => ({ newWidget: conf => dispatch(newWidget(conf)), }) + 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( @@ -200,6 +208,17 @@ export const ActionDialog = connect( 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) // ---------------------------------------------------------------------------------------- // // DEFAULTS @@ -209,4 +228,5 @@ 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' \ No newline at end of file +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 d891bfb2..0307fbe8 100644 --- a/webapp/components/instantiation/NetPyNEInstantiated.js +++ b/webapp/components/instantiation/NetPyNEInstantiated.js @@ -1,20 +1,11 @@ import React, { createRef } from 'react'; import ReactDOM from 'react-dom' -import Dialog from '@material-ui/core/Dialog'; -import Button from '@material-ui/core/Button'; import Canvas from '@geppettoengine/geppetto-client/js/components/interface/3dCanvas/Canvas'; import ControlPanel from 'geppetto-client/js/components/interface/controlPanel/controlpanel'; 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 { 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', @@ -22,18 +13,9 @@ const styles = { zIndex: '999', height: '100%', width: '100%', - top: 0 - }, - - menuItemDiv: { - fontSize: '12px', - lineHeight: '28px' + overflow: 'hidden' }, - menuItem: { - lineHeight: '28px', - minHeight: '28px' - }, instantiatedContainer: { height: '100%', width: '100%', @@ -43,29 +25,7 @@ const styles = { 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,106 +34,26 @@ export default class NetPyNEInstantiated extends React.Component { this.state = { model: props.model, controlPanelHidden: true, - plotButtonOpen: false, - openDialog: false, bringItToFront: 0, update: 0 }; this.dimensions = { width: 200, height: 200 } - this.canvasRef = createRef(); - - - this.plotFigure = this.plotFigure.bind(this); - this.newPlotWidget = this.newPlotWidget.bind(this); - this.handleClick = this.handleClick.bind(this); - this.handleRequestClose = this.handleRequestClose.bind(this); + this.canvasRef = createRef(); } componentDidUpdate (){ this.resizeIfNeeded() } - - handleCloseDialog = () => { - this.setState({ openDialog: false }); - }; - - 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: 'topPanel' - }) - - 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) { - 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(); - if (Instances.length > 2) { - // update canvas only if there are instances to show - this.canvasRef.current.engine.updateSceneWithNewInstances(window.Instances); - } - this.canvasRef.current.setBackgroundColor('#191919') - + window.addEventListener('resize', this.delayedResize.bind(this)) this.resizeIfNeeded() - + this.updateInstances() GEPPETTO.on(GEPPETTO.Events.Control_panel_close, () => { this.setState({ bringItToFront: 0 }) @@ -186,22 +66,11 @@ export default class NetPyNEInstantiated extends React.Component { window.removeEventListener('resize', this.delayedResize) } - handleClick (event) { - // This prevents ghost click. - event.preventDefault(); - - this.setState({ - plotButtonOpen: true, - anchorEl: event.currentTarget, - }); - } - - handleRequestClose () { - this.setState({ plotButtonOpen: false, }); - } - updateInstances () { - this.canvasRef.current.engine.updateSceneWithNewInstances(window.Instances); + if (Instances.network) { + // update canvas only if there are instances to show + this.canvasRef.current.engine.updateSceneWithNewInstances(window.Instances); + } } resizeCanvas () { @@ -252,80 +121,18 @@ export default class NetPyNEInstantiated extends React.Component { >
+ { - $('#controlpanel').show(); this.setState({ bringItToFront: 1 }) + $('#controlpanel').show() + this.setState({ bringItToFront: 1 }) }} icon={"fa-list"} - id="ControlPanelButton" /> -
- - - {plots.map((plot, index) => ( - this.plotFigure(plot.plotName, plot.plotMethod, plot.plotType)} - > - {plot.primaryText} - - ))} - -
- - - 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} - /> - - - 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} + id="ControlPanelButton" /> - - - {this.state.dialogTitle} - - - {this.state.dialogMessage} - - - - - - + + +
); diff --git a/webapp/components/instantiation/NetWorkControlButtons.js b/webapp/components/instantiation/NetWorkControlButtons.js new file mode 100644 index 00000000..24f83713 --- /dev/null +++ b/webapp/components/instantiation/NetWorkControlButtons.js @@ -0,0 +1,75 @@ +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(3), + 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 ( +
+
+ +
+ 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..2714335c --- /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: 'topPanel' + }) + + 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/settings/NetPyNETabs.js b/webapp/components/settings/NetPyNETabs.js index bf7dd7a6..de432ea1 100644 --- a/webapp/components/settings/NetPyNETabs.js +++ b/webapp/components/settings/NetPyNETabs.js @@ -38,9 +38,7 @@ class NetPyNETabs extends React.Component { editTab: true }; this.rightTabAction = this.props.createNetwork; - this.handleTransitionOptionsChange = this.handleTransitionOptionsChange.bind( - this - ); + this.handleTransitionOptionsChange = this.handleTransitionOptionsChange.bind(this); } componentDidUpdate (prevProps, prevState) { diff --git a/webapp/constants.js b/webapp/constants.js index 377b2f65..4ed15129 100644 --- a/webapp/constants.js +++ b/webapp/constants.js @@ -15,7 +15,12 @@ export const WidgetStatus = { 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', diff --git a/webapp/redux/actions/general.js b/webapp/redux/actions/general.js index 5e2cc8b0..a94274c9 100644 --- a/webapp/redux/actions/general.js +++ b/webapp/redux/actions/general.js @@ -1,9 +1,10 @@ // Action Types export const UPDATE_CARDS = 'UPDATE_CARDS'; export const MODEL_LOADED = 'MODEL_LOADED'; -export const CREATE_NETWORK = 'CREATE_NETWORK'; 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' @@ -14,6 +15,7 @@ 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 }; diff --git a/webapp/redux/middleware/middleware.js b/webapp/redux/middleware/middleware.js index f999ca97..3b685252 100644 --- a/webapp/redux/middleware/middleware.js +++ b/webapp/redux/middleware/middleware.js @@ -1,4 +1,7 @@ -import { UPDATE_CARDS, showNetwork, editModel, CREATE_NETWORK, CREATE_SIMULATE_NETWORK, PYTHON_CALL } from '../actions/general'; +import { + UPDATE_CARDS, CREATE_NETWORK, CREATE_SIMULATE_NETWORK, PYTHON_CALL, SIMULATE_NETWORK, + showNetwork, createNetwork, createAndSimulateNetwork, editModel, +} from '../actions/general'; import { openBackendErrorDialog } from '../actions/errors'; import { closeDrawerDialogBox } from '../actions/drawer'; import Utils from '../../Utils'; @@ -12,12 +15,15 @@ export default store => next => action => { console.log("Triggered card update") next(action); break; - case CREATE_SIMULATE_NETWORK: - simulateNetwork({ parallelSimulation: false }, next, showNetwork) - break; case CREATE_NETWORK: - instantiateNetwork({}, next, showNetwork) + instantiateNetwork({}, next, createNetwork) + break; + case CREATE_SIMULATE_NETWORK: + simulateNetwork({ parallelSimulation: false }, next, action) break; + case SIMULATE_NETWORK: + simulateNetwork({ parallelSimulation: false, usePrevInst: true }, next, action) + break case PYTHON_CALL: pythonCall(next, action) break; diff --git a/webapp/redux/reducers/general.js b/webapp/redux/reducers/general.js index 9a8a741c..1379364f 100644 --- a/webapp/redux/reducers/general.js +++ b/webapp/redux/reducers/general.js @@ -1,11 +1,13 @@ // import action types -import { UPDATE_CARDS, MODEL_LOADED, SHOW_NETWORK, EDIT_MODEL } 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, modelLoaded: false, - editMode: true + editMode: true, + modelState: MODEL_STATE.NOT_INSTANTIATED }; // reducer @@ -24,8 +26,14 @@ function reduceGeneral (state, action) { 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 } + return { editMode: true, modelState: MODEL_STATE.NOT_INSTANTIATED } default: { return { ...state }; } From ae88155c64e0c113060b9c88aebd0966a0f18d24 Mon Sep 17 00:00:00 2001 From: rodriguez-facundo Date: Tue, 24 Mar 2020 15:23:39 -0300 Subject: [PATCH 07/14] #135 Reduce hub memory request --- k8s/cf.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/k8s/cf.yaml b/k8s/cf.yaml index 7bb61876..10747a47 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 From 42529444085aee10d4111a010e83f77e28a9ea29 Mon Sep 17 00:00:00 2001 From: rodriguez-facundo Date: Tue, 24 Mar 2020 15:50:20 -0300 Subject: [PATCH 08/14] #135 Use CF env variable to select deployment cluster --- k8s/cf.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/k8s/cf.yaml b/k8s/cf.yaml index 10747a47..3d5a2e34 100644 --- a/k8s/cf.yaml +++ b/k8s/cf.yaml @@ -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 From 599d101ee49ef9a9ab5fbabce4fdefa382eaff0e Mon Sep 17 00:00:00 2001 From: rodriguez-facundo Date: Sat, 4 Apr 2020 12:43:40 -0300 Subject: [PATCH 09/14] #136 Moving HLS to flexlaout. Styling --- webapp/Main.js | 1 + webapp/Theme.js | 38 +++- webapp/components/NetPyNE.js | 105 +++------ .../definition/cellRules/NetPyNECellRules.js | 9 +- .../sections/NetPyNESectionThumbnail.js | 2 +- .../mechanisms/NetPyNEMechanismThumbnail.js | 7 +- .../configuration/NetPyNESimConfig.js | 6 - .../connectivity/NetPyNEConnectivityRules.js | 5 - .../definition/plots/NetPyNEInclude.js | 2 +- .../definition/plots/NetPyNENewPlot.js | 4 +- .../definition/plots/NetPyNEPlots.js | 5 - .../populations/NetPyNEPopulations.js | 5 - .../NetPyNEStimulationSources.js | 5 - .../NetPyNEStimulationTargets.js | 5 - .../definition/synapses/NetPyNESynapses.js | 5 - webapp/components/general/Checkbox.js | 1 + webapp/components/general/NetPyNEAddNew.js | 7 +- webapp/components/general/NetPyNEHome.js | 8 +- webapp/components/general/NetPyNEThumbnail.js | 2 +- .../components/instantiation/PlotButtons.js | 2 +- webapp/components/layout/LayoutManager.js | 55 +---- webapp/components/layout/TabsetIconFactory.js | 14 ++ webapp/components/layout/WidgetFactory.js | 36 +++ webapp/components/layout/layoutConf.json | 73 ++++++ webapp/css/colors.less | 12 +- webapp/css/flexlayout.less | 213 ++++++++++++++++++ webapp/css/material.less | 14 +- webapp/css/netpyne.less | 16 +- webapp/redux/reducers/flexlayout.js | 112 ++++++++- webapp/webpack.config.js | 1 + 30 files changed, 562 insertions(+), 208 deletions(-) create mode 100644 webapp/components/layout/TabsetIconFactory.js create mode 100644 webapp/components/layout/layoutConf.json create mode 100644 webapp/css/flexlayout.less diff --git a/webapp/Main.js b/webapp/Main.js index 39e6c0dc..06a0a00c 100644 --- a/webapp/Main.js +++ b/webapp/Main.js @@ -25,6 +25,7 @@ jQuery(function () { require('./css/netpyne.less'); require('./css/material.less'); require('./css/traceback.less'); + require('./css/flexlayout.less'); const store = configureStore(); diff --git a/webapp/Theme.js b/webapp/Theme.js index 147605d3..20a5a8ea 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: { @@ -40,6 +45,23 @@ export default createMuiTheme({ boxShadow: 'none !important' }, select: { "&:focus" :{ background: "none" } }, - } + }, + 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/components/NetPyNE.js b/webapp/components/NetPyNE.js index bc6ca20f..a237f70d 100644 --- a/webapp/components/NetPyNE.js +++ b/webapp/components/NetPyNE.js @@ -1,21 +1,40 @@ import React from "react"; import Toolbar from "@material-ui/core/Toolbar"; -import Transition from "./transition/Transition"; + import { - NetPyNESynapses, - NetPyNEConnectivityRules, - NetPyNECellRules, - NetPyNEStimulationSources, - NetPyNEStimulationTargets, - NetPyNESimConfig, NetPyNEToolBar, NetPyNETabs, - NetPyNEPopulations, LayoutManager, - NetPyNEPlots } from "netpyne/components"; -export default class NetPyNE extends React.Component { +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.appBar } +}) +class NetPyNE extends React.Component { constructor (props) { super(props); this.widgets = {}; @@ -50,79 +69,29 @@ export default class NetPyNE extends React.Component { if (!this.props.data) { return
; } else { + const { classes } = this.props if (this.props.editMode) { - var content = ( -
- - - - - - - - -
- ); + var content = ; } else { var content = ; } return ( -
-
- -
+
+
+ +
-
+
- - {/** TODO Reengineer Transition using the middleware to handle simulation and instantiation. The transition should only show content related to what actually we want to do - */} - {content}
); } } } +export default withStyles(styles)(NetPyNE) \ No newline at end of file diff --git a/webapp/components/definition/cellRules/NetPyNECellRules.js b/webapp/components/definition/cellRules/NetPyNECellRules.js index 4d3ec7c3..09a4e91c 100644 --- a/webapp/components/definition/cellRules/NetPyNECellRules.js +++ b/webapp/components/definition/cellRules/NetPyNECellRules.js @@ -463,7 +463,7 @@ export default class NetPyNECellRules extends React.Component { return selectedCellRule } } else { - return + return } case 'sections': @@ -479,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){ @@ -659,11 +659,6 @@ export default class NetPyNECellRules extends React.Component { return ( - {content} {dialogPop} diff --git a/webapp/components/definition/cellRules/sections/NetPyNESectionThumbnail.js b/webapp/components/definition/cellRules/sections/NetPyNESectionThumbnail.js index d8988316..9e2d7c42 100644 --- a/webapp/components/definition/cellRules/sections/NetPyNESectionThumbnail.js +++ b/webapp/components/definition/cellRules/sections/NetPyNESectionThumbnail.js @@ -46,7 +46,7 @@ export default class NetPyNESectionThumbnail extends React.Component { , - - ]; - } 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/css/flexlayout.less b/webapp/css/flexlayout.less index f84a7dc5..7d13cf61 100644 --- a/webapp/css/flexlayout.less +++ b/webapp/css/flexlayout.less @@ -66,8 +66,6 @@ input { width: 100%; - font-style: italic; - font-weight: 200; } #pythonConsoleOutput { padding: 0; @@ -146,6 +144,7 @@ padding: 0; font-weight: normal; color: #b9b9b9; + margin-left: @spacing; } // Thin white tab icons @@ -209,5 +208,4 @@ .node-legend { visibility: visible; } - } - \ No newline at end of file +} \ No newline at end of file diff --git a/webapp/css/material.less b/webapp/css/material.less index f4bb14b0..0f77e23c 100644 --- a/webapp/css/material.less +++ b/webapp/css/material.less @@ -7,7 +7,8 @@ label { input[type="text"], input[type="number"]{ box-shadow: none!important; border-radius:0px!important; - color: white!important + color: white!important; + font-size: inherit; } div.MuiExpansionPanelSummary-root { diff --git a/webapp/redux/reducers/flexlayout.js b/webapp/redux/reducers/flexlayout.js index 9a9dffeb..166c1b07 100644 --- a/webapp/redux/reducers/flexlayout.js +++ b/webapp/redux/reducers/flexlayout.js @@ -17,20 +17,18 @@ function removeUndefined (obj) { export const FLEXLAYOUT_DEFAULT_STATE = { widgets: { - /* - * 'python': { - * id: 'python', - * name: 'Python', - * status: WidgetStatus.ACTIVE, - * icon: 'fa-python', - * component: 'PythonConsole', - * panelName: "consolePanel", - * enableClose: false - * }, - */ - - ...EDIT_MODE_INITIAL_WIDGET_STATE + 'python': { + id: 'python', + name: 'Python', + status: WidgetStatus.ACTIVE, + icon: 'fa fa-code', + component: 'PythonConsole', + panelName: "consolePanel", + enableClose: false, + enableDrag: false, + enableRename: false + }, }, }; @@ -40,7 +38,7 @@ const SIMULATE_MODE_INITIAL_WIDGET_STATE = { id: 'D3Canvas', name: 'Morphology', status: WidgetStatus.ACTIVE, - icon: 'geppetto/build/popParams.png', + icon: 'fa fa-dot-circle-o', component: 'D3Canvas', panelName: "rightPanel", enableClose: false, @@ -220,5 +218,3 @@ function updateWidgetState (widgets, { status, panelName }) { function extractPanelName (action) { return action.data.component == "Plot" ? "bottomPanel" : "leftPanel"; } - - From 3fe62dfc241e0366cd582ba8fc59bd15cb9dcc8d Mon Sep 17 00:00:00 2001 From: rodriguez-facundo Date: Sun, 5 Apr 2020 12:42:57 -0300 Subject: [PATCH 11/14] #135 Use multiple flexlayout configurations and switch between them --- .../definition/cellRules/NetPyNECellRules.js | 2 +- webapp/components/general/HTMLViewer.js | 11 ++- .../general/NetPyNEPythonConsole.js | 43 ++++----- webapp/components/index.js | 8 +- .../instantiation/NetPyNEInstantiated.js | 16 +--- .../instantiation/NetWorkControlButtons.js | 17 +++- .../components/instantiation/PlotButtons.js | 2 +- webapp/components/layout/LayoutManager.js | 56 +++++++++--- webapp/components/layout/layoutConf.json | 2 +- .../components/layout/simulateLayoutConf.json | 8 +- webapp/css/flexlayout.less | 4 + webapp/css/netpyne.less | 9 ++ webapp/redux/actions/flexlayout.js | 8 +- webapp/redux/middleware/middleware.js | 11 ++- webapp/redux/reducers/flexlayout.js | 91 +++++++++---------- 15 files changed, 171 insertions(+), 117 deletions(-) diff --git a/webapp/components/definition/cellRules/NetPyNECellRules.js b/webapp/components/definition/cellRules/NetPyNECellRules.js index 09a4e91c..18841442 100644 --- a/webapp/components/definition/cellRules/NetPyNECellRules.js +++ b/webapp/components/definition/cellRules/NetPyNECellRules.js @@ -308,7 +308,7 @@ export default class NetPyNECellRules extends React.Component { this.setState({ selectedMechanism: newMechanismName }); } - if (Object.keys(this.state.value).length === 0) { + if (this.state.value && Object.keys(this.state.value).length === 0) { this.setState({ selectedCellRule: undefined, selectedSection: undefined, diff --git a/webapp/components/general/HTMLViewer.js b/webapp/components/general/HTMLViewer.js index c51c99ef..d17585dc 100644 --- a/webapp/components/general/HTMLViewer.js +++ b/webapp/components/general/HTMLViewer.js @@ -51,10 +51,13 @@ class CustomHTMLViewer extends Component { adjustSVGSize () { const svg = this.getSvgComponent() - svg.removeAttribute('width'); - svg.removeAttribute('height'); - svg.setAttribute('width', `${this.dimensions.width}px`); - svg.setAttribute('height', `${this.dimensions.height}px`); + if (svg) { + svg.removeAttribute('width'); + svg.removeAttribute('height'); + svg.setAttribute('width', `${this.dimensions.width - 20}px`); + svg.setAttribute('height', `${this.dimensions.height - 20}px`); + } + } resizeIfNeeded (){ diff --git a/webapp/components/general/NetPyNEPythonConsole.js b/webapp/components/general/NetPyNEPythonConsole.js index 6cebcecf..f31ad8ce 100644 --- a/webapp/components/general/NetPyNEPythonConsole.js +++ b/webapp/components/general/NetPyNEPythonConsole.js @@ -3,8 +3,6 @@ import Utils from '../../Utils' import PythonConsole from '@geppettoengine/geppetto-client/js/components/interface/pythonConsole/PythonConsole'; -import { EDIT_MODE_INITIAL_WIDGET_STATE } from '../../redux/reducers/flexlayout'; - export default class NetPyNEPythonConsole extends Component { addMetadataToWindow (data) { @@ -12,34 +10,29 @@ export default class NetPyNEPythonConsole extends Component { window.metadata = data.metadata; window.currentFolder = data.currentFolder; window.isDocker = data.isDocker; - + window.pythonConsoleLoaded = true } - addWidgets () { - Object.keys(EDIT_MODE_INITIAL_WIDGET_STATE).forEach(widgetId => { - this.props.newWidget(EDIT_MODE_INITIAL_WIDGET_STATE[widgetId]) - }) + componentWillUnmount () { + console.log("unmounting python console") } - componentDidMount () { - 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.on(GEPPETTO.Events.Model_loaded, () => { - this.addWidgets() - }); - GEPPETTO.trigger("spinner:hide"); - }) - }); + 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/index.js b/webapp/components/index.js index 2bda4b7d..8453ab14 100644 --- a/webapp/components/index.js +++ b/webapp/components/index.js @@ -89,7 +89,7 @@ export const NetPyNECellRule = connect((state, ownProps) => ({ import _LayoutManager from "./layout/LayoutManager"; export const LayoutManager = connect( - state => state.flexlayout, + state => ({ ...state.flexlayout, editMode: state.general.editMode }), dispatch => ({ minimizeWidget: id => dispatch(minimizeWidget(id)), destroyWidget: id => dispatch(destroyWidget(id)), @@ -227,7 +227,11 @@ export const PlotButtons = connect( import _NetPyNEPythonConsole from './general/NetPyNEPythonConsole' export const NetPyNEPythonConsole = connect( null, - dispatch => ({ modelLoaded: () => dispatch(modelLoaded) }) + dispatch => ({ + modelLoaded: () => dispatch(modelLoaded), + newWidget: widget => dispatch(newWidget(widget)), + activateWidget: widgetId => dispatch(activateWidget(widgetId)) + }) )(_NetPyNEPythonConsole) // ---------------------------------------------------------------------------------------- // diff --git a/webapp/components/instantiation/NetPyNEInstantiated.js b/webapp/components/instantiation/NetPyNEInstantiated.js index 0307fbe8..e61afdab 100644 --- a/webapp/components/instantiation/NetPyNEInstantiated.js +++ b/webapp/components/instantiation/NetPyNEInstantiated.js @@ -12,7 +12,7 @@ const styles = { backgroundColor: 'rgba(0, 0, 0, 0.6)', zIndex: '999', height: '100%', - width: '100%', + width: '98%', overflow: 'hidden' }, @@ -20,11 +20,6 @@ const styles = { height: '100%', width: '100%', }, - controlpanelBtn: { - position: 'absolute', - left: 34, - top: 280 - }, }; export default class NetPyNEInstantiated extends React.Component { @@ -122,15 +117,6 @@ export default class NetPyNEInstantiated extends React.Component {
- { - $('#controlpanel').show() - this.setState({ bringItToFront: 1 }) - }} - icon={"fa-list"} - id="ControlPanelButton" - /> -
diff --git a/webapp/components/instantiation/NetWorkControlButtons.js b/webapp/components/instantiation/NetWorkControlButtons.js index 24f83713..413774b0 100644 --- a/webapp/components/instantiation/NetWorkControlButtons.js +++ b/webapp/components/instantiation/NetWorkControlButtons.js @@ -9,7 +9,7 @@ import { PlotButtons } from 'netpyne/components' const styles = ({ spacing }) => ({ container: { position: 'absolute', - top: spacing(3), + top: spacing(2), right: spacing(2) }, innerContainer: { position: 'relative' }, @@ -26,6 +26,21 @@ class NetWorkControlButtons extends React.Component { return (
+ + +
+ $('#controlpanel').show()} + icon={"fa-list"} + id="ControlPanelButton" + /> +
+
+ 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'); @@ -42,19 +42,34 @@ export default class LayoutManager extends Component { 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) { + 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) { @@ -68,8 +83,15 @@ export default class LayoutManager extends Component { } } + getModel () { + if (this.props.editMode) { + return this.cacheModels.edit + } + return this.cacheModels.simulate + } + addWidgets (widgets) { - const { model } = this; + const model = this.getModel() for (let newWidgetDescriptor of widgets) { if (!model.getNodeById(newWidgetDescriptor.id)) { @@ -82,7 +104,7 @@ export default class LayoutManager extends Component { for (let widget of widgets) { if (widget.status == WidgetStatus.ACTIVE) { - this.model.doAction(FlexLayout.Actions.selectTab(widget.id)); + model.doAction(FlexLayout.Actions.selectTab(widget.id)); } } @@ -90,8 +112,9 @@ export default class LayoutManager extends Component { } deleteWidgets (widgets) { + const model = this.getModel() for (let widget of widgets) { - this.model.doAction(FlexLayout.Actions.deleteTab(widget.id)); + model.doAction(FlexLayout.Actions.deleteTab(widget.id)); } } @@ -100,14 +123,14 @@ export default class LayoutManager extends Component { } 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) { - this.model.doAction(FlexLayout.Actions.selectTab(widget.id)); + model.doAction(FlexLayout.Actions.selectTab(widget.id)); } } @@ -115,9 +138,10 @@ export default class LayoutManager extends Component { } updateWidget (widget) { + const model = this.getModel() if (widget) { this.widgetFactory.updateWidget(widget); - this.model.doAction(Actions.updateNodeAttributes(widget.id, widget2Node(widget))); + model.doAction(Actions.updateNodeAttributes(widget.id, widget2Node(widget))); } } @@ -128,6 +152,7 @@ export default class LayoutManager extends Component { } iconFactory (node) { + // TODO move to newest flexlayout-react to add this functionality return this.tabsetIconFactory.factory(node.getConfig()); } @@ -152,6 +177,7 @@ export default class LayoutManager extends Component { onAction (action) { + const model = this.getModel() switch (action.type){ case Actions.SET_ACTIVE_TABSET: break; @@ -173,11 +199,11 @@ export default class LayoutManager extends Component { break; } - this.model.doAction(action); + model.doAction(action); } onActionMaximizeWidget (action) { - const { model } = this; + const model = this.getModel() const { widgets } = this.props; const { maximizeWidget, activateWidget } = this; const panel2maximize = model.getNodeById(action.data.node); @@ -203,7 +229,7 @@ export default class LayoutManager extends Component { } onActionDeleteWidget (action) { - const { model } = this; + const model = this.getModel() const { widgets } = this.props; const maximizedWidget = this.findMaximizedWidget(widgets); // change widget status @@ -226,14 +252,16 @@ export default class LayoutManager extends Component { clickOnBordersAction (node) { - this.model.doAction(FlexLayout.Actions.moveNode(node.getId(), 'bottomPanel', FlexLayout.DockLocation.CENTER, 0)); + const model = this.getModel() + model.doAction(FlexLayout.Actions.moveNode(node.getId(), 'bottomPanel', FlexLayout.DockLocation.CENTER, 0)); } onRenderTabSet (panel, renderValues) { + const model = this.getModel() if (panel.getType() === "tabset") { if (panel.getId() != 'leftPanel' && panel.getChildren().length > 0){ renderValues.buttons.push(
{ - this.model.doAction(FlexLayout.Actions.moveNode(panel.getSelectedNode().getId(), "border_bottom", FlexLayout.DockLocation.CENTER, 0)); + model.doAction(FlexLayout.Actions.moveNode(panel.getSelectedNode().getId(), "border_bottom", FlexLayout.DockLocation.CENTER, 0)); }} />); } } @@ -244,7 +272,7 @@ export default class LayoutManager extends Component {
this.onAction(action)} diff --git a/webapp/components/layout/layoutConf.json b/webapp/components/layout/layoutConf.json index 7e6cb4a9..567939be 100644 --- a/webapp/components/layout/layoutConf.json +++ b/webapp/components/layout/layoutConf.json @@ -34,7 +34,7 @@ { "type": "tabset", "weight": 100, - "id": "rightPanel", + "id": "hlsPanel", "enableDeleteWhenEmpty": false, "children": [ ] diff --git a/webapp/components/layout/simulateLayoutConf.json b/webapp/components/layout/simulateLayoutConf.json index 9d3f7991..24345705 100644 --- a/webapp/components/layout/simulateLayoutConf.json +++ b/webapp/components/layout/simulateLayoutConf.json @@ -30,7 +30,7 @@ { "type": "tabset", "weight": 100, - "id": "middlePanel", + "id": "morphoPanel", "enableDeleteWhenEmpty": false, "children": [] } @@ -42,12 +42,12 @@ "children": [ { "type": "row", - "weight": 80, + "weight": 60, "children": [ { "type": "tabset", "weight": 100, - "id": "rightPanel", + "id": "plotPanel", "enableDeleteWhenEmpty": false, "children": [ ] @@ -56,7 +56,7 @@ }, { "type": "row", - "weight": 20, + "weight": 40, "children": [ { "type": "tabset", diff --git a/webapp/css/flexlayout.less b/webapp/css/flexlayout.less index 7d13cf61..a111e812 100644 --- a/webapp/css/flexlayout.less +++ b/webapp/css/flexlayout.less @@ -134,6 +134,9 @@ 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 { @@ -166,6 +169,7 @@ &:before { content: "\00d7"; } + height: inherit; } .flexlayout__splitter { background-color: inherit; diff --git a/webapp/css/netpyne.less b/webapp/css/netpyne.less index d07fde44..79eb12bd 100644 --- a/webapp/css/netpyne.less +++ b/webapp/css/netpyne.less @@ -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; diff --git a/webapp/redux/actions/flexlayout.js b/webapp/redux/actions/flexlayout.js index 98130769..9cebb1f1 100644 --- a/webapp/redux/actions/flexlayout.js +++ b/webapp/redux/actions/flexlayout.js @@ -7,8 +7,10 @@ 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 }) => ({ + +export const newWidget = ({ path, component, panelName, ...others }) => ({ type: ADD_WIDGET, data: { id: path, @@ -16,7 +18,8 @@ export const newWidget = ({ path, component, panelName }) => ({ component: component, name: path.slice(FILEVARIABLE_LENGTH), status: WidgetStatus.ACTIVE, - panelName: panelName + panelName: panelName, + ...others } }); @@ -56,3 +59,4 @@ export const destroyWidget = id => ({ export const resetLayout = { type: RESET_LAYOUT, }; +export const switchLayout = { type: SWITCH_LAYOUT, }; \ No newline at end of file diff --git a/webapp/redux/middleware/middleware.js b/webapp/redux/middleware/middleware.js index 3b685252..6f866462 100644 --- a/webapp/redux/middleware/middleware.js +++ b/webapp/redux/middleware/middleware.js @@ -1,7 +1,8 @@ import { UPDATE_CARDS, CREATE_NETWORK, CREATE_SIMULATE_NETWORK, PYTHON_CALL, SIMULATE_NETWORK, - showNetwork, createNetwork, createAndSimulateNetwork, editModel, + 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'; @@ -15,6 +16,11 @@ 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) break; @@ -67,7 +73,10 @@ const createSimulateBackendCall = async (cmd, payload, next, action, consoleMess GEPPETTO.Manager.loadModel(response); GEPPETTO.CommandController.log('Instantiation / Simulation completed.'); GEPPETTO.trigger(GEPPETTO.Events.Hide_spinner); + next(action) + next(switchLayout) + } } diff --git a/webapp/redux/reducers/flexlayout.js b/webapp/redux/reducers/flexlayout.js index 166c1b07..3d176b2c 100644 --- a/webapp/redux/reducers/flexlayout.js +++ b/webapp/redux/reducers/flexlayout.js @@ -4,6 +4,7 @@ import { RESET_LAYOUT, DESTROY_WIDGET, ACTIVATE_WIDGET, + SWITCH_LAYOUT } from '../actions/flexlayout'; import { MODEL_LOADED } from '../actions/general'; @@ -14,47 +15,37 @@ function removeUndefined (obj) { return Object.keys(obj).forEach(key => obj[key] === undefined ? delete obj[key] : ''); } -export const FLEXLAYOUT_DEFAULT_STATE = { - widgets: { - - - 'python': { - id: 'python', - name: 'Python', - status: WidgetStatus.ACTIVE, - icon: 'fa fa-code', - component: 'PythonConsole', - panelName: "consolePanel", - enableClose: false, - enableDrag: false, - enableRename: false - }, - }, - +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 }; -const SIMULATE_MODE_INITIAL_WIDGET_STATE = { - 'D3Canvas': { - id: 'D3Canvas', - name: 'Morphology', - status: WidgetStatus.ACTIVE, - icon: 'fa fa-dot-circle-o', - component: 'D3Canvas', - panelName: "rightPanel", - enableClose: false, - enableDrag: false, - enableRename: false - }, +const MORPHOLOGY_WIDGET = { + id: 'D3Canvas', + name: 'Morphology', + status: WidgetStatus.ACTIVE, + icon: 'fa fa-dot-circle-o', + component: 'D3Canvas', + panelName: "morphoPanel", + enableClose: false, + enableDrag: false, + enableRename: false } - -const EDIT_MODE_INITIAL_WIDGET_STATE = { +const HLS_WIDGETS = { 'popParams': { id: 'popParams', name: 'popParams', status: WidgetStatus.ACTIVE, icon: 'fa fa-dot-circle-o', component: 'popParams', - panelName: "rightPanel", + panelName: "hlsPanel", enableClose: false, enableDrag: false, enableRename: false @@ -65,7 +56,7 @@ const EDIT_MODE_INITIAL_WIDGET_STATE = { status: WidgetStatus.HIDDEN, icon: 'fa fa-dot-circle-o', component: 'cellParams', - panelName: "rightPanel", + panelName: "hlsPanel", enableClose: false, enableDrag: false, enableRename: false @@ -76,7 +67,7 @@ const EDIT_MODE_INITIAL_WIDGET_STATE = { status: WidgetStatus.HIDDEN, icon: 'fa fa-dot-circle-o', component: 'synMechParams', - panelName: "rightPanel", + panelName: "hlsPanel", enableClose: false, enableDrag: false, enableRename: false @@ -87,7 +78,7 @@ const EDIT_MODE_INITIAL_WIDGET_STATE = { status: WidgetStatus.HIDDEN, icon: 'fa fa-dot-circle-o', component: 'connParams', - panelName: "rightPanel", + panelName: "hlsPanel", enableClose: false, enableDrag: false, enableRename: false @@ -98,7 +89,7 @@ const EDIT_MODE_INITIAL_WIDGET_STATE = { status: WidgetStatus.HIDDEN, icon: 'fa fa-dot-circle-o', component: 'stimSourceParams', - panelName: "rightPanel", + panelName: "hlsPanel", enableClose: false, enableDrag: false, enableRename: false @@ -109,7 +100,7 @@ const EDIT_MODE_INITIAL_WIDGET_STATE = { status: WidgetStatus.HIDDEN, icon: 'fa fa-dot-circle-o', component: 'stimTargetParams', - panelName: "rightPanel", + panelName: "hlsPanel", enableClose: false, enableDrag: false, enableRename: false @@ -120,7 +111,7 @@ const EDIT_MODE_INITIAL_WIDGET_STATE = { status: WidgetStatus.HIDDEN, icon: 'fa fa-dot-circle-o', component: 'simConfig', - panelName: "rightPanel", + panelName: "hlsPanel", enableClose: false, enableDrag: false, enableRename: false @@ -131,7 +122,7 @@ const EDIT_MODE_INITIAL_WIDGET_STATE = { status: WidgetStatus.HIDDEN, icon: 'fa fa-dot-circle-o', component: 'analysis', - panelName: "rightPanel", + panelName: "hlsPanel", enableClose: false, enableDrag: false, enableRename: false @@ -140,6 +131,14 @@ const EDIT_MODE_INITIAL_WIDGET_STATE = { } +export const FLEXLAYOUT_DEFAULT_STATE = { + widgets: { 'python': PYTHON_CONSOLE_WIDGET }, + widgetsBackground: { + 'python': PYTHON_CONSOLE_WIDGET, + 'D3Canvas': MORPHOLOGY_WIDGET + } +}; + export default (state = FLEXLAYOUT_DEFAULT_STATE, action) => { if (action.data) { @@ -180,14 +179,14 @@ export default (state = FLEXLAYOUT_DEFAULT_STATE, action) => { return FLEXLAYOUT_DEFAULT_STATE; case MODEL_LOADED: - return { - ...state, widgets: { - ...state.widgets, - ...EDIT_MODE_INITIAL_WIDGET_STATE - } - } + return { ...state, widgets: { ...state.widgets, ...HLS_WIDGETS } } + + case SWITCH_LAYOUT: { + const { widgets, widgetsBackground, ...others } = state + return { ...others, widgets: { ...widgetsBackground }, widgetsBackground: { ...widgets } } + } + - default: return state } From 96f60ba5d0e07fa7e6c8d4116526a319195cb336 Mon Sep 17 00:00:00 2001 From: rodriguez-facundo Date: Mon, 6 Apr 2020 11:42:38 -0300 Subject: [PATCH 12/14] #136 Add left drawer --- webapp/Theme.js | 2 +- webapp/components/NetPyNE.js | 8 +- webapp/components/index.js | 12 ++ webapp/components/layout/layoutConf.json | 26 +--- .../components/layout/simulateLayoutConf.json | 26 +--- webapp/components/settings/Drawer.js | 131 ++++++++++++++++++ webapp/redux/reducers/flexlayout.js | 45 ++---- 7 files changed, 168 insertions(+), 82 deletions(-) create mode 100644 webapp/components/settings/Drawer.js diff --git a/webapp/Theme.js b/webapp/Theme.js index e2e8bdda..4167087d 100644 --- a/webapp/Theme.js +++ b/webapp/Theme.js @@ -25,7 +25,7 @@ export default createMuiTheme({ palette: { type: 'dark', primary: { main: primaryColor, }, - secondary: { main: bgLight, } + secondary: { main: bgLight, }, }, overrides: { MuiInputLabel: { formControl: { top: '-6px' } }, diff --git a/webapp/components/NetPyNE.js b/webapp/components/NetPyNE.js index eb778f03..2cf15f50 100644 --- a/webapp/components/NetPyNE.js +++ b/webapp/components/NetPyNE.js @@ -5,6 +5,7 @@ import { NetPyNEToolBar, NetPyNETabs, LayoutManager, + Drawer } from "netpyne/components"; import { withStyles } from '@material-ui/core/styles' @@ -32,7 +33,7 @@ const styles = ({ zIndex, palette, spacing }) => ({ marginRight: spacing(-1) }, drawer: { marginLeft: spacing(-1) }, - content: { position: "relative", zIndex: zIndex.appBar } + content: { position: "relative", zIndex: zIndex.drawer + 1 } }) @@ -66,7 +67,10 @@ class NetPyNE extends React.Component {
- +
+ + +
); } diff --git a/webapp/components/index.js b/webapp/components/index.js index 8453ab14..ff5cd359 100644 --- a/webapp/components/index.js +++ b/webapp/components/index.js @@ -234,6 +234,18 @@ export const NetPyNEPythonConsole = connect( }) )(_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 diff --git a/webapp/components/layout/layoutConf.json b/webapp/components/layout/layoutConf.json index 567939be..01af14bb 100644 --- a/webapp/components/layout/layoutConf.json +++ b/webapp/components/layout/layoutConf.json @@ -12,20 +12,7 @@ "children": [ { "type": "row", - "weight": 10, - "children": [ - { - "type": "tabset", - "weight": 100, - "id": "leftPanel", - "enableDeleteWhenEmpty": false, - "children": [] - } - ] - }, - { - "type": "row", - "weight": 90, + "weight": 100, "children": [ { "type": "row", @@ -59,15 +46,6 @@ ] } ] - }, - "borders": [ - { - "type": "border", - "location": "bottom", - "size": 100, - "children": [], - "barSize": 10 - } - ] + } } \ No newline at end of file diff --git a/webapp/components/layout/simulateLayoutConf.json b/webapp/components/layout/simulateLayoutConf.json index 24345705..aabd1bdf 100644 --- a/webapp/components/layout/simulateLayoutConf.json +++ b/webapp/components/layout/simulateLayoutConf.json @@ -12,20 +12,7 @@ "children": [ { "type": "row", - "weight": 10, - "children": [ - { - "type": "tabset", - "weight": 100, - "id": "leftPanel", - "enableDeleteWhenEmpty": false, - "children": [] - } - ] - }, - { - "type": "row", - "weight": 40, + "weight": 50, "children": [ { "type": "tabset", @@ -72,15 +59,6 @@ ] } ] - }, - "borders": [ - { - "type": "border", - "location": "bottom", - "size": 100, - "children": [], - "barSize": 10 - } - ] + } } \ 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..e14f51fc --- /dev/null +++ b/webapp/components/settings/Drawer.js @@ -0,0 +1,131 @@ +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 createTransition = (entering, transitions) => ( + transitions.create('width', { + easing: transitions.easing.sharp, + duration: entering ? transitions.duration.enteringScreen : transitions.duration.leavingScreen, + }) +) + +const drawerCss = (entering, transitions, palette) => ({ + overflow: 'hidden', + width: props => props.width, + flexShrink: 0, + backgroundColor: props => props.dark ? palette.grey[900] : palette.grey[800], + 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({ dark: !editMode, 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)} + > + {expand ? : } + + + +
+ +
+
+ ) + +} diff --git a/webapp/redux/reducers/flexlayout.js b/webapp/redux/reducers/flexlayout.js index 3d176b2c..a439ba71 100644 --- a/webapp/redux/reducers/flexlayout.js +++ b/webapp/redux/reducers/flexlayout.js @@ -15,7 +15,7 @@ function removeUndefined (obj) { return Object.keys(obj).forEach(key => obj[key] === undefined ? delete obj[key] : ''); } -const PYTHON_CONSOLE_WIDGET = { +export const PYTHON_CONSOLE_WIDGET = { id: 'python', name: 'Python', status: WidgetStatus.ACTIVE, @@ -27,104 +27,87 @@ const PYTHON_CONSOLE_WIDGET = { enableRename: false }; -const MORPHOLOGY_WIDGET = { +export const MORPHOLOGY_WIDGET = { id: 'D3Canvas', name: 'Morphology', status: WidgetStatus.ACTIVE, icon: 'fa fa-dot-circle-o', component: 'D3Canvas', panelName: "morphoPanel", - enableClose: false, - enableDrag: false, enableRename: false } -const HLS_WIDGETS = { + +export const HLS_WIDGETS = { 'popParams': { id: 'popParams', - name: 'popParams', + name: 'Populations', status: WidgetStatus.ACTIVE, icon: 'fa fa-dot-circle-o', component: 'popParams', panelName: "hlsPanel", - enableClose: false, - enableDrag: false, enableRename: false }, 'cellParams': { id: 'cellParams', - name: 'cellParams', + name: 'Cell rules', status: WidgetStatus.HIDDEN, icon: 'fa fa-dot-circle-o', component: 'cellParams', panelName: "hlsPanel", - enableClose: false, - enableDrag: false, enableRename: false }, 'synMechParams': { id: 'synMechParams', - name: 'synMechParams', + name: 'Synapses', status: WidgetStatus.HIDDEN, icon: 'fa fa-dot-circle-o', component: 'synMechParams', panelName: "hlsPanel", - enableClose: false, - enableDrag: false, enableRename: false }, 'connParams': { id: 'connParams', - name: 'connParams', + name: 'Connections', status: WidgetStatus.HIDDEN, icon: 'fa fa-dot-circle-o', component: 'connParams', panelName: "hlsPanel", - enableClose: false, - enableDrag: false, enableRename: false }, 'stimSourceParams': { id: 'stimSourceParams', - name: 'stimSourceParams', + name: 'Stim. sources', status: WidgetStatus.HIDDEN, icon: 'fa fa-dot-circle-o', component: 'stimSourceParams', panelName: "hlsPanel", - enableClose: false, - enableDrag: false, enableRename: false }, 'stimTargetParams': { id: 'stimTargetParams', - name: 'stimTargetParams', + name: 'Stim. targets', status: WidgetStatus.HIDDEN, icon: 'fa fa-dot-circle-o', component: 'stimTargetParams', panelName: "hlsPanel", - enableClose: false, - enableDrag: false, enableRename: false }, 'simConfig': { id: 'simConfig', - name: 'simConfig', + name: 'Settings', status: WidgetStatus.HIDDEN, icon: 'fa fa-dot-circle-o', component: 'simConfig', panelName: "hlsPanel", - enableClose: false, - enableDrag: false, enableRename: false }, 'analysis': { id: 'analysis', - name: 'analysis', + name: 'Anaylysis', status: WidgetStatus.HIDDEN, icon: 'fa fa-dot-circle-o', component: 'analysis', panelName: "hlsPanel", - enableClose: false, - enableDrag: false, enableRename: false } @@ -134,8 +117,8 @@ const HLS_WIDGETS = { export const FLEXLAYOUT_DEFAULT_STATE = { widgets: { 'python': PYTHON_CONSOLE_WIDGET }, widgetsBackground: { + 'D3Canvas': MORPHOLOGY_WIDGET, 'python': PYTHON_CONSOLE_WIDGET, - 'D3Canvas': MORPHOLOGY_WIDGET } }; @@ -179,7 +162,7 @@ export default (state = FLEXLAYOUT_DEFAULT_STATE, action) => { return FLEXLAYOUT_DEFAULT_STATE; case MODEL_LOADED: - return { ...state, widgets: { ...state.widgets, ...HLS_WIDGETS } } + return { ...state, widgets: { ...HLS_WIDGETS, ...state.widgets } } case SWITCH_LAYOUT: { const { widgets, widgetsBackground, ...others } = state From ba5a1db8ba26d2143f03f69b8c66e0e4e6063f64 Mon Sep 17 00:00:00 2001 From: rodriguez-facundo Date: Mon, 6 Apr 2020 16:24:25 -0300 Subject: [PATCH 13/14] #136 Css and styling --- webapp/components/layout/LayoutManager.js | 63 ++++++++++++++--------- webapp/components/settings/Drawer.js | 35 +++++++++---- webapp/css/flexlayout.less | 11 +++- 3 files changed, 73 insertions(+), 36 deletions(-) diff --git a/webapp/components/layout/LayoutManager.js b/webapp/components/layout/LayoutManager.js index 4832d48b..5f7ae021 100644 --- a/webapp/components/layout/LayoutManager.js +++ b/webapp/components/layout/LayoutManager.js @@ -9,6 +9,10 @@ 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 @@ -29,7 +33,20 @@ function widget2Node (configuration) { }; } -export default class LayoutManager extends Component { +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); @@ -256,31 +273,31 @@ export default class LayoutManager extends Component { model.doAction(FlexLayout.Actions.moveNode(node.getId(), 'bottomPanel', FlexLayout.DockLocation.CENTER, 0)); } - onRenderTabSet (panel, renderValues) { - const model = this.getModel() - if (panel.getType() === "tabset") { - if (panel.getId() != 'leftPanel' && panel.getChildren().length > 0){ - renderValues.buttons.push(
{ - model.doAction(FlexLayout.Actions.moveNode(panel.getSelectedNode().getId(), "border_bottom", FlexLayout.DockLocation.CENTER, 0)); - }} />); - } - } + onRenderTab (node,renderValues) { + renderValues.leading = } render () { - + const { classes } = this.props return ( -
- this.onAction(action)} - clickOnBordersAction={node => this.clickOnBordersAction(node)} - onRenderTabSet={(node, renderValues) => this.onRenderTabSet(node, renderValues)} - /> - +
+
+
+ this.onAction(action)} + clickOnBordersAction={node => this.clickOnBordersAction(node)} + onRenderTab={(node,renderValues) => this.onRenderTab(node,renderValues)} + /> +
+ +
) } -} \ No newline at end of file +} + + +export default withStyles(styles)(LayoutManager) \ No newline at end of file diff --git a/webapp/components/settings/Drawer.js b/webapp/components/settings/Drawer.js index e14f51fc..419b8c11 100644 --- a/webapp/components/settings/Drawer.js +++ b/webapp/components/settings/Drawer.js @@ -24,18 +24,10 @@ import { const drawerOpenWidth = 200; const drawerCloseWidth = 54; -const createTransition = (entering, transitions) => ( - transitions.create('width', { - easing: transitions.easing.sharp, - duration: entering ? transitions.duration.enteringScreen : transitions.duration.leavingScreen, - }) -) - const drawerCss = (entering, transitions, palette) => ({ overflow: 'hidden', width: props => props.width, flexShrink: 0, - backgroundColor: props => props.dark ? palette.grey[900] : palette.grey[800], borderRight: 'none', transition: transitions.create('width', { easing: transitions.easing.sharp, @@ -47,7 +39,7 @@ const useStyles = makeStyles(({ transitions, palette }) => ({ openDrawer: drawerCss(true, transitions, palette), closeDrawer: drawerCss(false, transitions, palette), - + colapse: { color: 'white', position: 'absolute', @@ -64,7 +56,7 @@ const useStyles = makeStyles(({ transitions, palette }) => ({ export default ({ widgets, newWidget, editMode }) => { const [expand, setExpand] = useState({ dark: !editMode }) - const classes = useStyles({ dark: !editMode, width: expand ? drawerOpenWidth : drawerCloseWidth }); + const classes = useStyles({ width: expand ? drawerOpenWidth : drawerCloseWidth }); function createWidget (widgetId) { if (!widgets[widgetId]) { @@ -116,7 +108,11 @@ export default ({ widgets, newWidget, editMode }) => { setExpand(!expand)} + onClick={() => { + setExpand(!expand) + setTimeout(() => window.dispatchEvent(new Event('resize')), 400) + // pepe() + }} > {expand ? : } @@ -129,3 +125,20 @@ export default ({ widgets, newWidget, editMode }) => { ) } + +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/css/flexlayout.less b/webapp/css/flexlayout.less index a111e812..ed7e8354 100644 --- a/webapp/css/flexlayout.less +++ b/webapp/css/flexlayout.less @@ -34,6 +34,8 @@ @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; @@ -96,7 +98,7 @@ border-top-left-radius: @panelradius; color: @color-white; display: flex; - align-items: center; + align-items: flex-end; margin-right: @tab-spacing; .flexlayout__tab_button_content { color: @secondary_color; @@ -109,7 +111,7 @@ border-top-right-radius: @panelradius; border-top-left-radius: @panelradius; display: flex; - align-items: center; + align-items: flex-end; } .flexlayout__border_bottom { @@ -150,6 +152,11 @@ margin-left: @spacing; } + .flexlayout__tab_button_trailing { + display: flex; + align-items: center; + } + // Thin white tab icons .flexlayout__border_button_trailing, .flexlayout__tab_button_trailing, From 56546801d580ed6cc9eb6b5035ab2908d65427f5 Mon Sep 17 00:00:00 2001 From: rodriguez-facundo Date: Wed, 8 Apr 2020 09:40:00 -0300 Subject: [PATCH 14/14] #136 Styling FileBrowser --- netpyne_ui/netpyne_geppetto.py | 2 + webapp/Main.js | 1 + webapp/components/general/FileBrowser.js | 4 +- webapp/components/general/HTMLViewer.js | 6 +- webapp/components/layout/layoutConf.json | 4 +- .../components/layout/simulateLayoutConf.json | 4 +- webapp/components/settings/NetPyNETabs.js | 13 +- .../components/settings/actions/NewModel.js | 2 +- .../settings/actions/UploadDownloadFiles.js | 7 +- webapp/css/tree.less | 829 ++++++++++++++++++ webapp/redux/middleware/middleware.js | 30 +- 11 files changed, 874 insertions(+), 28 deletions(-) create mode 100644 webapp/css/tree.less 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/webapp/Main.js b/webapp/Main.js index 7198afc5..0b5200e0 100644 --- a/webapp/Main.js +++ b/webapp/Main.js @@ -17,6 +17,7 @@ jQuery(function () { require('./css/material.less'); require('./css/traceback.less'); require('./css/flexlayout.less'); + require('./css/tree.less'); const store = configureStore(); 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 index d17585dc..a0b3fde3 100644 --- a/webapp/components/general/HTMLViewer.js +++ b/webapp/components/general/HTMLViewer.js @@ -52,10 +52,12 @@ class CustomHTMLViewer extends Component { 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', `${this.dimensions.width - 20}px`); - svg.setAttribute('height', `${this.dimensions.height - 20}px`); + svg.setAttribute('width', width); + svg.setAttribute('height', height); } } diff --git a/webapp/components/layout/layoutConf.json b/webapp/components/layout/layoutConf.json index 01af14bb..bfb193ca 100644 --- a/webapp/components/layout/layoutConf.json +++ b/webapp/components/layout/layoutConf.json @@ -16,7 +16,7 @@ "children": [ { "type": "row", - "weight": 80, + "weight": 65, "children": [ { "type": "tabset", @@ -30,7 +30,7 @@ }, { "type": "row", - "weight": 20, + "weight": 35, "children": [ { "type": "tabset", diff --git a/webapp/components/layout/simulateLayoutConf.json b/webapp/components/layout/simulateLayoutConf.json index aabd1bdf..b0f32009 100644 --- a/webapp/components/layout/simulateLayoutConf.json +++ b/webapp/components/layout/simulateLayoutConf.json @@ -29,7 +29,7 @@ "children": [ { "type": "row", - "weight": 60, + "weight": 65, "children": [ { "type": "tabset", @@ -43,7 +43,7 @@ }, { "type": "row", - "weight": 40, + "weight": 35, "children": [ { "type": "tabset", diff --git a/webapp/components/settings/NetPyNETabs.js b/webapp/components/settings/NetPyNETabs.js index de432ea1..a9e7e4b7 100644 --- a/webapp/components/settings/NetPyNETabs.js +++ b/webapp/components/settings/NetPyNETabs.js @@ -48,11 +48,16 @@ class NetPyNETabs extends React.Component { } handleChange = tab => { if (tab == "define") { - this.props.editModel(); - this.setState({ editTab: true }) + if (!this.props.editMode) { + this.props.editModel(); + this.setState({ editTab: true }) + } + } else { - this.rightTabAction(); - this.setState({ editTab: false }) + if (this.props.editMode) { + this.rightTabAction(); + this.setState({ editTab: false }) + } } }; diff --git a/webapp/components/settings/actions/NewModel.js b/webapp/components/settings/actions/NewModel.js index 5e519bc4..c241bb76 100644 --- a/webapp/components/settings/actions/NewModel.js +++ b/webapp/components/settings/actions/NewModel.js @@ -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/UploadDownloadFiles.js b/webapp/components/settings/actions/UploadDownloadFiles.js index 32573853..56d48f04 100644 --- a/webapp/components/settings/actions/UploadDownloadFiles.js +++ b/webapp/components/settings/actions/UploadDownloadFiles.js @@ -14,7 +14,8 @@ import FileBrowser from '../../general/FileBrowser'; import { withStyles } from '@material-ui/core/styles'; -const styles = ({ spacing, typography, zIndex }) => ({ +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 = ( -
+
+
| + * +-----+ + */ + .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/redux/middleware/middleware.js b/webapp/redux/middleware/middleware.js index 6f866462..e050a816 100644 --- a/webapp/redux/middleware/middleware.js +++ b/webapp/redux/middleware/middleware.js @@ -21,14 +21,17 @@ export default store => next => action => { next(switchLayout) break } - case CREATE_NETWORK: - instantiateNetwork({}, next, createNetwork) + case CREATE_NETWORK:{ + instantiateNetwork({}, next, createNetwork, switchLayout, true) break; - case CREATE_SIMULATE_NETWORK: - simulateNetwork({ parallelSimulation: false }, next, action) + } + case CREATE_SIMULATE_NETWORK:{ + simulateNetwork({ parallelSimulation: false }, next, action, true) break; + } + case SIMULATE_NETWORK: - simulateNetwork({ parallelSimulation: false, usePrevInst: true }, next, action) + simulateNetwork({ parallelSimulation: false, usePrevInst: true }, next, action, false) break case PYTHON_CALL: pythonCall(next, action) @@ -40,25 +43,27 @@ export default store => next => action => { } -const instantiateNetwork = async (payload, 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 + GEPPETTO.Resources.INSTANTIATING_MODEL, + shouldSwitchLayout ) } -const simulateNetwork = async (payload, next, action) => { +const simulateNetwork = async (payload, next, action, shouldSwitchLayout) => { createSimulateBackendCall( NETPYNE_COMMANDS.simulateModel, payload, next, action, "The NetPyNE model is getting simulated...", - GEPPETTO.Resources.RUNNING_SIMULATION + GEPPETTO.Resources.RUNNING_SIMULATION, + shouldSwitchLayout ) } -const createSimulateBackendCall = async (cmd, payload, next, action, consoleMessage, spinnerType) => { +const createSimulateBackendCall = async (cmd, payload, next, action, consoleMessage, spinnerType, shouldSwitchLayout) => { GEPPETTO.CommandController.log(consoleMessage); GEPPETTO.trigger(GEPPETTO.Events.Show_spinner, spinnerType); @@ -75,8 +80,9 @@ const createSimulateBackendCall = async (cmd, payload, next, action, consoleMess GEPPETTO.trigger(GEPPETTO.Events.Hide_spinner); next(action) - next(switchLayout) - + if (shouldSwitchLayout) { + next(switchLayout) + } } }