mirror of
https://github.com/willmiao/ComfyUI-Lora-Manager.git
synced 2026-03-25 15:15:44 -03:00
feat(context-menu): refactor context menu initialization and coordination for improved bulk operations
This commit is contained in:
@@ -1,7 +1,6 @@
|
|||||||
import { appCore } from './core.js';
|
import { appCore } from './core.js';
|
||||||
import { confirmDelete, closeDeleteModal, confirmExclude, closeExcludeModal } from './utils/modalUtils.js';
|
import { confirmDelete, closeDeleteModal, confirmExclude, closeExcludeModal } from './utils/modalUtils.js';
|
||||||
import { createPageControls } from './components/controls/index.js';
|
import { createPageControls } from './components/controls/index.js';
|
||||||
import { CheckpointContextMenu } from './components/ContextMenu/index.js';
|
|
||||||
import { ModelDuplicatesManager } from './components/ModelDuplicatesManager.js';
|
import { ModelDuplicatesManager } from './components/ModelDuplicatesManager.js';
|
||||||
import { MODEL_TYPES } from './api/apiConfig.js';
|
import { MODEL_TYPES } from './api/apiConfig.js';
|
||||||
|
|
||||||
@@ -30,10 +29,7 @@ class CheckpointsPageManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async initialize() {
|
async initialize() {
|
||||||
// Initialize context menu
|
// Initialize common page features (including context menus)
|
||||||
new CheckpointContextMenu();
|
|
||||||
|
|
||||||
// Initialize common page features
|
|
||||||
appCore.initializePageFeatures();
|
appCore.initializePageFeatures();
|
||||||
|
|
||||||
console.log('Checkpoints Manager initialized');
|
console.log('Checkpoints Manager initialized');
|
||||||
@@ -48,4 +44,4 @@ document.addEventListener('DOMContentLoaded', async () => {
|
|||||||
// Initialize checkpoints page
|
// Initialize checkpoints page
|
||||||
const checkpointsPage = new CheckpointsPageManager();
|
const checkpointsPage = new CheckpointsPageManager();
|
||||||
await checkpointsPage.initialize();
|
await checkpointsPage.initialize();
|
||||||
});
|
});
|
||||||
@@ -15,17 +15,6 @@ export class BaseContextMenu {
|
|||||||
init() {
|
init() {
|
||||||
// Hide menu on regular clicks
|
// Hide menu on regular clicks
|
||||||
document.addEventListener('click', () => this.hideMenu());
|
document.addEventListener('click', () => this.hideMenu());
|
||||||
|
|
||||||
// Show menu on right-click on cards
|
|
||||||
document.addEventListener('contextmenu', (e) => {
|
|
||||||
const card = e.target.closest(this.cardSelector);
|
|
||||||
if (!card) {
|
|
||||||
this.hideMenu();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
e.preventDefault();
|
|
||||||
this.showMenu(e.clientX, e.clientY, card);
|
|
||||||
});
|
|
||||||
|
|
||||||
// Handle menu item clicks
|
// Handle menu item clicks
|
||||||
this.menu.addEventListener('click', (e) => {
|
this.menu.addEventListener('click', (e) => {
|
||||||
|
|||||||
@@ -1,8 +1,7 @@
|
|||||||
import { BaseContextMenu } from './BaseContextMenu.js';
|
import { BaseContextMenu } from './BaseContextMenu.js';
|
||||||
import { state } from '../../state/index.js';
|
import { state } from '../../state/index.js';
|
||||||
import { bulkManager } from '../../managers/BulkManager.js';
|
import { bulkManager } from '../../managers/BulkManager.js';
|
||||||
import { translate, updateElementText } from '../../utils/i18nHelpers.js';
|
import { updateElementText } from '../../utils/i18nHelpers.js';
|
||||||
import { MODEL_TYPES } from '../../api/apiConfig.js';
|
|
||||||
|
|
||||||
export class BulkContextMenu extends BaseContextMenu {
|
export class BulkContextMenu extends BaseContextMenu {
|
||||||
constructor() {
|
constructor() {
|
||||||
@@ -10,39 +9,6 @@ export class BulkContextMenu extends BaseContextMenu {
|
|||||||
this.setupBulkMenuItems();
|
this.setupBulkMenuItems();
|
||||||
}
|
}
|
||||||
|
|
||||||
init() {
|
|
||||||
// Override parent init to handle bulk-specific context menu logic
|
|
||||||
document.addEventListener('click', () => this.hideMenu());
|
|
||||||
|
|
||||||
document.addEventListener('contextmenu', (e) => {
|
|
||||||
const card = e.target.closest('.model-card');
|
|
||||||
if (!card || !state.bulkMode) {
|
|
||||||
this.hideMenu();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Show bulk menu only if right-clicking on a selected card
|
|
||||||
if (card.classList.contains('selected')) {
|
|
||||||
e.preventDefault();
|
|
||||||
this.showMenu(e.clientX, e.clientY, card);
|
|
||||||
} else {
|
|
||||||
this.hideMenu();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Handle menu item clicks
|
|
||||||
this.menu.addEventListener('click', (e) => {
|
|
||||||
const menuItem = e.target.closest('.context-menu-item');
|
|
||||||
if (!menuItem || !this.currentCard) return;
|
|
||||||
|
|
||||||
const action = menuItem.dataset.action;
|
|
||||||
if (!action) return;
|
|
||||||
|
|
||||||
this.handleMenuAction(action, menuItem);
|
|
||||||
this.hideMenu();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
setupBulkMenuItems() {
|
setupBulkMenuItems() {
|
||||||
if (!this.menu) return;
|
if (!this.menu) return;
|
||||||
|
|
||||||
|
|||||||
@@ -2,4 +2,56 @@ export { LoraContextMenu } from './LoraContextMenu.js';
|
|||||||
export { RecipeContextMenu } from './RecipeContextMenu.js';
|
export { RecipeContextMenu } from './RecipeContextMenu.js';
|
||||||
export { CheckpointContextMenu } from './CheckpointContextMenu.js';
|
export { CheckpointContextMenu } from './CheckpointContextMenu.js';
|
||||||
export { EmbeddingContextMenu } from './EmbeddingContextMenu.js';
|
export { EmbeddingContextMenu } from './EmbeddingContextMenu.js';
|
||||||
export { ModelContextMenuMixin } from './ModelContextMenuMixin.js';
|
export { ModelContextMenuMixin } from './ModelContextMenuMixin.js';
|
||||||
|
|
||||||
|
import { LoraContextMenu } from './LoraContextMenu.js';
|
||||||
|
import { RecipeContextMenu } from './RecipeContextMenu.js';
|
||||||
|
import { CheckpointContextMenu } from './CheckpointContextMenu.js';
|
||||||
|
import { EmbeddingContextMenu } from './EmbeddingContextMenu.js';
|
||||||
|
import { state } from '../../state/index.js';
|
||||||
|
|
||||||
|
// Factory method to create page-specific context menu instances
|
||||||
|
export function createPageContextMenu(pageType) {
|
||||||
|
switch (pageType) {
|
||||||
|
case 'loras':
|
||||||
|
return new LoraContextMenu();
|
||||||
|
case 'recipes':
|
||||||
|
return new RecipeContextMenu();
|
||||||
|
case 'checkpoints':
|
||||||
|
return new CheckpointContextMenu();
|
||||||
|
case 'embeddings':
|
||||||
|
return new EmbeddingContextMenu();
|
||||||
|
default:
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialize context menu coordination for pages that support it
|
||||||
|
export function initializeContextMenuCoordination(pageContextMenu, bulkContextMenu) {
|
||||||
|
// Centralized context menu event handler
|
||||||
|
document.addEventListener('contextmenu', (e) => {
|
||||||
|
const card = e.target.closest('.model-card');
|
||||||
|
if (!card) {
|
||||||
|
// Hide all menus if not right-clicking on a card
|
||||||
|
pageContextMenu?.hideMenu();
|
||||||
|
bulkContextMenu?.hideMenu();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
e.preventDefault();
|
||||||
|
|
||||||
|
// Hide all menus first
|
||||||
|
pageContextMenu?.hideMenu();
|
||||||
|
bulkContextMenu?.hideMenu();
|
||||||
|
|
||||||
|
// Determine which menu to show based on bulk mode and selection state
|
||||||
|
if (state.bulkMode && card.classList.contains('selected')) {
|
||||||
|
// Show bulk menu for selected cards in bulk mode
|
||||||
|
bulkContextMenu?.showMenu(e.clientX, e.clientY, card);
|
||||||
|
} else if (!state.bulkMode) {
|
||||||
|
// Show regular menu when not in bulk mode
|
||||||
|
pageContextMenu?.showMenu(e.clientX, e.clientY, card);
|
||||||
|
}
|
||||||
|
// Don't show any menu for unselected cards in bulk mode
|
||||||
|
});
|
||||||
|
}
|
||||||
@@ -16,11 +16,14 @@ import { migrateStorageItems } from './utils/storageHelpers.js';
|
|||||||
import { i18n } from './i18n/index.js';
|
import { i18n } from './i18n/index.js';
|
||||||
import { onboardingManager } from './managers/OnboardingManager.js';
|
import { onboardingManager } from './managers/OnboardingManager.js';
|
||||||
import { BulkContextMenu } from './components/ContextMenu/BulkContextMenu.js';
|
import { BulkContextMenu } from './components/ContextMenu/BulkContextMenu.js';
|
||||||
|
import { createPageContextMenu, initializeContextMenuCoordination } from './components/ContextMenu/index.js';
|
||||||
|
|
||||||
// Core application class
|
// Core application class
|
||||||
export class AppCore {
|
export class AppCore {
|
||||||
constructor() {
|
constructor() {
|
||||||
this.initialized = false;
|
this.initialized = false;
|
||||||
|
this.pageContextMenu = null;
|
||||||
|
this.bulkContextMenu = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initialize core functionality
|
// Initialize core functionality
|
||||||
@@ -93,13 +96,27 @@ export class AppCore {
|
|||||||
initializePageFeatures() {
|
initializePageFeatures() {
|
||||||
const pageType = this.getPageType();
|
const pageType = this.getPageType();
|
||||||
|
|
||||||
// Initialize virtual scroll for pages that need it
|
|
||||||
if (['loras', 'recipes', 'checkpoints', 'embeddings'].includes(pageType)) {
|
if (['loras', 'recipes', 'checkpoints', 'embeddings'].includes(pageType)) {
|
||||||
|
this.initializeContextMenus(pageType);
|
||||||
initializeInfiniteScroll(pageType);
|
initializeInfiniteScroll(pageType);
|
||||||
}
|
}
|
||||||
|
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Initialize context menus for the current page
|
||||||
|
initializeContextMenus(pageType) {
|
||||||
|
// Create page-specific context menu
|
||||||
|
this.pageContextMenu = createPageContextMenu(pageType);
|
||||||
|
|
||||||
|
// Get bulk context menu from bulkManager
|
||||||
|
this.bulkContextMenu = bulkManager.bulkContextMenu;
|
||||||
|
|
||||||
|
// Initialize context menu coordination
|
||||||
|
if (this.pageContextMenu || this.bulkContextMenu) {
|
||||||
|
initializeContextMenuCoordination(this.pageContextMenu, this.bulkContextMenu);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
document.addEventListener('DOMContentLoaded', () => {
|
document.addEventListener('DOMContentLoaded', () => {
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import { appCore } from './core.js';
|
import { appCore } from './core.js';
|
||||||
import { confirmDelete, closeDeleteModal, confirmExclude, closeExcludeModal } from './utils/modalUtils.js';
|
import { confirmDelete, closeDeleteModal, confirmExclude, closeExcludeModal } from './utils/modalUtils.js';
|
||||||
import { createPageControls } from './components/controls/index.js';
|
import { createPageControls } from './components/controls/index.js';
|
||||||
import { EmbeddingContextMenu } from './components/ContextMenu/index.js';
|
|
||||||
import { ModelDuplicatesManager } from './components/ModelDuplicatesManager.js';
|
import { ModelDuplicatesManager } from './components/ModelDuplicatesManager.js';
|
||||||
import { MODEL_TYPES } from './api/apiConfig.js';
|
import { MODEL_TYPES } from './api/apiConfig.js';
|
||||||
|
|
||||||
@@ -30,10 +29,7 @@ class EmbeddingsPageManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async initialize() {
|
async initialize() {
|
||||||
// Initialize context menu
|
// Initialize common page features (including context menus)
|
||||||
new EmbeddingContextMenu();
|
|
||||||
|
|
||||||
// Initialize common page features
|
|
||||||
appCore.initializePageFeatures();
|
appCore.initializePageFeatures();
|
||||||
|
|
||||||
console.log('Embeddings Manager initialized');
|
console.log('Embeddings Manager initialized');
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import { appCore } from './core.js';
|
import { appCore } from './core.js';
|
||||||
import { state } from './state/index.js';
|
import { state } from './state/index.js';
|
||||||
import { updateCardsForBulkMode } from './components/shared/ModelCard.js';
|
import { updateCardsForBulkMode } from './components/shared/ModelCard.js';
|
||||||
import { LoraContextMenu } from './components/ContextMenu/index.js';
|
|
||||||
import { createPageControls } from './components/controls/index.js';
|
import { createPageControls } from './components/controls/index.js';
|
||||||
import { confirmDelete, closeDeleteModal, confirmExclude, closeExcludeModal } from './utils/modalUtils.js';
|
import { confirmDelete, closeDeleteModal, confirmExclude, closeExcludeModal } from './utils/modalUtils.js';
|
||||||
import { ModelDuplicatesManager } from './components/ModelDuplicatesManager.js';
|
import { ModelDuplicatesManager } from './components/ModelDuplicatesManager.js';
|
||||||
@@ -37,13 +36,10 @@ class LoraPageManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async initialize() {
|
async initialize() {
|
||||||
// Initialize page-specific components
|
|
||||||
new LoraContextMenu();
|
|
||||||
|
|
||||||
// Initialize cards for current bulk mode state (should be false initially)
|
// Initialize cards for current bulk mode state (should be false initially)
|
||||||
updateCardsForBulkMode(state.bulkMode);
|
updateCardsForBulkMode(state.bulkMode);
|
||||||
|
|
||||||
// Initialize common page features (virtual scroll)
|
// Initialize common page features (including context menus and virtual scroll)
|
||||||
appCore.initializePageFeatures();
|
appCore.initializePageFeatures();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -71,6 +71,8 @@ export const state = {
|
|||||||
pageSize: 20,
|
pageSize: 20,
|
||||||
showFavoritesOnly: false,
|
showFavoritesOnly: false,
|
||||||
duplicatesMode: false,
|
duplicatesMode: false,
|
||||||
|
bulkMode: false,
|
||||||
|
selectedModels: new Set(),
|
||||||
},
|
},
|
||||||
|
|
||||||
[MODEL_TYPES.CHECKPOINT]: {
|
[MODEL_TYPES.CHECKPOINT]: {
|
||||||
|
|||||||
Reference in New Issue
Block a user