Control 설정 모달 입력 보존 개선

This commit is contained in:
seo
2026-06-26 04:42:43 +09:00
parent da8c4e863d
commit ebef879634
5 changed files with 58 additions and 27 deletions
+41 -20
View File
@@ -86,6 +86,7 @@
batteryTooltipText: '',
settingsPayload: null,
settingsOpen: false,
settingsDirty: false,
};
const storageKeys = {
@@ -521,9 +522,7 @@
cleanup(true);
};
const onCancel = () => cleanup(mode === 'confirm' ? false : null);
const onLayerClick = event => {
if (event.target === layer) onCancel();
};
const onLayerClick = () => {};
const onKeydown = event => {
if (event.key === 'Escape') {
event.preventDefault();
@@ -606,8 +605,13 @@
return json.data;
}
function renderSettings(payload = state.settingsPayload) {
function settingDomId(key) {
return `setting-${String(key).replace(/[^A-Za-z0-9_-]/g, '-')}`;
}
function renderSettings(payload = state.settingsPayload, options = {}) {
if (!els.settingsBody || !payload) return;
if (state.settingsOpen && state.settingsDirty && !options.force) return;
state.settingsPayload = payload;
const groups = payload.groups || {};
const byGroup = {};
@@ -618,21 +622,23 @@
});
const fieldHtml = item => {
const key = escapeHtml(item.key);
const id = escapeHtml(settingDomId(item.key));
const value = item.value ?? item.default ?? '';
const type = item.type || 'number';
if (type === 'boolean') {
const checked = value === true || value === 1 || value === '1' || value === 'true';
return `
<span class="settings-toggle">
<input data-setting-key="${key}" type="checkbox" value="1" ${checked ? 'checked' : ''}>
<span>${checked ? '켜기' : '끄기'}</span>
<input id="${id}" name="${key}" data-setting-key="${key}" type="checkbox" value="1" autocomplete="off" ${checked ? 'checked' : ''}>
<span class="settings-toggle-on">켜기</span>
<span class="settings-toggle-off">끄기</span>
</span>
`;
}
if (type === 'select') {
const options = item.options || {};
return `
<select data-setting-key="${key}" class="settings-select">
<select id="${id}" name="${key}" data-setting-key="${key}" class="settings-select" autocomplete="off">
${Object.entries(options).map(([optionValue, label]) => `
<option value="${escapeHtml(optionValue)}" ${String(optionValue) === String(value) ? 'selected' : ''}>${escapeHtml(label)}</option>
`).join('')}
@@ -642,19 +648,25 @@
if (type === 'text') {
return `
<input
id="${id}"
name="${key}"
data-setting-key="${key}"
type="text"
maxlength="${escapeHtml(item.max_length ?? 255)}"
autocomplete="off"
value="${escapeHtml(value)}">
`;
}
return `
<input
id="${id}"
name="${key}"
data-setting-key="${key}"
type="number"
min="${escapeHtml(item.min ?? '')}"
max="${escapeHtml(item.max ?? '')}"
step="${escapeHtml(item.step ?? '1')}"
autocomplete="off"
value="${escapeHtml(value)}">
`;
};
@@ -675,7 +687,7 @@
<h4>${escapeHtml(groups[group] || group)}</h4>
<div class="settings-grid">
${items.map(item => `
<label class="settings-field">
<div class="settings-field">
<span class="settings-label">
<span>${escapeHtml(item.label || item.key)}</span>
<span>${escapeHtml(item.unit || '')}</span>
@@ -684,17 +696,27 @@
${fieldHtml(item)}
<span class="settings-default">${t('settingsDefault')} ${escapeHtml(defaultValueText(item))}</span>
</span>
</label>
</div>
`).join('')}
</div>
</section>
`).join('');
els.settingsBody.querySelectorAll('[data-setting-key]').forEach(input => {
input.addEventListener('input', () => {
state.settingsDirty = true;
});
input.addEventListener('change', () => {
state.settingsDirty = true;
});
});
els.settingsBody.querySelectorAll('.settings-toggle input').forEach(input => {
input.addEventListener('change', () => {
const label = input.closest('.settings-toggle')?.querySelector('span');
if (label) label.textContent = input.checked ? '켜기' : '끄기';
const toggle = input.closest('.settings-toggle');
if (toggle) toggle.dataset.checked = input.checked ? '1' : '0';
});
input.closest('.settings-toggle')?.addEventListener('click', () => {
const toggle = input.closest('.settings-toggle');
toggle?.setAttribute('data-checked', input.checked ? '1' : '0');
toggle?.addEventListener('click', () => {
input.checked = !input.checked;
input.dispatchEvent(new Event('change'));
});
@@ -716,6 +738,7 @@
return;
}
state.settingsOpen = true;
state.settingsDirty = false;
els.settingsDialog.dataset.open = '1';
els.settingsDialog.setAttribute('aria-hidden', 'false');
}
@@ -723,6 +746,7 @@
function closeSettings() {
if (!els.settingsDialog) return;
state.settingsOpen = false;
state.settingsDirty = false;
delete els.settingsDialog.dataset.open;
els.settingsDialog.setAttribute('aria-hidden', 'true');
}
@@ -742,8 +766,9 @@
const data = await api('settings_save', {
settings: JSON.stringify(currentSettingsFormValues()),
});
state.settingsDirty = false;
render(data);
renderSettings(data.settings);
renderSettings(data.settings, { force: true });
notice(t('settingsSaved'), 'success');
} catch (e) {
console.error(e);
@@ -757,8 +782,9 @@
}
try {
const data = await api('settings_reset', {});
state.settingsDirty = false;
render(data);
renderSettings(data.settings);
renderSettings(data.settings, { force: true });
notice(t('settingsResetDone'), 'success');
} catch (e) {
console.error(e);
@@ -1809,7 +1835,7 @@
function render(data) {
if (data.settings) {
state.settingsPayload = data.settings;
if (state.settingsOpen) {
if (!state.settingsOpen) {
renderSettings(data.settings);
}
}
@@ -2062,11 +2088,6 @@
els.settingsClose?.addEventListener('click', closeSettings);
els.settingsSave?.addEventListener('click', saveSettings);
els.settingsReset?.addEventListener('click', resetSettings);
els.settingsDialog?.addEventListener('click', event => {
if (event.target === els.settingsDialog) {
closeSettings();
}
});
els.translateBtn?.addEventListener('click', () => {
applyLang(state.lang === 'ko' ? 'en' : 'ko');
refreshStatus();