From 5e9aa5cd099540fcac3a70c172f32ae2f3937af4 Mon Sep 17 00:00:00 2001 From: Robin Scholtes Date: Wed, 26 Apr 2023 23:08:44 +1200 Subject: [PATCH] [#181] fix selected / checkmark classes on operation list, based on the current recipe list. Update TODO --- src/web/App.mjs | 5 +- src/web/TODO.md | 3 +- src/web/waiters/OperationsWaiter.mjs | 2 +- src/web/waiters/RecipeWaiter.mjs | 76 ++++++++++++++++++++++++++-- src/web/waiters/WindowWaiter.mjs | 9 ++-- 5 files changed, 82 insertions(+), 13 deletions(-) diff --git a/src/web/App.mjs b/src/web/App.mjs index a3556aa1..b0cbeba4 100755 --- a/src/web/App.mjs +++ b/src/web/App.mjs @@ -63,6 +63,7 @@ class App { this.setCompileMessage(); this.loadLocalStorage(); this.populateOperationsList(); + this.manager.recipe.updateSelectedOperations(); this.manager.setup(); this.manager.output.saveBombe(); this.uriParams = this.getURIParams(); @@ -329,6 +330,8 @@ class App { }); this.adjustComponentSizes(); + + // @TODO: handle sortable, draggable, popovers functionality etc. } /** @@ -801,7 +804,7 @@ class App { /** - * Handler for CyerChef statechange events. + * Handler for CyberChef statechange events. * Fires whenever the input or recipe changes in any way. * * @listens Manager#statechange diff --git a/src/web/TODO.md b/src/web/TODO.md index 8d27c2b1..23454d96 100644 --- a/src/web/TODO.md +++ b/src/web/TODO.md @@ -4,9 +4,8 @@ --- #### Mobile UI ( on real device ): -- OperationsWaiter operationDblClick: class 'selected' needs to be removed once it is deleted from recipe list, through any type of event - How to add operations to favourites since drag and drop is now disabled on mobile ( maybe a star icon...? ) -- 'name' in recipe waiter > addOperation is whatever instead of a plain title.. +- 'name' in recipe waiter > addOperation is whatever instead of a plain data-name, updateSelectedOperations not working for search results because of that - Recipe list on mobile panel is too small to comfortably scroll and change order of recipes - test *thoroughly* with keyboard popping up because that messes with view-heights on mobile probably and might make it a very frustrating experience diff --git a/src/web/waiters/OperationsWaiter.mjs b/src/web/waiters/OperationsWaiter.mjs index cec9e909..504a1ad6 100755 --- a/src/web/waiters/OperationsWaiter.mjs +++ b/src/web/waiters/OperationsWaiter.mjs @@ -236,7 +236,6 @@ class OperationsWaiter { */ operationDblclick(e) { const li = e.target; - e.target.classList.add("selected"); this.manager.recipe.addOperation(li.getAttribute("data-name")); } @@ -340,6 +339,7 @@ class OperationsWaiter { this.app.saveFavourites(favouritesList); this.app.loadFavourites(); this.app.populateOperationsList(); + this.manager.recipe.updateSelectedOperations(); this.manager.recipe.initialiseOperationDragNDrop(); } diff --git a/src/web/waiters/RecipeWaiter.mjs b/src/web/waiters/RecipeWaiter.mjs index 7e29a558..3b957c12 100755 --- a/src/web/waiters/RecipeWaiter.mjs +++ b/src/web/waiters/RecipeWaiter.mjs @@ -388,7 +388,8 @@ class RecipeWaiter { /** - * Adds the specified operation to the recipe. + * Adds the specified operation to the recipe and + * adds a checkmark to the operation in Operations op-list * * @fires Manager#operationadd * @param {string} name - The name of the operation to add @@ -396,6 +397,7 @@ class RecipeWaiter { */ addOperation(name) { const item = document.createElement("li"); + item.setAttribute("data-name", name); item.classList.add("operation"); item.innerText = name; @@ -404,6 +406,8 @@ class RecipeWaiter { $(item).find("[data-toggle='tooltip']").tooltip(); + this.updateSelectedOperations(); + item.dispatchEvent(this.manager.operationadd); return item; } @@ -419,6 +423,7 @@ class RecipeWaiter { while (recList.firstChild) { recList.removeChild(recList.firstChild); } + this.clearAllSelectedClasses(); recList.dispatchEvent(this.manager.operationremove); } @@ -466,8 +471,8 @@ class RecipeWaiter { * @param {event} e */ opAdd(e) { - log.debug(`'${e.target.querySelector(".op-title").textContent}' added to recipe`); - + log.debug(`'${e.target.getAttribute("data-name")}' added to recipe`); + this.updateSelectedOperations(); this.triggerArgEvents(e.target); window.dispatchEvent(this.manager.statechange); } @@ -482,6 +487,7 @@ class RecipeWaiter { */ opRemove(e) { log.debug("Operation removed from recipe"); + this.updateSelectedOperations(); window.dispatchEvent(this.manager.statechange); } @@ -605,6 +611,70 @@ class RecipeWaiter { controlsContent.style.transform = `scale(${scale})`; } + + /** + * Remove all "selected" classes for op-list list items at once + * + * This hides all the checkmark icons of previously added ( selected ) + * operations to the recipe list + */ + clearAllSelectedClasses(){ + const list = document.querySelectorAll(".operation.selected"); + + // check if any operations are selected at all to prevent errors + if (list.length){ + list.forEach((item) => { + item.classList.remove("selected"); + }) + } + } + + /** + * Add "selected" to the operation that is added to the recipe + * + * This displays a checkmark icon to the operation in the op-list + * + * @param {string} opDataName the data-name of the target operation + */ + addSelectedClass(opDataName){ + const list = document.querySelectorAll(".operation"); + const item = Array.from(list).find((item) => item.getAttribute("data-name") === opDataName ); + + item.classList.add("selected"); + } + + /** + * Update which items are selected in op-list. + * + * First, all selected classes are removed from op-list, then we get the current + * recipe-list ingredient names and add 'selected' back to the matching operations. + * + * Note: It seems a little overkill, but with the current tightly coupled code this is + * a reliable way to make sure the 'selected' operations are always in sync with + * the recipe list ( I think this is preferable to complicating a lot of existing + * code ), I'd recommend to refactor this at one point, but that will mean a huge code + * overhaul for another time / issue. + */ + updateSelectedOperations(){ + let recipeList, operations; + + recipeList = document.querySelectorAll("#rec-list > li"); + operations = document.querySelectorAll(".operation"); + + this.clearAllSelectedClasses(); + + if ( recipeList.length ){ + recipeList.forEach((ingredient) => { + const ingredientName = ingredient.getAttribute("data-name"); + + operations.forEach((operation) => { + if ( ingredientName === operation.getAttribute("data-name")){ + this.addSelectedClass(ingredientName); + } + }) + }) + } + } } export default RecipeWaiter; diff --git a/src/web/waiters/WindowWaiter.mjs b/src/web/waiters/WindowWaiter.mjs index 29cfbccf..f5d6042c 100755 --- a/src/web/waiters/WindowWaiter.mjs +++ b/src/web/waiters/WindowWaiter.mjs @@ -27,16 +27,13 @@ class WindowWaiter { * continuous resetting). */ windowResize() { - debounce(this.app.adjustComponentSizes, 200, "windowResize", this.app, [])(); - if ( window.innerWidth >= this.app.breakpoint ) { - this.app.setDesktopLayout(false); + this.app.setDesktopLayout(false); //@TODO: use setDesktopUI() func } else { - this.app.setMobileLayout(); + this.app.setMobileLayout(); //@TODO: use mobileUI() if needed } - // we must repopulate the op lists to handle sortable, draggable, popovers etc. - debounce(this.app.populateOperationsList, 200, "windowResize", this.app, [])(); + debounce(this.app.adjustComponentSizes, 200, "windowResize", this.app, [])(); }