Improve queue panel resize UX with edge-drag collapse preview and collapse-to-edge toggle
This commit is contained in:
@@ -4038,19 +4038,19 @@
|
||||
max-width: var(--ml-missing-icon-button-size, 28px);
|
||||
}
|
||||
#model-linker-modal #queue-toggle-icon {
|
||||
right: 8px;
|
||||
min-width: 28px;
|
||||
height: 32px;
|
||||
padding: 0 7px;
|
||||
right: 0;
|
||||
min-width: 12px;
|
||||
width: 12px;
|
||||
height: 76px;
|
||||
padding: 0;
|
||||
color: #cfcfcf;
|
||||
background: rgba(255,255,255,0.05);
|
||||
border-color: rgba(255,255,255,0.12);
|
||||
border-radius: 7px;
|
||||
background: transparent;
|
||||
border: 0;
|
||||
border-radius: 0;
|
||||
box-shadow: none;
|
||||
}
|
||||
#model-linker-modal #queue-toggle-icon:hover {
|
||||
background: rgba(255,255,255,0.075);
|
||||
border-color: rgba(255,255,255,0.18);
|
||||
background: transparent;
|
||||
}
|
||||
#model-linker-modal #model-linker-content,
|
||||
#model-linker-modal .ml-missing-detail-pane,
|
||||
|
||||
@@ -237,7 +237,8 @@
|
||||
|
||||
#model-linker-splitter:hover::before,
|
||||
#model-linker-splitter:focus-visible::before,
|
||||
#model-linker-body.ml-is-resizing-queue #model-linker-splitter::before {
|
||||
#model-linker-body.ml-is-resizing-queue #model-linker-splitter::before,
|
||||
#model-linker-body.ml-queue-preview-collapsed #model-linker-splitter::before {
|
||||
background: rgba(114, 211, 138, 0.78);
|
||||
box-shadow: 0 0 0 3px rgba(114, 211, 138, 0.12);
|
||||
}
|
||||
@@ -245,15 +246,45 @@
|
||||
#queue-toggle-icon {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
right: var(--ml-spacing-6);
|
||||
right: 0;
|
||||
z-index: 1000;
|
||||
padding: var(--ml-spacing-2) var(--ml-spacing-6);
|
||||
width: 12px;
|
||||
min-width: 12px;
|
||||
height: 76px;
|
||||
padding: 0;
|
||||
transform: translateY(-50%);
|
||||
opacity: 0.9;
|
||||
cursor: pointer;
|
||||
background: var(--comfy-input-bg, #2f2f2f);
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: var(--ml-radius-md);
|
||||
opacity: 0.72;
|
||||
cursor: col-resize;
|
||||
background: transparent;
|
||||
border: 0;
|
||||
border-radius: 0;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
#queue-toggle-icon::before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
top: 8px;
|
||||
bottom: 8px;
|
||||
left: 50%;
|
||||
width: 2px;
|
||||
border-radius: 999px;
|
||||
background: rgba(255, 255, 255, 0.34);
|
||||
transform: translateX(-50%);
|
||||
transition: background 0.15s ease, box-shadow 0.15s ease, opacity 0.15s ease;
|
||||
}
|
||||
|
||||
#queue-toggle-icon:hover,
|
||||
#queue-toggle-icon:focus-visible {
|
||||
opacity: 1;
|
||||
outline: none;
|
||||
}
|
||||
|
||||
#queue-toggle-icon:hover::before,
|
||||
#queue-toggle-icon:focus-visible::before,
|
||||
#queue-toggle-icon.is-collapsed::before {
|
||||
background: rgba(114, 211, 138, 0.82);
|
||||
box-shadow: 0 0 0 3px rgba(114, 211, 138, 0.12);
|
||||
}
|
||||
|
||||
.ml-queue-panel,
|
||||
|
||||
+123
-12
@@ -4085,8 +4085,11 @@ class LinkerManagerDialog extends ComfyDialog {
|
||||
try {
|
||||
this.queueToggleIcon = $el("button", {
|
||||
id: "queue-toggle-icon",
|
||||
onclick: () => this.toggleQueueCollapsed()
|
||||
}, [document.createTextNode('⮜')]);
|
||||
type: "button",
|
||||
"aria-label": "Toggle queued selections",
|
||||
onclick: (event) => this.onQueueEdgeClick(event),
|
||||
onmousedown: (event) => this.startQueueEdgeDrag(event)
|
||||
});
|
||||
body.appendChild(this.queueToggleIcon);
|
||||
this.updateQueueToggleIcon();
|
||||
} catch (e) { }
|
||||
@@ -4311,18 +4314,50 @@ class LinkerManagerDialog extends ComfyDialog {
|
||||
updateQueueToggleIcon() {
|
||||
if (!this.queueToggleIcon) return;
|
||||
this.queueToggleIcon.style.display = this.activeTab === 'missing' ? '' : 'none';
|
||||
this.queueToggleIcon.textContent = '';
|
||||
this.queueToggleIcon.removeAttribute('data-tooltip');
|
||||
this.queueToggleIcon.removeAttribute('title');
|
||||
if (this.queueCollapsed) {
|
||||
this.queueToggleIcon.textContent = '⮞';
|
||||
this.setTooltip(this.queueToggleIcon, 'Show queued selections');
|
||||
this.queueToggleIcon.classList.add('is-collapsed');
|
||||
this.queueToggleIcon.setAttribute('aria-label', 'Show queued selections');
|
||||
} else {
|
||||
this.queueToggleIcon.textContent = '⮜';
|
||||
this.setTooltip(this.queueToggleIcon, 'Hide queued selections');
|
||||
this.queueToggleIcon.classList.remove('is-collapsed');
|
||||
this.queueToggleIcon.setAttribute('aria-label', 'Hide queued selections');
|
||||
}
|
||||
}
|
||||
|
||||
onQueueEdgeClick(event) {
|
||||
if (this._suppressQueueEdgeClick) {
|
||||
event?.preventDefault?.();
|
||||
event?.stopPropagation?.();
|
||||
this._suppressQueueEdgeClick = false;
|
||||
return;
|
||||
}
|
||||
this.toggleQueueCollapsed();
|
||||
}
|
||||
|
||||
startQueueEdgeDrag(event) {
|
||||
if (event.button !== 0 || !this.queueCollapsed || !this.queueElement) return;
|
||||
|
||||
const startX = event.clientX;
|
||||
const previousCursor = document.body.style.cursor;
|
||||
event.preventDefault();
|
||||
document.body.style.cursor = 'col-resize';
|
||||
this._suppressQueueEdgeClick = true;
|
||||
this.splitterElement.style.display = '';
|
||||
document.getElementById('model-linker-body')?.classList.add('ml-queue-preview-collapsed');
|
||||
this.queueElement.style.display = 'none';
|
||||
this.queueElement.style.width = '56px';
|
||||
this.startSplitDrag(
|
||||
{ clientX: startX, preventDefault: () => {} },
|
||||
{ startWidth: 56, edgeOpen: true, previousCursor, startCollapsedPreview: true }
|
||||
);
|
||||
}
|
||||
|
||||
// Begin split drag for resizable panels
|
||||
startSplitDrag(e) {
|
||||
startSplitDrag(e, { startWidth = null, edgeOpen = false, previousCursor = null, startCollapsedPreview = false } = {}) {
|
||||
try {
|
||||
e?.preventDefault?.();
|
||||
if (!this.queueElement) return;
|
||||
const rect = this.queueElement.getBoundingClientRect();
|
||||
const body = document.getElementById('model-linker-body');
|
||||
@@ -4331,10 +4366,14 @@ class LinkerManagerDialog extends ComfyDialog {
|
||||
body?.classList.add('ml-is-resizing-queue');
|
||||
this._splitStart = {
|
||||
x: e.clientX,
|
||||
startWidth: rect.width,
|
||||
startWidth: Number.isFinite(startWidth) ? startWidth : rect.width,
|
||||
containerWidth: bodyRect.width
|
||||
};
|
||||
this._lastSplitDragApply = 0;
|
||||
this._splitPreviewCollapsed = !!startCollapsedPreview;
|
||||
this._splitEdgeOpen = !!edgeOpen;
|
||||
this._splitEdgeOpenedPastThreshold = false;
|
||||
this._splitPreviousCursor = previousCursor;
|
||||
this._prevUserSelect = document.body.style.userSelect;
|
||||
document.body.style.userSelect = 'none';
|
||||
this._onSplitMove = (ev) => this.onSplitDrag(ev);
|
||||
@@ -4348,18 +4387,36 @@ class LinkerManagerDialog extends ComfyDialog {
|
||||
if (!this._splitDragging || !this._splitStart || !this.queueElement) return;
|
||||
const dx = e.clientX - this._splitStart.x;
|
||||
let newW = this._splitStart.startWidth - dx;
|
||||
const minW = 240;
|
||||
const minW = 56;
|
||||
const maxW = Math.max(minW, Math.floor(this._splitStart.containerWidth - 360));
|
||||
if (newW < minW) newW = minW;
|
||||
if (newW > maxW) newW = maxW;
|
||||
this._pendingSplitWidth = Math.round(newW);
|
||||
if (this._splitEdgeOpen && this._pendingSplitWidth > 140) {
|
||||
this._splitEdgeOpenedPastThreshold = true;
|
||||
if (this.queueCollapsed) {
|
||||
this.queueCollapsed = false;
|
||||
try { localStorage.setItem('model_linker_queue_collapsed', '0'); } catch (error) { }
|
||||
this.updateQueueToggleIcon();
|
||||
this.updateQueuePanel();
|
||||
}
|
||||
}
|
||||
const previewCollapsed = this._splitEdgeOpen
|
||||
? (!this._splitEdgeOpenedPastThreshold || this._pendingSplitWidth <= 140)
|
||||
: this._pendingSplitWidth <= 140;
|
||||
if (previewCollapsed !== this._splitPreviewCollapsed) {
|
||||
this._splitPreviewCollapsed = previewCollapsed;
|
||||
const body = document.getElementById('model-linker-body');
|
||||
body?.classList.toggle('ml-queue-preview-collapsed', previewCollapsed);
|
||||
this.queueElement.style.display = previewCollapsed ? 'none' : '';
|
||||
}
|
||||
const now = performance.now();
|
||||
if (this._lastSplitDragApply && now - this._lastSplitDragApply < 33) return;
|
||||
this._lastSplitDragApply = now;
|
||||
if (this._splitDragFrame) return;
|
||||
this._splitDragFrame = requestAnimationFrame(() => {
|
||||
this._splitDragFrame = null;
|
||||
if (!this._splitDragging || !this.queueElement || !this._pendingSplitWidth) return;
|
||||
if (!this._splitDragging || !this.queueElement || !this._pendingSplitWidth || this._splitPreviewCollapsed) return;
|
||||
this.queueElement.style.width = `${this._pendingSplitWidth}px`;
|
||||
});
|
||||
}
|
||||
@@ -4371,18 +4428,72 @@ class LinkerManagerDialog extends ComfyDialog {
|
||||
cancelAnimationFrame(this._splitDragFrame);
|
||||
this._splitDragFrame = null;
|
||||
}
|
||||
if (this.queueElement && this._pendingSplitWidth) {
|
||||
if (this.queueElement && this._pendingSplitWidth && !this._splitPreviewCollapsed) {
|
||||
this.queueElement.style.width = `${this._pendingSplitWidth}px`;
|
||||
}
|
||||
document.getElementById('model-linker-body')?.classList.remove('ml-is-resizing-queue');
|
||||
const body = document.getElementById('model-linker-body');
|
||||
body?.classList.remove('ml-is-resizing-queue');
|
||||
body?.classList.remove('ml-queue-preview-collapsed');
|
||||
document.removeEventListener('mousemove', this._onSplitMove);
|
||||
if (this._splitEdgeOpen && !this._pendingSplitWidth) {
|
||||
try {
|
||||
const restoreWidth = Math.max(240, Math.round(this._splitStart?.startWidth || 320));
|
||||
this.queueElement.style.width = `${restoreWidth}px`;
|
||||
this.queueElement.style.display = '';
|
||||
if (this.splitterElement) this.splitterElement.style.display = '';
|
||||
localStorage.setItem('model_linker_split_w', String(restoreWidth));
|
||||
} catch (e) { }
|
||||
this.queueCollapsed = false;
|
||||
try { localStorage.setItem('model_linker_queue_collapsed', '0'); } catch (e) { }
|
||||
this.updateQueueVisibility();
|
||||
this.updateQueuePanel();
|
||||
this._splitEdgeOpen = false;
|
||||
this._pendingSplitWidth = null;
|
||||
this._lastSplitDragApply = 0;
|
||||
this._splitPreviewCollapsed = false;
|
||||
try { document.body.style.userSelect = this._prevUserSelect || ''; } catch (e) { }
|
||||
try { document.body.style.cursor = this._splitPreviousCursor || ''; } catch (e) { }
|
||||
this._splitPreviousCursor = null;
|
||||
this._splitEdgeOpenedPastThreshold = false;
|
||||
setTimeout(() => { this._suppressQueueEdgeClick = false; }, 0);
|
||||
return;
|
||||
}
|
||||
const shouldCollapse = !!this._splitPreviewCollapsed;
|
||||
if (shouldCollapse) {
|
||||
try {
|
||||
const restoreWidth = Math.max(240, Math.round(this._splitStart?.startWidth || 320));
|
||||
this.queueElement.style.width = `${restoreWidth}px`;
|
||||
this.queueElement.style.display = '';
|
||||
localStorage.setItem('model_linker_split_w', String(restoreWidth));
|
||||
} catch (e) { }
|
||||
this._pendingSplitWidth = null;
|
||||
this._lastSplitDragApply = 0;
|
||||
this._splitPreviewCollapsed = false;
|
||||
this._splitEdgeOpen = false;
|
||||
this._splitEdgeOpenedPastThreshold = false;
|
||||
try { document.body.style.userSelect = this._prevUserSelect || ''; } catch (e) { }
|
||||
try { document.body.style.cursor = this._splitPreviousCursor || ''; } catch (e) { }
|
||||
this._splitPreviousCursor = null;
|
||||
this.setQueueCollapsed(true);
|
||||
setTimeout(() => { this._suppressQueueEdgeClick = false; }, 0);
|
||||
return;
|
||||
}
|
||||
if (this.queueElement) this.queueElement.style.display = '';
|
||||
try {
|
||||
const rect = this.queueElement.getBoundingClientRect();
|
||||
localStorage.setItem('model_linker_split_w', String(Math.round(rect.width)));
|
||||
} catch (e) { }
|
||||
this._pendingSplitWidth = null;
|
||||
this._lastSplitDragApply = 0;
|
||||
this._splitPreviewCollapsed = false;
|
||||
this._splitEdgeOpen = false;
|
||||
this._splitEdgeOpenedPastThreshold = false;
|
||||
try { document.body.style.userSelect = this._prevUserSelect || ''; } catch (e) { }
|
||||
if (this._splitPreviousCursor !== null) {
|
||||
try { document.body.style.cursor = this._splitPreviousCursor || ''; } catch (e) { }
|
||||
this._splitPreviousCursor = null;
|
||||
}
|
||||
setTimeout(() => { this._suppressQueueEdgeClick = false; }, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user