Extract Modal base class and fix target editor defaults
Add core/modal.js with reusable Modal class that handles open/close, body locking, backdrop close, dirty checking, error display, and a static stack for ESC key management. Migrate all 13 modals across 8 feature files to use the base class, eliminating ~200 lines of duplicated boilerplate. Replace manual ESC handler list in app.js with Modal.closeTopmost(), fixing 3 modals that were previously unreachable via ESC. Remove 5 unused initialValues variables from state.js. Fix target editor to auto-select first device/source and auto-generate name like the KC editor does. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -6,7 +6,6 @@ import {
|
||||
patternEditorRects, setPatternEditorRects,
|
||||
patternEditorSelectedIdx, setPatternEditorSelectedIdx,
|
||||
patternEditorBgImage, setPatternEditorBgImage,
|
||||
patternEditorInitialValues, setPatternEditorInitialValues,
|
||||
patternCanvasDragMode, setPatternCanvasDragMode,
|
||||
patternCanvasDragStart, setPatternCanvasDragStart,
|
||||
patternCanvasDragOrigRect, setPatternCanvasDragOrigRect,
|
||||
@@ -17,7 +16,30 @@ import {
|
||||
} from '../core/state.js';
|
||||
import { API_BASE, getHeaders, fetchWithAuth, escapeHtml, handle401Error } from '../core/api.js';
|
||||
import { t } from '../core/i18n.js';
|
||||
import { lockBody, unlockBody, showToast, showConfirm, setupBackdropClose } from '../core/ui.js';
|
||||
import { showToast, showConfirm } from '../core/ui.js';
|
||||
import { Modal } from '../core/modal.js';
|
||||
|
||||
class PatternTemplateModal extends Modal {
|
||||
constructor() {
|
||||
super('pattern-template-modal');
|
||||
}
|
||||
|
||||
snapshotValues() {
|
||||
return {
|
||||
name: document.getElementById('pattern-template-name').value,
|
||||
description: document.getElementById('pattern-template-description').value,
|
||||
rectangles: JSON.stringify(patternEditorRects),
|
||||
};
|
||||
}
|
||||
|
||||
onForceClose() {
|
||||
setPatternEditorRects([]);
|
||||
setPatternEditorSelectedIdx(-1);
|
||||
setPatternEditorBgImage(null);
|
||||
}
|
||||
}
|
||||
|
||||
const patternModal = new PatternTemplateModal();
|
||||
|
||||
export function createPatternTemplateCard(pt) {
|
||||
const rectCount = (pt.rectangles || []).length;
|
||||
@@ -77,20 +99,13 @@ export async function showPatternTemplateEditor(templateId = null) {
|
||||
setPatternEditorRects([]);
|
||||
}
|
||||
|
||||
setPatternEditorInitialValues({
|
||||
name: document.getElementById('pattern-template-name').value,
|
||||
description: document.getElementById('pattern-template-description').value,
|
||||
rectangles: JSON.stringify(patternEditorRects),
|
||||
});
|
||||
patternModal.snapshot();
|
||||
|
||||
renderPatternRectList();
|
||||
renderPatternCanvas();
|
||||
_attachPatternCanvasEvents();
|
||||
|
||||
const modal = document.getElementById('pattern-template-modal');
|
||||
modal.style.display = 'flex';
|
||||
lockBody();
|
||||
setupBackdropClose(modal, closePatternTemplateModal);
|
||||
patternModal.open();
|
||||
|
||||
document.getElementById('pattern-template-error').style.display = 'none';
|
||||
setTimeout(() => document.getElementById('pattern-template-name').focus(), 100);
|
||||
@@ -101,40 +116,24 @@ export async function showPatternTemplateEditor(templateId = null) {
|
||||
}
|
||||
|
||||
export function isPatternEditorDirty() {
|
||||
return (
|
||||
document.getElementById('pattern-template-name').value !== patternEditorInitialValues.name ||
|
||||
document.getElementById('pattern-template-description').value !== patternEditorInitialValues.description ||
|
||||
JSON.stringify(patternEditorRects) !== patternEditorInitialValues.rectangles
|
||||
);
|
||||
return patternModal.isDirty();
|
||||
}
|
||||
|
||||
export async function closePatternTemplateModal() {
|
||||
if (isPatternEditorDirty()) {
|
||||
const confirmed = await showConfirm(t('modal.discard_changes'));
|
||||
if (!confirmed) return;
|
||||
}
|
||||
forceClosePatternTemplateModal();
|
||||
await patternModal.close();
|
||||
}
|
||||
|
||||
export function forceClosePatternTemplateModal() {
|
||||
document.getElementById('pattern-template-modal').style.display = 'none';
|
||||
document.getElementById('pattern-template-error').style.display = 'none';
|
||||
unlockBody();
|
||||
setPatternEditorRects([]);
|
||||
setPatternEditorSelectedIdx(-1);
|
||||
setPatternEditorBgImage(null);
|
||||
setPatternEditorInitialValues({});
|
||||
patternModal.forceClose();
|
||||
}
|
||||
|
||||
export async function savePatternTemplate() {
|
||||
const templateId = document.getElementById('pattern-template-id').value;
|
||||
const name = document.getElementById('pattern-template-name').value.trim();
|
||||
const description = document.getElementById('pattern-template-description').value.trim();
|
||||
const errorEl = document.getElementById('pattern-template-error');
|
||||
|
||||
if (!name) {
|
||||
errorEl.textContent = t('pattern.error.required');
|
||||
errorEl.style.display = 'block';
|
||||
patternModal.showError(t('pattern.error.required'));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -165,13 +164,12 @@ export async function savePatternTemplate() {
|
||||
}
|
||||
|
||||
showToast(templateId ? t('pattern.updated') : t('pattern.created'), 'success');
|
||||
forceClosePatternTemplateModal();
|
||||
patternModal.forceClose();
|
||||
// Use window.* to avoid circular import with targets.js
|
||||
if (typeof window.loadTargetsTab === 'function') window.loadTargetsTab();
|
||||
} catch (error) {
|
||||
console.error('Error saving pattern template:', error);
|
||||
errorEl.textContent = error.message;
|
||||
errorEl.style.display = 'block';
|
||||
patternModal.showError(error.message);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user