diff --git a/static/js/components/ContextMenu/CheckpointContextMenu.js b/static/js/components/ContextMenu/CheckpointContextMenu.js index 748f27aa..cc9661e9 100644 --- a/static/js/components/ContextMenu/CheckpointContextMenu.js +++ b/static/js/components/ContextMenu/CheckpointContextMenu.js @@ -11,9 +11,10 @@ export class CheckpointContextMenu extends BaseContextMenu { this.modelType = 'checkpoint'; this.resetAndReload = resetAndReload; - // Initialize NSFW Level Selector events - if (this.nsfwSelector) { + // Initialize NSFW Level Selector events only if not already initialized + if (this.nsfwSelector && !this.nsfwSelector.dataset.initialized) { this.initNSFWSelector(); + this.nsfwSelector.dataset.initialized = 'true'; } } diff --git a/static/js/components/ContextMenu/EmbeddingContextMenu.js b/static/js/components/ContextMenu/EmbeddingContextMenu.js index 0629bff9..ab26f1a9 100644 --- a/static/js/components/ContextMenu/EmbeddingContextMenu.js +++ b/static/js/components/ContextMenu/EmbeddingContextMenu.js @@ -11,9 +11,10 @@ export class EmbeddingContextMenu extends BaseContextMenu { this.modelType = 'embedding'; this.resetAndReload = resetAndReload; - // Initialize NSFW Level Selector events - if (this.nsfwSelector) { + // Initialize NSFW Level Selector events only if not already initialized + if (this.nsfwSelector && !this.nsfwSelector.dataset.initialized) { this.initNSFWSelector(); + this.nsfwSelector.dataset.initialized = 'true'; } } diff --git a/static/js/components/ContextMenu/LoraContextMenu.js b/static/js/components/ContextMenu/LoraContextMenu.js index 4b72cafa..bce9c8cc 100644 --- a/static/js/components/ContextMenu/LoraContextMenu.js +++ b/static/js/components/ContextMenu/LoraContextMenu.js @@ -12,9 +12,10 @@ export class LoraContextMenu extends BaseContextMenu { this.modelType = 'lora'; this.resetAndReload = resetAndReload; - // Initialize NSFW Level Selector events - if (this.nsfwSelector) { + // Initialize NSFW Level Selector events only if not already initialized + if (this.nsfwSelector && !this.nsfwSelector.dataset.initialized) { this.initNSFWSelector(); + this.nsfwSelector.dataset.initialized = 'true'; } } diff --git a/static/js/components/ContextMenu/ModelContextMenuMixin.js b/static/js/components/ContextMenu/ModelContextMenuMixin.js index 1c2c0a9a..17c9339b 100644 --- a/static/js/components/ContextMenu/ModelContextMenuMixin.js +++ b/static/js/components/ContextMenu/ModelContextMenuMixin.js @@ -8,9 +8,12 @@ import { bulkManager } from '../../managers/BulkManager.js'; export const ModelContextMenuMixin = { // NSFW Selector methods initNSFWSelector() { - // Close button + // Remove any existing event listeners by cloning and replacing elements + // This is a simple way to ensure we don't have duplicate event listeners const closeBtn = this.nsfwSelector.querySelector('.close-nsfw-selector'); - closeBtn.addEventListener('click', () => { + const newCloseBtn = closeBtn.cloneNode(true); + closeBtn.parentNode.replaceChild(newCloseBtn, closeBtn); + newCloseBtn.addEventListener('click', () => { this.nsfwSelector.style.display = 'none'; this.resetNSFWSelectorState(); }); @@ -18,8 +21,12 @@ export const ModelContextMenuMixin = { // Level buttons const levelButtons = this.nsfwSelector.querySelectorAll('.nsfw-level-btn'); levelButtons.forEach(btn => { - btn.addEventListener('click', async () => { - const level = parseInt(btn.dataset.level); + // Remove any existing event listeners by cloning and replacing the button + const newBtn = btn.cloneNode(true); + btn.parentNode.replaceChild(newBtn, btn); + + newBtn.addEventListener('click', async () => { + const level = parseInt(newBtn.dataset.level); const mode = this.nsfwSelector.dataset.mode || 'single'; if (mode === 'bulk') { @@ -56,15 +63,24 @@ export const ModelContextMenuMixin = { }); }); - // Close when clicking outside - document.addEventListener('click', (e) => { + // Close when clicking outside - use a named function so we can remove it later + const outsideClickListener = (e) => { if (this.nsfwSelector.style.display === 'block' && !this.nsfwSelector.contains(e.target) && !e.target.closest('.context-menu-item[data-action="set-nsfw"], .context-menu-item[data-action="set-content-rating"]')) { this.nsfwSelector.style.display = 'none'; this.resetNSFWSelectorState(); } - }); + }; + + // Remove previous listener if it exists + if (this._outsideClickListener) { + document.removeEventListener('click', this._outsideClickListener); + } + + // Store and add new listener + this._outsideClickListener = outsideClickListener; + document.addEventListener('click', this._outsideClickListener); }, resetNSFWSelectorState() { diff --git a/static/js/components/ContextMenu/RecipeContextMenu.js b/static/js/components/ContextMenu/RecipeContextMenu.js index bb8b8e69..e5bb53b8 100644 --- a/static/js/components/ContextMenu/RecipeContextMenu.js +++ b/static/js/components/ContextMenu/RecipeContextMenu.js @@ -11,9 +11,10 @@ export class RecipeContextMenu extends BaseContextMenu { this.nsfwSelector = document.getElementById('nsfwLevelSelector'); this.modelType = 'recipe'; - // Initialize NSFW Level Selector events - if (this.nsfwSelector) { + // Initialize NSFW Level Selector events only if not already initialized + if (this.nsfwSelector && !this.nsfwSelector.dataset.initialized) { this.initNSFWSelector(); + this.nsfwSelector.dataset.initialized = 'true'; } }