refactor: update SimilarTags UI for improved layout and positioning logic
This commit is contained in:
@@ -43,7 +43,7 @@
|
||||
--comfy-hover-bg
|
||||
); /* Use a hover variable if available, or adjust */
|
||||
|
||||
color: var(--desc-text); /* Adjust text color on hover if needed */
|
||||
color: var(--descrip-text); /* Adjust text color on hover if needed */
|
||||
}
|
||||
|
||||
/* Selected item style */
|
||||
@@ -68,9 +68,10 @@
|
||||
top: 0;
|
||||
left: 0;
|
||||
display: none;
|
||||
width: 800px;
|
||||
width: fit-content;
|
||||
background-color: var(--comfy-input-bg);
|
||||
color: var(--input-text);
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
#similar-tags-header {
|
||||
@@ -80,7 +81,7 @@
|
||||
padding: 8px 12px;
|
||||
border-bottom: 1px solid var(--border-color);
|
||||
background-color: var(--comfy-menu-bg);
|
||||
color: var(--desc-text);
|
||||
color: var(--descrip-text);
|
||||
}
|
||||
|
||||
.similar-tags-header-tag-name {
|
||||
@@ -89,45 +90,48 @@
|
||||
|
||||
#similar-tags-list {
|
||||
display: grid;
|
||||
padding: 5px;
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: 4px;
|
||||
box-shadow: 0 2px 8px rgb(0 0 0 / 30%);
|
||||
grid-auto-rows: auto;
|
||||
grid-template-columns: max-content 1fr auto;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.similar-tag-item {
|
||||
display: contents;
|
||||
padding: 5px 10px; /* Adjusted padding */
|
||||
border-bottom: 1px solid var(--border-color); /* Separator line */
|
||||
cursor: pointer;
|
||||
white-space: nowrap; /* Prevent wrapping */
|
||||
}
|
||||
|
||||
.similar-tag-item:last-child {
|
||||
border-bottom: none;
|
||||
.similar-tag-item span {
|
||||
align-content: center;
|
||||
padding: 4px 8px;
|
||||
border-bottom: 1px solid var(--border-color);
|
||||
}
|
||||
|
||||
.similar-tag-item:hover {
|
||||
background-color: var(--comfy-hover-bg);
|
||||
.similar-tag-item:nth-child(even) span {
|
||||
background-color: var(--comfy-menu-bg);
|
||||
}
|
||||
|
||||
.similar-tag-item:nth-child(odd) span {
|
||||
background-color: var(--comfy-input-bg);
|
||||
}
|
||||
|
||||
.similar-tag-item:hover span {
|
||||
background-color: var(--p-form-field-filled-hover-background);
|
||||
}
|
||||
|
||||
.similar-tag-name {
|
||||
padding: 8px;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.similar-tag-alias {
|
||||
overflow: hidden;
|
||||
color: var(--descrip-text);
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.similar-tag-similarity {
|
||||
border-radius: 10px;
|
||||
background-color: var(--comfy-menu-bg);
|
||||
color: var(--desc-text);
|
||||
color: var(--descrip-text);
|
||||
text-align: right;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
+106
-89
@@ -25,7 +25,7 @@ class SimilarTagsUI {
|
||||
this.root.appendChild(this.header);
|
||||
|
||||
// Create a tbody for the tags
|
||||
this.tagsContainer = document.createElement('ul');
|
||||
this.tagsContainer = document.createElement('div');
|
||||
this.tagsContainer.id = 'similar-tags-list';
|
||||
this.root.appendChild(this.tagsContainer);
|
||||
|
||||
@@ -87,6 +87,10 @@ class SimilarTagsUI {
|
||||
* @param {Array<{tag: string, similarity: number, count: number, alias?: string[]}>} similarTags
|
||||
*/
|
||||
updateContent(similarTags) {
|
||||
this.root.style.left = 0;
|
||||
this.root.style.top = 0;
|
||||
this.root.style.maxWidth = `${window.innerWidth / 2}px`;
|
||||
this.root.style.maxHeight = `${window.innerHeight / 2}px`;
|
||||
this.tagsContainer.innerHTML = '';
|
||||
|
||||
// Update header with current tag
|
||||
@@ -125,10 +129,9 @@ class SimilarTagsUI {
|
||||
* @returns {HTMLTableRowElement} The tag row element
|
||||
*/
|
||||
createTagElement(tagData) {
|
||||
const tagRow = document.createElement('li');
|
||||
const tagRow = document.createElement('div');
|
||||
tagRow.className = 'similar-tag-item';
|
||||
tagRow.dataset.tag = tagData.tag;
|
||||
tagRow.style.cursor = 'pointer';
|
||||
|
||||
// Tag name cell
|
||||
const tagNameCell = document.createElement('span');
|
||||
@@ -138,11 +141,11 @@ class SimilarTagsUI {
|
||||
// Alias cell (middle column)
|
||||
const aliasCell = document.createElement('span');
|
||||
aliasCell.className = 'similar-tag-alias';
|
||||
|
||||
|
||||
// Display alias if available
|
||||
if (tagData.alias && tagData.alias.length > 0) {
|
||||
let aliasText = tagData.alias.join(', ');
|
||||
aliasCell.textContent = aliasText;
|
||||
aliasCell.textContent = `${aliasText}`;
|
||||
aliasCell.title = tagData.alias.join(', '); // Full alias on hover
|
||||
}
|
||||
|
||||
@@ -173,98 +176,42 @@ class SimilarTagsUI {
|
||||
* @param {HTMLElement} inputElement The input element to position
|
||||
*/
|
||||
updatePosition(inputElement) {
|
||||
// Reset position for accurate measurement
|
||||
this.root.style.maxHeight = '';
|
||||
this.root.style.maxWidth = '';
|
||||
const margin = 10;
|
||||
|
||||
// Get the bounds of the input element
|
||||
const inputRect = inputElement.getBoundingClientRect();
|
||||
|
||||
// Get viewport dimensions
|
||||
const viewportWidth = window.innerWidth;
|
||||
const viewportHeight = window.innerHeight;
|
||||
const mergin = 10;
|
||||
|
||||
// Get ComfyUI canvas scale if available
|
||||
const scale = window.app?.canvas?.ds?.scale ?? 1.0; // Note: scale is not currently used in positioning logic
|
||||
|
||||
// Make element briefly visible for measurement but not actually showing
|
||||
// Measure the element size without causing reflow
|
||||
this.root.style.visibility = 'hidden';
|
||||
this.root.style.position = 'absolute'; // Ensure position is absolute for measurement
|
||||
this.root.style.display = 'block';
|
||||
this.root.style.left = '-9999px';
|
||||
this.root.style.top = '-9999px';
|
||||
this.root.style.maxWidth = ''; // Reset max dimensions before measuring
|
||||
this.root.style.maxHeight = '';
|
||||
const elemRect = this.root.getBoundingClientRect();
|
||||
// Hide it again after measurement
|
||||
this.root.style.display = 'none';
|
||||
this.root.style.visibility = 'visible';
|
||||
this.root.style.position = ''; // Reset position style
|
||||
this.root.style.left = '';
|
||||
this.root.style.top = '';
|
||||
|
||||
let [left, top] = [0, 0];
|
||||
// Get the optimal placement area
|
||||
const placementArea = this.#getOptimalPlacementArea(inputElement.getBoundingClientRect(), elemRect.width, elemRect.height, margin);
|
||||
|
||||
// Determine initial position based on setting
|
||||
if (settingValues.similarTagsDisplayPosition === 'vertical') {
|
||||
// --- Vertical Positioning ---
|
||||
// Calculate available space above and below the input
|
||||
const spaceAbove = inputRect.top;
|
||||
const spaceBelow = viewportHeight - inputRect.bottom;
|
||||
|
||||
// Determine if we should place above or below based on which has more space
|
||||
const placeAbove = spaceAbove > spaceBelow;
|
||||
|
||||
if (placeAbove) {
|
||||
// Place above the input with appropriate margin
|
||||
top = inputRect.top - elemRect.height - mergin;
|
||||
// Adjust max height to not exceed available space
|
||||
this.root.style.maxHeight = `${spaceAbove - (mergin * 2)}px`;
|
||||
} else {
|
||||
// Place below the input with appropriate margin
|
||||
top = inputRect.bottom + mergin;
|
||||
// Adjust max height to not exceed available space
|
||||
this.root.style.maxHeight = `${spaceBelow - (mergin * 2)}px`;
|
||||
}
|
||||
// Calculate final styles, fitting the element within the placement area
|
||||
const finalMaxWidth = Math.min(elemRect.width, placementArea.width);
|
||||
const finalMaxHeight = Math.min(elemRect.height, placementArea.height);
|
||||
|
||||
// Ensure horizontal alignment within viewport if placed vertically
|
||||
left = Math.max(mergin, Math.min(viewportWidth - elemRect.width - mergin, left));
|
||||
// Align left edge with input where possible
|
||||
left = inputRect.left;
|
||||
// Adjust position if the element is smaller than the area (e.g., center or align based on mode)
|
||||
// For simplicity, we'll just use the calculated top-left corner of the area for now.
|
||||
// More sophisticated alignment could be added here if needed.
|
||||
let finalLeft = placementArea.x;
|
||||
let finalTop = placementArea.y;
|
||||
|
||||
} else {
|
||||
// --- Horizontal Positioning (Default) ---
|
||||
// Initial position: to the right of the textarea
|
||||
left = inputRect.right + mergin;
|
||||
top = inputRect.top;
|
||||
|
||||
// Check if we have enough space to the right
|
||||
const rightSpace = viewportWidth - left;
|
||||
if (rightSpace < elemRect.width && left > viewportWidth / 2) { // Only move left if it fits better
|
||||
// Not enough space to the right, try placing it to the left
|
||||
left = inputRect.left - elemRect.width - mergin;
|
||||
}
|
||||
|
||||
// Fallback to vertical if horizontal doesn't fit well (e.g., goes off left)
|
||||
if (left < 0) {
|
||||
left = inputRect.left;
|
||||
top = inputRect.bottom + mergin;
|
||||
const bottomSpace = viewportHeight - top;
|
||||
if (bottomSpace < elemRect.height) {
|
||||
top = inputRect.top - elemRect.height - mergin;
|
||||
}
|
||||
}
|
||||
// Ensure vertical alignment within viewport if placed horizontally
|
||||
top = Math.max(mergin, Math.min(viewportHeight - elemRect.height - mergin, top));
|
||||
}
|
||||
|
||||
// Ensure the element stays within viewport (final check for both cases)
|
||||
// left = Math.max(mergin, Math.min(viewportWidth - elw - mergin, left));
|
||||
// top = Math.max(mergin, Math.min(viewportHeight - elemHeight - mergin, top));
|
||||
|
||||
// Update element position
|
||||
this.root.style.left = `${left}px`;
|
||||
this.root.style.top = `${top}px`;
|
||||
|
||||
// Set max dimensions to ensure scrolling if content is too large
|
||||
// Adjust max height based on final top position
|
||||
const maxHeight = viewportHeight - top - 20;
|
||||
this.root.style.maxHeight = `${maxHeight}px`;
|
||||
// Adjust max width based on final left position (less critical usually)
|
||||
const maxWidth = Math.min(elemRect.width, viewportWidth - left - 20);
|
||||
this.root.style.maxWidth = `${maxWidth}px`;
|
||||
// Apply Styles
|
||||
this.root.style.left = `${finalLeft}px`;
|
||||
this.root.style.top = `${finalTop}px`;
|
||||
this.root.style.maxWidth = `${finalMaxWidth}px`;
|
||||
this.root.style.maxHeight = `${finalMaxHeight}px`;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -289,6 +236,76 @@ class SimilarTagsUI {
|
||||
isVisible() {
|
||||
return this.root.style.display !== 'none';
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Calculates the optimal placement area for the panel based on available space.
|
||||
* @param {DOMRect} inputRect - Bounding rectangle of the input element.
|
||||
* @param {number} elemWidth - Width of the panel element.
|
||||
* @param {number} elemHeight - Height of the panel element.
|
||||
* @param {number} margin - Margin around the element.
|
||||
* @returns {{ x: number, y: number, width: number, height: number }} The calculated placement area.
|
||||
*/
|
||||
#getOptimalPlacementArea(inputRect, elemWidth, elemHeight, margin) {
|
||||
const viewportWidth = window.innerWidth;
|
||||
const viewportHeight = window.innerHeight;
|
||||
|
||||
// Calculate available space around the input element
|
||||
const spaceAbove = inputRect.top - margin;
|
||||
const spaceBelow = viewportHeight - inputRect.bottom - margin;
|
||||
const spaceLeft = inputRect.left - margin;
|
||||
const spaceRight = viewportWidth - inputRect.right - margin;
|
||||
|
||||
const isVertical = settingValues.similarTagsDisplayPosition === 'vertical';
|
||||
|
||||
let area = { x: 0, y: 0, width: 0, height: 0 };
|
||||
|
||||
if (isVertical) {
|
||||
// --- Vertical Placement ---
|
||||
// Determine available width first (usually aligned with input)
|
||||
area.width = viewportWidth - margin * 2; // Max available width within viewport margins
|
||||
area.x = margin; // Default x position
|
||||
|
||||
// Decide whether to place above or below
|
||||
if (spaceAbove >= elemHeight || spaceAbove > spaceBelow) {
|
||||
// Place Above
|
||||
area.y = inputRect.top - margin - Math.min(elemHeight, spaceAbove); // Position below the available space top edge
|
||||
area.height = Math.min(elemHeight, spaceAbove); // Fit height within available space
|
||||
} else {
|
||||
// Place Below
|
||||
area.y = inputRect.bottom + margin;
|
||||
area.height = Math.min(elemHeight, spaceBelow);
|
||||
}
|
||||
// Adjust x and width to align with input if possible, while staying in viewport
|
||||
area.x = Math.max(margin, inputRect.left);
|
||||
area.width = Math.min(elemWidth, viewportWidth - area.x - margin);
|
||||
} else {
|
||||
// --- Horizontal Placement ---
|
||||
// Determine available height first (usually aligned with input top)
|
||||
area.height = viewportHeight - margin * 2; // Max available height within viewport margins
|
||||
area.y = margin; // Default y position
|
||||
|
||||
// Decide whether to place left or right
|
||||
if (spaceLeft >= elemWidth || spaceLeft > spaceRight) {
|
||||
// Place Left
|
||||
area.x = inputRect.left - margin - Math.min(elemWidth, spaceLeft);
|
||||
area.width = Math.min(elemWidth, spaceLeft);
|
||||
} else {
|
||||
// Place Right
|
||||
area.x = inputRect.right + margin;
|
||||
area.width = Math.min(elemWidth, spaceRight);
|
||||
}
|
||||
// Adjust y and height to align with input top if possible, while staying in viewport
|
||||
area.y = Math.max(margin, inputRect.top);
|
||||
area.height = Math.min(elemHeight, viewportHeight - area.y - margin);
|
||||
}
|
||||
|
||||
// Ensure dimensions are non-negative
|
||||
area.width = Math.max(0, area.width);
|
||||
area.height = Math.max(0, area.height);
|
||||
|
||||
return area;
|
||||
}
|
||||
}
|
||||
|
||||
// --- Helper Functions ---
|
||||
@@ -528,7 +545,7 @@ export class SimilarTagsEventHandler {
|
||||
|
||||
// If the focus is not within the similar tags UI, hide it.
|
||||
if (similarTagsUI && !similarTagsElement.contains(activeElement)) {
|
||||
// similarTagsUI.hide();
|
||||
similarTagsUI.hide();
|
||||
}
|
||||
}, 150); // Delay in milliseconds (adjust if necessary)
|
||||
}
|
||||
@@ -543,7 +560,7 @@ export class SimilarTagsEventHandler {
|
||||
similarTagsUI.hide();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Show similar tags on Ctrl+Space
|
||||
if (settingValues.enableSimilarTags) {
|
||||
if (event.key === ' ' && event.ctrlKey) {
|
||||
|
||||
Reference in New Issue
Block a user