mirror of
https://github.com/willmiao/ComfyUI-Lora-Manager.git
synced 2026-03-25 07:05:43 -03:00
fix(ModelTags): fix performance and UX issues in ModelTags
This commit is contained in:
@@ -92,6 +92,39 @@
|
|||||||
border-radius: var(--border-radius-xs);
|
border-radius: var(--border-radius-xs);
|
||||||
padding: 4px 8px;
|
padding: 4px 8px;
|
||||||
position: relative;
|
position: relative;
|
||||||
|
cursor: grab;
|
||||||
|
transition: transform 0.18s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.metadata-item:active {
|
||||||
|
cursor: grabbing;
|
||||||
|
}
|
||||||
|
|
||||||
|
.metadata-item-dragging {
|
||||||
|
box-shadow: 0 10px 24px rgba(0, 0, 0, 0.25);
|
||||||
|
cursor: grabbing;
|
||||||
|
opacity: 0.95;
|
||||||
|
transition: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.metadata-item-placeholder {
|
||||||
|
border: 1px dashed var(--lora-accent);
|
||||||
|
border-radius: var(--border-radius-xs);
|
||||||
|
background: rgba(255, 255, 255, 0.1);
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.metadata-items-sorting .metadata-item {
|
||||||
|
transition: transform 0.18s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
body.metadata-drag-active {
|
||||||
|
user-select: none;
|
||||||
|
cursor: grabbing;
|
||||||
|
}
|
||||||
|
|
||||||
|
body.metadata-drag-active * {
|
||||||
|
cursor: grabbing !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.metadata-item-content {
|
.metadata-item-content {
|
||||||
|
|||||||
@@ -19,11 +19,15 @@ const MODEL_TYPE_SUGGESTION_KEY_MAP = {
|
|||||||
const METADATA_ITEM_SELECTOR = '.metadata-item';
|
const METADATA_ITEM_SELECTOR = '.metadata-item';
|
||||||
const METADATA_ITEMS_CONTAINER_SELECTOR = '.metadata-items';
|
const METADATA_ITEMS_CONTAINER_SELECTOR = '.metadata-items';
|
||||||
const METADATA_ITEM_DRAGGING_CLASS = 'metadata-item-dragging';
|
const METADATA_ITEM_DRAGGING_CLASS = 'metadata-item-dragging';
|
||||||
|
const METADATA_ITEM_PLACEHOLDER_CLASS = 'metadata-item-placeholder';
|
||||||
|
const METADATA_ITEMS_SORTING_CLASS = 'metadata-items-sorting';
|
||||||
|
const BODY_DRAGGING_CLASS = 'metadata-drag-active';
|
||||||
|
|
||||||
let activeModelTypeKey = '';
|
let activeModelTypeKey = '';
|
||||||
let priorityTagSuggestions = [];
|
let priorityTagSuggestions = [];
|
||||||
let priorityTagSuggestionsLoaded = false;
|
let priorityTagSuggestionsLoaded = false;
|
||||||
let priorityTagSuggestionsPromise = null;
|
let priorityTagSuggestionsPromise = null;
|
||||||
|
let activeTagDragState = null;
|
||||||
|
|
||||||
function normalizeModelTypeKey(modelType) {
|
function normalizeModelTypeKey(modelType) {
|
||||||
if (!modelType) {
|
if (!modelType) {
|
||||||
@@ -496,87 +500,202 @@ function setupTagDragAndDrop() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!container._dragEventsBound) {
|
|
||||||
container.addEventListener('dragover', handleTagContainerDragOver);
|
|
||||||
container.addEventListener('drop', handleTagContainerDrop);
|
|
||||||
container._dragEventsBound = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
container.querySelectorAll(METADATA_ITEM_SELECTOR).forEach((item) => {
|
container.querySelectorAll(METADATA_ITEM_SELECTOR).forEach((item) => {
|
||||||
if (item.dataset.dragInit === 'true') {
|
item.removeAttribute('draggable');
|
||||||
|
if (item.classList.contains(METADATA_ITEM_PLACEHOLDER_CLASS)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
item.setAttribute('draggable', 'true');
|
if (item.dataset.pointerDragInit === 'true') {
|
||||||
item.addEventListener('dragstart', handleTagDragStart);
|
return;
|
||||||
item.addEventListener('dragend', handleTagDragEnd);
|
}
|
||||||
item.dataset.dragInit = 'true';
|
|
||||||
|
item.addEventListener('pointerdown', handleTagPointerDown);
|
||||||
|
item.dataset.pointerDragInit = 'true';
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleTagDragStart(event) {
|
function handleTagPointerDown(event) {
|
||||||
const item = event.currentTarget;
|
if (event.button !== 0) {
|
||||||
if (!item) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (event.target.closest('.metadata-delete-btn')) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const item = event.currentTarget;
|
||||||
|
const container = item?.closest(METADATA_ITEMS_CONTAINER_SELECTOR);
|
||||||
|
if (!item || !container) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
event.preventDefault();
|
||||||
|
startPointerDrag({ item, container, startEvent: event });
|
||||||
|
}
|
||||||
|
|
||||||
|
function startPointerDrag({ item, container, startEvent }) {
|
||||||
|
if (activeTagDragState) {
|
||||||
|
finishPointerDrag();
|
||||||
|
}
|
||||||
|
|
||||||
|
const itemRect = item.getBoundingClientRect();
|
||||||
|
const placeholder = document.createElement('div');
|
||||||
|
placeholder.className = `metadata-item ${METADATA_ITEM_PLACEHOLDER_CLASS}`;
|
||||||
|
placeholder.style.width = `${itemRect.width}px`;
|
||||||
|
placeholder.style.height = `${itemRect.height}px`;
|
||||||
|
|
||||||
|
container.insertBefore(placeholder, item);
|
||||||
|
|
||||||
item.classList.add(METADATA_ITEM_DRAGGING_CLASS);
|
item.classList.add(METADATA_ITEM_DRAGGING_CLASS);
|
||||||
if (event.dataTransfer) {
|
item.style.width = `${itemRect.width}px`;
|
||||||
event.dataTransfer.effectAllowed = 'move';
|
item.style.height = `${itemRect.height}px`;
|
||||||
try {
|
item.style.position = 'fixed';
|
||||||
event.dataTransfer.setData('text/plain', item.dataset.tag || '');
|
item.style.left = `${itemRect.left}px`;
|
||||||
} catch (error) {
|
item.style.top = `${itemRect.top}px`;
|
||||||
// Some browsers may throw if dataTransfer is unavailable; ignore.
|
item.style.pointerEvents = 'none';
|
||||||
|
item.style.zIndex = '1000';
|
||||||
|
|
||||||
|
container.classList.add(METADATA_ITEMS_SORTING_CLASS);
|
||||||
|
if (document.body) {
|
||||||
|
document.body.classList.add(BODY_DRAGGING_CLASS);
|
||||||
|
}
|
||||||
|
|
||||||
|
const dragState = {
|
||||||
|
container,
|
||||||
|
item,
|
||||||
|
placeholder,
|
||||||
|
offsetX: startEvent.clientX - itemRect.left,
|
||||||
|
offsetY: startEvent.clientY - itemRect.top,
|
||||||
|
lastKnownPointer: { x: startEvent.clientX, y: startEvent.clientY },
|
||||||
|
rafId: null,
|
||||||
|
};
|
||||||
|
|
||||||
|
activeTagDragState = dragState;
|
||||||
|
|
||||||
|
document.addEventListener('pointermove', handlePointerMove);
|
||||||
|
document.addEventListener('pointerup', handlePointerUp);
|
||||||
|
document.addEventListener('pointercancel', handlePointerUp);
|
||||||
|
}
|
||||||
|
|
||||||
|
function handlePointerMove(event) {
|
||||||
|
if (!activeTagDragState) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
activeTagDragState.lastKnownPointer = { x: event.clientX, y: event.clientY };
|
||||||
|
|
||||||
|
if (activeTagDragState.rafId !== null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
activeTagDragState.rafId = requestAnimationFrame(() => {
|
||||||
|
if (!activeTagDragState) {
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
}
|
activeTagDragState.rafId = null;
|
||||||
|
updateDraggingItemPosition();
|
||||||
|
updatePlaceholderPosition();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleTagDragEnd(event) {
|
function handlePointerUp() {
|
||||||
const item = event.currentTarget;
|
finishPointerDrag();
|
||||||
if (!item) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
item.classList.remove(METADATA_ITEM_DRAGGING_CLASS);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleTagContainerDragOver(event) {
|
function updateDraggingItemPosition() {
|
||||||
event.preventDefault();
|
if (!activeTagDragState) {
|
||||||
const container = event.currentTarget;
|
|
||||||
const draggingItem = container.querySelector(`.${METADATA_ITEM_DRAGGING_CLASS}`);
|
|
||||||
if (!draggingItem) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const afterElement = getDragAfterElement(container, event.clientY);
|
const { item, offsetX, offsetY, lastKnownPointer } = activeTagDragState;
|
||||||
if (!afterElement) {
|
const left = lastKnownPointer.x - offsetX;
|
||||||
container.appendChild(draggingItem);
|
const top = lastKnownPointer.y - offsetY;
|
||||||
|
item.style.left = `${left}px`;
|
||||||
|
item.style.top = `${top}px`;
|
||||||
|
}
|
||||||
|
|
||||||
|
function updatePlaceholderPosition() {
|
||||||
|
if (!activeTagDragState) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (afterElement !== draggingItem) {
|
const { container, placeholder, item, lastKnownPointer } = activeTagDragState;
|
||||||
container.insertBefore(draggingItem, afterElement);
|
const siblings = Array.from(
|
||||||
}
|
container.querySelectorAll(
|
||||||
}
|
`${METADATA_ITEM_SELECTOR}:not(.${METADATA_ITEM_PLACEHOLDER_CLASS})`
|
||||||
|
)
|
||||||
|
).filter((element) => element !== item);
|
||||||
|
|
||||||
function handleTagContainerDrop(event) {
|
let insertAfter = null;
|
||||||
event.preventDefault();
|
|
||||||
updateSuggestionsDropdown();
|
|
||||||
}
|
|
||||||
|
|
||||||
function getDragAfterElement(container, y) {
|
for (const sibling of siblings) {
|
||||||
const draggableElements = Array.from(
|
const rect = sibling.getBoundingClientRect();
|
||||||
container.querySelectorAll(`${METADATA_ITEM_SELECTOR}:not(.${METADATA_ITEM_DRAGGING_CLASS})`)
|
|
||||||
);
|
|
||||||
|
|
||||||
return draggableElements.reduce(
|
if (lastKnownPointer.y < rect.top) {
|
||||||
(closest, child) => {
|
container.insertBefore(placeholder, sibling);
|
||||||
const box = child.getBoundingClientRect();
|
return;
|
||||||
const offset = y - box.top - box.height / 2;
|
}
|
||||||
if (offset < 0 && offset > closest.offset) {
|
|
||||||
return { offset, element: child };
|
if (lastKnownPointer.y <= rect.bottom) {
|
||||||
|
if (lastKnownPointer.x < rect.left + rect.width / 2) {
|
||||||
|
container.insertBefore(placeholder, sibling);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
return closest;
|
insertAfter = sibling;
|
||||||
},
|
continue;
|
||||||
{ offset: Number.NEGATIVE_INFINITY, element: null }
|
}
|
||||||
).element;
|
|
||||||
|
insertAfter = sibling;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!insertAfter) {
|
||||||
|
container.insertBefore(placeholder, container.firstElementChild);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
container.insertBefore(placeholder, insertAfter.nextSibling);
|
||||||
|
}
|
||||||
|
|
||||||
|
function finishPointerDrag() {
|
||||||
|
if (!activeTagDragState) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const { container, item, placeholder, rafId } = activeTagDragState;
|
||||||
|
|
||||||
|
document.removeEventListener('pointermove', handlePointerMove);
|
||||||
|
document.removeEventListener('pointerup', handlePointerUp);
|
||||||
|
document.removeEventListener('pointercancel', handlePointerUp);
|
||||||
|
|
||||||
|
container.classList.remove(METADATA_ITEMS_SORTING_CLASS);
|
||||||
|
if (document.body) {
|
||||||
|
document.body.classList.remove(BODY_DRAGGING_CLASS);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rafId !== null) {
|
||||||
|
cancelAnimationFrame(rafId);
|
||||||
|
activeTagDragState.rafId = null;
|
||||||
|
updateDraggingItemPosition();
|
||||||
|
updatePlaceholderPosition();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (placeholder && placeholder.parentNode === container) {
|
||||||
|
container.insertBefore(item, placeholder);
|
||||||
|
container.removeChild(placeholder);
|
||||||
|
}
|
||||||
|
|
||||||
|
item.classList.remove(METADATA_ITEM_DRAGGING_CLASS);
|
||||||
|
item.style.position = '';
|
||||||
|
item.style.width = '';
|
||||||
|
item.style.height = '';
|
||||||
|
item.style.left = '';
|
||||||
|
item.style.top = '';
|
||||||
|
item.style.pointerEvents = '';
|
||||||
|
item.style.zIndex = '';
|
||||||
|
|
||||||
|
activeTagDragState = null;
|
||||||
|
|
||||||
|
updateSuggestionsDropdown();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -694,8 +813,12 @@ function updateSuggestionsDropdown() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function getCurrentEditTags() {
|
function getCurrentEditTags() {
|
||||||
const currentTags = document.querySelectorAll('.metadata-item');
|
const currentTags = document.querySelectorAll(
|
||||||
return Array.from(currentTags).map(tag => tag.dataset.tag);
|
`${METADATA_ITEM_SELECTOR}[data-tag]`
|
||||||
|
);
|
||||||
|
return Array.from(currentTags)
|
||||||
|
.map(tag => tag.dataset.tag)
|
||||||
|
.filter(Boolean);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
Reference in New Issue
Block a user