mis-custom-nodes / Comfyui-Resolution-Master /js /utils /PresetManagerDialog.js
MarkLilly's picture
Add files using upload-large-folder tool
70ceb8b verified
Raw History Blame Contribute Delete
33.1 kB
/**
* PresetManagerDialog.js
* Main dialog coordinator for managing custom presets
*/
// Load CSS styles
import "../css-loader.js";
import { createModuleLogger } from "./LoggerUtils.js";
import { SearchableDropdown } from "../SearchableDropdown.js";
import { AspectRatioUtils } from "./AspectRatioUtils.js";
import { loadIcons, getIconHtml } from "./IconUtils.js";
import { presetManagerTooltips } from "./ResolutionMasterConfig.js";
import { PresetUIComponents } from './preset-manager/PresetUIComponents.js';
import { DragDropHandler } from './preset-manager/DragDropHandler.js';
import { RenameDialogManager } from './preset-manager/RenameDialogManager.js';
import { JSONEditorDialog } from './preset-manager/JSONEditorDialog.js';
import { PresetListRenderer } from './preset-manager/PresetListRenderer.js';
import { PresetAddViewRenderer } from './preset-manager/PresetAddViewRenderer.js';
import { TooltipManager } from './preset-manager/TooltipManager.js';
const log = createModuleLogger('PresetManagerDialog');
export class PresetManagerDialog {
constructor(customPresetsManager) {
this.manager = customPresetsManager;
this.dialog = null;
this.currentView = 'list'; // 'list' or 'add'
this.editingPresetId = null;
this.editingCategoryId = null;
this.quickAddShape = 'landscape';
this.quickAddCategory = null;
this.columnMode = 'aspect_ratio'; // or 'simple'
this.selectedPresets = new Set(); // For multi-selection with Shift
this.lastSelectedIndex = -1; // For Shift-click range selection
this.selectedBulkCategory = null;
// Dialog state
this.isActive = false;
this.container = null;
this.overlay = null;
// Bulk deletion
this.selectedPresetsForDeletion = new Set();
this.lastClickedPresetKey = null;
// Add/Edit view state
this.selectedCategory = null;
this.editingPresetName = null;
this.editingPresetData = null;
this.presetsGrid = null;
this.presetPreviewContainer = null;
this.presetPreviewResizeHandler = null;
this.horizontalScrollState = { indicator: null }; // Persistent state for horizontal scroll indicator
// SearchableDropdown instance
this.searchableDropdown = new SearchableDropdown();
// Icons container
const iconContainer = {};
// Ikony, które chcemy nadpisać kolor
const iconColors = {
add_plus: "#000000" // czarny kolor dla ikony 'add_plus'
};
// Wczytanie ikon z nadpisanym kolorem dla add_plus
loadIcons(iconContainer, "#ffffffff", iconColors);
// Assign icons to properties with correct names
this.deleteIcon = iconContainer.delete;
this.importIcon = iconContainer.import;
this.exportIcon = iconContainer.export;
this.editIcon = iconContainer.edit;
this.customPresetIcon = iconContainer.customPreset;
this.dragAndDuplicateIcon = iconContainer.dragAndDuplicate;
this.addPlusIcon = iconContainer.add_plus;
this.settingsIcon = iconContainer.settings;
// Initialize helper modules
this.uiComponents = new PresetUIComponents(this);
this.dragDropHandler = new DragDropHandler(this);
this.renameDialogManager = new RenameDialogManager(this);
this.jsonEditor = new JSONEditorDialog(this);
this.listRenderer = new PresetListRenderer(this);
this.addViewRenderer = new PresetAddViewRenderer(this);
// Initialize tooltip manager
this.tooltipManager = new TooltipManager({
delay: 500,
maxWidth: 300
});
// Register tooltips for common elements
this.registerTooltips();
}
/**
* Registers all tooltips for the dialog
*/
registerTooltips() {
// Use tooltips from ResolutionMasterConfig.js
this.tooltipManager.registerTooltips(presetManagerTooltips);
}
/**
* Attaches tooltips to rendered elements
*/
attachTooltips() {
// List of selectors for elements that should have tooltips
const selectors = [
'.resolution-master-preset-manager-footer button[id]',
'.resolution-master-preset-list-edit-btn',
'.resolution-master-preset-add-rename-category-btn',
'.resolution-master-preset-list-delete-btn',
'.resolution-master-aspect-ratio-preset-action-btn.delete',
'.resolution-master-aspect-ratio-preset-action-btn.hide',
'.resolution-master-aspect-ratio-preset-action-btn.unhide',
'.resolution-master-preset-list-category-header',
'.resolution-master-preset-list-category-name',
'.resolution-master-preset-list-edit-category-btn',
'.resolution-master-preset-list-name',
'.resolution-master-preset-list-checkbox',
'#category-select-btn',
'#quick-add-button',
'.resolution-master-preset-list-clone-handle'
];
// Attach tooltips to all matching elements
selectors.forEach(selector => {
const elements = this.container.querySelectorAll(selector);
elements.forEach(el => this.tooltipManager.attach(el));
});
}
/**
* Shows the preset manager dialog
*/
show() {
if (this.isActive) return;
this.isActive = true;
this.currentView = 'list';
this.editingPreset = null;
this.selectedPresetsForDeletion.clear(); // Clear selection when opening dialog
this.lastClickedPresetKey = null; // Reset last clicked for shift-click
// Create overlay
this.overlay = document.createElement('div');
this.overlay.className = 'resolution-master-preset-manager-overlay';
this.overlay.addEventListener('mousedown', (e) => {
if (e.target === this.overlay) this.hide();
});
document.body.appendChild(this.overlay);
// Create container
this.container = document.createElement('div');
this.container.className = 'resolution-master-preset-manager-dialog';
document.body.appendChild(this.container);
this.renderDialog();
}
/**
* Renders the dialog content based on current view
*/
renderDialog() {
this.container.innerHTML = '';
// Header
const header = document.createElement('div');
header.className = 'resolution-master-preset-manager-header';
const title = document.createElement('div');
title.className = 'resolution-master-preset-manager-title';
const settingsIconHtml = getIconHtml(this.settingsIcon, '⚙', 24, 'vertical-align: middle; margin-right: 6px;');
title.innerHTML = settingsIconHtml + ' Custom Presets Manager';
const closeBtn = document.createElement('button');
closeBtn.className = 'resolution-master-preset-manager-close-btn';
closeBtn.textContent = '✕';
closeBtn.addEventListener('click', () => this.hide());
header.appendChild(title);
header.appendChild(closeBtn);
this.container.appendChild(header);
// Content area
const content = document.createElement('div');
content.className = 'resolution-master-preset-manager-content';
if (this.currentView === 'list') {
this.listRenderer.render(content);
} else if (this.currentView === 'add') {
this.addViewRenderer.render(content);
}
this.container.appendChild(content);
// Footer with action buttons
this.renderFooter();
// Attach tooltips to rendered elements
this.attachTooltips();
}
/**
* Renders the footer with action buttons
*/
renderFooter() {
const footer = document.createElement('div');
footer.className = 'resolution-master-preset-manager-footer';
// Left side buttons
const leftButtons = document.createElement('div');
leftButtons.className = 'resolution-master-preset-manager-footer-left';
if (this.currentView === 'list') {
const addPlusIconHtml = getIconHtml(this.addPlusIcon, '➕', 18, 'vertical-align: middle; margin-right: 4px;');
const addBtn = PresetUIComponents.createFooterButton(addPlusIconHtml + ' Add Preset', 'primary', () => {
this.currentView = 'add';
this.editingPreset = null;
this.renderDialog();
});
addBtn.id = 'add-preset-btn';
leftButtons.appendChild(addBtn);
// Delete selected button (only shown when there are selected items)
const selectedCount = this.selectedPresetsForDeletion.size;
const deleteIconHtml = this.deleteIcon ? `<img src="${this.deleteIcon.src}" style="width: 14px; height: 14px; vertical-align: middle; margin-right: 4px;">` : '🗑️ ';
const btnText = selectedCount > 0 ? `Delete Selected (${selectedCount})` : 'Delete Selected';
const deleteSelectedBtn = PresetUIComponents.createFooterButton(deleteIconHtml + btnText, 'secondary', () => {
this.deleteSelectedPresets();
});
deleteSelectedBtn.id = 'delete-selected-btn';
// Apply danger styling when items are selected
if (selectedCount > 0) {
deleteSelectedBtn.style.background = '#f55';
deleteSelectedBtn.style.color = '#fff';
deleteSelectedBtn.style.opacity = '1';
deleteSelectedBtn.style.borderColor = '#f55';
} else {
deleteSelectedBtn.disabled = true;
deleteSelectedBtn.style.opacity = '0.5';
deleteSelectedBtn.style.cursor = 'not-allowed';
}
// Add hover effects for danger state
deleteSelectedBtn.addEventListener('mouseenter', () => {
if (selectedCount > 0) {
deleteSelectedBtn.style.background = '#f77';
deleteSelectedBtn.style.borderColor = '#f77';
}
});
deleteSelectedBtn.addEventListener('mouseleave', () => {
if (selectedCount > 0) {
deleteSelectedBtn.style.background = '#f55';
deleteSelectedBtn.style.borderColor = '#f55';
}
});
leftButtons.appendChild(deleteSelectedBtn);
const importIconHtml = getIconHtml(this.importIcon, '📥', 18, 'vertical-align: middle; margin-right: 4px;');
const importBtn = PresetUIComponents.createFooterButton(importIconHtml + ' Import', 'secondary', () => {
this.importPresets();
});
importBtn.id = 'import-btn';
leftButtons.appendChild(importBtn);
const exportIconHtml = getIconHtml(this.exportIcon, '📤', 18, 'vertical-align: middle; margin-right: 4px;');
const exportBtn = PresetUIComponents.createFooterButton(exportIconHtml + ' Export', 'secondary', () => {
this.exportPresets();
});
exportBtn.id = 'export-btn';
leftButtons.appendChild(exportBtn);
const editJsonBtn = PresetUIComponents.createFooterButton('{ } Edit JSON', 'secondary', () => {
this.showJSONEditor();
});
editJsonBtn.id = 'edit-json-btn';
leftButtons.appendChild(editJsonBtn);
} else if (this.currentView === 'add') {
const backBtn = PresetUIComponents.createFooterButton('← Back to List', 'secondary', () => {
// Close SearchableDropdown if it's open
if (this.searchableDropdown) {
this.searchableDropdown.hide();
}
this.currentView = 'list';
this.editingPreset = null;
this.selectedCategory = null; // Reset selected category
this.renderDialog();
});
backBtn.id = 'back-btn';
leftButtons.appendChild(backBtn);
}
footer.appendChild(leftButtons);
this.container.appendChild(footer);
}
/**
* Edits an existing preset
*/
editPreset(category, name, dims) {
// Use new editing system
this.selectedCategory = category;
this.editingPresetName = name;
this.editingPresetData = {
name: name,
width: dims.width,
height: dims.height
};
this.currentView = 'add';
this.renderDialog();
}
/**
* Deletes a preset after confirmation
*/
deletePreset(category, name) {
if (confirm(`Delete preset "${name}" from category "${category}"?`)) {
this.manager.deletePreset(category, name);
this.renderDialog(); // Refresh view
log.debug(`Deleted preset: ${category}/${name}`);
}
}
/**
* Saves the preset (add or edit)
*/
savePreset() {
const categoryInput = document.getElementById('category-input');
const nameInput = document.getElementById('name-input');
const widthInput = document.getElementById('width-input');
const heightInput = document.getElementById('height-input');
const validationMsg = document.getElementById('validation-msg');
const category = categoryInput.value.trim();
const name = nameInput.value.trim();
const width = parseInt(widthInput.value);
const height = parseInt(heightInput.value);
// Validation
if (!category) {
validationMsg.textContent = 'Category is required';
categoryInput.focus();
return;
}
if (!name) {
validationMsg.textContent = 'Preset name is required';
nameInput.focus();
return;
}
if (!width || width < 64) {
validationMsg.textContent = 'Width must be at least 64px';
widthInput.focus();
return;
}
if (!height || height < 64) {
validationMsg.textContent = 'Height must be at least 64px';
heightInput.focus();
return;
}
// If editing, delete the old preset first (in case category or name changed)
if (this.editingPreset) {
this.manager.deletePreset(this.editingPreset.category, this.editingPreset.name);
}
// Add/update preset
this.manager.addPreset(category, name, width, height);
log.debug(`Saved preset: ${category}/${name} (${width}×${height})`);
// Return to list view
this.currentView = 'list';
this.editingPreset = null;
this.renderDialog();
}
/**
* Imports presets from JSON
*/
importPresets() {
const input = document.createElement('input');
input.type = 'file';
input.accept = '.json';
input.addEventListener('change', (e) => {
const file = e.target.files[0];
if (!file) return;
const reader = new FileReader();
reader.onload = (event) => {
try {
// Use importFromJSON which expects a JSON string and merge=true
const success = this.manager.importFromJSON(event.target.result, true);
if (success) {
this.renderDialog(); // Refresh view
log.debug('Presets imported successfully');
alert('Presets imported successfully!');
} else {
throw new Error('Import failed');
}
} catch (error) {
alert('Error importing presets: ' + error.message);
log.error('Import error:', error);
}
};
reader.readAsText(file);
});
input.click();
}
/**
* Exports presets to JSON file
*/
exportPresets() {
const json = this.manager.exportToJSON();
const blob = new Blob([json], { type: 'application/json' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = 'custom-presets.json';
a.click();
URL.revokeObjectURL(url);
log.debug('Presets exported');
}
/**
* Shows the category dropdown for selection
*/
showCategoryDropdown(event) {
// Get ALL categories (built-in + custom)
const builtInPresets = this.manager.rm.presetCategories; // Access built-in presets
const mergedPresets = this.manager.getMergedPresets(builtInPresets);
// Create category objects with isCustom flag
const categoryObjects = Object.keys(mergedPresets).map(categoryName => {
// A category is truly custom if it does NOT exist in built-in categories
const isTrulyCustom = !builtInPresets.hasOwnProperty(categoryName);
return {
text: categoryName,
isCustom: isTrulyCustom
};
});
this.searchableDropdown.show(categoryObjects, {
event: event,
title: 'Select or Create Category',
allowCustomValues: true,
callback: (selectedCategory) => {
this.selectedCategory = selectedCategory;
this.editingPresetName = null; // Reset editing mode when category changes
this.editingPresetData = null; // Reset editing data
// Update button text and re-render to show quick add form and preview
const categoryBtn = document.getElementById('category-select-btn');
if (categoryBtn) {
categoryBtn.textContent = selectedCategory;
categoryBtn.style.color = '#fff';
}
// Re-render the add view to show quick add form and preview
this.renderDialog();
}
});
}
/**
* Updates the preset preview for the selected category (using AspectRatioSelector style columns)
* Shows ALL presets (built-in + custom) for the selected category
*/
updatePresetPreview() {
// Use stored reference instead of getElementById (element may not be in DOM yet)
const presetsGrid = this.presetsGrid;
if (!presetsGrid || !this.selectedCategory) return;
// Save scroll positions before clearing DOM
const scrollPositions = new Map();
// Save main grid horizontal scroll
const gridScrollLeft = presetsGrid.scrollLeft;
// Save each column's vertical scroll position
const existingColumns = presetsGrid.querySelectorAll('.resolution-master-aspect-ratio-column');
existingColumns.forEach(column => {
const ratioText = column.querySelector('.resolution-master-aspect-ratio-column-title')?.textContent;
const ratioList = column.querySelector('.resolution-master-aspect-ratio-column-list');
if (ratioText && ratioList) {
scrollPositions.set(ratioText, ratioList.scrollTop);
}
});
presetsGrid.innerHTML = '';
// Change to flex layout for columns (matching AspectRatioSelector structure)
presetsGrid.style.cssText = `
display: flex;
gap: 4px;
overflow-x: auto;
overflow-y: hidden;
padding: 4px;
flex: 1;
`;
// Get ALL presets (built-in + custom) for this category
const builtInPresets = this.manager.rm.presetCategories;
const mergedPresets = this.manager.getMergedPresets(builtInPresets);
const categoryPresets = mergedPresets[this.selectedCategory] || {};
if (Object.keys(categoryPresets).length === 0) {
presetsGrid.style.cssText = `
display: grid;
grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
gap: 8px;
`;
const emptyMsg = document.createElement('div');
emptyMsg.textContent = 'No presets in this category yet. Add some above!';
emptyMsg.style.cssText = `
grid-column: 1 / -1;
text-align: center;
padding: 20px;
color: #888;
font-size: 13px;
`;
presetsGrid.appendChild(emptyMsg);
return;
}
// Group presets by aspect ratio
const groupedPresets = AspectRatioUtils.groupPresetsByAspectRatio(categoryPresets);
// Add custom scrollbar styling for better visibility (matching AspectRatioSelector)
if (!document.getElementById('presets-grid-scrollbar-style')) {
const style = document.createElement('style');
style.id = 'presets-grid-scrollbar-style';
style.textContent = `
#presets-grid::-webkit-scrollbar {
height: 12px;
width: 12px;
}
#presets-grid::-webkit-scrollbar-track {
background: rgba(0, 0, 0, 0.3);
border-radius: 4px;
}
#presets-grid::-webkit-scrollbar-thumb {
background: #666;
border-radius: 6px;
border: 2px solid rgba(0, 0, 0, 0.3);
}
#presets-grid::-webkit-scrollbar-thumb:hover {
background: #888;
}
`;
document.head.appendChild(style);
}
// Create a column for each aspect ratio
const columns = [];
for (const [ratio, presetList] of Object.entries(groupedPresets)) {
const column = this.addViewRenderer.createRatioColumn(ratio, presetList);
presetsGrid.appendChild(column);
columns.push(column);
}
// Remove previous resize handler if it exists
if (this.presetPreviewResizeHandler) {
window.removeEventListener('resize', this.presetPreviewResizeHandler);
this.presetPreviewResizeHandler = null;
}
// Update scroll indicators after DOM renders (using requestAnimationFrame to ensure layout is complete)
requestAnimationFrame(() => {
// Restore scroll positions after DOM is recreated
// Restore horizontal scroll of main grid
presetsGrid.scrollLeft = gridScrollLeft;
// Restore vertical scroll of each column
columns.forEach(column => {
const ratioText = column.querySelector('.resolution-master-aspect-ratio-column-title')?.textContent;
const ratioList = column.querySelector('.resolution-master-aspect-ratio-column-list');
if (ratioText && ratioList && scrollPositions.has(ratioText)) {
ratioList.scrollTop = scrollPositions.get(ratioText);
}
});
// Update column scroll indicators
AspectRatioUtils.updateColumnScrollIndicators(columns);
// Add horizontal scroll indicator using persistent state
const updateHorizontalScrollIndicator = AspectRatioUtils.createHorizontalScrollManager(
presetsGrid,
this.presetPreviewContainer,
this.horizontalScrollState // Use persistent state to avoid duplicates
);
updateHorizontalScrollIndicator();
// Add resize listener to update indicators dynamically (matching AspectRatioSelector)
this.presetPreviewResizeHandler = () => {
updateHorizontalScrollIndicator();
AspectRatioUtils.updateColumnScrollIndicators(columns);
};
window.addEventListener('resize', this.presetPreviewResizeHandler);
});
}
/**
* Cancels the current edit operation
*/
cancelEdit() {
// Clear editing mode
this.editingPresetName = null;
this.editingPresetData = null;
// Re-render to hide cancel button and clear form
this.renderDialog();
}
/**
* Quickly adds a preset from the inline form
*/
quickAddPreset() {
const nameInput = document.getElementById('quick-name-input');
const widthInput = document.getElementById('quick-width-input');
const heightInput = document.getElementById('quick-height-input');
const validationMsg = document.getElementById('validation-msg');
const quickAddTitle = document.getElementById('quick-add-title');
if (!this.selectedCategory) {
validationMsg.textContent = 'Please select a category first';
return;
}
const name = nameInput.value.trim();
const width = parseInt(widthInput.value);
const height = parseInt(heightInput.value);
// Validation
if (!name) {
validationMsg.textContent = 'Preset name is required';
nameInput.focus();
return;
}
if (!width || width < 64) {
validationMsg.textContent = 'Width must be at least 64px';
widthInput.focus();
return;
}
if (!height || height < 64) {
validationMsg.textContent = 'Height must be at least 64px';
heightInput.focus();
return;
}
// If editing, use updatePreset to preserve position
if (this.editingPresetName) {
const success = this.manager.updatePreset(
this.selectedCategory,
this.editingPresetName,
name,
width,
height
);
if (!success) {
validationMsg.textContent = 'Failed to update preset. It may already exist.';
return;
}
log.debug(`Updated preset: ${this.selectedCategory}/${this.editingPresetName} -> ${name} (${width}×${height})`);
} else {
// Add new preset
this.manager.addPreset(this.selectedCategory, name, width, height);
log.debug(`Added preset: ${this.selectedCategory}/${name} (${width}×${height})`);
}
// Clear editing mode
this.editingPresetName = null;
this.editingPresetData = null;
// Clear form
nameInput.value = '';
widthInput.value = '';
heightInput.value = '';
validationMsg.textContent = '';
// Update preview and re-render to hide cancel button
this.updatePresetPreview();
// Re-render to hide cancel button
this.renderDialog();
}
/**
* Handles shift-click range selection of checkboxes
*/
handleShiftClickSelection(currentKey, checked) {
// Get all checkboxes in DOM order
const allCheckboxes = Array.from(document.querySelectorAll('input[type="checkbox"][data-preset-key]'));
// Find indices
const lastIndex = allCheckboxes.findIndex(cb => cb.dataset.presetKey === this.lastClickedPresetKey);
const currentIndex = allCheckboxes.findIndex(cb => cb.dataset.presetKey === currentKey);
if (lastIndex === -1 || currentIndex === -1) {
// Fallback to normal behavior
if (checked) {
this.selectedPresetsForDeletion.add(currentKey);
} else {
this.selectedPresetsForDeletion.delete(currentKey);
}
this.lastClickedPresetKey = currentKey;
return;
}
// Select/deselect range
const start = Math.min(lastIndex, currentIndex);
const end = Math.max(lastIndex, currentIndex);
for (let i = start; i <= end; i++) {
const cb = allCheckboxes[i];
const key = cb.dataset.presetKey;
if (checked) {
this.selectedPresetsForDeletion.add(key);
cb.checked = true;
} else {
this.selectedPresetsForDeletion.delete(key);
cb.checked = false;
}
}
this.lastClickedPresetKey = currentKey;
}
/**
* Updates the delete selected button state
*/
updateDeleteSelectedButton() {
const deleteBtn = document.getElementById('delete-selected-btn');
if (!deleteBtn) return;
const selectedCount = this.selectedPresetsForDeletion.size;
const deleteIconHtml = this.deleteIcon ? `<img src="${this.deleteIcon.src}" style="width: 14px; height: 14px; vertical-align: middle; margin-right: 4px;">` : '🗑️ ';
const btnText = selectedCount > 0 ? `Delete Selected (${selectedCount})` : 'Delete Selected';
deleteBtn.innerHTML = deleteIconHtml + btnText;
deleteBtn.disabled = selectedCount === 0;
deleteBtn.style.background = selectedCount > 0 ? '#f55' : '#666';
deleteBtn.style.color = selectedCount > 0 ? '#fff' : '#999';
deleteBtn.style.opacity = selectedCount > 0 ? '1' : '0.5';
deleteBtn.style.cursor = selectedCount > 0 ? 'pointer' : 'not-allowed';
}
/**
* Deletes all selected presets after confirmation
*/
deleteSelectedPresets() {
const selectedCount = this.selectedPresetsForDeletion.size;
if (selectedCount === 0) return;
if (confirm(`Delete ${selectedCount} selected preset(s)?`)) {
// Delete all selected presets
this.selectedPresetsForDeletion.forEach(presetKey => {
const [category, name] = presetKey.split('|');
this.manager.deletePreset(category, name);
log.debug(`Deleted preset: ${category}/${name}`);
});
// Clear selection and refresh view
this.selectedPresetsForDeletion.clear();
this.renderDialog();
}
}
/**
* Shows a dialog to rename the category (delegated to RenameDialogManager)
*/
showRenameCategoryDialog(currentCategoryName) {
this.renameDialogManager.showRenameCategoryDialog(currentCategoryName);
}
/**
* Starts renaming a category (delegated to RenameDialogManager)
*/
startRenamingCategory(titleElement, categoryName) {
this.renameDialogManager.startRenamingCategory(titleElement, categoryName);
}
/**
* Starts renaming a preset (delegated to RenameDialogManager)
*/
startRenamingPreset(nameElement, category, presetName, dims) {
this.renameDialogManager.startRenamingPreset(nameElement, category, presetName, dims);
}
/**
* Shows JSON editor dialog (delegated to JSONEditorDialog)
*/
showJSONEditor() {
this.jsonEditor.show();
}
/**
* Hides and cleans up the dialog
*/
hide() {
// Remove resize event listener if it exists
if (this.presetPreviewResizeHandler) {
window.removeEventListener('resize', this.presetPreviewResizeHandler);
this.presetPreviewResizeHandler = null;
}
// Clean up horizontal scroll indicator
if (this.horizontalScrollState && this.horizontalScrollState.indicator) {
if (this.horizontalScrollState.indicator.parentNode) {
this.horizontalScrollState.indicator.parentNode.removeChild(this.horizontalScrollState.indicator);
}
this.horizontalScrollState.indicator = null;
}
// Close SearchableDropdown if it's open
if (this.searchableDropdown) {
this.searchableDropdown.hide();
}
// Clean up tooltips
if (this.tooltipManager) {
this.tooltipManager.destroy();
// Recreate for next use
this.tooltipManager = new TooltipManager({
delay: 500,
maxWidth: 300
});
this.registerTooltips();
}
if (this.container && this.container.parentNode) {
document.body.removeChild(this.container);
}
if (this.overlay && this.overlay.parentNode) {
document.body.removeChild(this.overlay);
}
this.container = null;
this.overlay = null;
this.isActive = false;
this.currentView = 'list';
this.editingPreset = null;
this.selectedPresetsForDeletion.clear();
this.lastClickedPresetKey = null; // Reset last clicked for shift-click
}
}