Added export popup (only UI)
This commit is contained in:
@ -12,6 +12,10 @@
|
|||||||
<title>SEAWebClient</title>
|
<title>SEAWebClient</title>
|
||||||
|
|
||||||
<!-- Components -->
|
<!-- Components -->
|
||||||
|
|
||||||
|
<script src="components/export_popup/export_popup.js"></script>
|
||||||
|
<script src="components/action_entry/action_entry.js"></script>
|
||||||
|
|
||||||
<script src="components/dates_popup/dates_popup.js"></script>
|
<script src="components/dates_popup/dates_popup.js"></script>
|
||||||
<script src="components/divider/horizontal_divider.js"></script>
|
<script src="components/divider/horizontal_divider.js"></script>
|
||||||
<script src="components/help_entry/help_entry.js"></script>
|
<script src="components/help_entry/help_entry.js"></script>
|
||||||
|
21
client/components/action_entry/action_entry.css
Normal file
21
client/components/action_entry/action_entry.css
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
.action-entry{
|
||||||
|
height: 28px;
|
||||||
|
display: flex;
|
||||||
|
margin-left: 10px;
|
||||||
|
margin-right: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.action-entry-title{
|
||||||
|
text-align: center;
|
||||||
|
vertical-align: middle;
|
||||||
|
line-height: 28px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.action-entry-arrow{
|
||||||
|
width: 18px;
|
||||||
|
height: 18px;
|
||||||
|
margin-left: auto;
|
||||||
|
margin-top: auto;
|
||||||
|
margin-bottom: auto;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
28
client/components/action_entry/action_entry.js
Normal file
28
client/components/action_entry/action_entry.js
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
class ActionEntry extends HTMLElement{
|
||||||
|
constructor(title, actionCallback){
|
||||||
|
super();
|
||||||
|
this.title = title;
|
||||||
|
this.actionCallback = actionCallback;
|
||||||
|
}
|
||||||
|
|
||||||
|
performActionCallback(){
|
||||||
|
this.actionCallback()
|
||||||
|
}
|
||||||
|
|
||||||
|
connectedCallback(){
|
||||||
|
this.render();
|
||||||
|
this.getElementsByTagName("img")[0].onclick = () => {this.performActionCallback();};
|
||||||
|
}
|
||||||
|
|
||||||
|
render(){
|
||||||
|
this.innerHTML = `
|
||||||
|
<link rel="stylesheet" href="components/action_entry/action_entry.css">
|
||||||
|
<div class="action-entry">
|
||||||
|
<span class="action-entry-title">${this.title}</span>
|
||||||
|
<img src="res/arrow.png" class="action-entry-arrow">
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
customElements.define("sea-action-entry", ActionEntry);
|
94
client/components/export_popup/export_popup.css
Normal file
94
client/components/export_popup/export_popup.css
Normal file
@ -0,0 +1,94 @@
|
|||||||
|
#export-popup{
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
z-index: 53;
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
box-sizing: border-box;
|
||||||
|
background-color: rgba(0, 0, 0, 0.2);
|
||||||
|
margin-top: 30px;
|
||||||
|
padding-bottom: 60px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#export-popup-container{
|
||||||
|
width: 380px;
|
||||||
|
height: 100%;
|
||||||
|
border: 2px solid black;
|
||||||
|
margin: auto;
|
||||||
|
background-color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
#export-popup-header{
|
||||||
|
height: 40px;
|
||||||
|
display: flex;
|
||||||
|
border-bottom: 2px solid black;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
#export-popup-header span{
|
||||||
|
margin-top: auto;
|
||||||
|
margin-bottom: auto;
|
||||||
|
margin-left: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#export-popup-header img{
|
||||||
|
width: 15px;
|
||||||
|
height: 15px;
|
||||||
|
margin-left: auto;
|
||||||
|
margin-right: 10px;
|
||||||
|
margin-top: auto;
|
||||||
|
margin-bottom: auto;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
#export-popup-content{
|
||||||
|
padding-left: 10px;
|
||||||
|
padding-right: 10px;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
height: calc(100% - 50px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.export-section-container{
|
||||||
|
padding: 10px;
|
||||||
|
border-radius: 10px;
|
||||||
|
border: 2px solid gray;
|
||||||
|
box-sizing: border-box;
|
||||||
|
row-gap: 5px;
|
||||||
|
margin-top: 10px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
#texport-times-section{
|
||||||
|
height: 100px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.time-label{
|
||||||
|
float: left; /* Used to be able to set a width*/
|
||||||
|
width: 45px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#export-curves-section{
|
||||||
|
height: calc(100% - 150px);
|
||||||
|
}
|
||||||
|
|
||||||
|
#export-button-container{
|
||||||
|
height: 20px;
|
||||||
|
width: 100%;
|
||||||
|
display: flex;
|
||||||
|
justify-content:center;
|
||||||
|
align-items: center;
|
||||||
|
margin-top: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.export-button{
|
||||||
|
margin-top: auto;
|
||||||
|
margin-bottom: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.export-curves-container{
|
||||||
|
padding: 5px;
|
||||||
|
border: 2px solid gray;
|
||||||
|
box-sizing: border-box;
|
||||||
|
overflow-y: auto;
|
||||||
|
}
|
189
client/components/export_popup/export_popup.js
Normal file
189
client/components/export_popup/export_popup.js
Normal file
@ -0,0 +1,189 @@
|
|||||||
|
class ExportPopup extends HTMLElement{
|
||||||
|
|
||||||
|
constructor(exportCallback){
|
||||||
|
super();
|
||||||
|
this.exportCallback = exportCallback;
|
||||||
|
}
|
||||||
|
|
||||||
|
updateValuesOnOpen(blocks, startDateTimeMs, endDateTimeMs){
|
||||||
|
|
||||||
|
this.setDateTimeInputs(startDateTimeMs, "start");
|
||||||
|
this.setDateTimeInputs(endDateTimeMs, "end");
|
||||||
|
|
||||||
|
let defaultBinningValue = Math.ceil(((endDateTimeMs - startDateTimeMs)/1000)/1000);
|
||||||
|
let binningInput = this.getElementsByClassName("binning-input")[0];
|
||||||
|
binningInput.value = defaultBinningValue;
|
||||||
|
|
||||||
|
this.getElementsByClassName("export-select-all-checkbox")[0].checked = false;
|
||||||
|
|
||||||
|
let exportCurvesContainer = this.getElementsByClassName("export-curves-container")[0]
|
||||||
|
exportCurvesContainer.innerHTML = ""
|
||||||
|
for (let block of blocks){
|
||||||
|
for(let curve of block["curves"]){
|
||||||
|
let exportCurveContainer = document.createElement("div");
|
||||||
|
exportCurveContainer.classList.add("export-curve-container");
|
||||||
|
let exportCheckboxCurve = document.createElement("input");
|
||||||
|
exportCheckboxCurve.type = "checkbox"
|
||||||
|
exportCheckboxCurve.value = curve["name"]
|
||||||
|
let exportCurveLabel = document.createElement("span");
|
||||||
|
exportCurveLabel.innerHTML = curve["label"]
|
||||||
|
exportCurveContainer.appendChild(exportCheckboxCurve);
|
||||||
|
exportCurveContainer.appendChild(exportCurveLabel);
|
||||||
|
exportCurvesContainer.appendChild(exportCurveContainer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
toggleAllCurves(){
|
||||||
|
let exportCurvesContainer = this.getElementsByClassName("export-curves-container")[0];
|
||||||
|
let checked = this.getElementsByClassName("export-select-all-checkbox")[0].checked;
|
||||||
|
for(let node of exportCurvesContainer.childNodes){
|
||||||
|
node.getElementsByTagName("input")[0].checked = checked;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
setDateTimeInputs(timestamp, type){
|
||||||
|
let date = new Date(timestamp)
|
||||||
|
let truncatedDate = new Date(timestamp);
|
||||||
|
truncatedDate.setHours(0,0,0,0);
|
||||||
|
let time = new Date(date - truncatedDate);
|
||||||
|
time.setMilliseconds(0);
|
||||||
|
|
||||||
|
let exportTypeDate = this.getElementsByClassName(`export-${type}-date`)[0];
|
||||||
|
exportTypeDate.valueAsNumber = timestamp;
|
||||||
|
let exportTypeTime = this.getElementsByClassName(`export-${type}-time`)[0];
|
||||||
|
exportTypeTime.valueAsDate = time;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
getDateTimeTimestampMsInput(type){
|
||||||
|
let dateInputValue = this.getElementsByClassName(`export-${type}-date`)[0].valueAsDate;
|
||||||
|
dateInputValue.setHours(0,0,0,0);
|
||||||
|
let timeInputValue = this.getElementsByClassName(`export-${type}-time`)[0].valueAsNumber;
|
||||||
|
|
||||||
|
return dateInputValue.getTime() + timeInputValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
getSelectedVariables(){
|
||||||
|
let selectedVariables = []
|
||||||
|
let exportCurvesContainer = this.getElementsByClassName("export-curves-container")[0];
|
||||||
|
|
||||||
|
for(let node of exportCurvesContainer.childNodes){
|
||||||
|
let input = node.getElementsByTagName("input")[0]
|
||||||
|
if(input.checked){
|
||||||
|
selectedVariables.push(input.value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return selectedVariables;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
doExportCallback(){
|
||||||
|
|
||||||
|
let startDateTimeMs = this.getDateTimeTimestampMsInput("start");
|
||||||
|
let endDateTimeMs = this.getDateTimeTimestampMsInput("end");
|
||||||
|
|
||||||
|
if (endDateTimeMs <= startDateTimeMs){
|
||||||
|
alertify.error("End date is below or equal to start date.");
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
let binningCheckbox = this.getElementsByClassName("binning-checkbox")[0];
|
||||||
|
let binningValue = null;
|
||||||
|
if(binningCheckbox.checked){
|
||||||
|
let binningInput = this.getElementsByClassName("binning-input")[0];
|
||||||
|
binningValue = binningInput.value;
|
||||||
|
if (binningValue <= 0){
|
||||||
|
alertify.error("Binning is negative or 0");
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let selectedVariables = this.getSelectedVariables();
|
||||||
|
|
||||||
|
if(selectedVariables.length == 0){
|
||||||
|
alertify.error("No curves selected");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
this.hide();
|
||||||
|
this.exportCallback(selectedVariables, startDateTimeMs, endDateTimeMs, binningValue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
hide(){
|
||||||
|
this.style.visibility = "hidden";
|
||||||
|
}
|
||||||
|
|
||||||
|
show(variables, startTime, endTime){
|
||||||
|
this.style.visibility = "visible";
|
||||||
|
this.updateValuesOnOpen(variables, startTime, endTime);
|
||||||
|
}
|
||||||
|
|
||||||
|
connectedCallback(){
|
||||||
|
this.render();
|
||||||
|
this.hide();
|
||||||
|
this.getElementsByTagName("img")[0].onclick = () => {this.hide();};
|
||||||
|
this.getElementsByClassName("export-select-all-checkbox")[0].onclick = () => {this.toggleAllCurves();};
|
||||||
|
this.getElementsByClassName("export-button")[0].onclick = () => {this.doExportCallback();};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
render(){
|
||||||
|
this.innerHTML = `
|
||||||
|
<link rel="stylesheet" href="components/export_popup/export_popup.css"/>
|
||||||
|
<div id="export-popup">
|
||||||
|
<div id="export-popup-container">
|
||||||
|
|
||||||
|
<div id="export-popup-header">
|
||||||
|
<span>Export</span>
|
||||||
|
<img src="res/close.png"/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="export-popup-content">
|
||||||
|
<div class="export-section-container" id="export-times-section">
|
||||||
|
<div class="time-container">
|
||||||
|
<span class="time-label">Start : </span>
|
||||||
|
<input type=date class="export-start-date">
|
||||||
|
<input type=time class="export-start-time" step="1">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="time-container">
|
||||||
|
<span class="time-label">End : </span>
|
||||||
|
<input type=date class="export-end-date">
|
||||||
|
<input type=time class="export-end-time" step="1">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="binning-container">
|
||||||
|
<input type=checkbox class="binning-checkbox">
|
||||||
|
<span class="binning-label">Binning (s) : </span>
|
||||||
|
<input type=number class="binning-input" min=1>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="export-button-container">
|
||||||
|
<button class="export-button">Export</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="export-section-container" id="export-curves-section">
|
||||||
|
|
||||||
|
<span>Curves to export</span>
|
||||||
|
<div>
|
||||||
|
<input type=checkbox class="export-select-all-checkbox">
|
||||||
|
<span>Select all curves</span>
|
||||||
|
</div>
|
||||||
|
<div class="export-curves-container">
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
customElements.define("sea-export-popup", ExportPopup);
|
@ -261,7 +261,10 @@ let globalIndicators = (function (){
|
|||||||
function loadGraphicsMenu(panel){
|
function loadGraphicsMenu(panel){
|
||||||
|
|
||||||
let menuGraphicsPopup = new MenuPopup();
|
let menuGraphicsPopup = new MenuPopup();
|
||||||
|
let exportActionEntry = new ActionEntry("Export", graphs.displayExportPopup);
|
||||||
let removeCursorHelpEntry = new HelpEntry("How to remove the cursor", "You can double click/tap on any graph.");
|
let removeCursorHelpEntry = new HelpEntry("How to remove the cursor", "You can double click/tap on any graph.");
|
||||||
|
menuGraphicsPopup.addEntry(exportActionEntry)
|
||||||
|
menuGraphicsPopup.addHorizontalDivider();
|
||||||
menuGraphicsPopup.addEntry(removeCursorHelpEntry);
|
menuGraphicsPopup.addEntry(removeCursorHelpEntry);
|
||||||
|
|
||||||
let graphicsMenuControl = new Control("res/menu_white.png", "res/menu_white.png", "Menu", () => {menuGraphicsPopup.show()});
|
let graphicsMenuControl = new Control("res/menu_white.png", "res/menu_white.png", "Menu", () => {menuGraphicsPopup.show()});
|
||||||
@ -273,6 +276,18 @@ function loadGraphicsMenu(panel){
|
|||||||
graphicsMenuControl.style.marginRight="6px";
|
graphicsMenuControl.style.marginRight="6px";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let exportPopup = undefined;
|
||||||
|
|
||||||
|
function loadExportPopup(){
|
||||||
|
let graphsContainer = document.getElementsByClassName("graphs-container")[0];
|
||||||
|
exportPopup = new ExportPopup(exportCallback);
|
||||||
|
graphsContainer.appendChild(exportPopup);
|
||||||
|
}
|
||||||
|
|
||||||
|
function exportCallback(selectedVariables, startDateTimeMs, endDateTimeMs, binning=null){
|
||||||
|
console.log(selectedVariables, startDateTimeMs, endDateTimeMs, binning);
|
||||||
|
}
|
||||||
|
|
||||||
let graphs = (function (){
|
let graphs = (function (){
|
||||||
let dataset_to_graph_map = {}; // a dictionnary mapping a variable name to a two values array, containing its graph index and its position inside the graph
|
let dataset_to_graph_map = {}; // a dictionnary mapping a variable name to a two values array, containing its graph index and its position inside the graph
|
||||||
let blocks, liveMode=true, top_vars=[], bottom_vars=[];
|
let blocks, liveMode=true, top_vars=[], bottom_vars=[];
|
||||||
@ -891,6 +906,7 @@ let graphs = (function (){
|
|||||||
currentSwiper.slideNext();
|
currentSwiper.slideNext();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
loadExportPopup();
|
||||||
globalIndicators.loadIndicators(graphicsPanel);
|
globalIndicators.loadIndicators(graphicsPanel);
|
||||||
globalControls.loadControls(graphicsPanel);
|
globalControls.loadControls(graphicsPanel);
|
||||||
loadGraphicsMenu(graphicsPanel);
|
loadGraphicsMenu(graphicsPanel);
|
||||||
@ -1145,6 +1161,10 @@ let graphs = (function (){
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function displayExportPopup(){
|
||||||
|
exportPopup.show(blocks,currentMinTime, currentMaxTime);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Make sure that the updatAuto() function called in chartJS' onZoomComplete callback is delayed
|
* Make sure that the updatAuto() function called in chartJS' onZoomComplete callback is delayed
|
||||||
* This is needed for mobile phones, as this callback is triggered too often
|
* This is needed for mobile phones, as this callback is triggered too often
|
||||||
@ -1188,6 +1208,8 @@ let graphs = (function (){
|
|||||||
jumpToDate: jumpToDate,
|
jumpToDate: jumpToDate,
|
||||||
initGraphs: initGraphs,
|
initGraphs: initGraphs,
|
||||||
onZoomCompleteCallback:onZoomCompleteCallback,
|
onZoomCompleteCallback:onZoomCompleteCallback,
|
||||||
|
|
||||||
|
displayExportPopup:displayExportPopup
|
||||||
}
|
}
|
||||||
})();
|
})();
|
||||||
|
|
||||||
|
@ -151,6 +151,8 @@ def subdir_test_file(file):
|
|||||||
resp = flask.send_file("client/test/"+file, mimetype=guess_mimetype(file))
|
resp = flask.send_file("client/test/"+file, mimetype=guess_mimetype(file))
|
||||||
return resp
|
return resp
|
||||||
|
|
||||||
|
@app.route('/components/action_entry/<file>')
|
||||||
|
@app.route('/components/export_popup/<file>')
|
||||||
@app.route('/components/dates_popup/<file>')
|
@app.route('/components/dates_popup/<file>')
|
||||||
@app.route('/components/menu_popup/<file>')
|
@app.route('/components/menu_popup/<file>')
|
||||||
@app.route('/components/help_popup/<file>')
|
@app.route('/components/help_popup/<file>')
|
||||||
|
Reference in New Issue
Block a user