diff --git a/javascript/clusterize.js b/javascript/clusterize.js index b7f8d7d14..689516fb5 100644 --- a/javascript/clusterize.js +++ b/javascript/clusterize.js @@ -24,10 +24,12 @@ class Clusterize { cols_in_block: 1, blocks_in_cluster: 5, rows_in_cluster: 50 * 5, // default is rows_in_block * blocks_in_cluster - tag: null, + tag: "div", id_attr: "data-div-id", no_data_class: "clusterize-no-data", - no_data_text: "No Data", + no_data_html: "No Data", + error_class: "clusterize-error", + error_html: "Data Error", show_no_data_row: true, keep_parity: true, callbacks: {}, @@ -144,15 +146,18 @@ class Clusterize { } async refresh(force) { - if (!this.setup_has_run || !this.enabled || isNullOrUndefined(this.content_elem.offsetParent)) { + if (!this.setup_has_run || !this.enabled) { return; } + // Refresh can be a longer operation so we want to debounce it to // avoid refreshing too often. clearTimeout(this.#refresh_debounce_timer); this.#refresh_debounce_timer = setTimeout( async () => { - this.#fixElementReferences(); + if (!isElement(this.content_elem.offsetParent)) { + return; + } if (this.#recalculateDims() || force) { await this.update() @@ -342,17 +347,16 @@ class Clusterize { return prev_options !== JSON.stringify(this.options); } - #generateEmptyRow() { - const row = document.createElement(this.options.tag); - row.className = this.options.no_data_class; - const node = document.createTextNode(this.options.no_data_text); + #generateEmptyRow({is_error}={}) { + const row = document.createElement(is_error ? "div" : this.options.tag); + row.className = is_error ? this.options.error_class : this.options.no_data_class; if (this.options.tag === "tr") { const td = document.createElement("td"); td.colSpan = 100; - td.appendChild(node); + td.innerHTML = is_error ? this.options.error_html : this.options.no_data_html; row.appendChild(td); } else { - row.appendChild(node); + row.innerHTML = is_error ? this.options.error_html : this.options.no_data_html; } return [row.outerHTML]; } @@ -386,7 +390,7 @@ class Clusterize { top_offset: 0, bottom_offset: 0, rows_above: 0, - rows: this_cluster_rows.length ? this_cluster_rows : this.#generateEmptyRow(), + rows: this_cluster_rows.length ? this_cluster_rows : this.#generateEmptyRow({is_error: true}), }; } @@ -522,7 +526,8 @@ class Clusterize { return; } - if (!isNullOrUndefined(this.content_elem.offsetParent)) { + // Element is already in DOM. Don't need to do anything. + if (isElement(this.content_elem.offsetParent)) { return; } diff --git a/javascript/extraNetworks.js b/javascript/extraNetworks.js index ee79d9fc0..77a497cdc 100644 --- a/javascript/extraNetworks.js +++ b/javascript/extraNetworks.js @@ -111,10 +111,6 @@ class ExtraNetworksTab { refresh_in_progress = false; dirs_view_en = false; tree_view_en = false; - cards_list_loading_splash_elem = null; - cards_list_no_data_splash_elem = null; - tree_list_loading_splash_elem = null; - tree_list_no_data_splash_elem = null; cards_list_splash_state = null; tree_list_splash_state = null; constructor({tabname, extra_networks_tabname}) { @@ -139,19 +135,6 @@ class ExtraNetworksTab { waitForElement(`#${this.tabname_full}_cards_list_content_area`), ]); - this.cards_list_loading_splash_elem = document.getElementById( - `${this.tabname_full}_cards_list_loading_splash` - ); - this.cards_list_no_data_splash_elem = document.getElementById( - `${this.tabname_full}_cards_list_no_data_splash` - ); - this.tree_list_loading_splash_elem = document.getElementById( - `${this.tabname_full}_tree_list_loading_splash` - ); - this.tree_list_no_data_splash_elem = document.getElementById( - `${this.tabname_full}_tree_list_no_data_splash` - ); - this.updateSplashState({cards_list_state: "loading", tree_list_state: "loading"}); this.txt_search_elem = this.controls_elem.querySelector(".extra-network-control--search-text"); @@ -170,6 +153,15 @@ class ExtraNetworksTab { this.controls_elem.id = `${this.tabname_full}_controls`; controls_div.insertBefore(this.controls_elem, null); + const error_btn = this.controls_elem.querySelector( + ".extra-network-control--refresh" + ).cloneNode(true); + error_btn.id = null; + error_btn.dataset.tabnameFull = this.tabname_full; + this.clusterize_error_html = "
Data Error.
" + + "Please refresh tab.
" + + `${error_btn.outerHTML}
`; + await Promise.all([this.setupTreeList(), this.setupCardsList()]); const btn_dirs_view = this.controls_elem.querySelector(".extra-network-control--dirs-view"); @@ -249,6 +241,7 @@ class ExtraNetworksTab { scrollId: `${this.tabname_full}_tree_list_scroll_area`, contentId: `${this.tabname_full}_tree_list_content_area`, tag: "button", + error_html: this.clusterize_error_html, callbacks: { initData: this.onInitTreeData.bind(this), fetchData: this.onFetchTreeData.bind(this), @@ -267,6 +260,8 @@ class ExtraNetworksTab { scrollId: `${this.tabname_full}_cards_list_scroll_area`, contentId: `${this.tabname_full}_cards_list_content_area`, tag: "div", + no_data_html: "No data matching filter.", + error_html: this.clusterize_error_html, callbacks: { initData: this.onInitCardsData.bind(this), fetchData: this.onFetchCardsData.bind(this), @@ -358,23 +353,22 @@ class ExtraNetworksTab { } }; + let loading_elem; + let no_data_elem; + if (isString(cards_list_state)) { this.cards_list_splash_state = cards_list_state; + loading_elem = document.getElementById(`${this.tabname_full}_cards_list_loading_splash`); + no_data_elem = document.getElementById(`${this.tabname_full}_cards_list_no_data_splash`); + _handle_state(cards_list_state, loading_elem, no_data_elem); } - _handle_state( - cards_list_state, - this.cards_list_loading_splash_elem, - this.cards_list_no_data_splash_elem, - ); if (isString(tree_list_state)) { this.tree_list_splash_state = tree_list_state; + loading_elem = document.getElementById(`${this.tabname_full}_tree_list_loading_splash`); + no_data_elem = document.getElementById(`${this.tabname_full}_tree_list_no_data_splash`); + _handle_state(tree_list_state, loading_elem, no_data_elem); } - _handle_state( - tree_list_state, - this.tree_list_loading_splash_elem, - this.tree_list_no_data_splash_elem, - ); } async #refresh() { @@ -579,9 +573,15 @@ class ExtraNetworksTab { if (response.missing_div_ids.length) { console.warn(`Failed to fetch multiple div_ids: ${response.missing_div_ids}`); } + if (Object.keys(response.data).length === 0) { + this.updateSplashState({cards_list_state: "no_data"}); + } else { + this.updateSplashState({cards_list_state: "show"}); + } return response.data; } catch (error) { console.error(`onFetchCardsData error: ${error.message}`); + this.updateSplashState({cards_list_state: "no_data"}); return {}; } } @@ -596,9 +596,15 @@ class ExtraNetworksTab { if (response.missing_div_ids.length) { console.warn(`Failed to fetch multiple div_ids: ${response.missing_div_ids}`); } + if (Object.keys(response.data).length === 0) { + this.updateSplashState({tree_list_state: "no_data"}); + } else { + this.updateSplashState({tree_list_state: "show"}); + } return response.data; } catch (error) { console.error(`onFetchTreeData error: ${error.message}`); + this.updateSplashState({tree_list_state: "no_data"}); return {}; } } @@ -1178,7 +1184,19 @@ function extraNetworksControlRefreshOnClick(event) { extra_networks_refresh_internal_debounce_timer = setTimeout(async() => { const btn = event.target.closest(".extra-network-control--refresh"); const controls = btn.closest(".extra-network-controls"); - const tab = extra_networks_tabs[controls.dataset.tabnameFull]; + // Bit of lazy workaround to allow for us to create copies of the refresh + // button anywhere we want as long as we include tabnameFull in the dataset. + let tabname_full; + if (isElement(controls)) { + tabname_full = controls.dataset.tabnameFull; + } else { + tabname_full = btn.dataset.tabnameFull; + } + const tab = extra_networks_tabs[tabname_full]; + if (isNullOrUndefined(tab)) { + return; + } + tab.updateSplashState({cards_list_state: "loading", tree_list_state: "loading"}); // We want to reset tab lists on refresh click so that the viewing area // shows that it is loading new data. @@ -1186,7 +1204,7 @@ function extraNetworksControlRefreshOnClick(event) { tab.cards_list.clear(); // Fire an event for this button click. gradioApp().getElementById( - `${controls.dataset.tabnameFull}_extra_refresh_internal` + `${tabname_full}_extra_refresh_internal` ).dispatchEvent(new Event("click")); }, EXTRA_NETWORKS_REFRESH_INTERNAL_DEBOUNCE_TIMEOUT_MS); } @@ -1588,8 +1606,7 @@ function extraNetworksSetupEventDelegators() { } } } - - // long_press_event_map is handled by the timer setup in "mousedown" handlers. + // NOTE: long_press_event_map is handled by the timer setup in "mousedown" handlers. }); } diff --git a/style.css b/style.css index 0747e77d1..913392f92 100644 --- a/style.css +++ b/style.css @@ -1212,7 +1212,8 @@ body.resizing .resize-handle { display: none; } -.clusterize-no-data { +.clusterize-no-data, .clusterize-error { + width: 100%; text-align: center; color: var(--input-placeholder-color) !important; }