mirror of
https://github.com/willmiao/ComfyUI-Lora-Manager.git
synced 2026-03-22 05:32:12 -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;
|
||||
position: relative;
|
||||
cursor: ew-resize;
|
||||
touch-action: none;
|
||||
}
|
||||
|
||||
.lm-toggle-container {
|
||||
@@ -155,6 +156,8 @@
|
||||
margin-bottom: 4px;
|
||||
border: 1px solid transparent;
|
||||
background-color: rgba(45, 55, 72, 0.7);
|
||||
touch-action: none;
|
||||
cursor: pointer !important;
|
||||
}
|
||||
|
||||
.lm-lora-entry[data-active="false"] {
|
||||
@@ -486,6 +489,8 @@ body.lm-lora-reordering * {
|
||||
align-items: center;
|
||||
transition: all 0.2s ease;
|
||||
margin-top: -2px;
|
||||
touch-action: none;
|
||||
cursor: pointer !important;
|
||||
}
|
||||
|
||||
.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";
|
||||
|
||||
// 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)
|
||||
// Using 0.01 per 10 pixels of movement
|
||||
const sensitivity = 0.001;
|
||||
@@ -37,18 +37,21 @@ export function handleStrengthDrag(name, initialStrength, initialX, event, widge
|
||||
syncClipStrengthIfCollapsed(lorasData[loraIndex]);
|
||||
}
|
||||
|
||||
// Update the widget value
|
||||
widget.value = formatLoraValue(lorasData);
|
||||
// Update the widget value only if updateWidget flag is true
|
||||
// This allows us to update inputs directly during drag without triggering re-render
|
||||
if (updateWidget) {
|
||||
widget.value = formatLoraValue(lorasData);
|
||||
}
|
||||
|
||||
// Force re-render via callback
|
||||
if (widget.callback) {
|
||||
// Force re-render via callback only if updateWidget is true
|
||||
if (updateWidget && widget.callback) {
|
||||
widget.callback(widget.value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 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)
|
||||
const sensitivity = 0.0005;
|
||||
|
||||
@@ -87,11 +90,13 @@ export function handleAllStrengthsDrag(initialStrengths, initialX, event, widget
|
||||
lorasData[index].clipStrength = Number(newClipStrength);
|
||||
});
|
||||
|
||||
// Update widget value
|
||||
widget.value = formatLoraValue(lorasData);
|
||||
// Update widget value only if updateWidget flag is true
|
||||
if (updateWidget) {
|
||||
widget.value = formatLoraValue(lorasData);
|
||||
}
|
||||
|
||||
// Force re-render via callback
|
||||
if (widget.callback) {
|
||||
// Force re-render via callback only if updateWidget is true
|
||||
if (updateWidget && widget.callback) {
|
||||
widget.callback(widget.value);
|
||||
}
|
||||
}
|
||||
@@ -107,12 +112,14 @@ export function initDrag(
|
||||
dragCallbacks = {}
|
||||
) {
|
||||
let isDragging = false;
|
||||
let activePointerId = null;
|
||||
let initialX = 0;
|
||||
let initialStrength = 0;
|
||||
let currentDragElement = null;
|
||||
const { onDragStart, onDragEnd } = dragCallbacks;
|
||||
|
||||
// Create a drag handler
|
||||
dragEl.addEventListener('mousedown', (e) => {
|
||||
// Create a drag handler using pointer events for Vue DOM render mode compatibility
|
||||
dragEl.addEventListener('pointerdown', (e) => {
|
||||
// Skip if clicking on toggle or strength control areas
|
||||
if (e.target.closest('.lm-lora-toggle') ||
|
||||
e.target.closest('input') ||
|
||||
@@ -131,6 +138,12 @@ export function initDrag(
|
||||
initialX = e.clientX;
|
||||
initialStrength = isClipStrength ? loraData.clipStrength : loraData.strength;
|
||||
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
|
||||
document.body.classList.add('lm-lora-strength-dragging');
|
||||
@@ -139,21 +152,29 @@ export function initDrag(
|
||||
onDragStart();
|
||||
}
|
||||
|
||||
// Prevent text selection during drag
|
||||
// Prevent text selection and default behavior
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
});
|
||||
|
||||
// Use the document for move and up events to ensure drag continues
|
||||
// even if mouse leaves the element
|
||||
document.addEventListener('mousemove', (e) => {
|
||||
dragEl.addEventListener('pointermove', (e) => {
|
||||
if (!isDragging) return;
|
||||
|
||||
// Call the strength adjustment function
|
||||
handleStrengthDrag(name, initialStrength, initialX, e, widget, isClipStrength);
|
||||
// Stop propagation to prevent canvas from interfering
|
||||
e.stopPropagation();
|
||||
|
||||
// Force re-render to show updated strength value
|
||||
if (renderFunction) {
|
||||
renderFunction(widget.value, widget);
|
||||
// Call the strength adjustment function without updating widget.value during drag
|
||||
handleStrengthDrag(name, initialStrength, initialX, e, widget, isClipStrength, false);
|
||||
|
||||
// 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
|
||||
@@ -162,28 +183,52 @@ export function initDrag(
|
||||
}
|
||||
});
|
||||
|
||||
document.addEventListener('mouseup', () => {
|
||||
if (isDragging) {
|
||||
isDragging = false;
|
||||
// Remove the class to restore normal cursor behavior
|
||||
document.body.classList.remove('lm-lora-strength-dragging');
|
||||
|
||||
if (typeof onDragEnd === 'function') {
|
||||
onDragEnd();
|
||||
const endDrag = (e) => {
|
||||
if (!isDragging) return;
|
||||
|
||||
e.stopPropagation();
|
||||
|
||||
// Release pointer capture if still have the element
|
||||
if (currentDragElement && activePointerId !== null) {
|
||||
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
|
||||
export function initHeaderDrag(headerEl, widget, renderFunction) {
|
||||
let isDragging = false;
|
||||
let activePointerId = null;
|
||||
let initialX = 0;
|
||||
let initialStrengths = [];
|
||||
let currentHeaderElement = null;
|
||||
|
||||
// Add cursor style to indicate draggable
|
||||
// Create a drag handler
|
||||
headerEl.addEventListener('mousedown', (e) => {
|
||||
// Create a drag handler using pointer events for Vue DOM render mode compatibility
|
||||
headerEl.addEventListener('pointerdown', (e) => {
|
||||
// Skip if clicking on toggle or other interactive elements
|
||||
if (e.target.closest('.lm-lora-toggle') ||
|
||||
e.target.closest('input')) {
|
||||
@@ -201,53 +246,96 @@ export function initHeaderDrag(headerEl, widget, renderFunction) {
|
||||
}));
|
||||
|
||||
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
|
||||
document.body.classList.add('lm-lora-strength-dragging');
|
||||
|
||||
// Prevent text selection during drag
|
||||
// Prevent text selection and default behavior
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
});
|
||||
|
||||
// Handle mouse move for dragging
|
||||
document.addEventListener('mousemove', (e) => {
|
||||
// Handle pointer move for dragging
|
||||
headerEl.addEventListener('pointermove', (e) => {
|
||||
if (!isDragging) return;
|
||||
|
||||
// Call the strength adjustment function
|
||||
handleAllStrengthsDrag(initialStrengths, initialX, e, widget);
|
||||
e.stopPropagation();
|
||||
|
||||
// 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) {
|
||||
renderFunction(widget.value, widget);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
// Handle mouse up to end dragging
|
||||
document.addEventListener('mouseup', () => {
|
||||
if (isDragging) {
|
||||
isDragging = false;
|
||||
// Remove the class to restore normal cursor behavior
|
||||
document.body.classList.remove('lm-lora-strength-dragging');
|
||||
}
|
||||
});
|
||||
// Handle pointer up to end dragging
|
||||
headerEl.addEventListener('pointerup', endDrag);
|
||||
|
||||
// Handle pointer cancel to end dragging
|
||||
headerEl.addEventListener('pointercancel', endDrag);
|
||||
}
|
||||
|
||||
// Function to initialize drag-and-drop for reordering
|
||||
export function initReorderDrag(dragHandle, loraName, widget, renderFunction) {
|
||||
let isDragging = false;
|
||||
let activePointerId = null;
|
||||
let draggedElement = null;
|
||||
let dropIndicator = null;
|
||||
let container = null;
|
||||
let scale = 1;
|
||||
|
||||
dragHandle.addEventListener('mousedown', (e) => {
|
||||
dragHandle.addEventListener('pointerdown', (e) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
|
||||
isDragging = true;
|
||||
activePointerId = e.pointerId;
|
||||
draggedElement = dragHandle.closest('.lm-lora-entry');
|
||||
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
|
||||
draggedElement.classList.add('lm-lora-entry--dragging');
|
||||
|
||||
@@ -269,9 +357,11 @@ export function initReorderDrag(dragHandle, loraName, widget, renderFunction) {
|
||||
scale = app.canvas.ds.scale;
|
||||
});
|
||||
|
||||
document.addEventListener('mousemove', (e) => {
|
||||
dragHandle.addEventListener('pointermove', (e) => {
|
||||
if (!isDragging || !draggedElement || !dropIndicator) return;
|
||||
|
||||
e.stopPropagation();
|
||||
|
||||
const targetIndex = getDropTargetIndex(container, e.clientY);
|
||||
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
|
||||
document.body.classList.remove('lm-lora-reordering');
|
||||
|
||||
if (!isDragging || !draggedElement) return;
|
||||
|
||||
// Release pointer capture
|
||||
const target = e.currentTarget;
|
||||
if (activePointerId !== null) {
|
||||
target.releasePointerCapture(activePointerId);
|
||||
}
|
||||
|
||||
const targetIndex = getDropTargetIndex(container, e.clientY);
|
||||
|
||||
// Get current LoRA data
|
||||
@@ -346,6 +444,7 @@ export function initReorderDrag(dragHandle, loraName, widget, renderFunction) {
|
||||
|
||||
// Cleanup
|
||||
isDragging = false;
|
||||
activePointerId = null;
|
||||
if (draggedElement) {
|
||||
draggedElement.classList.remove('lm-lora-entry--dragging');
|
||||
draggedElement = null;
|
||||
@@ -362,9 +461,37 @@ export function initReorderDrag(dragHandle, loraName, widget, renderFunction) {
|
||||
container = null;
|
||||
});
|
||||
|
||||
// Also reset cursor when mouse leaves the document
|
||||
document.addEventListener('mouseleave', () => {
|
||||
dragHandle.addEventListener('pointercancel', (e) => {
|
||||
e.stopPropagation();
|
||||
|
||||
// Always reset cursor regardless of isDragging state
|
||||
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