Fix dblclick of resize grid handle. Fix resizing bugs.

This commit is contained in:
Sj-Si 2024-05-24 15:20:30 -04:00
parent ecfa65f12a
commit 16495c203e
4 changed files with 206 additions and 139 deletions

View File

@ -676,34 +676,24 @@ class ExtraNetworksTab {
this.applyFilter(this.txt_search_elem.value);
}
autoSetTreeWidth() {
const row = this.container_elem.querySelector(".resize-handle-row");
if (!isElementLogError(row)) {
autoSetTreeWidth(handle_elem) {
const siblings = this.resize_grid.getSiblings(handle_elem);
const tree_item = siblings.prev;
// Only process if the prev element is our tree view.
if (tree_item.elem !== this.tree_list.scroll_elem.closest(".resize-grid--cell")) {
return;
}
let new_size_px = this.tree_list.getMaxRowWidth();
if (!isNumber(new_size_px)) {
return;
}
const left_col = row.firstElementChild;
if (!isElementLogError(left_col)) {
return;
}
// If the left column is hidden then we don't want to do anything.
if (left_col.classList.contains("hidden")) {
return;
}
const pad = parseFloat(row.style.gridTemplateColumns.split(" ")[1]);
const min_left_col_width = parseFloat(left_col.style.flexBasis.slice(0, -2));
// We know that the tree list is the left column. That is the only one we want to resize.
let max_width = this.tree_list.getMaxRowWidth();
if (!isNumber(max_width)) {
return;
}
// Add the resize handle's padding to the result and default to minLeftColWidth if necessary.
max_width = Math.max(max_width + pad, min_left_col_width);
// Mimicks resizeHandle.js::setLeftColGridTemplate().
row.style.gridTemplateColumns = `${max_width}px ${pad}px 1fr`;
// Account for border dims on the container.
const div_tree = this.tree_list.scroll_elem.closest(".extra-network-content--tree-view");
new_size_px += div_tree.offsetWidth - div_tree.clientWidth;
// Clamp the value to the min_size.
new_size_px = Math.max(tree_item.min_size, new_size_px);
tree_item.parent.resizeItem(tree_item, new_size_px);
}
async clearSelectedButtons({excluded_div_ids} = {}) {
@ -1612,11 +1602,12 @@ function extraNetworksSetupEventDelegators() {
dbl_press_time_ms = 0;
}
window.addEventListener("resizeHandleDblClick", event => {
// See resizeHandle.js::onDoubleClick() for event detail.
window.addEventListener("resize_grid_handle_dblclick", event => {
// See resizeGrid.js::ResizeGrid.setupEvents() for event detail.
event.stopPropagation();
const pane = event.target.closest(".extra-network-pane");
extra_networks_tabs[pane.dataset.tabnameFull].autoSetTreeWidth();
const handle = event.target.closest(".resize-grid--handle");
extra_networks_tabs[pane.dataset.tabnameFull].autoSetTreeWidth(handle);
});
// Debounce search text input. This way we only search after user is done typing.

View File

@ -343,23 +343,33 @@ class ExtraNetworksClusterizeTreeList extends ExtraNetworksClusterize {
let row_width = 0;
for (let j = 0; j < this.options.cols_in_block; 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 += child.scrollWidth;
// Restore previous style.
child.style.cssText = prev_style;
// Child first element is the indent div. Just use offset for this
// since we do some overlapping with ::after in CSS.
row_width += child.children[0].offsetWidth;
// Button is second element. We want entire scroll width of this one.
// But first we need to allow it to shrink to content.
const prev_css_text = child.children[1].cssText;
child.children[1].style.flex = "0 1 auto";
row_width += child.children[1].scrollWidth;
// Add the button label's overflow to the width.
const lbl = child.querySelector(".tree-list-item-label");
row_width += lbl.scrollWidth - lbl.offsetWidth;
// Revert changes to element style.
if (!prev_css_text) {
child.children[1].removeAttribute("style");
} else {
child.children[1].cssText = prev_css_text;
}
}
max_width = Math.max(max_width, row_width);
max_width = Math.max(row_width, max_width);
}
if (max_width <= 0) {
return;
}
// Adds the scroll element's border and the scrollbar's width to the result.
// If scrollbar isn't visible, then only the element border is added.
max_width += this.scroll_elem.offsetWidth - this.scroll_elem.clientWidth;
// Adds the scroll_elem's scrollbar and padding to the result.
// If scrollbar isn't visible, then only the element border/padding is added.
max_width += this.scroll_elem.offsetWidth - this.content_elem.offsetWidth;
return max_width;
}

View File

@ -535,26 +535,6 @@ class ResizeGridAxis extends ResizeGridItem {
}
}
getSiblings(handle_elem) {
/** Returns the nearest visible ResizeGridItems surrounding a ResizeGridHandle.
*
* Args:
* handle_elem (Element): The handle element in the grid to lookup.
*
* Returns:
* Object: Keys=(prev, next). Values are ResizeGridItems.
*/
let prev = this.getItem({elem: handle_elem.previousElementSibling});
if (!prev.visible) {
prev = prev.parent.items.slice(0, this.items.indexOf(prev)).findLast(x => x.visible);
}
let next = this.getItem({elem: handle_elem.nextElementSibling});
if (!next.visible) {
next = next.parent.items.slice(this.items.indexOf(next) + 1).findLast(x => x.visible);
}
return {prev: prev, next: next};
}
updateVisibleHandles() {
/** Sets the visibility of each ResizeGridHandle based on surrounding items. */
for (const item of this.items) {
@ -657,6 +637,78 @@ class ResizeGridAxis extends ResizeGridItem {
}
}
resizeItem(item, size_px) {
// Don't resize invisible items.
if (!item.visible) {
return;
}
// Don't resize item if it is the only visible item in the axis.
if (this.items.filter(x => x.visible).length === 1) {
return;
}
if (size_px < item.min_size) {
console.error(`Requested size is too small: ${size_px} < ${item.min_size}`);
return;
}
const dims = this.elem.getBoundingClientRect();
let max_size = parseInt(this.axis === 0 ? dims.width : dims.height);
const vis_siblings = this.items.filter(x => x.visible && x !== item);
max_size -= vis_siblings.reduce((acc, obj) => {
return acc + obj.min_size + (obj.handle.visible ? obj.handle.pad_px : 0);
}, 0);
if (size_px > max_size) {
console.error(`Requested size is too large: ${size_px} > ${max_size}`);
return;
}
// Find the direct sibling of this item.
const idx = this.items.indexOf(item);
isNullOrUndefinedThrowError(item); // Indicates programmer error.
// Look after item.
let sibling = this.items.slice(idx + 1).find(x => x.visible);
if (isNullOrUndefined(sibling)) {
// No valid siblings after item, look before item.
sibling = this.items.slice(0, idx).findLast(x => x.visible);
isNullOrUndefinedThrowError(sibling); // Indicates programmer error.
}
const sibling_idx = this.items.indexOf(sibling);
const _make_room = (sibling, others, tot_px) => {
let rem = tot_px;
// Shrink from the sibling first.
rem = sibling.shrink(rem, {limit_to_base: false});
if (rem <= 0) {
return;
}
// Shrink all other items next, starting from the end.
for (const other of others.slice().reverse()) {
rem = other.shrink(rem, {limit_to_base: false});
if (rem <= 0) {
return;
}
}
// This indicates a programmer error.
throw new Error(`No space for item. tot: ${tot_px}, rem: ${rem}`);
};
const curr_size = item.getSize();
if (size_px < curr_size) { // shrink
item.shrink(curr_size - size_px, {limit_to_base: false});
sibling.grow(-1);
} else if (size_px > curr_size) { // grow
const others = this.items.filter((x, i) => {
return x.visible && i !== idx && i !== sibling_idx;
});
_make_room(sibling, others, size_px - curr_size);
item.setSize(size_px);
}
}
show({id, idx, elem, item} = {}) {
/** Shows an item along this axis.
*
@ -875,6 +927,7 @@ class ResizeGrid extends ResizeGridAxis {
handle.elem.setPointerCapture(event.pointerId);
// Temporarily set styles for elements. These are cleared on pointerup.
// Also cleared if dblclick is fired.
// See `onMove()` comments for more info.
prev.setSize(prev.getSize());
next.setSize(next.getSize());
@ -888,6 +941,34 @@ class ResizeGrid extends ResizeGridAxis {
} else {
document.body.classList.add('resizing-row');
}
if (!dblclick_timer) {
handle.elem.dataset.awaitDblClick = '';
dblclick_timer = setTimeout(
(elem) => {
dblclick_timer = null;
delete elem.dataset.awaitDblClick;
},
DBLCLICK_TIME_MS,
handle.elem
);
} else if ('awaitDblClick' in handle.elem.dataset) {
clearTimeout(dblclick_timer);
dblclick_timer = null;
delete handle.elem.dataset.awaitDblClick;
handle.elem.dispatchEvent(
new CustomEvent('resize_grid_handle_dblclick', {
bubbles: true,
detail: this,
})
);
prev.render();
next.render();
prev = null;
handle = null;
next = null;
}
},
{signal: this.event_abort_controller.signal}
);
@ -918,13 +999,9 @@ class ResizeGrid extends ResizeGridAxis {
window.addEventListener(
'pointerup',
(event) => {
if (
isNullOrUndefined(prev) ||
isNullOrUndefined(handle) ||
isNullOrUndefined(next)
) {
return;
}
document.body.classList.remove('resizing');
document.body.classList.remove('resizing-col');
document.body.classList.remove('resizing-row');
if (event.target.hasPointerCapture(event.pointerId)) {
event.target.releasePointerCapture(event.pointerId);
@ -933,6 +1010,15 @@ class ResizeGrid extends ResizeGridAxis {
if (event.pointerType === 'mouse' && event.button !== 0) {
return;
}
if (
isNullOrUndefined(prev) ||
isNullOrUndefined(handle) ||
isNullOrUndefined(next)
) {
return;
}
if (event.pointerType === 'touch') {
touch_count--;
}
@ -948,31 +1034,6 @@ class ResizeGrid extends ResizeGridAxis {
prev.render();
next.render();
document.body.classList.remove('resizing');
document.body.classList.remove('resizing-col');
document.body.classList.remove('resizing-row');
if (!dblclick_timer) {
handle.elem.dataset.awaitDblClick = '';
dblclick_timer = setTimeout(
(elem) => {
dblclick_timer = null;
delete elem.dataset.awaitDblClick;
},
DBLCLICK_TIME_MS,
handle.elem
);
} else if ('awaitDblClick' in handle.elem.dataset) {
clearTimeout(dblclick_timer);
dblclick_timer = null;
delete handle.elem.dataset.awaitDblClick;
handle.elem.dispatchEvent(
new CustomEvent('resize_handle_dblclick', {
bubbles: true,
detail: this,
})
);
}
prev = null;
handle = null;
next = null;
@ -1092,6 +1153,26 @@ class ResizeGrid extends ResizeGridAxis {
}
}
getSiblings(handle_elem) {
/** Returns the nearest visible ResizeGridItems surrounding a ResizeGridHandle.
*
* Args:
* handle_elem (Element): The handle element in the grid to lookup.
*
* Returns:
* Object: Keys=(prev, next). Values are ResizeGridItems.
*/
let prev = this.getItem({elem: handle_elem.previousElementSibling});
if (!prev.visible) {
prev = prev.parent.items.slice(0, this.items.indexOf(prev)).findLast(x => x.visible);
}
let next = this.getItem({elem: handle_elem.nextElementSibling});
if (!next.visible) {
next = next.parent.items.slice(this.items.indexOf(next) + 1).findLast(x => x.visible);
}
return {prev: prev, next: next};
}
onMove(event, a, handle, b) {
/** Handles pointermove events by calculating and setting new size of elements.
*

View File

@ -1269,6 +1269,7 @@ body.resizing.resize-grid-row {
height: 100%;
/* Use scroll instead of auto so that content size doesn't change when there is no content. */
overflow: clip scroll;
padding: var(--spacing-md) 0 var(--spacing-md) var(--spacing-md);
}
.clusterize-content {
@ -1277,7 +1278,6 @@ body.resizing.resize-grid-row {
/* need to manually set the gap to 0 to fix item dimension calcs. */
gap: 0;
counter-reset: clusterize-counter;
padding: var(--spacing-md);
}
.clusterize-extra-row {
@ -1533,33 +1533,33 @@ body.resizing.resize-grid-row {
/* BUTTON ELEMENTS */
.tree-list-item {
display: grid;
grid-auto-rows: 1fr;
grid-template-columns: auto 1fr;
display: flex;
flex-direction: row;
flex-wrap: nowrap;
width: 100%;
height: calc(var(--button-large-text-size) + (2 * var(--spacing-sm)));
position: relative;
width: 100%;
padding: 0;
margin: 0;
user-select: none;
border: none;
}
/* <button> */
.tree-list-item button {
display: grid;
flex: 1;
width: 100%;
overflow: hidden;
z-index: 0;
padding: 0 !important;
margin: 0 !important;
border: none !important;
user-select: none;
grid-template-rows: min-content;
grid-template-areas: "leading-action leading-visual label trailing-visual trailing-action";
grid-template-columns: min-content min-content minmax(calc(var(--body-text-size) * 1), auto) min-content min-content;
grid-gap: var(--spacing-sm);
place-items: center stretch;
text-align: center;
/* prevents height from changing on overflow */
}
.tree-list-item > button {
display: flex;
flex: 1;
flex-direction: row;
flex-wrap: nowrap;
gap: var(--spacing-sm);
user-select: none;
padding: 0 !important;
margin: 0 !important;
border: none !important;
min-width: 0;
text-align: center;
place-items: center stretch;
}
.tree-list-item:hover {
@ -1568,7 +1568,10 @@ body.resizing.resize-grid-row {
background: var(--button-secondary-background-fill-hover);
}
.tree-list-item button > span {
.tree-list-item > button > span {
display: block;
flex: 0 0 auto;
text-align: center;
font-size: var(--button-large-text-size);
color: var(--button-secondary-text-color);
}
@ -1632,10 +1635,8 @@ body.resizing.resize-grid-row {
}
/* Text for button. */
.tree-list-item-label {
position: relative;
grid-area: label;
padding-left: 0.25em;
.tree-list-item .tree-list-item-label {
flex: 0 1 auto;
text-align: start;
}
@ -1649,39 +1650,30 @@ body.resizing.resize-grid-row {
/* Icon for button. */
.tree-list-item-visual {
pointer-events: none;
align-items: right;
}
/* Icon for button when it is before label. */
.tree-list-item-visual--leading {
grid-area: leading-visual;
.tree-list-item .tree-list-item-visual--leading {
width: var(--button-large-text-size);
text-align: right;
}
/* Icon for button when it is after label. */
.tree-list-item-visual--trailing {
grid-area: trailing-visual;
width: var(--button-large-text-size);
text-align: right;
margin-left: auto;
}
/* Dropdown arrow for button. */
.tree-list-item-action--leading {
grid-area: leading-action;
}
.tree-list-item-action--leading {}
.tree-list-item--file .tree-list-item-action--leading {
visibility: hidden;
}
/* Allow trailing action to overlap previous columns when area is too small. */
.tree-list-item-action--trailing {
z-index: 10;
grid-area: 1 / label / 1 / trailing-action;
justify-self: end;
padding-left: var(--spacing-sm);
}
.tree-list-item-action--trailing {}
/* Force background color on hover to hide any overlapped column content. */
.tree-list-item:hover .tree-list-item-action--trailing {
@ -1799,13 +1791,6 @@ body.resizing.resize-grid-row {
outline-offset: calc(-1 * var(--spacing-xs));
}
.tree-list-item {
position: relative;
overflow: hidden;
z-index: 0;
user-select: none;
}
.tree-list-item:not(.pressed):hover {
-webkit-transition: all 0.05s ease-in-out;
transition: all 0.05s ease-in-out;