diff --git a/client/SEAWebClient.html b/client/SEAWebClient.html index 5d23faf..06687bf 100644 --- a/client/SEAWebClient.html +++ b/client/SEAWebClient.html @@ -13,6 +13,9 @@ + + + diff --git a/client/components/curves_settings_popup/color_selector/color_selector.css b/client/components/curves_settings_popup/color_selector/color_selector.css new file mode 100644 index 0000000..d8c25d4 --- /dev/null +++ b/client/components/curves_settings_popup/color_selector/color_selector.css @@ -0,0 +1,3 @@ +.color-selector-select{ + width:57px; +} \ No newline at end of file diff --git a/client/components/curves_settings_popup/color_selector/color_selector.js b/client/components/curves_settings_popup/color_selector/color_selector.js new file mode 100644 index 0000000..bfd0d07 --- /dev/null +++ b/client/components/curves_settings_popup/color_selector/color_selector.js @@ -0,0 +1,49 @@ +class ColorSelector extends HTMLElement{ + constructor(){ + super(); + } + + connectedCallback(){ + this.render(); + } + + setValue(value){ + let options = this.querySelectorAll("select option"); + + for(let option of options){ + if(option.value == value){ + option.selected = true; + } + } + } + + getValue(){ + return this.getElementsByTagName("select")[0].value; + } + + render(){ + this.innerHTML = ` + + + `; + } +} + +customElements.define("sea-color-selector", ColorSelector); \ No newline at end of file diff --git a/client/components/curves_settings_popup/curves_settings_popup.css b/client/components/curves_settings_popup/curves_settings_popup.css new file mode 100644 index 0000000..d5ad891 --- /dev/null +++ b/client/components/curves_settings_popup/curves_settings_popup.css @@ -0,0 +1,119 @@ +#curves-settings-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; +} + +#curves-settings-popup-container{ + width: 380px; + height: 100%; + border: 2px solid black; + margin: auto; + background-color: white; +} + +#curves-settings-popup-header{ + height: 40px; + display: flex; + border-bottom: 2px solid black; + box-sizing: border-box; +} + +#curves-settings-popup-header span{ + margin-top: auto; + margin-bottom: auto; + margin-left: 10px; +} + +#curves-settings-popup-header img{ + width: 15px; + height: 15px; + margin-left: auto; + margin-right: 10px; + margin-top: auto; + margin-bottom: auto; + cursor: pointer; +} + +#curves-settings-popup-content{ + padding: 10px; + height: calc(100% - 80px); +} + +.button-center-wrapper{ + width: 100%; + height: 20px; + padding-top: 5px; + padding-bottom: 5px; + display: flex; + justify-content: center; + align-items: center; +} + +.scrollable-content{ + height: 100%; + overflow-y: auto; +} + +#curves-settings-popup-table{ + width: 335px; +} +#curves-settings-popup-table, +#curves-settings-popup-table tr th, +#curves-settings-popup-table tr td +{ + border-collapse: collapse; + border: 1px solid black; + padding: 0; +} + +#curves-settings-popup-table th { + text-align: left; +} + +.text-input{ + padding: 0; + border: 0; +} + +.bin-cell{ + width: 15px; +} + +td img{ + width: 15px; + height: 15px; +} + +.variable-cell, .parameter-cell, .cat-cell{ + width: 73px; +} +.color-cell{ + width: 57px; +} + +.unit-cell{ + width: 37px; +} + +#curves-settings-popup-footer{ + height: 40px; + display: flex; + border-top: 2px solid black; + box-sizing: border-box; + justify-content: flex-end; + display: flex; + +} + +#curves-settings-popup-footer button{ + margin-top: auto; + margin-bottom: auto; + margin-right: 10px; +} diff --git a/client/components/curves_settings_popup/curves_settings_popup.js b/client/components/curves_settings_popup/curves_settings_popup.js new file mode 100644 index 0000000..aca89e4 --- /dev/null +++ b/client/components/curves_settings_popup/curves_settings_popup.js @@ -0,0 +1,250 @@ +class CurvesSettingsPopup extends HTMLElement{ + + constructor(applySettingsCallback){ + super(); + this.applySettingsCallback = applySettingsCallback; + } + + initTable(){ + + let userConfiguration = this.getUserConfiguration(); + let tbody = this.querySelector("#curves-settings-popup-table tbody"); + tbody.innerHTML = ""; + + for(let lineConfiguration of userConfiguration){ + this.createRow(tbody, lineConfiguration); + } + + this.createRow(tbody); // we add another row by default at the end + } + + doApplySettingsCallback(){ + + try{ + let localStorageBuffer = []; + let formattedUserConfiguration = this.getFormattedUserConfiguration(localStorageBuffer); + + localStorage.clear(); + this.saveUserConfiguration(localStorageBuffer); + + this.hide(); + this.applySettingsCallback(formattedUserConfiguration); + }catch{} + + } + + /** + * Feeds localStorageBuffer with user rows to save, while formatting each row for later API call + * Throws an error if a row is invalid (no variable is given for a non empty line) + * @param {[]} localStorageBuffer an array feeded by this method, that contains the JS object representing the data out of a line + * @returns the formatted user configuration object to be passed as the payload to the server + */ + getFormattedUserConfiguration(localStorageBuffer){ + let formatedUserConfiguration = {}; + + let rows = this.querySelectorAll("tbody tr"); + for(let row of rows){ + + let configurationLineObject = this.getRowValues(row); + let formmatedLineConfiguration = {...configurationLineObject}; + + if(!formmatedLineConfiguration.hasOwnProperty("variable") && Object.keys(formmatedLineConfiguration).length > 0){ + alertify.error("Variable not defined for some row(s)."); + throw Error; + }else{ + let key = formmatedLineConfiguration["variable"]; + delete formmatedLineConfiguration["variable"]; + if(formmatedLineConfiguration.hasOwnProperty("parameter")){ + key += "." + formmatedLineConfiguration["parameter"]; + delete formmatedLineConfiguration["parameter"]; + } + if(Object.keys(formmatedLineConfiguration).length > 0){ + formatedUserConfiguration[key] = formmatedLineConfiguration; + localStorageBuffer.push(configurationLineObject); + } + } + } + return formatedUserConfiguration; + } + + /** + * Gets the data out of the given HTML row as a JSON object + * @param {*} row An HTML tr element of the table + * @returns A JS object containing the data out of the HTML element : there is a key if the value is not an empty string + */ + getRowValues(row){ + let configuration = {}; + + for(let cell of row.children){ + let content = cell.children[0]; //there is only one child per cell + if(content.nodeName == "INPUT" && content.value !== ""){ + configuration[content.name] = content.value; + } + else if (content.nodeName == "SEA-COLOR-SELECTOR" && content.getValue() !== ""){ + configuration["color"] = content.getValue(); + } + } + return configuration; + } + + getUserConfiguration(){ + let userConfiguration = []; + for(let i = 0; i < localStorage.length; i++){ + userConfiguration.push(JSON.parse(localStorage.getItem(localStorage.key(i)))); + } + return userConfiguration; + } + + saveUserConfiguration(userConfiguration){ + for(let i = 0; i < userConfiguration.length; i++){ + localStorage.setItem(i, JSON.stringify(userConfiguration[i])); + } + } + + addNewRowIfEmpty(){ + let tbody = this.querySelector("#curves-settings-popup-table tbody"); + if(tbody.childNodes.length == 0){ + this.createRow(tbody); + } + } + + addRow(){ + let tbody = this.querySelector("#curves-settings-popup-table tbody"); + this.createRow(tbody); + } + + show(){ + this.style.visibility = "visible"; + this.initTable(); + window.addEventListener("click", this.backgroundClickCallback); + } + + hide(){ + this.style.visibility = "hidden"; + window.removeEventListener("click", this.backgroundClickCallback); + } + + backgroundClickCallback = ({target}) => { + if(target.id == "curves-settings-popup"){ + this.doApplySettingsCallback(); + } + } + + connectedCallback(){ + this.render(); + this.hide(); + this.getElementsByTagName("img")[0].onclick = () => {this.doApplySettingsCallback();}; + this.getElementsByClassName("add-row-button")[0].onclick = () => {this.addRow();}; + this.getElementsByClassName("cancel-button")[0].onclick = () => {this.hide();}; + this.getElementsByClassName("apply-button")[0].onclick = () => {this.doApplySettingsCallback();}; + } + + // + /** + * Adds a row to tbody + * The process is disigned in this way because seaColorSelector.setValue is called, and its content has to be + * generated before calling this method, that is why we need to append the newly created HTML element + * as soon as possible. + * If lineConfiguration == null, an empty row is added to the table + * @param {*} tbody - The HTML element of the table body + * @param {*} lineConfiguration - The object representing one line of configuration on the client side + * @returns + */ + createRow(tbody, lineConfiguration = null){ + + let row = document.createElement("tr"); + tbody.appendChild(row) + let binCell = document.createElement("td"); + + let binImg = document.createElement("img"); + binImg.src = "res/bin.png"; + binImg.classList.add("bin-cell"); + binImg.onclick = () => { + binImg.parentNode.parentNode.remove(); + this.addNewRowIfEmpty(); + } + binCell.appendChild(binImg); + row.append(binCell) + + this.createTextInput(row, lineConfiguration, "variable") + + this.createTextInput(row, lineConfiguration, "parameter") + + this.createTextInput(row, lineConfiguration, "cat") + + let colorCell = document.createElement("td"); + let seaColorSelector = new ColorSelector(); + row.append(colorCell) + colorCell.appendChild(seaColorSelector); //need to first append it before calling setValue + seaColorSelector.setValue(""); + if(lineConfiguration != null && lineConfiguration.hasOwnProperty('color')){ + seaColorSelector.setValue(lineConfiguration['color']); + } + + this.createTextInput(row, lineConfiguration, "unit") + + return row; + } + + createTextInput(row, lineConfiguration, type){ + let cell = document.createElement("td"); + let input = document.createElement("input"); + input.type = "text"; + input.spellcheck = false + input.autocorrect = "off" + input.name = type; + input.classList.add(`${type}-cell`, "text-input"); + input.value = ""; + if(lineConfiguration != null && lineConfiguration.hasOwnProperty(type)){ + input.value = lineConfiguration[type]; + } + cell.appendChild(input); + row.append(cell) + } + + render(){ + this.innerHTML = ` + +
+ | Variable | +Parameter | +Category | +Color | +Unit | +
---|