mirror of
https://github.com/willmiao/ComfyUI-Lora-Manager.git
synced 2026-03-23 22:22:11 -03:00
fix: improve loras widget drag functionality in Vue DOM render mode
- Use pointer events (pointerdown/pointermove/pointerup/pointercancel) with proper capture - Fix drag not updating strength values by avoiding re-renders during drag - Fix cursor stuck in resize state by ensuring proper cleanup - Fix cursor showing wrong icon on hover (should be pointer) - Ensure strength values display fixed width with 2 decimal places - Remove unnecessary data-capture-wheel attribute (no wheel adjustment in loras widget) - Add font-variant-numeric: tabular-nums for consistent number display This ensures loras widget works consistently in both Canvas and Vue DOM render modes.
This commit is contained in:
@@ -107,6 +107,7 @@
|
|||||||
margin-bottom: 5px;
|
margin-bottom: 5px;
|
||||||
position: relative;
|
position: relative;
|
||||||
cursor: ew-resize;
|
cursor: ew-resize;
|
||||||
|
touch-action: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.lm-toggle-container {
|
.lm-toggle-container {
|
||||||
@@ -155,6 +156,8 @@
|
|||||||
margin-bottom: 4px;
|
margin-bottom: 4px;
|
||||||
border: 1px solid transparent;
|
border: 1px solid transparent;
|
||||||
background-color: rgba(45, 55, 72, 0.7);
|
background-color: rgba(45, 55, 72, 0.7);
|
||||||
|
touch-action: none;
|
||||||
|
cursor: pointer !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.lm-lora-entry[data-active="false"] {
|
.lm-lora-entry[data-active="false"] {
|
||||||
@@ -486,6 +489,8 @@ body.lm-lora-reordering * {
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
transition: all 0.2s ease;
|
transition: all 0.2s ease;
|
||||||
margin-top: -2px;
|
margin-top: -2px;
|
||||||
|
touch-action: none;
|
||||||
|
cursor: pointer !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.lm-lora-clip-entry[data-active="false"] {
|
.lm-lora-clip-entry[data-active="false"] {
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import { createMenuItem, createDropIndicator } from "./loras_widget_components.j
|
|||||||
import { parseLoraValue, formatLoraValue, syncClipStrengthIfCollapsed, saveRecipeDirectly, copyToClipboard, showToast, moveLoraByDirection, getDropTargetIndex } from "./loras_widget_utils.js";
|
import { parseLoraValue, formatLoraValue, syncClipStrengthIfCollapsed, saveRecipeDirectly, copyToClipboard, showToast, moveLoraByDirection, getDropTargetIndex } from "./loras_widget_utils.js";
|
||||||
|
|
||||||
// Function to handle strength adjustment via dragging
|
// Function to handle strength adjustment via dragging
|
||||||
export function handleStrengthDrag(name, initialStrength, initialX, event, widget, isClipStrength = false) {
|
export function handleStrengthDrag(name, initialStrength, initialX, event, widget, isClipStrength = false, updateWidget = true) {
|
||||||
// Calculate drag sensitivity (how much the strength changes per pixel)
|
// Calculate drag sensitivity (how much the strength changes per pixel)
|
||||||
// Using 0.01 per 10 pixels of movement
|
// Using 0.01 per 10 pixels of movement
|
||||||
const sensitivity = 0.001;
|
const sensitivity = 0.001;
|
||||||
@@ -37,18 +37,21 @@ export function handleStrengthDrag(name, initialStrength, initialX, event, widge
|
|||||||
syncClipStrengthIfCollapsed(lorasData[loraIndex]);
|
syncClipStrengthIfCollapsed(lorasData[loraIndex]);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update the widget value
|
// Update the widget value only if updateWidget flag is true
|
||||||
widget.value = formatLoraValue(lorasData);
|
// This allows us to update inputs directly during drag without triggering re-render
|
||||||
|
if (updateWidget) {
|
||||||
|
widget.value = formatLoraValue(lorasData);
|
||||||
|
}
|
||||||
|
|
||||||
// Force re-render via callback
|
// Force re-render via callback only if updateWidget is true
|
||||||
if (widget.callback) {
|
if (updateWidget && widget.callback) {
|
||||||
widget.callback(widget.value);
|
widget.callback(widget.value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Function to handle proportional strength adjustment for all LoRAs via header dragging
|
// Function to handle proportional strength adjustment for all LoRAs via header dragging
|
||||||
export function handleAllStrengthsDrag(initialStrengths, initialX, event, widget) {
|
export function handleAllStrengthsDrag(initialStrengths, initialX, event, widget, updateWidget = true) {
|
||||||
// Define sensitivity (less sensitive than individual adjustment)
|
// Define sensitivity (less sensitive than individual adjustment)
|
||||||
const sensitivity = 0.0005;
|
const sensitivity = 0.0005;
|
||||||
|
|
||||||
@@ -87,11 +90,13 @@ export function handleAllStrengthsDrag(initialStrengths, initialX, event, widget
|
|||||||
lorasData[index].clipStrength = Number(newClipStrength);
|
lorasData[index].clipStrength = Number(newClipStrength);
|
||||||
});
|
});
|
||||||
|
|
||||||
// Update widget value
|
// Update widget value only if updateWidget flag is true
|
||||||
widget.value = formatLoraValue(lorasData);
|
if (updateWidget) {
|
||||||
|
widget.value = formatLoraValue(lorasData);
|
||||||
|
}
|
||||||
|
|
||||||
// Force re-render via callback
|
// Force re-render via callback only if updateWidget is true
|
||||||
if (widget.callback) {
|
if (updateWidget && widget.callback) {
|
||||||
widget.callback(widget.value);
|
widget.callback(widget.value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -107,12 +112,14 @@ export function initDrag(
|
|||||||
dragCallbacks = {}
|
dragCallbacks = {}
|
||||||
) {
|
) {
|
||||||
let isDragging = false;
|
let isDragging = false;
|
||||||
|
let activePointerId = null;
|
||||||
let initialX = 0;
|
let initialX = 0;
|
||||||
let initialStrength = 0;
|
let initialStrength = 0;
|
||||||
|
let currentDragElement = null;
|
||||||
const { onDragStart, onDragEnd } = dragCallbacks;
|
const { onDragStart, onDragEnd } = dragCallbacks;
|
||||||
|
|
||||||
// Create a drag handler
|
// Create a drag handler using pointer events for Vue DOM render mode compatibility
|
||||||
dragEl.addEventListener('mousedown', (e) => {
|
dragEl.addEventListener('pointerdown', (e) => {
|
||||||
// Skip if clicking on toggle or strength control areas
|
// Skip if clicking on toggle or strength control areas
|
||||||
if (e.target.closest('.lm-lora-toggle') ||
|
if (e.target.closest('.lm-lora-toggle') ||
|
||||||
e.target.closest('input') ||
|
e.target.closest('input') ||
|
||||||
@@ -131,6 +138,12 @@ export function initDrag(
|
|||||||
initialX = e.clientX;
|
initialX = e.clientX;
|
||||||
initialStrength = isClipStrength ? loraData.clipStrength : loraData.strength;
|
initialStrength = isClipStrength ? loraData.clipStrength : loraData.strength;
|
||||||
isDragging = true;
|
isDragging = true;
|
||||||
|
activePointerId = e.pointerId;
|
||||||
|
currentDragElement = e.currentTarget;
|
||||||
|
|
||||||
|
// Capture pointer to receive all subsequent events regardless of stopPropagation
|
||||||
|
const target = e.currentTarget;
|
||||||
|
target.setPointerCapture(e.pointerId);
|
||||||
|
|
||||||
// Add class to body to enforce cursor style globally
|
// Add class to body to enforce cursor style globally
|
||||||
document.body.classList.add('lm-lora-strength-dragging');
|
document.body.classList.add('lm-lora-strength-dragging');
|
||||||
@@ -139,21 +152,29 @@ export function initDrag(
|
|||||||
onDragStart();
|
onDragStart();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Prevent text selection during drag
|
// Prevent text selection and default behavior
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
|
e.stopPropagation();
|
||||||
});
|
});
|
||||||
|
|
||||||
// Use the document for move and up events to ensure drag continues
|
dragEl.addEventListener('pointermove', (e) => {
|
||||||
// even if mouse leaves the element
|
|
||||||
document.addEventListener('mousemove', (e) => {
|
|
||||||
if (!isDragging) return;
|
if (!isDragging) return;
|
||||||
|
|
||||||
// Call the strength adjustment function
|
// Stop propagation to prevent canvas from interfering
|
||||||
handleStrengthDrag(name, initialStrength, initialX, e, widget, isClipStrength);
|
e.stopPropagation();
|
||||||
|
|
||||||
// Force re-render to show updated strength value
|
// Call the strength adjustment function without updating widget.value during drag
|
||||||
if (renderFunction) {
|
handleStrengthDrag(name, initialStrength, initialX, e, widget, isClipStrength, false);
|
||||||
renderFunction(widget.value, widget);
|
|
||||||
|
// Update strength input directly instead of re-rendering to avoid losing event listeners
|
||||||
|
const strengthInput = currentDragElement.querySelector('.lm-lora-strength-input');
|
||||||
|
if (strengthInput) {
|
||||||
|
const lorasData = parseLoraValue(widget.value);
|
||||||
|
const loraData = lorasData.find(l => l.name === name);
|
||||||
|
if (loraData) {
|
||||||
|
const strengthValue = isClipStrength ? loraData.clipStrength : loraData.strength;
|
||||||
|
strengthInput.value = strengthValue;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Prevent showing the preview tooltip during drag
|
// Prevent showing the preview tooltip during drag
|
||||||
@@ -162,28 +183,52 @@ export function initDrag(
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
document.addEventListener('mouseup', () => {
|
const endDrag = (e) => {
|
||||||
if (isDragging) {
|
if (!isDragging) return;
|
||||||
isDragging = false;
|
|
||||||
// Remove the class to restore normal cursor behavior
|
e.stopPropagation();
|
||||||
document.body.classList.remove('lm-lora-strength-dragging');
|
|
||||||
|
// Release pointer capture if still have the element
|
||||||
if (typeof onDragEnd === 'function') {
|
if (currentDragElement && activePointerId !== null) {
|
||||||
onDragEnd();
|
try {
|
||||||
|
currentDragElement.releasePointerCapture(activePointerId);
|
||||||
|
} catch (err) {
|
||||||
|
// Ignore errors if element is no longer in DOM
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
|
||||||
|
isDragging = false;
|
||||||
|
activePointerId = null;
|
||||||
|
currentDragElement = null;
|
||||||
|
|
||||||
|
// Remove the class to restore normal cursor behavior
|
||||||
|
document.body.classList.remove('lm-lora-strength-dragging');
|
||||||
|
|
||||||
|
if (typeof onDragEnd === 'function') {
|
||||||
|
onDragEnd();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now do the re-render after drag is complete
|
||||||
|
if (renderFunction) {
|
||||||
|
renderFunction(widget.value, widget);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
dragEl.addEventListener('pointerup', endDrag);
|
||||||
|
dragEl.addEventListener('pointercancel', endDrag);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Function to initialize header drag for proportional strength adjustment
|
// Function to initialize header drag for proportional strength adjustment
|
||||||
export function initHeaderDrag(headerEl, widget, renderFunction) {
|
export function initHeaderDrag(headerEl, widget, renderFunction) {
|
||||||
let isDragging = false;
|
let isDragging = false;
|
||||||
|
let activePointerId = null;
|
||||||
let initialX = 0;
|
let initialX = 0;
|
||||||
let initialStrengths = [];
|
let initialStrengths = [];
|
||||||
|
let currentHeaderElement = null;
|
||||||
|
|
||||||
// Add cursor style to indicate draggable
|
// Add cursor style to indicate draggable
|
||||||
// Create a drag handler
|
// Create a drag handler using pointer events for Vue DOM render mode compatibility
|
||||||
headerEl.addEventListener('mousedown', (e) => {
|
headerEl.addEventListener('pointerdown', (e) => {
|
||||||
// Skip if clicking on toggle or other interactive elements
|
// Skip if clicking on toggle or other interactive elements
|
||||||
if (e.target.closest('.lm-lora-toggle') ||
|
if (e.target.closest('.lm-lora-toggle') ||
|
||||||
e.target.closest('input')) {
|
e.target.closest('input')) {
|
||||||
@@ -201,53 +246,96 @@ export function initHeaderDrag(headerEl, widget, renderFunction) {
|
|||||||
}));
|
}));
|
||||||
|
|
||||||
isDragging = true;
|
isDragging = true;
|
||||||
|
activePointerId = e.pointerId;
|
||||||
|
currentHeaderElement = e.currentTarget;
|
||||||
|
|
||||||
|
// Capture pointer to receive all subsequent events regardless of stopPropagation
|
||||||
|
const target = e.currentTarget;
|
||||||
|
target.setPointerCapture(e.pointerId);
|
||||||
|
|
||||||
// Add class to body to enforce cursor style globally
|
// Add class to body to enforce cursor style globally
|
||||||
document.body.classList.add('lm-lora-strength-dragging');
|
document.body.classList.add('lm-lora-strength-dragging');
|
||||||
|
|
||||||
// Prevent text selection during drag
|
// Prevent text selection and default behavior
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
|
e.stopPropagation();
|
||||||
});
|
});
|
||||||
|
|
||||||
// Handle mouse move for dragging
|
// Handle pointer move for dragging
|
||||||
document.addEventListener('mousemove', (e) => {
|
headerEl.addEventListener('pointermove', (e) => {
|
||||||
if (!isDragging) return;
|
if (!isDragging) return;
|
||||||
|
|
||||||
// Call the strength adjustment function
|
e.stopPropagation();
|
||||||
handleAllStrengthsDrag(initialStrengths, initialX, e, widget);
|
|
||||||
|
|
||||||
// Force re-render to show updated strength values
|
// Call the strength adjustment function without updating widget.value during drag
|
||||||
|
handleAllStrengthsDrag(initialStrengths, initialX, e, widget, false);
|
||||||
|
|
||||||
|
// Update strength inputs directly instead of re-rendering to avoid losing event listeners
|
||||||
|
const strengthInputs = currentHeaderElement.parentElement.querySelectorAll('.lm-lora-strength-input');
|
||||||
|
const lorasData = parseLoraValue(widget.value);
|
||||||
|
strengthInputs.forEach((input, index) => {
|
||||||
|
if (lorasData[index]) {
|
||||||
|
input.value = lorasData[index].strength.toFixed(2);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
const endDrag = (e) => {
|
||||||
|
if (!isDragging) return;
|
||||||
|
|
||||||
|
e.stopPropagation();
|
||||||
|
|
||||||
|
// Release pointer capture if still have the element
|
||||||
|
if (currentHeaderElement && activePointerId !== null) {
|
||||||
|
try {
|
||||||
|
currentHeaderElement.releasePointerCapture(activePointerId);
|
||||||
|
} catch (err) {
|
||||||
|
// Ignore errors if element is no longer in DOM
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
isDragging = false;
|
||||||
|
activePointerId = null;
|
||||||
|
currentHeaderElement = null;
|
||||||
|
|
||||||
|
// Remove the class to restore normal cursor behavior
|
||||||
|
document.body.classList.remove('lm-lora-strength-dragging');
|
||||||
|
|
||||||
|
// Now do a re-render after drag is complete
|
||||||
if (renderFunction) {
|
if (renderFunction) {
|
||||||
renderFunction(widget.value, widget);
|
renderFunction(widget.value, widget);
|
||||||
}
|
}
|
||||||
});
|
};
|
||||||
|
|
||||||
// Handle mouse up to end dragging
|
// Handle pointer up to end dragging
|
||||||
document.addEventListener('mouseup', () => {
|
headerEl.addEventListener('pointerup', endDrag);
|
||||||
if (isDragging) {
|
|
||||||
isDragging = false;
|
// Handle pointer cancel to end dragging
|
||||||
// Remove the class to restore normal cursor behavior
|
headerEl.addEventListener('pointercancel', endDrag);
|
||||||
document.body.classList.remove('lm-lora-strength-dragging');
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Function to initialize drag-and-drop for reordering
|
// Function to initialize drag-and-drop for reordering
|
||||||
export function initReorderDrag(dragHandle, loraName, widget, renderFunction) {
|
export function initReorderDrag(dragHandle, loraName, widget, renderFunction) {
|
||||||
let isDragging = false;
|
let isDragging = false;
|
||||||
|
let activePointerId = null;
|
||||||
let draggedElement = null;
|
let draggedElement = null;
|
||||||
let dropIndicator = null;
|
let dropIndicator = null;
|
||||||
let container = null;
|
let container = null;
|
||||||
let scale = 1;
|
let scale = 1;
|
||||||
|
|
||||||
dragHandle.addEventListener('mousedown', (e) => {
|
dragHandle.addEventListener('pointerdown', (e) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
|
|
||||||
isDragging = true;
|
isDragging = true;
|
||||||
|
activePointerId = e.pointerId;
|
||||||
draggedElement = dragHandle.closest('.lm-lora-entry');
|
draggedElement = dragHandle.closest('.lm-lora-entry');
|
||||||
container = draggedElement.parentElement;
|
container = draggedElement.parentElement;
|
||||||
|
|
||||||
|
// Capture pointer to receive all subsequent events regardless of stopPropagation
|
||||||
|
const target = e.currentTarget;
|
||||||
|
target.setPointerCapture(e.pointerId);
|
||||||
|
|
||||||
// Add dragging class and visual feedback
|
// Add dragging class and visual feedback
|
||||||
draggedElement.classList.add('lm-lora-entry--dragging');
|
draggedElement.classList.add('lm-lora-entry--dragging');
|
||||||
|
|
||||||
@@ -269,9 +357,11 @@ export function initReorderDrag(dragHandle, loraName, widget, renderFunction) {
|
|||||||
scale = app.canvas.ds.scale;
|
scale = app.canvas.ds.scale;
|
||||||
});
|
});
|
||||||
|
|
||||||
document.addEventListener('mousemove', (e) => {
|
dragHandle.addEventListener('pointermove', (e) => {
|
||||||
if (!isDragging || !draggedElement || !dropIndicator) return;
|
if (!isDragging || !draggedElement || !dropIndicator) return;
|
||||||
|
|
||||||
|
e.stopPropagation();
|
||||||
|
|
||||||
const targetIndex = getDropTargetIndex(container, e.clientY);
|
const targetIndex = getDropTargetIndex(container, e.clientY);
|
||||||
const entries = container.querySelectorAll('.lm-lora-entry, .lm-lora-clip-entry');
|
const entries = container.querySelectorAll('.lm-lora-entry, .lm-lora-clip-entry');
|
||||||
|
|
||||||
@@ -305,12 +395,20 @@ export function initReorderDrag(dragHandle, loraName, widget, renderFunction) {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
document.addEventListener('mouseup', (e) => {
|
dragHandle.addEventListener('pointerup', (e) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
|
||||||
// Always reset cursor regardless of isDragging state
|
// Always reset cursor regardless of isDragging state
|
||||||
document.body.classList.remove('lm-lora-reordering');
|
document.body.classList.remove('lm-lora-reordering');
|
||||||
|
|
||||||
if (!isDragging || !draggedElement) return;
|
if (!isDragging || !draggedElement) return;
|
||||||
|
|
||||||
|
// Release pointer capture
|
||||||
|
const target = e.currentTarget;
|
||||||
|
if (activePointerId !== null) {
|
||||||
|
target.releasePointerCapture(activePointerId);
|
||||||
|
}
|
||||||
|
|
||||||
const targetIndex = getDropTargetIndex(container, e.clientY);
|
const targetIndex = getDropTargetIndex(container, e.clientY);
|
||||||
|
|
||||||
// Get current LoRA data
|
// Get current LoRA data
|
||||||
@@ -346,6 +444,7 @@ export function initReorderDrag(dragHandle, loraName, widget, renderFunction) {
|
|||||||
|
|
||||||
// Cleanup
|
// Cleanup
|
||||||
isDragging = false;
|
isDragging = false;
|
||||||
|
activePointerId = null;
|
||||||
if (draggedElement) {
|
if (draggedElement) {
|
||||||
draggedElement.classList.remove('lm-lora-entry--dragging');
|
draggedElement.classList.remove('lm-lora-entry--dragging');
|
||||||
draggedElement = null;
|
draggedElement = null;
|
||||||
@@ -362,9 +461,37 @@ export function initReorderDrag(dragHandle, loraName, widget, renderFunction) {
|
|||||||
container = null;
|
container = null;
|
||||||
});
|
});
|
||||||
|
|
||||||
// Also reset cursor when mouse leaves the document
|
dragHandle.addEventListener('pointercancel', (e) => {
|
||||||
document.addEventListener('mouseleave', () => {
|
e.stopPropagation();
|
||||||
|
|
||||||
|
// Always reset cursor regardless of isDragging state
|
||||||
document.body.classList.remove('lm-lora-reordering');
|
document.body.classList.remove('lm-lora-reordering');
|
||||||
|
|
||||||
|
if (!isDragging || !draggedElement) return;
|
||||||
|
|
||||||
|
// Release pointer capture
|
||||||
|
const target = e.currentTarget;
|
||||||
|
if (activePointerId !== null) {
|
||||||
|
target.releasePointerCapture(activePointerId);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Cleanup without reordering
|
||||||
|
isDragging = false;
|
||||||
|
activePointerId = null;
|
||||||
|
if (draggedElement) {
|
||||||
|
draggedElement.classList.remove('lm-lora-entry--dragging');
|
||||||
|
draggedElement = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dropIndicator && container) {
|
||||||
|
container.removeChild(dropIndicator);
|
||||||
|
// Restore original position
|
||||||
|
container.style.position = container._originalPosition || '';
|
||||||
|
delete container._originalPosition;
|
||||||
|
dropIndicator = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
container = null;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user