From 7d953cc6c585a63e21a36cf427abffa180339252 Mon Sep 17 00:00:00 2001 From: Sj-Si Date: Sun, 21 Apr 2024 12:07:53 -0400 Subject: [PATCH] add expand/collapse buttons to directories in tree view --- javascript/clusterize.js | 16 +++++--- javascript/extraNetworks.js | 18 +++++---- javascript/extraNetworksClusterize.js | 57 ++++++++++++++++++++++++++- modules/ui_extra_networks.py | 7 ++++ style.css | 24 ++++++++++- 5 files changed, 106 insertions(+), 16 deletions(-) diff --git a/javascript/clusterize.js b/javascript/clusterize.js index c5fc92263..e0fe8a883 100644 --- a/javascript/clusterize.js +++ b/javascript/clusterize.js @@ -188,22 +188,26 @@ class Clusterize { } async setMaxItems(max_items) { + /** Sets the new max number of items. + * + * This is used to control the scroll bar's length. + * + * Returns whether the number of max items changed. + */ if (!this.setup_has_run || !this.enabled) { this.#max_items = max_items; - return; + return this.#max_items !== max_items; } - - if (max_items === this.#max_items) { + if (this.#max_items === max_items) { // No change. do nothing. - return; + return false; } - // If the number of items changed, we need to update the cluster. this.#max_items = max_items; await this.refresh(); - // Apply sort to the updated data. await this.sortData(); + return true; } // ==== PRIVATE FUNCTIONS ==== diff --git a/javascript/extraNetworks.js b/javascript/extraNetworks.js index 1a3e1fc1b..98069391d 100644 --- a/javascript/extraNetworks.js +++ b/javascript/extraNetworks.js @@ -938,15 +938,14 @@ function extraNetworksTreeDirectoryOnClick(event, btn, tabname_full) { const tab = extra_networks_tabs[tabname_full]; - if (true_targ.matches(".tree-list-item-action--leading, .tree-list-item-action-chevron")) { + const prev_selected_elem = gradioApp().querySelector(".tree-list-item[data-selected='']"); + if (true_targ.matches(".tree-list-item-action--leading .tree-list-item-action-chevron")) { // If user clicks on the chevron, then we do not select the folder. - const prev_selected_elem = gradioApp().querySelector(".tree-list-item[data-selected='']"); tab.tree_list.onRowExpandClick(div_id, btn); - const selected_elem = gradioApp().querySelector(".tree-list-item[data-selected='']"); - if (isElement(prev_selected_elem) && !isElement(selected_elem)) { - // is a selected element was removed, clear filter. - tab.updateSearch(""); - } + } else if (true_targ.matches(".tree-list-item-action--trailing .tree-list-item-action-expand")) { + tab.tree_list.onExpandAllClick(div_id); + } else if (true_targ.matches(".tree-list-item-action--trailing .tree-list-item-action-collapse")) { + tab.tree_list.onCollapseAllClick(div_id); } else { // user clicked anywhere else on the row tab.tree_list.onRowSelected(div_id, btn); @@ -963,6 +962,11 @@ function extraNetworksTreeDirectoryOnClick(event, btn, tabname_full) { } tab.updateSearch("selected" in btn.dataset ? btn.dataset.path : ""); } + const selected_elem = gradioApp().querySelector(".tree-list-item[data-selected='']"); + if (isElement(prev_selected_elem) && !isElement(selected_elem)) { + // if a selected element was removed, clear filter. + tab.updateSearch(""); + } } function extraNetworksTreeOnClick(event, tabname_full) { diff --git a/javascript/extraNetworksClusterize.js b/javascript/extraNetworksClusterize.js index 8e73e6ef6..1659cf4b2 100644 --- a/javascript/extraNetworksClusterize.js +++ b/javascript/extraNetworksClusterize.js @@ -338,6 +338,58 @@ class ExtraNetworksClusterizeTreeList extends ExtraNetworksClusterize { return max_width; } + async onExpandAllClick(div_id) { + if (!keyExistsLogError(this.data_obj, div_id)) { + return; + } + + const _expand = (parent_id) => { + const this_obj = this.data_obj[parent_id]; + this_obj.visible = true; + this_obj.expanded = true; + for (const child_id of this_obj.children) { + _expand(child_id); + } + }; + + this.data_obj[div_id].expanded = true; + for (const child_id of this.data_obj[div_id].children) { + _expand(child_id); + } + + const new_len = Object.values(this.data_obj).filter(v => v.visible).length; + const max_items_changed = await this.setMaxItems(new_len); + if (!max_items_changed) { + await this.refresh(true); + } + } + + async onCollapseAllClick(div_id) { + if (!keyExistsLogError(this.data_obj, div_id)) { + return; + } + + const _collapse = (parent_id) => { + const this_obj = this.data_obj[parent_id]; + this_obj.visible = false; + this_obj.expanded = false; + for (const child_id of this_obj.children) { + _collapse(child_id); + } + }; + + this.data_obj[div_id].expanded = false; + for (const child_id of this.data_obj[div_id].children) { + _collapse(child_id); + } + + const new_len = Object.values(this.data_obj).filter(v => v.visible).length; + const max_items_changed = await this.setMaxItems(new_len); + if (!max_items_changed) { + await this.refresh(true); + } + } + async onRowExpandClick(div_id, elem) { /** Expands or collapses a row to show/hide children. */ if (!keyExistsLogError(this.data_obj, div_id)) { @@ -353,7 +405,10 @@ class ExtraNetworksClusterizeTreeList extends ExtraNetworksClusterize { } const new_len = Object.values(this.data_obj).filter(v => v.visible).length; - await this.setMaxItems(new_len); + const max_items_changed = await this.setMaxItems(new_len); + if (!max_items_changed) { + await this.refresh(true); + } } async initData() { diff --git a/modules/ui_extra_networks.py b/modules/ui_extra_networks.py index 31278f8e7..41e5067f5 100644 --- a/modules/ui_extra_networks.py +++ b/modules/ui_extra_networks.py @@ -317,6 +317,13 @@ class ExtraNetworksPage: # Action buttons if item is not None: action_list_item_action_trailing += self.get_button_row(tabname, item) + else: + action_list_item_action_trailing += ( + "
" + "
" + "
" + "
" + ) data_attributes_str = "" for k, v in data_attributes.items(): diff --git a/style.css b/style.css index 41a02f223..352b5565b 100644 --- a/style.css +++ b/style.css @@ -1272,6 +1272,8 @@ body.resizing .resize-handle { } .extra-network-content.resize-handle-col { + display: flex; + flex-direction: column; overflow: auto; } @@ -1526,13 +1528,31 @@ body.resizing .resize-handle { color: var(--button-secondary-text-color); } + +/* ==== TREE VIEW EXPAND/COLLAPSE BUTTONS ==== */ +.tree-list-item-action-expand::before { + content: "≫"; +} + +.tree-list-item-action-expand { + transform: rotate(90deg); +} + +.tree-list-item-action-collapse::before { + content: "≫"; +} + +.tree-list-item-action-collapse { + transform: rotate(-90deg); +} + /* ==== CHEVRON ICON ACTIONS ==== */ /* Define the animation for the arrow when it is clicked. */ .tree-list-item-action-chevron { display: inline-flex; height: var(--button-large-text-size); width: var(--button-large-text-size); - mask-image: url('data:image/svg+xml,'); + mask-image: url('data:image/svg+xml,'); mask-repeat: no-repeat; mask-position: center center; mask-size: 100%; @@ -1594,7 +1614,6 @@ body.resizing .resize-handle { .tree-list-item-action--trailing { grid-area: trailing-action; - display: inline-flex; } .tree-list-item .button-row { @@ -1606,6 +1625,7 @@ body.resizing .resize-handle { gap: var(--spacing-sm); box-sizing: border-box; margin: 0; + margin-right: var(--spacing-sm); } .tree-list-item:hover .button-row {