fix more bugs and improve stability of code.

This commit is contained in:
Sj-Si 2024-03-22 16:16:45 -04:00
parent ef29f65a25
commit 26b0416611
2 changed files with 296 additions and 195 deletions

View file

@ -4,14 +4,41 @@ const re_extranet_neg = /\(([^:^>]+:[\d.]+)\)/;
const re_extranet_g_neg = /\(([^:^>]+:[\d.]+)\)/g;
const activePromptTextarea = {};
const clusterizers = {};
const clusterizers_initial_load = {};
var globalPopup = null;
var globalPopupInner = null;
const storedPopupIds = {};
const tab_setup_complete = {};
const extraPageUserMetadataEditors = {};
// A flag used by the `waitForBool` promise to determine when we first load Ui Options.
const initialUiOptionsLoaded = { state: false };
const debounce = (handler, timeout_ms) => {
/** Debounces a function call.
*
* NOTE: This will NOT work if called from within a class.
* It will drop `this` from scope.
*
* Repeated calls to the debounce handler will not call the handler until there are
* no new calls to the debounce handler for timeout_ms time.
*
* Example:
* function add(x, y) { return x + y; }
* let debounce_handler = debounce(add, 5000);
* let res;
* for (let i = 0; i < 10; i++) {
* res = debounce_handler(i, 100);
* }
* console.log("Result:", res);
*
* This example will print "Result: 109".
*/
let timer = null;
return (...args) => {
clearTimeout(timer);
timer = setTimeout(() => handler(...args), timeout_ms);
};
}
function waitForElement(selector) {
/** Promise that waits for an element to exist in DOM. */
return new Promise(resolve => {
@ -85,7 +112,7 @@ function waitForValueInObject(o) {
* Resolves when obj[k] == v
*/
return new Promise(resolve => {
waitForKeyInObject({ k: o.k, v: o.obj }).then(() => {
waitForKeyInObject({ k: o.k, obj: o.obj }).then(() => {
(function _waitForValueInObject() {
if (o.k in o.obj && o.obj[o.k] == o.v) {
@ -115,7 +142,6 @@ function toggleCss(key, css, enable) {
}
function extraNetworksRefreshTab(tabname_full) {
clusterizers_initial_load[tabname_full].state = false;
// Reapply controls since they don't change on refresh.
let btn_dirs_view = gradioApp().querySelector(`#${tabname_full}_extra_dirs_view`);
let btn_tree_view = gradioApp().querySelector(`#${tabname_full}_extra_tree_view`);
@ -127,9 +153,8 @@ function extraNetworksRefreshTab(tabname_full) {
div_dirs.classList.toggle("hidden", !btn_dirs_view.classList.contains("extra-network-control--enabled"));
div_tree.classList.toggle("hidden", !btn_tree_view.classList.contains("extra-network-control--enabled"));
waitForKeyInObject({ k: tabname_full, v: clusterizers }).then(() => {
extraNetworkClusterizersOnTabLoad(tabname_full);
});
waitForKeyInObject({ k: tabname_full, obj: clusterizers })
.then(() => extraNetworkClusterizersOnTabLoad(tabname_full));
}
function extraNetworksRegisterPromptForTab(tabname, id) {
@ -170,20 +195,13 @@ function extraNetworksSetupTabContent(tabname, pane, controls_div) {
}),
};
clusterizers_initial_load[tabname_full] = { state: false };
// Debounce search text input. This way we only search after user is done typing.
let typing_timer;
let done_typing_interval_ms = 250;
txt_search.addEventListener("keyup", () => {
clearTimeout(typing_timer);
let search_input_debounce = debounce(() => {
if (txt_search.value) {
typing_timer = setTimeout(
() => extraNetworksApplyFilter(tabname_full),
done_typing_interval_ms,
);
extraNetworksApplyFilter(tabname_full);
}
});
}, 250);
txt_search.addEventListener("keyup", search_input_debounce);
// Update search filter whenever the search input's clear button is pressed.
txt_search.addEventListener("extra-network-control--search-clear", () => extraNetworksApplyFilter(tabname_full));
@ -194,24 +212,9 @@ function extraNetworksSetupTabContent(tabname, pane, controls_div) {
if (pane.style.display != "none") {
extraNetworksShowControlsForPage(tabname, tabname_full);
}
// Handle extra-network-pane resize events.
let resize_observer_timer;
let done_resize_observer_interval_ms = 250;
let resize_observer = new ResizeObserver((entries) => {
for (const entry of entries) {
if (entry.target === pane) {
clearTimeout(resize_observer_timer);
resize_observer_timer = setTimeout(
() => extraNetworksClusterizersOnResize(tabname_full),
done_resize_observer_interval_ms,
);
break;
}
}
});
resize_observer.observe(pane);
})
}).then(() => {
tab_setup_complete[tabname_full] = true;
});
}
function extraNetworksSetupTab(tabname) {
@ -277,21 +280,8 @@ function extraNetworksTabSelected(tabname, id, showPrompt, showNegativePrompt, t
extraNetworksMovePromptToTab(tabname, id, showPrompt, showNegativePrompt);
extraNetworksShowControlsForPage(tabname, tabname_full);
console.log("HERE!!!:", clusterizers[tabname_full].cards_list.scroll_elem.isConnected);
Promise.all([
waitForKeyInObject({ obj: clusterizers, k: tabname_full }),
waitForKeyInObject({ obj: clusterizers_initial_load, k: tabname_full }).then(() => {
waitForBool(clusterizers_initial_load[tabname_full]).then(() => {
Promise.all([
clusterizers[tabname_full].tree_list.waitForElements(),
clusterizers[tabname_full].cards_list.waitForElements(),
]).then(() => {return;});
});
}),
]).then(() => {
extraNetworkClusterizersOnTabLoad(tabname_full);
});
waitForKeyInObject({ k: tabname_full, obj: clusterizers })
.then(() => extraNetworkClusterizersOnTabLoad(tabname_full));
}
function extraNetworksApplyFilter(tabname_full) {
@ -302,6 +292,24 @@ function extraNetworksApplyFilter(tabname_full) {
// We only want to filter/sort the cards list.
clusterizers[tabname_full].cards_list.applyFilter();
// If the search input has changed since selecting a button to populate it
// then we want to disable the button that previously populated the search input.
const txt_search = gradioApp().querySelector(`#${tabname_full}_extra_search`);
if (!txt_search) {
return;
}
// tree view buttons
let btn = gradioApp().querySelector(`#${tabname_full}_tree_list_content_area .tree-list-item[data-selected=""]`);
if (btn && btn.dataset.path !== txt_search.value) {
delete btn.dataset.selected;
}
// dirs view buttons
btn = gradioApp().querySelector(`#${tabname_full}_dirs .extra-network-dirs-view-button[data-selected=""]`);
if (btn && btn.textContent.trim() !== txt_search.value) {
delete btn.dataset.selected;
}
}
function extraNetworksClusterizersEnable(tabname_full) {
@ -349,15 +357,21 @@ function extraNetworksClusterizersRebuild(tabname_full, force) {
}
function extraNetworkClusterizersOnTabLoad(tabname_full) {
/** Enables a tab's clusterizer, updates its data, and rebuilds it. */
if (!(tabname_full in clusterizers)) {
return;
}
extraNetworksClusterizersEnable(tabname_full);
for (const v of Object.values(clusterizers[tabname_full])) {
v.load();
}
return new Promise(resolve => {
/** Enables a tab's clusterizer, updates its data, and rebuilds it. */
if (!(tabname_full in clusterizers)) {
return resolve();
}
(async () => {
extraNetworksClusterizersEnable(tabname_full);
for (const v of Object.values(clusterizers[tabname_full])) {
await v.load();
}
})().then(() => {
return resolve();
});
});
}
function extraNetworksClusterizersOnResize(tabname_full) {
@ -365,23 +379,8 @@ function extraNetworksClusterizersOnResize(tabname_full) {
}
function extraNetworksSetupEventHandlers() {
// Handle window resizes. Delay of 500ms after resize before firing an event
// as a way of "debouncing" resizes.
var resize_timer;
var resize_timeout_ms = 500;
window.addEventListener("resize", () => {
clearTimeout(resize_timer);
resize_timer = setTimeout(() => {extraNetworksClusterizersOnResize();}, resize_timeout_ms);
});
// Handle resizeHandle resizes. Only fires on mouseup after resizing.
window.addEventListener("resizeHandleResized", (e) => {
e.stopPropagation();
const tabname_full = e.target.closest(".extra-network-pane").id.replace("_pane", "");
// Force update rows after resizing.
extraNetworksClusterizersOnResize(tabname_full);
});
// Handle doubleclick events from the resize handle.
// This will automatically fit the left pane to the size of its largest loaded row.
window.addEventListener("resizeHandleDblClick", (e) => {
e.stopPropagation();
const tabname_full = e.target.closest(".extra-network-pane").id.replace("_pane", "");
@ -390,38 +389,19 @@ function extraNetworksSetupEventHandlers() {
return;
}
let max_width = 0;
const scroll_area = e.target.querySelector(".extra-network-tree");
const content = e.target.querySelector(".extra-network-tree-content");
const style = window.getComputedStyle(content, null);
const content_lpad = parseInt(style.getPropertyValue("padding-left"));
const content_rpad = parseInt(style.getPropertyValue("padding-right"));
content.querySelectorAll(".tree-list-item").forEach((row) => {
// Temporarily set the grid columns to maximize column width
// so we can calculate the full width of the row.
const row_style = window.getComputedStyle(row, null);
const prev_grid_template_columns = row.style.gridTemplateColumns;
const n_cols = row_style.getPropertyValue("grid-template-columns").split(" ").length;
row.style.gridTemplateColumns = `repeat(${n_cols}, max-content)`;
if (row.scrollWidth > max_width) {
max_width = row.scrollWidth;
}
//row.style.gridTemplateColumns = prev_grid_template_columns;
});
if (max_width <= 0) {
// Shouldn't be possible, but if tree is hidden then we want to ignore this event
// since there wouldn't be any content to calculate row width. When hidden,
// the resize handle shouldn't exist on page but just in case...
if (gradioApp().getElementById(`${tabname_full}_tree_list_scroll_area`).parentElement.classList.contains("hidden")) {
return;
}
// Add the container's padding to the result.
max_width += content_lpad + content_rpad;
// Add the scrollbar's width to the result. Will add 0 if scrollbar isnt present.
max_width += scroll_area.offsetWidth - scroll_area.clientWidth;
// We know that the tree list is the left column. That is the only one we want to resize.
let max_width = clusterizers[tabname_full].tree_list.getMaxRowWidth();
// Add the resize handle's padding to the result and default to minLeftColWidth if necessary.
max_width = Math.max(max_width + e.detail.pad, e.detail.minLeftColWidth);
e.detail.setLeftColGridTemplate(e.target, max_width);
extraNetworksClusterizersOnResize(tabname_full);
});
}
@ -726,10 +706,10 @@ function extraNetworksControlTreeViewOnClick(event, tabname_full) {
*/
var button = event.currentTarget;
button.classList.toggle("extra-network-control--enabled");
var show = !button.classList.contains("extra-network-control--enabled");
var show = button.classList.contains("extra-network-control--enabled");
gradioApp().getElementById(`${tabname_full}_tree_list_scroll_area`).parentElement.classList.toggle("hidden", show);
extraNetworksClusterizersonResize(tabname_full);
gradioApp().getElementById(`${tabname_full}_tree_list_scroll_area`).parentElement.classList.toggle("hidden", !show);
clusterizers[tabname_full].tree_list.enable(show);
}
function extraNetworksControlDirsViewOnClick(event, tabname_full) {
@ -744,10 +724,9 @@ function extraNetworksControlDirsViewOnClick(event, tabname_full) {
*/
var button = event.currentTarget;
button.classList.toggle("extra-network-control--enabled");
var show = !button.classList.contains("extra-network-control--enabled");
var show = button.classList.contains("extra-network-control--enabled");
gradioApp().getElementById(`${tabname_full}_dirs`).classList.toggle("hidden", show);
extraNetworksClusterizersOnResize(tabname_full);
gradioApp().getElementById(`${tabname_full}_dirs`).classList.toggle("hidden", !show);
}
function extraNetworksControlRefreshOnClick(event, tabname_full) {
@ -763,9 +742,11 @@ function extraNetworksControlRefreshOnClick(event, tabname_full) {
* @param tabname_full The full active tabname.
* i.e. txt2img_lora, img2img_checkpoints, etc.
*/
// reset states
initialUiOptionsLoaded.state = false;
var btn_refresh_internal = gradioApp().getElementById(`${tabname_full}_extra_refresh_internal`);
btn_refresh_internal.dispatchEvent(new Event("click"));
tab_setup_complete = {};
// Fire an event for this button click.
gradioApp().getElementById(`${tabname_full}_extra_refresh_internal`).dispatchEvent(new Event("click"));
}
function closePopup() {

View file

@ -42,6 +42,58 @@ const querySelectorLogError = x => {
return elem;
}
const getComputedPropertyDims = (elem, prop) => {
/** Returns the top/left/bottom/right float dimensions of an element for the specified property. */
const style = window.getComputedStyle(elem, null);
return {
top: parseFloat(style.getPropertyValue(`${prop}-top`)),
left: parseFloat(style.getPropertyValue(`${prop}-left`)),
bottom: parseFloat(style.getPropertyValue(`${prop}-bottom`)),
right: parseFloat(style.getPropertyValue(`${prop}-right`)),
};
}
const getComputedMarginDims = elem => {
const dims = getComputedPropertyDims(elem, "margin");
return {
width: dims.left + dims.right,
height: dims.top + dims.bottom,
};
}
const getComputedPaddingDims = elem => {
const dims = getComputedPropertyDims(elem, "padding");
return {
width: dims.left + dims.right,
height: dims.top + dims.bottom,
};
}
const getComputedBorderDims = elem => {
// computed border will always start with the pixel width so thankfully
// the parseFloat() conversion will just give us the width and ignore the rest.
// Otherwise we'd have to use border-<pos>-width instead.
const dims = getComputedPropertyDims(elem, "border");
return {
width: dims.left + dims.right,
height: dims.top + dims.bottom,
};
}
const getComputedDims = elem => {
/** Returns the full width and height of an element including its margin, padding, and border. */
const width = elem.scrollWidth;
const height = elem.scrollHeight;
const margin = getComputedMarginDims(elem);
const padding = getComputedPaddingDims(elem);
const border = getComputedBorderDims(elem);
return {
width: width + margin.width + padding.width + border.width,
height: height + margin.height + padding.height + border.height,
}
}
function compress(string) {
/** Compresses a string into a base64 encoded GZipped string. */
const cs = new CompressionStream('gzip');
@ -84,42 +136,17 @@ const getComputedValue = function (container, css_property) {
);
};
const calcColsPerRow = function (parent) {
// Returns the number of columns in a row of a flexbox.
//const parent = document.querySelector(selector);
const parent_width = getComputedValue(parent, "width");
const parent_padding_left = getComputedValue(parent, "padding-left");
const parent_padding_right = getComputedValue(parent, "padding-right");
const calcColsPerRow = function (parent, child) {
/** Calculates the number of columns of children that can fit in a parent's visible width. */
const parent_inner_width = parent.offsetWidth - getComputedPaddingDims(parent).width;
return parseInt(parent_inner_width / getComputedDims(child).width);
const child = parent.firstElementChild;
const child_width = getComputedValue(child, "width");
const child_margin_left = getComputedValue(child, "margin-left");
const child_margin_right = getComputedValue(child, "margin-right");
var parent_width_no_padding = parent_width - parent_padding_left - parent_padding_right;
const child_width_with_margin = child_width + child_margin_left + child_margin_right;
parent_width_no_padding += child_margin_left + child_margin_right;
return parseInt(parent_width_no_padding / child_width_with_margin);
}
const calcRowsPerCol = function (container, parent) {
// Returns the number of columns in a row of a flexbox.
//const parent = document.querySelector(selector);
const parent_height = getComputedValue(container, "height");
const parent_padding_top = getComputedValue(container, "padding-top");
const parent_padding_bottom = getComputedValue(container, "padding-bottom");
const child = parent.firstElementChild;
const child_height = getComputedValue(child, "height");
const child_margin_top = getComputedValue(child, "margin-top");
const child_margin_bottom = getComputedValue(child, "margin-bottom");
var parent_height_no_padding = parent_height - parent_padding_top - parent_padding_bottom;
const child_height_with_margin = child_height + child_margin_top + child_margin_bottom;
parent_height_no_padding += child_margin_top + child_margin_bottom;
return parseInt(parent_height_no_padding / child_height_with_margin);
const calcRowsPerCol = function (parent, child) {
/** Calculates the number of rows of children that can fit in a parent's visible height. */
const parent_inner_height = parent.offsetHeight - getComputedPaddingDims(parent).height;
return parseInt(parent_inner_height / getComputedDims(child).height);
}
class ExtraNetworksClusterize {
@ -166,6 +193,8 @@ class ExtraNetworksClusterize {
this.resize_observer_timer = null;
this.resize_observer_timeout_ms = 250;
this.element_observer = null;
this.data_update_timer = null
this.data_update_timeout_ms = 1000;
this.enabled = false;
@ -204,33 +233,35 @@ class ExtraNetworksClusterize {
}
load() {
return waitForElement(`#${this.data_id}`)
.then((elem) => this.data_elem = elem)
.then(() => this.parseJson(this.data_elem.dataset.json))
.then(() => this.init())
.then(() => this.repair())
.then(() => this.applyFilter());
return new Promise(resolve => {
waitForElement(`#${this.data_id}`)
.then((elem) => this.data_elem = elem)
.then(() => this.parseJson(this.data_elem.dataset.json))
.then(() => { return resolve(); });
});
}
parseJson(encoded_str) { /** promise */
return new Promise(resolve => {
// Skip parsing if the string hasnt actually updated.
if (this.encoded_str === encoded_str) {
console.log("no change");
return resolve();
}
return resolve(
Promise.resolve(encoded_str)
.then(v => decompress(v))
.then(v => JSON.parse(v))
.then(v => this.updateJson(v))
.then(() => {console.log("parse json done"); this.encoded_str = encoded_str; })
);
Promise.resolve(encoded_str)
.then(v => decompress(v))
.then(v => JSON.parse(v))
.then(v => this.updateJson(v))
.then(() => this.encoded_str = encoded_str)
.then(() => this.init())
.then(() => this.repair())
.then(() => this.applyFilter())
.then(() => { return resolve(); });
});
}
updateJson(json) { /** promise */
/** Must be overridden by inherited class. */
console.error("Base class method called. Must be overridden by child.");
return new Promise(resolve => {return resolve();});
}
@ -285,12 +316,17 @@ class ExtraNetworksClusterize {
return;
}
this.refresh();
this.refresh(true);
// Rebuild with `force=false` so we only rebuild if dimensions change.
this.rebuild(false);
}
getMaxRowWidth() {
// impliment in subclasses
return;
}
recalculateDims() {
let rebuild_required = false;
let clear_before_return = false;
@ -312,12 +348,24 @@ class ExtraNetworksClusterize {
clear_before_return = true;
}
const child = this.content_elem.querySelector(":not(.clusterize-extra-row)");
if (isNullOrUndefined(child)) {
if (clear_before_return) {
this.clear();
return rebuild_required;
}
}
// Calculate the visible rows and colums for the clusterize-content area.
let n_cols = calcColsPerRow(this.content_elem);
let n_rows = calcRowsPerCol(this.scroll_elem, this.content_elem);
let n_cols = calcColsPerRow(this.content_elem, child);
let n_rows = calcRowsPerCol(this.scroll_elem, child);
n_cols = (isNaN(n_cols) || n_cols <= 0) ? 1 : n_cols;
n_rows = (isNaN(n_rows) || n_rows <= 0) ? 1 : n_rows;
// Add two extra rows to account for partial row visibility on top and bottom
// of the content element view region.
n_rows += 2;
if (n_cols != this.n_cols || n_rows != this.n_rows) {
// Sizes have changed. Update the instance values.
this.n_cols = n_cols;
@ -372,15 +420,12 @@ class ExtraNetworksClusterize {
if (isNullOrUndefined(this.clusterize)) {
// If we have already initialized, don't do it again.
console.log("rebuild:: init");
this.init();
} else if (this.recalculateDims() || force) {
console.log("rebuild:: full", this.scroll_id);
this.destroy();
this.clusterize = null;
this.init();
} else {
console.log("rebuild:: update", this.scroll_id);
this.update();
}
}
@ -419,7 +464,6 @@ class ExtraNetworksClusterize {
}
onResize(elem_id) {
console.log("element resized:", elem_id);
this.updateRows();
}
@ -437,7 +481,6 @@ class ExtraNetworksClusterize {
default:
break;
}
console.log("onElementAdded::", elem_id, document.body.contains(this.scroll_elem));
}
onElementRemoved(elem_id) {
@ -454,48 +497,65 @@ class ExtraNetworksClusterize {
default:
break;
}
console.log("onElementRemoved::", elem_id, document.body.contains(this.scroll_elem));
}
onElementUpdated(elem_id) {
switch(elem_id) {
case this.data_id:
waitForElement(`#${this.data_id}`).then((elem) => this.data_elem = elem);
break;
case this.scroll_id:
this.repair();
break;
case this.content_id:
this.repair();
break;
default:
break;
}
}
onDataChanged(data) {
console.log("onDataChanged::", this.data_id);
this.parseJson(data);
}
setupElementObservers() {
this.element_observer = new MutationObserver((mutations) => {
for (const mutation of mutations) {
if (mutation.type === "childList") {
// added
if (mutation.addedNodes.length > 0) {
for (const node of mutation.addedNodes) {
if (node.id === this.data_id || node.id === this.scroll_id || node.id === this.content_id) {
this.onElementAdded(node.id);
}
}
}
// removed
if (mutation.removedNodes.length > 0) {
for (const node of mutation.removedNodes) {
if (node.id === this.data_id || node.id === this.scroll_id || node.id === this.content_id) {
this.onElementRemoved(node.id);
}
}
}
} else if (mutation.type === "attributes") {
if (mutation.target.id === this.data_id && mutation.attributeName === "data-json") {
this.onDataChanged(mutation.target.dataset.json);
}
}
// don't waste time if this object isn't enabled.
if (!this.enabled) {
return;
}
let data_elem = gradioApp().getElementById(this.data_id);
if (data_elem && data_elem !== this.data_elem) {
this.onElementUpdated(data_elem.id);
} else if (data_elem && data_elem.dataset.json !== this.encoded_str) {
// we don't want to get blasted with data updates so just wait for
// the data to settle down before updating.
clearTimeout(this.data_update_timer);
this.data_update_timer = setTimeout(() => {
this.onDataChanged(data_elem.dataset.json);
}, this.data_update_timeout_ms);
}
let scroll_elem = gradioApp().getElementById(this.scroll_id);
if (scroll_elem && scroll_elem !== this.scroll_elem) {
this.onElementUpdated(scroll_elem.id);
}
let content_elem = gradioApp().getElementById(this.content_id);
if (content_elem && content_elem !== this.content_elem) {
this.onElementUpdated(content_elem.id);
}
});
this.element_observer.observe(gradioApp(), {subtree: true, childList: true, attributes: true});
}
setupResizeHandlers() {
// Handle element resizes. Delay of `resize_observer_timeout_ms` after resize
// before firing an event as a way of "debouncing" resizes.
this.resize_observer = new ResizeObserver((entries) => {
for (const entry of entries) {
console.log("resizeObserver:", entry.target.id);
if (entry.target.id === this.scroll_id || entry.target.id === this.content_id) {
clearTimeout(this.resize_observer_timer);
this.resize_observer_timer = setTimeout(() => this.onResize(entry.id), this.resize_observer_timeout_ms);
@ -619,8 +679,6 @@ class ExtraNetworksClusterizeTreeList extends ExtraNetworksClusterize {
this.data_obj[k].active = false;
}
}
//this.applyFilter();
console.log("updateJson:: done", this.scroll_id);
return resolve();
});
}
@ -642,6 +700,42 @@ class ExtraNetworksClusterizeTreeList extends ExtraNetworksClusterize {
}
}
}
getMaxRowWidth() {
if (!this.enabled) {
// Inactive list is not displayed on screen. Can't calculate size.
return false;
}
if (this.rowCount() === 0) {
// If there is no data then just skip.
return false;
}
let max_width = 0;
for (let i = 0; i < this.content_elem.children.length; i += this.n_cols) {
let row_width = 0;
for (let j = 0; j < this.n_cols; j++) {
const child = this.content_elem.children[i + j];
const child_style = window.getComputedStyle(child, null);
const prev_style = child.style.cssText;
const n_cols = child_style.getPropertyValue("grid-template-columns").split(" ").length;
child.style.gridTemplateColumns = `repeat(${n_cols}, max-content)`;
row_width += getComputedDims(child).width;
// Re-apply previous style.
child.style.cssText = prev_style;
}
max_width = Math.max(max_width, row_width);
}
if (max_width <= 0) {
return;
}
// Add the container's padding to the result.
max_width += getComputedPaddingDims(this.content_elem).width;
// Add the scrollbar's width to the result. Will add 0 if scrollbar isnt present.
max_width += this.scroll_elem.offsetWidth - this.scroll_elem.clientWidth;
return max_width;
}
}
class ExtraNetworksClusterizeCardsList extends ExtraNetworksClusterize {
@ -664,9 +758,6 @@ class ExtraNetworksClusterizeCardsList extends ExtraNetworksClusterize {
active: true,
};
}
//this.applyFilter();
console.log("updateJson:: done", this.scroll_id);
if (this.scroll_id.includes("textual")) { console.log(this.data_obj); }
return resolve();
});
}
@ -770,4 +861,33 @@ class ExtraNetworksClusterizeCardsList extends ExtraNetworksClusterize {
this.applySort();
this.updateRows();
}
getMaxRowWidth() {
if (!this.enabled) {
// Inactive list is not displayed on screen. Can't calculate size.
return false;
}
if (this.rowCount() === 0) {
// If there is no data then just skip.
return false;
}
let max_width = 0;
for (let i = 0; i < this.content_elem.children.length; i += this.n_cols) {
let row_width = 0;
for (let j = 0; j < this.n_cols; j++) {
row_width += getComputedDims(this.content_elem.children[i + j]).width;
}
max_width = Math.max(max_width, row_width);
}
if (max_width <= 0) {
return;
}
// Add the container's padding to the result.
max_width += getComputedPaddingDims(this.content_elem).width;
// Add the scrollbar's width to the result. Will add 0 if scrollbar isnt present.
max_width += this.scroll_elem.offsetWidth - this.scroll_elem.clientWidth;
return max_width;
}
}