diff --git a/front-end/package-lock.json b/front-end/package-lock.json index a5eccc8..4288ba8 100644 --- a/front-end/package-lock.json +++ b/front-end/package-lock.json @@ -11563,6 +11563,14 @@ } } }, + "react-spinners-css": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/react-spinners-css/-/react-spinners-css-1.1.7.tgz", + "integrity": "sha512-wcnFtGXCgFbIDsPr9CDoDU+F01uojf+ot/jJi4DUfHJ9S/zVRE7lIfSbsSHtIeDltclr3Dq9rHnAUdYaOt4SIg==", + "requires": { + "prop-types": "^15.7.2" + } + }, "react-test-renderer": { "version": "16.13.1", "resolved": "https://registry.npmjs.org/react-test-renderer/-/react-test-renderer-16.13.1.tgz", diff --git a/front-end/package.json b/front-end/package.json index 7b0b6ce..c5a94d7 100644 --- a/front-end/package.json +++ b/front-end/package.json @@ -22,6 +22,7 @@ "react-bootstrap": "^1.0.0-beta.17", "react-dom": "^16.13.0", "react-scripts": "3.4.1", + "react-spinners-css": "^1.1.7", "react-window": "^1.8.5", "regenerator-runtime": "^0.13.5", "resize-observer-polyfill": "^1.5.1" diff --git a/front-end/src/API.js b/front-end/src/API.js index ef08f75..94790cd 100644 --- a/front-end/src/API.js +++ b/front-end/src/API.js @@ -144,9 +144,18 @@ export async function uploadWorkflow(formData) { async function handleEdge(link, method) { const sourceId = link.getSourcePort().getNode().options.id; const targetId = link.getTargetPort().getNode().options.id; - return fetchWrapper( - `/node/edge/${sourceId}/${targetId}`, - {method: method}); + + let endpoint; + + if (link.getSourcePort().options.in) { + // If edge goes from IN port -> OUT port, reverse the ports + endpoint = `/node/edge/${targetId}/${sourceId}`; + } else { + // Otherwise, keep source -> target edge + endpoint = `/node/edge/${sourceId}/${targetId}`; + } + + return fetchWrapper(endpoint, {method: method}); } diff --git a/front-end/src/components/CustomNode/CustomNodeWidget.js b/front-end/src/components/CustomNode/CustomNodeWidget.js index f042588..3cf8cbc 100644 --- a/front-end/src/components/CustomNode/CustomNodeWidget.js +++ b/front-end/src/components/CustomNode/CustomNodeWidget.js @@ -59,19 +59,29 @@ export default class CustomNodeWidget extends React.Component { ); } + + let graphView; + let width = 40; + if (this.props.node.options.node_type !== "flow_control") { + graphView = ( +
+ Tabular +
+ ); + width = 80; + } + return (
{this.props.node.options.name}
-
+
{String.fromCharCode(this.icon)}
-
- Tabular -
+ {graphView} { return 10 * this.state.widths[index]; - } + }; rowHeights = () => new Array(765) .fill(true) @@ -32,109 +33,127 @@ export default class GraphView extends React.Component { this.props.toggleShow(); }; - computeWidths = (columnCount, rowCount, json) => { - const widths = new Array(columnCount); - const keys = Object.keys(json); - for (let index = 0; index < columnCount; index++) { - const key = keys[index]; - widths[index] = key.length; - for (let rowIndex = 0; rowIndex < rowCount; rowIndex++) { - const value = json[key][rowIndex.toString()]; - if (value != null && value.length > widths[index]) { - widths[index] = value.length; - } + /** + * Compute width of grid columns. + * + * Width is based on the maximum-length cell contained within the JSON data. + * + * @param {Object} columns The column information from the data + * @param {int} rowCount Number of rows in the data + * @param {Object} data The raw data from Node execution + * @returns {any[]} + */ + computeWidths = (columns, rowCount, data) => { + const columnCount = columns.length; + const widths = new Array(columnCount); + let maxWidth = this.state.maxWidth; + + for (let index = 0; index < columnCount; index++) { + const column = columns[index]; + widths[index] = column.length; + + for (let rowIndex = 0; rowIndex < rowCount; rowIndex++) { + const row = data[column][rowIndex.toString()]; + + if (row != null && row.length > widths[index]) { + widths[index] = row.length; + } + + maxWidth += widths[index] * 10; + } } - } - return widths; - } + this.setState({maxWidth: maxWidth}); + return widths; + }; load = async () => { - this.setState({loading: true}) - API.retrieveData(this.key_id) + this.setState({loading: true}); + + API.retrieveData(this.key_id) .then(json => { - const keys = Object.keys(json); - const columnCount = keys.length; - const rows = Object.keys(json[keys[0]]); - const rowCount = rows.length; - const widths = this.computeWidths(columnCount, rowCount, json); - this.setState({ data: json, - keys: keys, - columnCount: columnCount, - rowCount: rowCount, - loading: false, - widths: widths}); - }).catch(err => console.log(err)); - } + const columns = Object.keys(json); + const rows = Object.keys(json[columns[0]]); + const widths = this.computeWidths(columns, rows.length, json); + + this.setState({ + data: json, + columns: columns, + rows: rows, + loading: false, + widths: widths + }); + }) + .catch(err => console.error(err)); + }; Cell = ({ columnIndex, rowIndex, style }) => { - const className = columnIndex % 2 - ? rowIndex % 2 === 0 - ? 'GridItemOdd' - : 'GridItemEven' - : rowIndex % 2 - ? 'GridItemOdd' - : 'GridItemEven'; - - if (rowIndex === 0) { - return ( -
- {this.state.keys[columnIndex]} -
- ); - } + const className = (rowIndex % 2 === 0) ? 'GridItemEven' : 'GridItemOdd'; + const column = this.state.columns[columnIndex]; return (
- {this.state.data[this.state.keys[columnIndex]][rowIndex.toString()] } + {(rowIndex === 0) ? column : this.state.data[column][rowIndex.toString()]}
); - } + }; render() { + let body; + let footer; + if (this.state.loading) { - return (
Loading data...
); + // Print loading spinner + body = (); + } else if (this.state.data.length < 1) { + // Print instructions about loading + body = "Loading the data might take a while depending on how big the data is."; + footer = ( + + + + + ); + } else { + // Display the grid + let displayHeight = this.state.rows.length * 20; + let displayWidth = this.state.maxWidth; + + body = ( + this.columnWidths(index)} + height={displayHeight < 600 ? displayHeight + 5 : 600} + rowCount={this.state.rows.length} + rowHeight={index => 20} + width={displayWidth < 900 ? displayWidth : 900} + > + {this.Cell} + + ); } - if (this.state.columnCount < 1) { - return ( - e.stopPropagation()}> - - {this.props.node.options.name} View + return ( + e.stopPropagation()} + > + + {this.props.node.options.name} View - Loading the data might take a while depending on how big the data is. + {body} - - - - - - ); - } - - return ( - e.stopPropagation()}> - - {this.props.node.options.name} View - - - this.columnWidths(index)} - height={150} - rowCount={this.state.rowCount} - rowHeight={index => 20} - width={480} - > - {this.Cell} - - + {footer} ); } @@ -145,4 +164,4 @@ GraphView.propTypes = { show: propTypes.bool, toggleShow: propTypes.func, onClose: propTypes.func, -} +}; diff --git a/front-end/src/styles/GraphView.css b/front-end/src/styles/GraphView.css index 8c4e7a5..4648200 100644 --- a/front-end/src/styles/GraphView.css +++ b/front-end/src/styles/GraphView.css @@ -1,14 +1,21 @@ .Grid { border: 1px solid #d9dddd; width: auto; + margin: auto; } .GridItemEven, .GridItemOdd { display: flex; align-items: center; justify-content: center; + border-right: 1px solid grey; + border-bottom: 1px solid grey; } .GridItemEven { background-color: #f8f8f0; } + +.GraphView { + max-width: 925px; +} \ No newline at end of file