1
0
Fork 0
mirror of https://git.rwth-aachen.de/acs/public/villas/web/ synced 2025-03-09 00:00:01 +01:00

Upload CIM model and retrieve it to/from backend and show spinner while loading.

This commit is contained in:
Ricardo Hernandez-Montoya 2018-01-09 17:16:41 +01:00
parent 3f1c2ade0a
commit 2b0d69f03a
5 changed files with 105 additions and 48 deletions

View file

@ -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(
<EditImageWidgetControl key={0} sessionToken={sessionToken} widget={widget} files={files} validate={(id) => validateForm(id)} simulation={simulation} handleChange={(e) => handleChange(e)} />,
<EditImageWidgetControl key={0} sessionToken={sessionToken} widget={widget} files={imageControlFiles} validate={(id) => validateForm(id)} simulation={simulation} handleChange={(e) => handleChange(e)} />,
<EditWidgetAspectControl key={1} widget={widget} handleChange={e => handleChange(e)} />
);
break;
@ -141,6 +143,13 @@ export default function createControls(widgetType = null, widget = null, session
<EditWidgetHTMLContent key={0} widget={widget} placeholder='HTML Code' controlId='content' handleChange={e => handleChange(e)} />
);
break;
case 'Topology':
// Restrict to only xml files (MIME)
let topologyControlFiles = files == null? [] : files.filter( file => file.type.includes('xml'));
dialogControls.push(
<EditImageWidgetControl key={0} sessionToken={sessionToken} widget={widget} files={topologyControlFiles} validate={(id) => validateForm(id)} simulation={simulation} handleChange={(e) => handleChange(e)} />
);
break;
default:
console.log('Non-valid widget type: ' + widgetType);

View file

@ -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') {

View file

@ -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 = <div style={spinnerContainerStyle}><div className="loader" /></div>; break;
case 'failed':
markup = <div style={msgContainerStyle}><div style={failedMsgStyle}>Topology could not be loaded</div></div>; break;
default:
markup = (<div>
<ReactSVGPanZoom
ref={Viewer => this.Viewer = Viewer}
style={{outline: "1px solid black"}}
detectAutoPan={false}
width={this.props.widget.width-2} height={this.props.widget.height-2} >
<svg width={this.props.widget.width} height={this.props.widget.height}>
<svg ref={ c => this.svgElem = c }width={this.props.widget.width} height={this.props.widget.height}>
<rect id="backing" style={pinturaBackingStyle} />
<g id="grid" style={pinturaGridStyle} />
<g id="diagrams"/>
</svg>
</svg>
</ReactSVGPanZoom>
</div>);
}
return (<div>
<input id="fileopen" ref={ c => this.input = c} type="file" multiple="true" onChange={ e => this.fileReader(e)} />
<ReactSVGPanZoom
ref={Viewer => 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)} >
<svg width={this.props.widget.width} height={this.props.widget.height}>
<svg ref={ c => this.svgElem = c }width={this.props.widget.width} height={this.props.widget.height}>
<rect id="backing" style={pinturaBackingStyle} />
<g id="grid" style={pinturaGridStyle} />
<g id="diagrams"/>
<g>
<rect id="testbutton" onClick={ e => console.log('I (%o) was clicked!') } x="2" y="6" width="70" height="20" />
<text className="svglabel-high" style={svgLabelStyles} x="8" y="20">Click Me!</text>
</g>
</svg>
</svg>
</ReactSVGPanZoom>
</div>);
return markup;
}
}

View file

@ -190,7 +190,7 @@ class Widget extends React.Component {
} else if (widget.type === 'HTML') {
element = <WidgetHTML widget={widget} editing={this.props.editing} />
} else if (widget.type === 'Topology') {
element = <WidgetTopology widget={widget} editing={this.props.editing} />
element = <WidgetTopology widget={widget} files={this.state.files} />
}
const widgetClasses = classNames({

View file

@ -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); }
}