diff --git a/node_modules/.yarn-integrity b/node_modules/.yarn-integrity
new file mode 100644
index 0000000..f498cf6
--- /dev/null
+++ b/node_modules/.yarn-integrity
@@ -0,0 +1,12 @@
+{
+ "systemParams": "linux-x64-93",
+ "modulesFolders": [
+ "node_modules"
+ ],
+ "flags": [],
+ "linkedModules": [],
+ "topLevelPatterns": [],
+ "lockfileEntries": {},
+ "files": [],
+ "artifacts": {}
+}
\ No newline at end of file
diff --git a/webapp/components/AddPlotMenu.js b/webapp/components/AddPlotMenu.js
index f09c0c1..d645f4e 100644
--- a/webapp/components/AddPlotMenu.js
+++ b/webapp/components/AddPlotMenu.js
@@ -33,9 +33,9 @@ export default class AddPlotMenu extends Component {
}
dontGoToSameHostTwice (widget) {
- const { instancePaths } = widget;
+ const { instancePaths } = widget.config;
- return instancePaths && instancePaths.indexOf(this.props.instancePath) == -1;
+ return widget.instancePaths && instancePaths.indexOf(this.props.instancePath) == -1;
}
goOnlyToTimeseriesWidgets (widget) {
diff --git a/webapp/components/ListMenu.js b/webapp/components/ListMenu.js
index 1d68648..8a6c7d7 100644
--- a/webapp/components/ListMenu.js
+++ b/webapp/components/ListMenu.js
@@ -57,6 +57,7 @@ export default class ListMenuComponent extends React.Component {
parameters: [addToPlot, {
hostId: availablePlot.id,
instancePath: this.props.entity.path,
+ component: 'Plot',
type: 'timeseries',
}],
},
diff --git a/webapp/components/NWBPlot.js b/webapp/components/NWBPlot.js
index 8c9bf41..75c9d88 100644
--- a/webapp/components/NWBPlot.js
+++ b/webapp/components/NWBPlot.js
@@ -47,7 +47,7 @@ export default class NWBTimeseriesPlotComponent extends React.Component {
const plots = instancePaths.map(instancePath => ({
x: `${instancePath}.timestamps`,
y: `${instancePath}.data`,
- lineOptions: { color: this.props.modelSettings[instancePath].color },
+ lineOptions: { color: this.props.modelSettings[instancePath]?.color },
}));
return (
diff --git a/webapp/components/configuration/AddToPlotComponent.jsx b/webapp/components/configuration/AddToPlotComponent.jsx
index 3b03e99..c5a06a5 100644
--- a/webapp/components/configuration/AddToPlotComponent.jsx
+++ b/webapp/components/configuration/AddToPlotComponent.jsx
@@ -3,11 +3,11 @@ import React from 'react';
import AddPlotMenuConnect from '../reduxconnect/AddPlotMenuConnect';
const AddToPlotComponent = ({ icon, label, action, tooltip }) => ({ value }) => (
-
- )
+
+)
export default AddToPlotComponent;
\ No newline at end of file
diff --git a/webapp/components/reduxconnect/AddPlotMenuConnect.js b/webapp/components/reduxconnect/AddPlotMenuConnect.js
index 5a431ec..64278a2 100644
--- a/webapp/components/reduxconnect/AddPlotMenuConnect.js
+++ b/webapp/components/reduxconnect/AddPlotMenuConnect.js
@@ -3,7 +3,7 @@ import AddPlotMenu from '../AddPlotMenu';
const mapStateToProps = (state, ownProps) => ({
icon: ownProps.icon,
- widgets: Object.values(state.flexlayout.widgets).filter(w => w.component == 'Plot').map(w => ({ instancePaths: w.instancePaths, id: w.id, name: w.name })),
+ widgets: Object.values(state.flexlayout.widgets).filter(w => w.component == 'Plot').map(w => ({ instancePaths: w.config.instancePaths, id: w.id, name: w.name })),
});
export default connect(mapStateToProps)(AddPlotMenu);
diff --git a/webapp/components/reduxconnect/ListMenuContainer.js b/webapp/components/reduxconnect/ListMenuContainer.js
index b501810..0be0e2b 100644
--- a/webapp/components/reduxconnect/ListMenuContainer.js
+++ b/webapp/components/reduxconnect/ListMenuContainer.js
@@ -5,7 +5,7 @@ import { updateSettings } from '../../redux/actions/nwbfile';
const mapStateToProps = state => ({
modelSettings: state.nwbfile.modelSettings,
- widgets: Object.values(state.widgets).filter(w => w.component == 'Plot').map(w => ({ instancePaths: w.instancePaths, id: w.id, name: w.name })),
+ widgets: Object.values(state.widgets).filter(w => w.component == 'Plot').map(w => ({ instancePaths: w.config.instancePaths, id: w.id, name: w.name })),
});
const mapDispatchToProps = dispatch => ({
diff --git a/webapp/package.json b/webapp/package.json
index 271bcbc..082b6b7 100644
--- a/webapp/package.json
+++ b/webapp/package.json
@@ -19,9 +19,9 @@
"@material-ui/core": "4.12.1",
"@material-ui/icons": "^4.11.2",
"@material-ui/lab": "^4.0.0-alpha.60",
- "@metacell/geppetto-meta-client": "1.0.0-final",
- "@metacell/geppetto-meta-core": "1.0.0-final",
- "@metacell/geppetto-meta-ui": "1.0.0-final",
+ "@metacell/geppetto-meta-client": "1.2.4",
+ "@metacell/geppetto-meta-core": "1.2.4",
+ "@metacell/geppetto-meta-ui": "1.2.4",
"griddle-react": "^1.13.1",
"jszip": "^3.2.1",
"less-vars-to-js": "^1.3.0",
diff --git a/webapp/redux/actions/widgets.js b/webapp/redux/actions/widgets.js
index 948cd0d..69acc6e 100644
--- a/webapp/redux/actions/widgets.js
+++ b/webapp/redux/actions/widgets.js
@@ -4,7 +4,6 @@ import * as LayoutActions from "@metacell/geppetto-meta-client/common/layout/act
export const {
ADD_WIDGET,
- ADD_PLOT_TO_EXISTING_WIDGET,
UPDATE_WIDGET,
SET_LAYOUT,
DESTROY_WIDGET,
@@ -12,6 +11,8 @@ export const {
RESET_LAYOUT
} = LayoutActions.layoutActions;
+export const ADD_PLOT_TO_EXISTING_WIDGET = "ADD_PLOT_TO_EXISTING_WIDGET";
+
export const showPlot = ({ path, title }) => ({
type: ADD_WIDGET,
data: {
@@ -19,7 +20,7 @@ export const showPlot = ({ path, title }) => ({
config: { instancePaths: [path] },
component: "Plot",
- type: "TimeSeries",
+
name: title || path.slice(FILEVARIABLE_LENGTH),
status: WidgetStatus.ACTIVE,
panelName: "bottomPanel"
@@ -31,7 +32,7 @@ export const addToPlot = ({ hostId, instancePath }) => ({
data: {
hostId,
config: { instancePath },
- type: "TimeSeries"
+ component: "Plot"
}
});
@@ -39,9 +40,7 @@ export const plotAll = ({ plots, title }) => ({
type: ADD_WIDGET,
data: {
id: `plot@${plots.join("-")}`,
-
component: "Plot",
- type: "TimeSeries",
name: title,
status: WidgetStatus.ACTIVE,
panelName: "bottomPanel",
@@ -55,7 +54,7 @@ export const showImageSeries = ({ path, showDetail }) => ({
id: `img@${path}`,
component: "ImageSeries",
- type: "ImageSeries",
+
name: path.slice(FILEVARIABLE_LENGTH),
status: WidgetStatus.ACTIVE,
panelName: "bottomPanel",
diff --git a/webapp/redux/middleware/nwbMiddleware.js b/webapp/redux/middleware/nwbMiddleware.js
index 007840e..f14b7b8 100644
--- a/webapp/redux/middleware/nwbMiddleware.js
+++ b/webapp/redux/middleware/nwbMiddleware.js
@@ -32,6 +32,8 @@ import { NOTEBOOK_READY, notebookReady } from "../actions/notebook";
import { WidgetStatus } from "@metacell/geppetto-meta-client/common/layout/model";
import { getNotebookPath } from "../../services/NotebookService";
+import { Layout } from "@metacell/geppetto-meta-ui/flex-layout/src";
+import { call } from "file-loader";
export const DEFAULT_WIDGETS = {
python: {
@@ -87,20 +89,24 @@ export async function resolveImportValue (typePath, callback) {
}
-function handleShowWidget (store, next, action) {
+function handleShowWidget (store, next, action, callback) {
// const instance = Instances.getInstance(path);
- if (action.data.type === "TimeSeries") {
+ if (action.data.component === "Plot") {
// Instances.getInstance(path).getType().wrappedObj.name
- return handlePlotTimeseries(store, next, action);
+ return handlePlotTimeseries(store, next, action, callback);
}
- if (action.data.type === "ImageSeries") {
+ if (action.data.component === "ImageSeries") {
// Instances.getInstance(path).getType().wrappedObj.name
action.data.config.showDetail
&& store.dispatch(updateDetailsWidget(action.data.config.instancePath));
return handleImportTimestamps(store, next, action);
}
if (action.data.id) {
- return next(action);
+ if (callback) {
+ callback();
+ }
+ next(action);
+
}
}
@@ -136,7 +142,7 @@ function fileLoadedLayout () {
return widgets;
}
-async function handlePlotTimeseries (store, next, action) {
+async function handlePlotTimeseries (store, next, action, callback) {
// If a set of actions are passed, loop through them and execute each one independently
async function retrieveImportValue (data, data_path) {
@@ -145,6 +151,7 @@ async function handlePlotTimeseries (store, next, action) {
GEPPETTO.ModelFactory.deleteInstance(data);
Instances.getInstance(data_path);
resolve();
+
});
});
}
@@ -169,14 +176,22 @@ async function handlePlotTimeseries (store, next, action) {
}
if (promises.length) {
store.dispatch(waitData("Loading timeseries data...", action.type));
- Promise.allSettled(promises).then(() => next(action));
+ Promise.allSettled(promises).then(() => {
+ next(action);
+ if (callback){
+ callback();
+ }
+ });
} else {
next(action);
+ if (callback){
+ callback();
+ }
}
}
function handleImportTimestamps (store, next, action) {
- const time_path = `${action.data.instancePath}.timestamps`;
+ const time_path = `${action.data.config.instancePath}.timestamps`;
const timestamps = Instances.getInstance(time_path);
if (timestamps.getValue().resolve == "ImportValue") {
@@ -232,8 +247,21 @@ const nwbMiddleware = store => next => action => {
case UPDATE_WIDGET:
case ADD_WIDGET:
- case ADD_PLOT_TO_EXISTING_WIDGET:
return handleShowWidget(store, next, action);
+
+ case ADD_PLOT_TO_EXISTING_WIDGET: {
+ const widgets = store.getState().widgets;
+ const widget = widgets[action.data.hostId];
+
+ const instancePaths = [...widget.config.instancePaths, action.data.config.instancePath];
+ const newId = 'plot@' + instancePaths.join('-');
+ return handleShowWidget(store, next, LayoutActions.addWidget({
+ ...widget,
+ id: newId,
+ name: widget.name + '+',
+ config: { ...widget.config, instancePaths }
+ }), () => next(LayoutActions.deleteWidget(action.data.hostId)));
+ }
case GeppettoActions.backendActions.MODEL_LOADED:
next(action);
next(nwbFileLoaded());
diff --git a/webapp/redux/reducers/all.js b/webapp/redux/reducers/all.js
index faadc2a..766fd9c 100644
--- a/webapp/redux/reducers/all.js
+++ b/webapp/redux/reducers/all.js
@@ -1,9 +1,8 @@
-import { combineReducers } from 'redux';
-
import general from './general';
import nwbfile from './nwbfile';
import notebook from './notebook';
+
export default {
general,
nwbfile,
diff --git a/webapp/redux/reducers/flexlayout.js b/webapp/redux/reducers/flexlayout.js
deleted file mode 100644
index 4e4c560..0000000
--- a/webapp/redux/reducers/flexlayout.js
+++ /dev/null
@@ -1,182 +0,0 @@
-import {
- ADD_WIDGET,
- UPDATE_WIDGET,
- RESET_LAYOUT,
- DESTROY_WIDGET,
- ACTIVATE_WIDGET,
- ADD_PLOT_TO_EXISTING_WIDGET,
- showList, showAcquisition, showStimulus, showProcessing, showSweeps, showGeneral
-} from '../actions/flexlayout';
-
-import { NWB_FILE_LOADED } from '../actions/nwbfile'
-
-import { WidgetStatus } from '../../constants';
-
-
-function removeUndefined (obj) {
- return Object.keys(obj).forEach(key => obj[key] === undefined ? delete obj[key] : '');
-}
-
-export const FLEXLAYOUT_DEFAULT_STATUS = {
- widgets: {
-
- 'python': {
- id: 'python',
- name: 'Python',
- status: WidgetStatus.MINIMIZED,
- icon: 'fa-python',
- component: 'PythonConsole',
- panelName: "bottomPanel",
- enableClose: false
- },
- 'general': {
- id: 'general',
- name: 'General',
- status: WidgetStatus.ACTIVE,
- panelName: "leftPanel",
- enableClose: false
- },
-
- 'details': {
- id: 'details',
- name: 'Details',
- instancePath: '',
- status: WidgetStatus.HIDDEN,
- component: 'Metadata',
- panelName: "leftPanel",
- enableClose: false,
- showObjectInfo: true
- }
-
-
- },
-
-};
-
-
-export default (state = FLEXLAYOUT_DEFAULT_STATUS, 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: {
- ...updateWidgetStatus(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];
- if (state.widgets['details'].panelName == activatedWidget.panelName) {
- return state;
- }
- const newDetails = activatedWidget.instancePath
- ? {
- ...state.widgets['details'],
- instancePath: state.widgets[action.data.id].instancePath
- } : state.widgets['details']; // We always show the meta data of currently selected widget
- return {
- ...state, widgets: {
- ...updateWidgetStatus(state.widgets, { panelName: state.widgets[action.data.id], status: WidgetStatus.ACTIVE }),
- details: newDetails,
- [action.data.id]: { ...activatedWidget, status: WidgetStatus.ACTIVE }
- }
- }
- }
-
- case RESET_LAYOUT:
- return FLEXLAYOUT_DEFAULT_STATUS;
-
- case ADD_PLOT_TO_EXISTING_WIDGET: {
- const widget = { ...state.widgets[action.data.hostId] };
- const widgets = { ...state.widgets };
- delete widgets[action.data.hostId];
-
- widget.instancePaths.push(action.data.instancePath);
- const newId = 'plot@' + widget.instancePaths.join('-');
- if (widget){
- return {
- widgets: {
- ...updateWidgetStatus(widgets, { panelName: widget.panelName, status: WidgetStatus.ACTIVE }),
- [newId]: {
- ...widget,
- id: newId,
- name: widget.name + '+',
- }
- }
- }
- }
-
-
- return state
- }
-
- case NWB_FILE_LOADED:
- return { widgets: { ...state.widgets, ...fileLoadedLayout() } };
-
- 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 updateWidgetStatus (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";
-}
-
-
-function fileLoadedLayout () {
- const widgets = { [showGeneral.data.id]: showGeneral.data };
-
- if (Instances.getInstance('nwbfile.stimulus') && Instances.getInstance('nwbfile.stimulus').getType().getVariables().length) {
- widgets[showStimulus.data.id] = showStimulus.data;
- }
-
- if (Instances.getInstance('nwbfile.acquisition')) {
- widgets[showAcquisition.data.id] = showAcquisition.data;
- }
-
- if (Instances.getInstance('nwbfile.sweep_table')) {
- widgets[showSweeps.data.id] = showSweeps.data;
- }
-
- if (Instances.getInstance('nwbfile.processing') && Instances.getInstance('nwbfile.processing').getType().getVariables().length) {
- widgets[showProcessing.data.id] = showProcessing.data;
- }
- return widgets;
-}
-
\ No newline at end of file
diff --git a/webapp/redux/reducers/nwbfile.js b/webapp/redux/reducers/nwbfile.js
index f914082..8b0747b 100644
--- a/webapp/redux/reducers/nwbfile.js
+++ b/webapp/redux/reducers/nwbfile.js
@@ -51,9 +51,9 @@ export default (state = {}, action) => {
return { ...state, modelSettings: { ...state.modelSettings, [action.data.path]: { ...action.data } } };
}
case LayoutActions.layoutActions.ADD_WIDGET: {
- if (action.data.instancePaths && action.data.type === 'TimeSeries') {
+ if (action.data.config?.instancePaths && action.data.component === 'Plot') {
const modelSettings = { ...state.modelSettings };
- for (const path of action.data.instancePaths) {
+ for (const path of action.data.config.instancePaths) {
if (!state.modelSettings[path]) {
const color = nextColor();
modelSettings[path] = { color };
@@ -64,7 +64,7 @@ export default (state = {}, action) => {
return state;
}
case LayoutActions.layoutActions.ADD_PLOT_TO_EXISTING_WIDGET: {
- const path = action.data.instancePath;
+ const path = action.data.config.instancePath;
if (!state.modelSettings[path]) {
const modelSettings = { ...state.modelSettings };
const color = nextColor();
diff --git a/webapp/redux/reducers/widgets.js b/webapp/redux/reducers/widgets.js
index cda494a..deb7f27 100644
--- a/webapp/redux/reducers/widgets.js
+++ b/webapp/redux/reducers/widgets.js
@@ -63,8 +63,8 @@ export default (state = {}, action) => {
const widgets = { ...state.widgets };
delete widgets[action.data.hostId];
- widget.instancePaths.push(action.data.instancePath);
- const newId = `plot@${widget.instancePaths.join('-')}`;
+ widget.config.instancePaths.push(action.data.config.instancePath);
+ const newId = `plot@${widget.config.instancePaths.join('-')}`;
if (widget) {
return {
widgets: {
diff --git a/webapp/yarn.lock b/webapp/yarn.lock
index 5b4b600..fc537bf 100644
--- a/webapp/yarn.lock
+++ b/webapp/yarn.lock
@@ -1201,10 +1201,10 @@
prop-types "^15.7.2"
react-is "^16.8.0 || ^17.0.0"
-"@metacell/geppetto-meta-client@^2.0.0-rc0":
- version "2.0.0-rc0"
- resolved "https://registry.yarnpkg.com/@metacell/geppetto-meta-client/-/geppetto-meta-client-2.0.0-rc0.tgz#e18fc527cf1af17b3e37b3e394b6bed73434d878"
- integrity sha512-Fm9YHLW461FqFfmov1tg9R5HG0Tg9R6oRxilUxNnwxvtaFYwiVLyNGfergyJt528IiDiUOUmXo53R5iKE5KMUw==
+"@metacell/geppetto-meta-client@1.2.4":
+ version "1.2.4"
+ resolved "https://registry.yarnpkg.com/@metacell/geppetto-meta-client/-/geppetto-meta-client-1.2.4.tgz#ba79df8849f52879d76245f9ea1b3ea0cabec9f3"
+ integrity sha512-CSwzeo8JwTZKT4TspyC3i6CU3FZp9XSprTPXN5rM7iIG6yOTN3WaEkgYTKuPM7dD8OvAEAQ24dJPK+fPDx3xyQ==
dependencies:
"@material-ui/core" "^4.1.3"
pako "^1.0.3"
@@ -1214,15 +1214,15 @@
redux "^4.1.0"
url-join "^4.0.0"
-"@metacell/geppetto-meta-core@^2.0.0-rc0":
- version "2.0.0-rc0"
- resolved "https://registry.yarnpkg.com/@metacell/geppetto-meta-core/-/geppetto-meta-core-2.0.0-rc0.tgz#823fd8aa612633e9184e58c77cecd07b9849d452"
- integrity sha512-c104DE3CZRyw+TUjqgD1uX/+CY//6Hc1Inj4gMjzuSoZ7SkdcvxYoE+GLg7T9dzd9xLLU11sLCX+S7scMogVzg==
+"@metacell/geppetto-meta-core@1.2.4":
+ version "1.2.4"
+ resolved "https://registry.yarnpkg.com/@metacell/geppetto-meta-core/-/geppetto-meta-core-1.2.4.tgz#3504817e35ef05e95909c39a76bbbe7616a1496e"
+ integrity sha512-2JfJm//3Lool1+X+LVVqGgw8O7YgSL1MCbRAsOzOYNdmbD1LUVDL2MdoMs0fdW5IilazxOAoEulWxyjBB5B7YQ==
-"@metacell/geppetto-meta-ui@^2.0.0-rc0":
- version "2.0.0-rc0"
- resolved "https://registry.yarnpkg.com/@metacell/geppetto-meta-ui/-/geppetto-meta-ui-2.0.0-rc0.tgz#2a40d6c91f56f3db57952d7f2aeb49763399c9c7"
- integrity sha512-hb55WyCPQnzZ6UWS08Vfj85D6oRp7XRvsFxTTk6DVJh3DBWNaLPt4X56bVZ5GuK1N2cux9wKYskPWCZEySex3g==
+"@metacell/geppetto-meta-ui@1.2.4":
+ version "1.2.4"
+ resolved "https://registry.yarnpkg.com/@metacell/geppetto-meta-ui/-/geppetto-meta-ui-1.2.4.tgz#fa062f37954e618524f710a6bb67029a698d6fdd"
+ integrity sha512-AVVcpflEr7Ad5S4HjHSSPgbT/pDltOjgtgRensNIUo7cqS1cHXdWqHK8g3mY5nBeJBoj/40KSyVtZhwCQIe+Vg==
"@plotly/d3-sankey-circular@0.33.1":
version "0.33.1"
diff --git a/yarn.lock b/yarn.lock
new file mode 100644
index 0000000..fb57ccd
--- /dev/null
+++ b/yarn.lock
@@ -0,0 +1,4 @@
+# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
+# yarn lockfile v1
+
+