From c1a5bcb3ad3712980b09672e259a5481c79c326b Mon Sep 17 00:00:00 2001 From: Elliott Rose Date: Tue, 7 Mar 2023 14:51:13 +0100 Subject: [PATCH 1/5] Add carets to the stats tables which rotate on click. --- assets/custom-functions.js | 94 ++++++++++++++++++++++++++++++++------ assets/sidebar.css | 2 +- assets/style.css | 22 +++++++++ 3 files changed, 103 insertions(+), 15 deletions(-) diff --git a/assets/custom-functions.js b/assets/custom-functions.js index a5b1221..39624a1 100644 --- a/assets/custom-functions.js +++ b/assets/custom-functions.js @@ -189,19 +189,85 @@ $(document).ready(function () { }); }); -// // CREATE WAIT FUNCTION TO WAIT FOR ELEMENT TO BECOME AVAILABLE -// function waitForElement(selector, func, timeout = 1000) { -// //wait for element to be available to change its position -// const start = Date.now(); -// let interval = setInterval(() => { -// var el = document.getElementById(selector); -// if (el) { -// clearInterval(interval); -// return func(el); -// } else if (Date.now() - start > timeout) { -// clearInterval(interval); -// } -// }, 100); -// } +function waitForElm(selector) { + return new Promise(resolve => { + if (document.querySelector(selector)) { + return resolve(document.querySelector(selector)); + } + + const observer = new MutationObserver(mutations => { + if (document.querySelector(selector)) { + resolve(document.querySelector(selector)); + observer.disconnect(); + } + }); + + observer.observe(document.body, { + childList: true, + subtree: true + }); + }); +} +// The aeronet stats table changes the position/index of the regions when they +// are clicked, so +// we cannot easily assign a class to the regions that will maintain on click, +// so we must remove the carets on click, and then add them back after the table has finished +// changing it's state // +//ADD INITIAL CARETS TO STATS TABLES +$(document).ready(function () { + $(document).on('click', "#scores-apply, #evaluation-tab", function () { + var areas = "td:contains('Europe'), td:contains('Mediterranean'),td:contains('NAfrica'),td:contains('MiddleEast')"; + waitForElm('td').then(() => { + $(areas).addClass('table_caret_down'); + }); + }) +}) + +//GET CURRENT DIRECTI0N OF CARET OF TABLE REGION ELEMENTS AND RETURN A LIST +function getClasses(elements) { + len = $(elements).length + results = [] + for( var i = 0; i < 4; i++) { + var temp = $(elements)[i]; + if(temp.classList.contains('table_caret_up') == true){ + var caret = 'table_caret_up'; + }else { + var caret = 'table_caret_down'; + } + results.push(caret); + }; + return results +} + +// ADD THE CARETS BACK AFTER THE TABLE HAS BEEN CLEARED +function addClasses(areas, results){ + for (var i = 0; i < 4; i++) { + var element = $(areas).eq(i); + element.addClass(results[i]); + } +} + +//CHANGE CARET DIRECTIONS ACCORDING TO CLICKS +$(document).ready(function () { + var areas = "td:contains('Europe'), td:contains('Mediterranean'),td:contains('NAfrica'),td:contains('MiddleEast')"; + $(document).on('click', areas, function resetStyles () { + elements = getClasses(areas); + $(".table_caret_down").removeClass('table_caret_down'); + $(".table_caret_up").removeClass(".table_caret_up"); + var origLength = $('tr').length; + setTimeout(() => { + var newLength = $('tr').length; + if (origLength < newLength) { + addClasses(areas, elements); + $(this).removeClass('table_caret_down'); + $(this).addClass('table_caret_up'); + }else if (newLength < origLength) { + addClasses(areas, results); + $(this).removeClass('table_caret_up'); + $(this).addClass('table_caret_down'); + }; + }, 200); + }) +}) diff --git a/assets/sidebar.css b/assets/sidebar.css index baec1ca..1074f11 100644 --- a/assets/sidebar.css +++ b/assets/sidebar.css @@ -19,7 +19,7 @@ color: var(--blue) !important; background-color: #FAFAFA; overflow: hidden; - z-index: 10000; + z-index: 1049; } .sidebar-first-item { diff --git a/assets/style.css b/assets/style.css index 6be0e97..ad7e237 100644 --- a/assets/style.css +++ b/assets/style.css @@ -880,3 +880,25 @@ div.dropdown-menu.show { color: white; font-size: 1.1rem; } + +.table_caret_down { + display: flex; +} + +.table_caret_down:after { + font-family: 'Font Awesome 5 free'; + content: "\f107"; + font-weight: 900; + font-size: 1.3rem; +} + +.table_caret_up { + display: flex; +} + +.table_caret_up:after { + font-family: 'Font Awesome 5 free'; + content: "\f106"; + font-weight: 900; + font-size: 1.3rem; +} -- GitLab From 54f2f40ab7698a1b25c10904f7b89fb60d48ed81 Mon Sep 17 00:00:00 2001 From: Elliott Rose Date: Tue, 7 Mar 2023 15:00:44 +0100 Subject: [PATCH 2/5] Update so multiple tables work with carets --- assets/custom-functions.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/assets/custom-functions.js b/assets/custom-functions.js index 39624a1..abbe37c 100644 --- a/assets/custom-functions.js +++ b/assets/custom-functions.js @@ -228,7 +228,7 @@ $(document).ready(function () { function getClasses(elements) { len = $(elements).length results = [] - for( var i = 0; i < 4; i++) { + for( var i = 0; i < len; i++) { var temp = $(elements)[i]; if(temp.classList.contains('table_caret_up') == true){ var caret = 'table_caret_up'; @@ -242,7 +242,7 @@ function getClasses(elements) { // ADD THE CARETS BACK AFTER THE TABLE HAS BEEN CLEARED function addClasses(areas, results){ - for (var i = 0; i < 4; i++) { + for (var i = 0; i < results.length; i++) { var element = $(areas).eq(i); element.addClass(results[i]); } -- GitLab From 94876a7721814ee130a3b29bf0a980c7e315d4af Mon Sep 17 00:00:00 2001 From: Elliott Rose Date: Wed, 8 Mar 2023 09:37:53 +0100 Subject: [PATCH 3/5] Update the caret functions to address issues with multiple tables. There are still issues with carets not populating for all stats tables on some occasions. Update tables to have 2 digits after decimal point. --- assets/custom-functions.js | 2 +- assets/style.css | 5 +++++ tabs/evaluation_callbacks.py | 19 +++++++++++++++++++ 3 files changed, 25 insertions(+), 1 deletion(-) diff --git a/assets/custom-functions.js b/assets/custom-functions.js index abbe37c..f9ffe79 100644 --- a/assets/custom-functions.js +++ b/assets/custom-functions.js @@ -267,7 +267,7 @@ $(document).ready(function () { $(this).removeClass('table_caret_up'); $(this).addClass('table_caret_down'); }; - }, 200); + }, 300); }) }) diff --git a/assets/style.css b/assets/style.css index ad7e237..7028384 100644 --- a/assets/style.css +++ b/assets/style.css @@ -902,3 +902,8 @@ div.dropdown-menu.show { font-weight: 900; font-size: 1.3rem; } + +td.column-0 { + max-width: 100%; + min-width: 10%; +} diff --git a/tabs/evaluation_callbacks.py b/tabs/evaluation_callbacks.py index be28c10..80cc470 100644 --- a/tabs/evaluation_callbacks.py +++ b/tabs/evaluation_callbacks.py @@ -238,6 +238,22 @@ def scores_maps_retrieve(n_clicks, model, score, network, selection, orig_model, return dash.no_update, False, dash.no_update, dash.no_update # PreventUpdate +def format_floats(df): + """This function takes a dataframe and changes all columns except for 'station' + so that floats will be formatted to 2 digits after the decimal place""" + for col in df.columns: + # check if the column is not 'station' + if col != 'station': + # convert the column to a string to allow for string formatting + df[col] = df[col].astype(str) + # iterate over the values in the column + for i, val in enumerate(df[col]): + # check if the value is a float + if '.' in val: + # if so, format it to have 2 decimal places + df.at[i, col] = '{:.2f}'.format(float(val)) + return df + @dash.callback( extend_l([ @@ -331,6 +347,9 @@ def aeronet_scores_tables_retrieve(n, *args): # *activel_cells, models, stat, n df = pd.read_hdf(filepath, tab_name) # replace "tables" columns + # import pudb; pudb.set_trace() + df = format_floats(df) + ret_tables[ret_idx] = [{'name': i in MODELS and [STATS[SCORES[table_idx]], MODELS[i]['name']] or [STATS[SCORES[table_idx]], ''], 'id': i} for -- GitLab From b0ba0a9460f90393d693c31f9a18a84aad2d20d0 Mon Sep 17 00:00:00 2001 From: Elliott Rose Date: Wed, 8 Mar 2023 10:59:56 +0100 Subject: [PATCH 4/5] Update to remove extra carets appearing in stations. Extra stats tables still not populating carets on first click of apply --- assets/custom-functions.js | 45 ++++++++++++++++++++++++++++++++++---- 1 file changed, 41 insertions(+), 4 deletions(-) diff --git a/assets/custom-functions.js b/assets/custom-functions.js index f9ffe79..8e57de8 100644 --- a/assets/custom-functions.js +++ b/assets/custom-functions.js @@ -198,13 +198,25 @@ function waitForElm(selector) { const observer = new MutationObserver(mutations => { if (document.querySelector(selector)) { resolve(document.querySelector(selector)); - observer.disconnect(); - } + for (const element of mutations) { + if (['Mediterranean','NAfrica', 'MiddleEast'].includes(element.oldValue)) { + //GET ELEMENTS PARENT NODE 2 ABOVE AS IT CONTAINS THE TARGET CLASS + const target = element.target.parentNode.parentNode; + target.classList.remove('table_caret_up'); + }; + } + } }); observer.observe(document.body, { childList: true, - subtree: true + subtree: true, + attributes: true, + attributeFilter: ['class', 'text'], + attributeOldValue: true, + characterData: true, + characterDataOldValue: true + }); }); } @@ -218,7 +230,7 @@ function waitForElm(selector) { $(document).ready(function () { $(document).on('click', "#scores-apply, #evaluation-tab", function () { var areas = "td:contains('Europe'), td:contains('Mediterranean'),td:contains('NAfrica'),td:contains('MiddleEast')"; - waitForElm('td').then(() => { + waitForElm('.column-0').then(() => { $(areas).addClass('table_caret_down'); }); }) @@ -271,3 +283,28 @@ $(document).ready(function () { }) }) + +// //CHANGE CARET DIRECTIONS ACCORDING TO CLICKS +// $(document).ready(function () { +// var areas = "td:contains('Europe'), td:contains('Mediterranean'),td:contains('NAfrica'),td:contains('MiddleEast')"; +// $(document).on('click', areas, function resetStyles () { +// elements = getClasses(areas); +// $(".table_caret_down").removeClass('table_caret_down'); +// $(".table_caret_up").removeClass(".table_caret_up"); +// var origLength = $('tr').length; +// setTimeout(() => { +// var newLength = $('tr').length; +// if (origLength < newLength) { +// addClasses(areas, elements); +// $(this).removeClass('table_caret_down'); +// $(this).addClass('table_caret_up'); +// }else if (newLength < origLength) { +// addClasses(areas, results); +// $(this).removeClass('table_caret_up'); +// $(this).addClass('table_caret_down'); +// }; +// }, 300); +// }) +// }) +// +// -- GitLab From 934d56f43639a3b736c00d10264f6b9f5ef9a6e1 Mon Sep 17 00:00:00 2001 From: Elliott Rose Date: Thu, 9 Mar 2023 14:58:35 +0100 Subject: [PATCH 5/5] Rework stats carets to use mutation observer instead of setTimeout. --- assets/custom-functions.js | 132 ++++++++++++------------------------- 1 file changed, 42 insertions(+), 90 deletions(-) diff --git a/assets/custom-functions.js b/assets/custom-functions.js index 8e57de8..e8ba802 100644 --- a/assets/custom-functions.js +++ b/assets/custom-functions.js @@ -189,122 +189,74 @@ $(document).ready(function () { }); }); -function waitForElm(selector) { +//================== Stats table carets =============================================== +// The aeronet stats table changes the position/index of the regions when they +// are clicked, so we cannot easily assign a class to the regions that will maintain on click, +// so we must remove the carets on click, and then add them back after the table has finished +// changing it's state +// ADD FUNCTION TO WAIT FOR TABLE TO FINISH CHANGING +function waitForMutation(selector) { return new Promise(resolve => { if (document.querySelector(selector)) { return resolve(document.querySelector(selector)); - } - + }; const observer = new MutationObserver(mutations => { if (document.querySelector(selector)) { resolve(document.querySelector(selector)); + //Carets will populate in the wrong rows after click, so we must remove them for (const element of mutations) { if (['Mediterranean','NAfrica', 'MiddleEast'].includes(element.oldValue)) { //GET ELEMENTS PARENT NODE 2 ABOVE AS IT CONTAINS THE TARGET CLASS const target = element.target.parentNode.parentNode; target.classList.remove('table_caret_up'); + target.classList.remove('table_caret_down'); }; } + // Now add the carets back in where appropriate + flipCarets(); } }); - observer.observe(document.body, { childList: true, subtree: true, - attributes: true, - attributeFilter: ['class', 'text'], attributeOldValue: true, - characterData: true, characterDataOldValue: true - }); }); } -// The aeronet stats table changes the position/index of the regions when they -// are clicked, so -// we cannot easily assign a class to the regions that will maintain on click, -// so we must remove the carets on click, and then add them back after the table has finished -// changing it's state -// -//ADD INITIAL CARETS TO STATS TABLES -$(document).ready(function () { - $(document).on('click', "#scores-apply, #evaluation-tab", function () { - var areas = "td:contains('Europe'), td:contains('Mediterranean'),td:contains('NAfrica'),td:contains('MiddleEast')"; - waitForElm('.column-0').then(() => { - $(areas).addClass('table_caret_down'); - }); - }) -}) -//GET CURRENT DIRECTI0N OF CARET OF TABLE REGION ELEMENTS AND RETURN A LIST -function getClasses(elements) { - len = $(elements).length - results = [] - for( var i = 0; i < len; i++) { - var temp = $(elements)[i]; - if(temp.classList.contains('table_caret_up') == true){ - var caret = 'table_caret_up'; - }else { - var caret = 'table_caret_down'; - } - results.push(caret); +//CREATE FUNCTION TO FIND REGIONS AND FLIP CARETS AS NEEDED +function flipCarets() { + var areas = "td:contains('Europe'), td:contains('Mediterranean'),td:contains('NAfrica'),td:contains('MiddleEast')"; + $(areas).addClass('table_caret_down'); + //Each table has the four regions. Split the returned areas into tables, and then address caret flips + for(var i = 0; i < ($(areas).length/4); i++){ + var medIndex = parseInt($("td:contains('Mediterranean')").eq(i).attr('data-dash-row')); + var nafricaIndex = parseInt($("td:contains('NAfrica')").eq(i).attr('data-dash-row')); + var middleEastIndex = parseInt($("td:contains('MiddleEast')").eq(i).attr('data-dash-row')); + var totalIndex = parseInt($("td:contains('Total')").eq(i).attr('data-dash-row')); + //for each table, check if the caret should be flipped + //if the next region is not at the next index, the dropdown is open + //trigger the caret to be flipped + if (medIndex !== 1) { + $("td:contains('Europe')").eq(i).addClass('table_caret_up'); + }; + if ((medIndex + 1) !== middleEastIndex) { + $("td:contains('Mediterranean')").eq(i).addClass('table_caret_up'); + }; + if ((middleEastIndex + 1) !== nafricaIndex) { + $("td:contains('MiddleEast')").eq(i).addClass('table_caret_up'); + }; + if ((nafricaIndex + 1) !== totalIndex) { + $("td:contains('NAfrica')").eq(i).addClass('table_caret_up'); + }; }; - return results -} - -// ADD THE CARETS BACK AFTER THE TABLE HAS BEEN CLEARED -function addClasses(areas, results){ - for (var i = 0; i < results.length; i++) { - var element = $(areas).eq(i); - element.addClass(results[i]); - } -} +}; -//CHANGE CARET DIRECTIONS ACCORDING TO CLICKS +//ADD FUNCTION TO WAIT FOR CLICKS ON AREAS THAT SHOULD TRIGGER CARET FLIPS $(document).ready(function () { - var areas = "td:contains('Europe'), td:contains('Mediterranean'),td:contains('NAfrica'),td:contains('MiddleEast')"; - $(document).on('click', areas, function resetStyles () { - elements = getClasses(areas); - $(".table_caret_down").removeClass('table_caret_down'); - $(".table_caret_up").removeClass(".table_caret_up"); - var origLength = $('tr').length; - setTimeout(() => { - var newLength = $('tr').length; - if (origLength < newLength) { - addClasses(areas, elements); - $(this).removeClass('table_caret_down'); - $(this).addClass('table_caret_up'); - }else if (newLength < origLength) { - addClasses(areas, results); - $(this).removeClass('table_caret_up'); - $(this).addClass('table_caret_down'); - }; - }, 300); + $(document).on('click', "td:contains('Europe'), td:contains('Mediterranean'),td:contains('NAfrica'),td:contains('MiddleEast'), #scores-apply, #evaluation-tab", function () { + waitForMutation('td.dash-cell.column-0'); }) }) - - -// //CHANGE CARET DIRECTIONS ACCORDING TO CLICKS -// $(document).ready(function () { -// var areas = "td:contains('Europe'), td:contains('Mediterranean'),td:contains('NAfrica'),td:contains('MiddleEast')"; -// $(document).on('click', areas, function resetStyles () { -// elements = getClasses(areas); -// $(".table_caret_down").removeClass('table_caret_down'); -// $(".table_caret_up").removeClass(".table_caret_up"); -// var origLength = $('tr').length; -// setTimeout(() => { -// var newLength = $('tr').length; -// if (origLength < newLength) { -// addClasses(areas, elements); -// $(this).removeClass('table_caret_down'); -// $(this).addClass('table_caret_up'); -// }else if (newLength < origLength) { -// addClasses(areas, results); -// $(this).removeClass('table_caret_up'); -// $(this).addClass('table_caret_down'); -// }; -// }, 300); -// }) -// }) -// -// +//================== Stats table carets END =============================================== -- GitLab