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:
@@ -3,14 +3,50 @@
|
||||
*/
|
||||
|
||||
import {
|
||||
calibrationInitialValues, setCalibrationInitialValues,
|
||||
calibrationTestState, EDGE_TEST_COLORS,
|
||||
} from '../core/state.js';
|
||||
import { API_BASE, getHeaders, handle401Error } from '../core/api.js';
|
||||
import { t } from '../core/i18n.js';
|
||||
import { lockBody, unlockBody, showToast, showConfirm } from '../core/ui.js';
|
||||
import { showToast } from '../core/ui.js';
|
||||
import { Modal } from '../core/modal.js';
|
||||
import { closeTutorial, startCalibrationTutorial } from './tutorials.js';
|
||||
|
||||
/* ── CalibrationModal subclass ────────────────────────────────── */
|
||||
|
||||
class CalibrationModal extends Modal {
|
||||
constructor() {
|
||||
super('calibration-modal');
|
||||
}
|
||||
|
||||
snapshotValues() {
|
||||
return {
|
||||
start_position: this.$('cal-start-position').value,
|
||||
layout: this.$('cal-layout').value,
|
||||
offset: this.$('cal-offset').value,
|
||||
top: this.$('cal-top-leds').value,
|
||||
right: this.$('cal-right-leds').value,
|
||||
bottom: this.$('cal-bottom-leds').value,
|
||||
left: this.$('cal-left-leds').value,
|
||||
spans: JSON.stringify(window.edgeSpans),
|
||||
skip_start: this.$('cal-skip-start').value,
|
||||
skip_end: this.$('cal-skip-end').value,
|
||||
border_width: this.$('cal-border-width').value,
|
||||
};
|
||||
}
|
||||
|
||||
onForceClose() {
|
||||
closeTutorial();
|
||||
const deviceId = this.$('calibration-device-id').value;
|
||||
if (deviceId) clearTestMode(deviceId);
|
||||
if (window._calibrationResizeObserver) window._calibrationResizeObserver.disconnect();
|
||||
const error = this.$('calibration-error');
|
||||
if (error) error.style.display = 'none';
|
||||
}
|
||||
}
|
||||
|
||||
const calibModal = new CalibrationModal();
|
||||
|
||||
/* ── Public API (exported names unchanged) ────────────────────── */
|
||||
|
||||
export async function showCalibration(deviceId) {
|
||||
try {
|
||||
const [response, displaysResponse] = await Promise.all([
|
||||
@@ -63,27 +99,12 @@ export async function showCalibration(deviceId) {
|
||||
left: { start: calibration.span_left_start ?? 0, end: calibration.span_left_end ?? 1 },
|
||||
};
|
||||
|
||||
setCalibrationInitialValues({
|
||||
start_position: calibration.start_position,
|
||||
layout: calibration.layout,
|
||||
offset: String(calibration.offset || 0),
|
||||
top: String(calibration.leds_top || 0),
|
||||
right: String(calibration.leds_right || 0),
|
||||
bottom: String(calibration.leds_bottom || 0),
|
||||
left: String(calibration.leds_left || 0),
|
||||
spans: JSON.stringify(window.edgeSpans),
|
||||
skip_start: String(calibration.skip_leds_start || 0),
|
||||
skip_end: String(calibration.skip_leds_end || 0),
|
||||
border_width: String(calibration.border_width || 10),
|
||||
});
|
||||
|
||||
calibrationTestState[device.id] = new Set();
|
||||
|
||||
updateCalibrationPreview();
|
||||
|
||||
const modal = document.getElementById('calibration-modal');
|
||||
modal.style.display = 'flex';
|
||||
lockBody();
|
||||
calibModal.snapshot();
|
||||
calibModal.open();
|
||||
|
||||
initSpanDrag();
|
||||
requestAnimationFrame(() => {
|
||||
@@ -109,40 +130,15 @@ export async function showCalibration(deviceId) {
|
||||
}
|
||||
|
||||
function isCalibrationDirty() {
|
||||
return (
|
||||
document.getElementById('cal-start-position').value !== calibrationInitialValues.start_position ||
|
||||
document.getElementById('cal-layout').value !== calibrationInitialValues.layout ||
|
||||
document.getElementById('cal-offset').value !== calibrationInitialValues.offset ||
|
||||
document.getElementById('cal-top-leds').value !== calibrationInitialValues.top ||
|
||||
document.getElementById('cal-right-leds').value !== calibrationInitialValues.right ||
|
||||
document.getElementById('cal-bottom-leds').value !== calibrationInitialValues.bottom ||
|
||||
document.getElementById('cal-left-leds').value !== calibrationInitialValues.left ||
|
||||
JSON.stringify(window.edgeSpans) !== calibrationInitialValues.spans ||
|
||||
document.getElementById('cal-skip-start').value !== calibrationInitialValues.skip_start ||
|
||||
document.getElementById('cal-skip-end').value !== calibrationInitialValues.skip_end ||
|
||||
document.getElementById('cal-border-width').value !== calibrationInitialValues.border_width
|
||||
);
|
||||
return calibModal.isDirty();
|
||||
}
|
||||
|
||||
export function forceCloseCalibrationModal() {
|
||||
closeTutorial();
|
||||
const deviceId = document.getElementById('calibration-device-id').value;
|
||||
if (deviceId) clearTestMode(deviceId);
|
||||
if (window._calibrationResizeObserver) window._calibrationResizeObserver.disconnect();
|
||||
const modal = document.getElementById('calibration-modal');
|
||||
const error = document.getElementById('calibration-error');
|
||||
modal.style.display = 'none';
|
||||
error.style.display = 'none';
|
||||
unlockBody();
|
||||
setCalibrationInitialValues({});
|
||||
calibModal.forceClose();
|
||||
}
|
||||
|
||||
export async function closeCalibrationModal() {
|
||||
if (isCalibrationDirty()) {
|
||||
const confirmed = await showConfirm(t('modal.discard_changes'));
|
||||
if (!confirmed) return;
|
||||
}
|
||||
forceCloseCalibrationModal();
|
||||
calibModal.close();
|
||||
}
|
||||
|
||||
export function updateOffsetSkipLock() {
|
||||
@@ -681,7 +677,7 @@ export async function saveCalibration() {
|
||||
if (response.status === 401) { handle401Error(); return; }
|
||||
if (response.ok) {
|
||||
showToast('Calibration saved', 'success');
|
||||
forceCloseCalibrationModal();
|
||||
calibModal.forceClose();
|
||||
window.loadDevices();
|
||||
} else {
|
||||
const errorData = await response.json();
|
||||
|
||||
Reference in New Issue
Block a user