diff --git a/package-lock.json b/package-lock.json
index c34bc0a..95bdc66 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -18,6 +18,7 @@
"react-dom": "^17.0.2",
"react-nvd3": "^0.5.7",
"react-scripts": "4.0.3",
+ "reactjs-popup": "^2.0.5",
"web-vitals": "^1.0.1"
}
},
@@ -15714,6 +15715,18 @@
"node": ">=0.10.0"
}
},
+ "node_modules/reactjs-popup": {
+ "version": "2.0.5",
+ "resolved": "https://registry.npmjs.org/reactjs-popup/-/reactjs-popup-2.0.5.tgz",
+ "integrity": "sha512-b5hv9a6aGsHEHXFAgPO5s1Jw1eSkopueyUVxQewGdLgqk2eW0IVXZrPRpHR629YcgIpC2oxtX8OOZ8a7bQJbxA==",
+ "engines": {
+ "node": ">=10"
+ },
+ "peerDependencies": {
+ "react": ">=16",
+ "react-dom": ">=16"
+ }
+ },
"node_modules/read-pkg": {
"version": "5.2.0",
"resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-5.2.0.tgz",
@@ -33056,6 +33069,12 @@
}
}
},
+ "reactjs-popup": {
+ "version": "2.0.5",
+ "resolved": "https://registry.npmjs.org/reactjs-popup/-/reactjs-popup-2.0.5.tgz",
+ "integrity": "sha512-b5hv9a6aGsHEHXFAgPO5s1Jw1eSkopueyUVxQewGdLgqk2eW0IVXZrPRpHR629YcgIpC2oxtX8OOZ8a7bQJbxA==",
+ "requires": {}
+ },
"read-pkg": {
"version": "5.2.0",
"resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-5.2.0.tgz",
diff --git a/package.json b/package.json
index 2336b35..ee76de9 100644
--- a/package.json
+++ b/package.json
@@ -13,6 +13,7 @@
"react-dom": "^17.0.2",
"react-nvd3": "^0.5.7",
"react-scripts": "4.0.3",
+ "reactjs-popup": "^2.0.5",
"web-vitals": "^1.0.1"
},
"scripts": {
diff --git a/src/Simulation/visualizations/GIFGenerator.jsx b/src/Simulation/visualizations/GIFGenerator.jsx
index 4b2c4f0..7bf8dc9 100644
--- a/src/Simulation/visualizations/GIFGenerator.jsx
+++ b/src/Simulation/visualizations/GIFGenerator.jsx
@@ -11,8 +11,13 @@ class GIFGenerator extends React.Component {
this.animationId = 0;
}
+
generateGIF = () => {
//clean input
+ if(!Math.abs(Number(this.state.duration)) > 0) {
+ console.log("Only numbers are allowed in duration!");
+ return;
+ }
this.setState({duration: Math.abs(this.state.duration)});
var gif = new GIF( {
workers: 2,
@@ -20,26 +25,38 @@ class GIFGenerator extends React.Component {
repeat: this.state.loop? 0 : -1,
background: this.state.background
});
+
+ //set download trigger
gif.on('finished', function(blob) {
- window.open(URL.createObjectURL(blob));
+ const link = document.createElement('a');
+ // create a blobURI pointing to our Blob
+ link.href = URL.createObjectURL(blob);
+ link.download = "simulationGIF";
+ // some browser needs the anchor to be in the doc
+ document.body.append(link);
+ link.click();
+ link.remove();
+ // in case the Blob uses a lot of memory
+ setTimeout(() => URL.revokeObjectURL(link.href), 7000);
});
- let stepBehtmlFore = this.state.step;
-
+
+ let stepBefore = this.state.step;
+
//clear playing animation
clearInterval(this.animationId);
this.props.setState({step: 0}, () => {
this.stepTime = 1;
//this.animationId = setInterval(this.visualizeOneStep, this.stepTime);
- this.animationId = setInterval(this.grabImageAndNextStep.bind(null, gif, stepBehtmlFore), this.stepTime);
+ this.animationId = setInterval(this.grabImageAndNextStep.bind(null, gif, stepBefore), this.stepTime);
this.props.setState({playing: true});
});
}
- grabImageAndNextStep = (gif, stepBehtmlFore) => {
+ grabImageAndNextStep = (gif, stepBefore) => {
//check if we are at the end
if (this.props.state.step > this.props.animationLength) {
- this.props.setState({step: stepBehtmlFore});
+ this.props.setState({step: stepBefore});
this.props.visualizeOneStep(false);
clearInterval(this.animationId);
gif.render();
@@ -57,18 +74,28 @@ class GIFGenerator extends React.Component {
render() {
return (
);
}
diff --git a/src/Simulation/visualizations/Graph.jsx b/src/Simulation/visualizations/Graph.jsx
index 58470a2..195dfe2 100644
--- a/src/Simulation/visualizations/Graph.jsx
+++ b/src/Simulation/visualizations/Graph.jsx
@@ -1,5 +1,6 @@
import React from 'react';
import CytoscapeComponent from 'react-cytoscapejs';
+import Popup from 'reactjs-popup';
import '../../css/Graph.css';
import Slider from '../Slider';
import GIFGenerator from './GIFGenerator';
@@ -201,14 +202,23 @@ class Graph extends React.Component {
//we want this to be a "tabbed" approach
return (
-
-
Duration (seconds):
-
+
Generate GIF 📸} modal>
+ {close => (
+
+
+
+
+ )}
+
+
+
Duration (seconds):
+
{ this.cy = cy }} elements={this.props.graphData}/>
-
+
+
);
}
}
diff --git a/src/css/Dropdown.css b/src/css/Dropdown.css
index 7efa745..08338e6 100644
--- a/src/css/Dropdown.css
+++ b/src/css/Dropdown.css
@@ -5,6 +5,7 @@
}
.Dropdown {
+ cursor: pointer;
color: #eeeeee;
font-size: 15pt;
display: block;
diff --git a/src/css/GIFGenerator.css b/src/css/GIFGenerator.css
index 72a2616..04e0c91 100644
--- a/src/css/GIFGenerator.css
+++ b/src/css/GIFGenerator.css
@@ -1,6 +1,69 @@
-#generateGIFPopup {
- position: absolute;
- top: 0;
- left: 0;
- border: solid black;
+.popup-overlay {
+ background: rgba(0, 0, 0, 0.5);
+}
+.popup-content {
+ background-color: #2d4059;
+ /*background-color: #222831;*/
+ padding: 1em;
+ border-radius: 30px;
+ filter: opacity(1);
+ max-width: 85%;
+}
+.modal {
+ font-size: 13pt;
+ font-family: 'Roboto', sans-serif;
+ color: #eeeeee; text-align: left;
+}
+.modal > .close {
+ color: white;
+ cursor: pointer;
+ position: absolute;
+ display: block;
+ padding: 2px 5px;
+ line-height: 20px;
+ right: -5px;
+ top: -5px;
+ font-size: 24px;
+ background: #ff5722;
+ border-radius: 18px;
+ border: 1px solid #ff5722;
+}
+.popupHeader {
+ font-size: 20pt;
+}
+.popupSection {
+ display: block;
+ margin-top: 10px;
+}
+.popupButton {
+ cursor: pointer;
+ margin: auto;
+ font-size: 16pt;
+ display: block;
+ margin-top: 10px;
+ border: none;
+ outline: none;
+ color: #eeeeee;
+ background-color: #0a8e4e;
+ border-radius: 10px;
+ padding: 5px 10px;
+ margin-top: 10px;
+}
+#gifLengthInput {
+ display: inline-block;
+ width: 1.55em;
+ /*transform: translateY(-3px);*/
+ margin-left: 2%;
+ background-color: #222831;
+ color: #ff5722;
+ border-color: #ff5722;
+ border-style: none;
+ border-radius: 3px;
+ font-size: 12pt;
+ outline: solid;
+}
+
+.gifLengthInput:focus {
+ outline-width: 0;
+ outline: solid;
}
diff --git a/src/css/Graph.css b/src/css/Graph.css
index ffd275f..a8926d1 100644
--- a/src/css/Graph.css
+++ b/src/css/Graph.css
@@ -1,4 +1,5 @@
#recalculate {
+ cursor: pointer;
margin-right: 20px;
margin-bottom: 20px;
border: none;
@@ -11,6 +12,7 @@
}
#switchView {
+ cursor: pointer;
margin-right: 20px;
margin-bottom: 20px;
border: none;
@@ -25,7 +27,9 @@
}
#runSimulationButton {
+ cursor: pointer;
margin-right: 20px;
+ margin-bottom: 20px;
border: none;
outline: none;
font-size: 25pt;
@@ -37,6 +41,23 @@
min-width: 295px;
}
+#gifGeneratorButton {
+ cursor: pointer;
+ border: none;
+ outline: none;
+ font-size: 25pt;
+ color: #eeeeee;
+ background-color: #0A8E4E;
+ border-radius: 10px;
+ padding: 5px 10px;
+ text-align: left;
+ min-width: 260px;
+}
+
+#durationWrapper {
+ margin-top: -10px;
+}
+
#durationDescription {
display: inline-block;
margin-right: 20px;
diff --git a/src/css/Model.css b/src/css/Model.css
index fd4feaa..11f4f3f 100644
--- a/src/css/Model.css
+++ b/src/css/Model.css
@@ -3,6 +3,7 @@
}
.ColorPicker {
+ cursor: pointer;
display: inline-block;
margin-left: 150px;
margin-top: 5px;
diff --git a/src/css/Slider.css b/src/css/Slider.css
index 6811c03..d47364a 100644
--- a/src/css/Slider.css
+++ b/src/css/Slider.css
@@ -41,6 +41,7 @@ input[type="range"]::-webkit-slider-thumb {
}
input[type="range"]::-moz-range-thumb {
+ cursor: pointer;
height: 14px;
width: 14px;
background: #ff5722;
diff --git a/yarn.lock b/yarn.lock
index 16ab365..0f89a51 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -9345,7 +9345,7 @@
"strip-ansi" "6.0.0"
"text-table" "0.2.0"
-"react-dom@*", "react-dom@^17.0.2", "react-dom@>=0.14.0", "react-dom@>=15.0.0":
+"react-dom@*", "react-dom@^17.0.2", "react-dom@>=0.14.0", "react-dom@>=15.0.0", "react-dom@>=16":
"integrity" "sha512-s4h96KtLDUQlsENhMn1ar8t2bEa+q/YAtj8pPPdIjPDGBDIVNsrD9aXNWqspUe6AzKCIG0C1HZZLqLV7qpOBGA=="
"resolved" "https://registry.npmjs.org/react-dom/-/react-dom-17.0.2.tgz"
"version" "17.0.2"
@@ -9448,7 +9448,7 @@
optionalDependencies:
"fsevents" "^2.1.3"
-"react@*", "react@^17.0.2", "react@>= 16", "react@>=0.14.0", "react@>=15.0.0", "react@17.0.2":
+"react@*", "react@^17.0.2", "react@>= 16", "react@>=0.14.0", "react@>=15.0.0", "react@>=16", "react@17.0.2":
"integrity" "sha512-gnhPt75i/dq/z3/6q/0asP78D0u592D5L1pd7M8P+dck6Fu/jJeL6iVVK23fptSUZj8Vjf++7wXA8UNclGQcbA=="
"resolved" "https://registry.npmjs.org/react/-/react-17.0.2.tgz"
"version" "17.0.2"
@@ -9456,6 +9456,11 @@
"loose-envify" "^1.1.0"
"object-assign" "^4.1.1"
+"reactjs-popup@^2.0.5":
+ "integrity" "sha512-b5hv9a6aGsHEHXFAgPO5s1Jw1eSkopueyUVxQewGdLgqk2eW0IVXZrPRpHR629YcgIpC2oxtX8OOZ8a7bQJbxA=="
+ "resolved" "https://registry.npmjs.org/reactjs-popup/-/reactjs-popup-2.0.5.tgz"
+ "version" "2.0.5"
+
"read-pkg-up@^7.0.1":
"integrity" "sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg=="
"resolved" "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-7.0.1.tgz"