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:
parent
3f1c2ade0a
commit
2b0d69f03a
5 changed files with 105 additions and 48 deletions
|
@ -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);
|
||||
|
|
|
@ -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') {
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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({
|
||||
|
|
19
src/styles/simple-spinner.css
Normal file
19
src/styles/simple-spinner.css
Normal 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); }
|
||||
}
|
Loading…
Add table
Reference in a new issue