mirror of
https://github.com/willmiao/ComfyUI-Lora-Manager.git
synced 2026-03-24 14:42:11 -03:00
feat(loras): add drag event callbacks and preview suppression
- Add onDragStart and onDragEnd callbacks to initDrag function - Implement preview suppression during and briefly after strength dragging - Clear preview timer on drag start/end to prevent tooltip conflicts - Update tests to verify drag callbacks are properly triggered This prevents tooltip previews from interfering with drag interactions and provides better control over drag lifecycle events.
This commit is contained in:
@@ -82,6 +82,8 @@ describe('LoRA widget drag interactions', () => {
|
|||||||
const module = await import(EVENTS_MODULE);
|
const module = await import(EVENTS_MODULE);
|
||||||
const renderSpy = vi.fn();
|
const renderSpy = vi.fn();
|
||||||
const previewSpy = { hide: vi.fn() };
|
const previewSpy = { hide: vi.fn() };
|
||||||
|
const onDragStart = vi.fn();
|
||||||
|
const onDragEnd = vi.fn();
|
||||||
|
|
||||||
const dragEl = document.createElement('div');
|
const dragEl = document.createElement('div');
|
||||||
dragEl.className = 'lm-lora-entry';
|
dragEl.className = 'lm-lora-entry';
|
||||||
@@ -92,10 +94,14 @@ describe('LoRA widget drag interactions', () => {
|
|||||||
callback: vi.fn(),
|
callback: vi.fn(),
|
||||||
};
|
};
|
||||||
|
|
||||||
module.initDrag(dragEl, 'Test', widget, false, previewSpy, renderSpy);
|
module.initDrag(dragEl, 'Test', widget, false, previewSpy, renderSpy, {
|
||||||
|
onDragStart,
|
||||||
|
onDragEnd,
|
||||||
|
});
|
||||||
|
|
||||||
dragEl.dispatchEvent(new MouseEvent('mousedown', { clientX: 50, bubbles: true }));
|
dragEl.dispatchEvent(new MouseEvent('mousedown', { clientX: 50, bubbles: true }));
|
||||||
expect(document.body.classList.contains('lm-lora-strength-dragging')).toBe(true);
|
expect(document.body.classList.contains('lm-lora-strength-dragging')).toBe(true);
|
||||||
|
expect(onDragStart).toHaveBeenCalledTimes(1);
|
||||||
|
|
||||||
document.dispatchEvent(new MouseEvent('mousemove', { clientX: 70, bubbles: true }));
|
document.dispatchEvent(new MouseEvent('mousemove', { clientX: 70, bubbles: true }));
|
||||||
expect(renderSpy).toHaveBeenCalledWith(widget.value, widget);
|
expect(renderSpy).toHaveBeenCalledWith(widget.value, widget);
|
||||||
@@ -104,6 +110,7 @@ describe('LoRA widget drag interactions', () => {
|
|||||||
|
|
||||||
document.dispatchEvent(new MouseEvent('mouseup'));
|
document.dispatchEvent(new MouseEvent('mouseup'));
|
||||||
expect(document.body.classList.contains('lm-lora-strength-dragging')).toBe(false);
|
expect(document.body.classList.contains('lm-lora-strength-dragging')).toBe(false);
|
||||||
|
expect(onDragEnd).toHaveBeenCalledTimes(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('deletes the selected LoRA when backspace is pressed outside of strength inputs', async () => {
|
it('deletes the selected LoRA when backspace is pressed outside of strength inputs', async () => {
|
||||||
|
|||||||
@@ -36,6 +36,28 @@ export function addLorasWidget(node, name, opts, callback) {
|
|||||||
// Selection state - only one LoRA can be selected at a time
|
// Selection state - only one LoRA can be selected at a time
|
||||||
let selectedLora = null;
|
let selectedLora = null;
|
||||||
let pendingFocusTarget = null;
|
let pendingFocusTarget = null;
|
||||||
|
|
||||||
|
const PREVIEW_SUPPRESSION_AFTER_DRAG_MS = 500;
|
||||||
|
let strengthDragActive = false;
|
||||||
|
let lastStrengthDragEndAt = 0;
|
||||||
|
|
||||||
|
const shouldSuppressPreview = () => {
|
||||||
|
if (strengthDragActive) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return Date.now() - lastStrengthDragEndAt < PREVIEW_SUPPRESSION_AFTER_DRAG_MS;
|
||||||
|
};
|
||||||
|
|
||||||
|
const markStrengthDragStart = () => {
|
||||||
|
strengthDragActive = true;
|
||||||
|
previewTooltip.hide();
|
||||||
|
};
|
||||||
|
|
||||||
|
const markStrengthDragEnd = () => {
|
||||||
|
strengthDragActive = false;
|
||||||
|
lastStrengthDragEndAt = Date.now();
|
||||||
|
previewTooltip.hide();
|
||||||
|
};
|
||||||
|
|
||||||
// Function to select a LoRA
|
// Function to select a LoRA
|
||||||
const selectLora = (loraName) => {
|
const selectLora = (loraName) => {
|
||||||
@@ -259,23 +281,47 @@ export function addLorasWidget(node, name, opts, callback) {
|
|||||||
nameEl.className = "lm-lora-name";
|
nameEl.className = "lm-lora-name";
|
||||||
|
|
||||||
// Move preview tooltip events to nameEl instead of loraEl
|
// Move preview tooltip events to nameEl instead of loraEl
|
||||||
let previewTimer; // Timer for delayed preview
|
let previewTimer = null; // Timer for delayed preview
|
||||||
nameEl.addEventListener('mouseenter', async (e) => {
|
|
||||||
|
const clearPreviewTimer = () => {
|
||||||
|
if (previewTimer) {
|
||||||
|
clearTimeout(previewTimer);
|
||||||
|
previewTimer = null;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
nameEl.addEventListener('mouseenter', (e) => {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
const rect = nameEl.getBoundingClientRect();
|
if (shouldSuppressPreview()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
previewTimer = setTimeout(async () => {
|
previewTimer = setTimeout(async () => {
|
||||||
|
previewTimer = null;
|
||||||
|
if (shouldSuppressPreview()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const rect = nameEl.getBoundingClientRect();
|
||||||
await previewTooltip.show(name, rect.right, rect.top);
|
await previewTooltip.show(name, rect.right, rect.top);
|
||||||
}, 400); // 400ms delay
|
}, 400); // 400ms delay
|
||||||
});
|
});
|
||||||
|
|
||||||
nameEl.addEventListener('mouseleave', (e) => {
|
nameEl.addEventListener('mouseleave', (e) => {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
clearTimeout(previewTimer); // Cancel if not triggered
|
clearPreviewTimer(); // Cancel if not triggered
|
||||||
previewTooltip.hide();
|
previewTooltip.hide();
|
||||||
});
|
});
|
||||||
|
|
||||||
// Initialize drag functionality for strength adjustment
|
// Initialize drag functionality for strength adjustment
|
||||||
initDrag(loraEl, name, widget, false, previewTooltip, renderLoras);
|
initDrag(loraEl, name, widget, false, previewTooltip, renderLoras, {
|
||||||
|
onDragStart: () => {
|
||||||
|
clearPreviewTimer();
|
||||||
|
markStrengthDragStart();
|
||||||
|
},
|
||||||
|
onDragEnd: () => {
|
||||||
|
clearPreviewTimer();
|
||||||
|
markStrengthDragEnd();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
// Add context menu event
|
// Add context menu event
|
||||||
loraEl.addEventListener('contextmenu', (e) => {
|
loraEl.addEventListener('contextmenu', (e) => {
|
||||||
@@ -511,7 +557,10 @@ export function addLorasWidget(node, name, opts, callback) {
|
|||||||
clipEl.appendChild(clipStrengthControl);
|
clipEl.appendChild(clipStrengthControl);
|
||||||
|
|
||||||
// Add drag functionality to clip entry
|
// Add drag functionality to clip entry
|
||||||
initDrag(clipEl, name, widget, true, previewTooltip, renderLoras);
|
initDrag(clipEl, name, widget, true, previewTooltip, renderLoras, {
|
||||||
|
onDragStart: markStrengthDragStart,
|
||||||
|
onDragEnd: markStrengthDragEnd
|
||||||
|
});
|
||||||
|
|
||||||
container.appendChild(clipEl);
|
container.appendChild(clipEl);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -97,10 +97,19 @@ export function handleAllStrengthsDrag(initialStrengths, initialX, event, widget
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Function to initialize drag operation
|
// Function to initialize drag operation
|
||||||
export function initDrag(dragEl, name, widget, isClipStrength = false, previewTooltip, renderFunction) {
|
export function initDrag(
|
||||||
|
dragEl,
|
||||||
|
name,
|
||||||
|
widget,
|
||||||
|
isClipStrength = false,
|
||||||
|
previewTooltip,
|
||||||
|
renderFunction,
|
||||||
|
dragCallbacks = {}
|
||||||
|
) {
|
||||||
let isDragging = false;
|
let isDragging = false;
|
||||||
let initialX = 0;
|
let initialX = 0;
|
||||||
let initialStrength = 0;
|
let initialStrength = 0;
|
||||||
|
const { onDragStart, onDragEnd } = dragCallbacks;
|
||||||
|
|
||||||
// Create a drag handler
|
// Create a drag handler
|
||||||
dragEl.addEventListener('mousedown', (e) => {
|
dragEl.addEventListener('mousedown', (e) => {
|
||||||
@@ -122,10 +131,14 @@ export function initDrag(dragEl, name, widget, isClipStrength = false, previewTo
|
|||||||
initialX = e.clientX;
|
initialX = e.clientX;
|
||||||
initialStrength = isClipStrength ? loraData.clipStrength : loraData.strength;
|
initialStrength = isClipStrength ? loraData.clipStrength : loraData.strength;
|
||||||
isDragging = true;
|
isDragging = true;
|
||||||
|
|
||||||
// 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');
|
||||||
|
|
||||||
|
if (typeof onDragStart === 'function') {
|
||||||
|
onDragStart();
|
||||||
|
}
|
||||||
|
|
||||||
// Prevent text selection during drag
|
// Prevent text selection during drag
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
});
|
});
|
||||||
@@ -154,6 +167,10 @@ export function initDrag(dragEl, name, widget, isClipStrength = false, previewTo
|
|||||||
isDragging = false;
|
isDragging = false;
|
||||||
// Remove the class to restore normal cursor behavior
|
// Remove the class to restore normal cursor behavior
|
||||||
document.body.classList.remove('lm-lora-strength-dragging');
|
document.body.classList.remove('lm-lora-strength-dragging');
|
||||||
|
|
||||||
|
if (typeof onDragEnd === 'function') {
|
||||||
|
onDragEnd();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user