From 07946fc851ec489b9e371bfd100089747ece2260 Mon Sep 17 00:00:00 2001 From: l_samenv Date: Fri, 23 Aug 2024 15:29:14 +0200 Subject: [PATCH] Added export popup (only UI) --- client/SEAWebClient.html | 4 + .../components/action_entry/action_entry.css | 21 ++ .../components/action_entry/action_entry.js | 28 +++ .../components/export_popup/export_popup.css | 94 +++++++++ .../components/export_popup/export_popup.js | 189 ++++++++++++++++++ client/jsFiles/SEAWebClientGraphics.js | 22 ++ seaweb.py | 2 + 7 files changed, 360 insertions(+) create mode 100644 client/components/action_entry/action_entry.css create mode 100644 client/components/action_entry/action_entry.js create mode 100644 client/components/export_popup/export_popup.css create mode 100644 client/components/export_popup/export_popup.js diff --git a/client/SEAWebClient.html b/client/SEAWebClient.html index c809baf..5d23faf 100644 --- a/client/SEAWebClient.html +++ b/client/SEAWebClient.html @@ -12,6 +12,10 @@ SEAWebClient + + + + diff --git a/client/components/action_entry/action_entry.css b/client/components/action_entry/action_entry.css new file mode 100644 index 0000000..2b0b060 --- /dev/null +++ b/client/components/action_entry/action_entry.css @@ -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; +} \ No newline at end of file diff --git a/client/components/action_entry/action_entry.js b/client/components/action_entry/action_entry.js new file mode 100644 index 0000000..7b79049 --- /dev/null +++ b/client/components/action_entry/action_entry.js @@ -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 = ` + +
+ ${this.title} + +
+ `; + } +} + +customElements.define("sea-action-entry", ActionEntry); \ No newline at end of file diff --git a/client/components/export_popup/export_popup.css b/client/components/export_popup/export_popup.css new file mode 100644 index 0000000..933b818 --- /dev/null +++ b/client/components/export_popup/export_popup.css @@ -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; +} \ No newline at end of file diff --git a/client/components/export_popup/export_popup.js b/client/components/export_popup/export_popup.js new file mode 100644 index 0000000..f4efa82 --- /dev/null +++ b/client/components/export_popup/export_popup.js @@ -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 = ` + +
+
+ +
+ Export + +
+ +
+
+
+ Start : + + +
+ +
+ End : + + +
+ +
+ + Binning (s) : + +
+
+ +
+ +
+ +
+ + Curves to export +
+ + Select all curves +
+
+ +
+ +
+ +
+ +
+
+ `; + } +} + +customElements.define("sea-export-popup", ExportPopup); \ No newline at end of file diff --git a/client/jsFiles/SEAWebClientGraphics.js b/client/jsFiles/SEAWebClientGraphics.js index 420cc4c..e954bbe 100644 --- a/client/jsFiles/SEAWebClientGraphics.js +++ b/client/jsFiles/SEAWebClientGraphics.js @@ -261,7 +261,10 @@ let globalIndicators = (function (){ function loadGraphicsMenu(panel){ 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."); + menuGraphicsPopup.addEntry(exportActionEntry) + menuGraphicsPopup.addHorizontalDivider(); menuGraphicsPopup.addEntry(removeCursorHelpEntry); 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"; } +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 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=[]; @@ -891,6 +906,7 @@ let graphs = (function (){ currentSwiper.slideNext(); }); + loadExportPopup(); globalIndicators.loadIndicators(graphicsPanel); globalControls.loadControls(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 * This is needed for mobile phones, as this callback is triggered too often @@ -1188,6 +1208,8 @@ let graphs = (function (){ jumpToDate: jumpToDate, initGraphs: initGraphs, onZoomCompleteCallback:onZoomCompleteCallback, + + displayExportPopup:displayExportPopup } })(); diff --git a/seaweb.py b/seaweb.py index ee95048..ae44b17 100755 --- a/seaweb.py +++ b/seaweb.py @@ -151,6 +151,8 @@ def subdir_test_file(file): resp = flask.send_file("client/test/"+file, mimetype=guess_mimetype(file)) return resp +@app.route('/components/action_entry/') +@app.route('/components/export_popup/') @app.route('/components/dates_popup/') @app.route('/components/menu_popup/') @app.route('/components/help_popup/')