diff --git a/src/components/dialog/edit-widget-control-creator.js b/src/components/dialog/edit-widget-control-creator.js index 22fe5c6..2c503ba 100644 --- a/src/components/dialog/edit-widget-control-creator.js +++ b/src/components/dialog/edit-widget-control-creator.js @@ -83,8 +83,10 @@ export default function createControls(widgetType = null, widget = null, session ); break; case 'Image': + // Restrict to only image file types (MIME) + let imageControlFiles = files == null? [] : files.filter(file => file.type.includes('image')); dialogControls.push( - validateForm(id)} simulation={simulation} handleChange={(e) => handleChange(e)} />, + validateForm(id)} simulation={simulation} handleChange={(e) => handleChange(e)} />, handleChange(e)} /> ); break; @@ -141,6 +143,13 @@ export default function createControls(widgetType = null, widget = null, session handleChange(e)} /> ); break; + case 'Topology': + // Restrict to only xml files (MIME) + let topologyControlFiles = files == null? [] : files.filter( file => file.type.includes('xml')); + dialogControls.push( + validateForm(id)} simulation={simulation} handleChange={(e) => handleChange(e)} /> + ); + break; default: console.log('Non-valid widget type: ' + widgetType); diff --git a/src/components/dialog/edit-widget.js b/src/components/dialog/edit-widget.js index 0528938..daf51fe 100644 --- a/src/components/dialog/edit-widget.js +++ b/src/components/dialog/edit-widget.js @@ -58,7 +58,7 @@ class EditWidgetDialog extends React.Component { // scale width to match aspect const aspectRatio = file.dimensions.width / file.dimensions.height; changeObject.width = this.state.temporal.height * aspectRatio; - + return changeObject; } @@ -90,8 +90,10 @@ class EditWidgetDialog extends React.Component { } else if (e.target.id === 'file') { changeObject[e.target.id] = e.target.value; - // get file and update size - changeObject = this.assignAspectRatio(changeObject, e.target.value); + // get file and update size (if it's an image) + if ('lockAspect' in this.state.temporal && this.state.temporal.lockAspect) { + changeObject = this.assignAspectRatio(changeObject, e.target.value); + } } else if (e.target.type === 'checkbox') { changeObject[e.target.id] = e.target.checked; } else if (e.target.type === 'number') { diff --git a/src/components/widget-topology.js b/src/components/widget-topology.js index 529d958..796d18e 100644 --- a/src/components/widget-topology.js +++ b/src/components/widget-topology.js @@ -21,21 +21,47 @@ import React from 'react'; import {ReactSVGPanZoom} from 'react-svg-pan-zoom'; +import config from '../config'; +import '../styles/simple-spinner.css'; + +// Do not show Pintura's grid const pinturaGridStyle = { display: 'none' } +// Avoid another color in the frontend const pinturaBackingStyle = { fill: 'transparent' } +// Center spinner +const spinnerContainerStyle = { + width: '100%', + height: '100%', + display: 'flex', + justifyContent: 'center', + alignItems: 'center' +} + +// Topology failed message +const msgContainerStyle = Object.assign({ + backgroundColor: '#ececec' +},spinnerContainerStyle) + +const failedMsgStyle = { + fontWeight: 'bold' +} + class WidgetTopology extends React.Component { constructor(props) { super(props); - this.input = null; this.svgElem = null; this.Viewer = null; + + this.state = { + visualizationState: 'initial' + }; } componentDidMount() { @@ -52,55 +78,56 @@ class WidgetTopology extends React.Component { } } - fileReader(e) { - let files = e.target.files; - if (files) { - window.cimxml.clearXmlData() - window.cimsvg.setFileCount(files.length); - for (var i=0, f; i < files.length; i++) { - f=files[i]; - if (!f) { - return; + componentWillReceiveProps(nextProps) { + const file = nextProps.files.find(file => file._id === nextProps.widget.file); + // Ensure model is requested only once or a different was selected + if (this.props.widget.file !== nextProps.widget.file || (this.state.visualizationState === 'initial' && file)) { + this.setState({'visualizationState': 'loading' }); + if (file) { + fetch(new Request('/' + config.publicPathBase + file.path)) + .then( response => { + if (response.status === 200) { + this.setState({'visualizationState': 'ready' }); + window.cimxml.clearXmlData() + window.cimsvg.setFileCount(1); + response.text().then( contents => window.cimsvg.loadFile(contents)); + } else { + throw new Error('Request failed'); } - var reader = new FileReader(); - reader.onload = function(e) { - var contents = e.target.result; - window.cimsvg.loadFile(contents); - }; - reader.readAsText(f); - } + }) + .catch(error => { + this.setState({'visualizationState': 'failed' }); + }); + } } } render() { + var markup = null; - let svgLabelStyles = { - 'fontSize': '10pt', - 'fill': 'white' + switch(this.state.visualizationState) { + case 'loading': + markup =
; break; + case 'failed': + markup =
Topology could not be loaded
; break; + default: + markup = (
+ this.Viewer = Viewer} + style={{outline: "1px solid black"}} + detectAutoPan={false} + width={this.props.widget.width-2} height={this.props.widget.height-2} > + + this.svgElem = c }width={this.props.widget.width} height={this.props.widget.height}> + + + + + + +
); } - - return (
- this.input = c} type="file" multiple="true" onChange={ e => this.fileReader(e)} /> - this.Viewer = Viewer} - style={{outline: "1px solid black"}} - detectAutoPan={false} - width={this.props.widget.width-2} height={this.props.widget.height-2} - onClick={event => console.log('click', event.x, event.y, event.originalEvent)} - onMouseMove={event => console.log('move', event.x, event.y)} > - - this.svgElem = c }width={this.props.widget.width} height={this.props.widget.height}> - - - - - console.log('I (%o) was clicked!') } x="2" y="6" width="70" height="20" /> - Click Me! - - - - -
); + return markup; } } diff --git a/src/containers/widget.js b/src/containers/widget.js index b0e03a4..7ebd0aa 100644 --- a/src/containers/widget.js +++ b/src/containers/widget.js @@ -190,7 +190,7 @@ class Widget extends React.Component { } else if (widget.type === 'HTML') { element = } else if (widget.type === 'Topology') { - element = + element = } const widgetClasses = classNames({ diff --git a/src/styles/simple-spinner.css b/src/styles/simple-spinner.css new file mode 100644 index 0000000..c86368a --- /dev/null +++ b/src/styles/simple-spinner.css @@ -0,0 +1,19 @@ + +/* + Basic spinner animation + taken from: https://www.w3schools.com/howto/howto_css_loader.asp +*/ + +.loader { + border: 16px solid #f3f3f3; + border-top: 16px solid #6ea2b0; + border-radius: 50%; + width: 120px; + height: 120px; + animation: spin 1s linear infinite; +} + +@keyframes spin { + 0% { transform: rotate(0deg); } + 100% { transform: rotate(360deg); } +}