From b553325d7c5a3f9517283566f5616220d76ed4df Mon Sep 17 00:00:00 2001 From: tophf Date: Wed, 29 Dec 2021 13:26:10 +0300 Subject: [PATCH] simplify css + save + autosave --- _locales/en/messages.json | 3 -- edit.html | 65 +++++++++++----------- edit/edit.css | 4 -- edit/settings.css | 46 ++++++---------- edit/settings.js | 111 +++++++++++++++++++------------------- 5 files changed, 102 insertions(+), 127 deletions(-) diff --git a/_locales/en/messages.json b/_locales/en/messages.json index 46655400..a6c4d3c2 100644 --- a/_locales/en/messages.json +++ b/_locales/en/messages.json @@ -88,9 +88,6 @@ "message": "Author", "description": "Label for the style author" }, - "autosaveNotice": { - "message": "Changes are saved automatically" - }, "backupButtons": { "message": "Backup", "description": "Heading for backup" diff --git a/edit.html b/edit.html index 79881afc..a433dee9 100644 --- a/edit.html +++ b/edit.html @@ -231,44 +231,41 @@ diff --git a/edit/edit.css b/edit/edit.css index ffebe4fe..0da9ca1d 100644 --- a/edit/edit.css +++ b/edit/edit.css @@ -411,10 +411,6 @@ input:invalid { .edit-actions button { margin-right: .2rem; } -.dirty > label::before { - content: "*"; - font-weight: bold; -} #sections { counter-reset: codebox; } diff --git a/edit/settings.css b/edit/settings.css index 2b215c73..32397434 100644 --- a/edit/settings.css +++ b/edit/settings.css @@ -1,3 +1,6 @@ +#help-popup.style-settings-popup.dirty .title::after { + content: ' *'; +} .compact-layout #help-popup.style-settings-popup { width: 90%; } @@ -6,48 +9,29 @@ border: 0; margin: 0; } +.style-settings > * { + display: block; + margin: 1rem 0; + padding: 0; +} .style-settings > :first-child { margin-top: 0; } .style-settings > :last-child { margin-bottom: 0; } -.form-group { - display: block; - margin: .6em 0; - padding: 0; +.style-settings input[type=radio] { + margin-left: -.5em; /* compensate for label's 16px margin in edit.css */ } -.form-label { - display: inline-block; - margin: .3em 0; -} -[disabled] .form-label { - opacity: 0.4; -} -.form-group input[type=text], -.form-group input[type=number], -.form-group select, -.form-group textarea { +.style-settings input[type=text], +.style-settings input[type=number], +.style-settings select, +.style-settings textarea { display: block; width: 100%; + margin-top: .25em; box-sizing: border-box; } -.radio-group .form-label { - display: block; -} -.radio-item { - display: flex; - margin: 0.3em 0 .3em; - padding: 0; - align-items: center; - width: max-content; -} -.radio-item input { - margin: 0 0.6em 0 0; -} -[disabled] .radio-label { - opacity: 0.4; -} .style-settings textarea { resize: vertical; min-width: 33vw; diff --git a/edit/settings.js b/edit/settings.js index ccf44363..0decb184 100644 --- a/edit/settings.js +++ b/edit/settings.js @@ -1,90 +1,91 @@ -/* global $ $$ $create moveFocus */// dom.js +/* global $ $$ moveFocus setupLivePrefs */// dom.js /* global API */// msg.js /* global editor */ /* global helpPopup */// util.js /* global t */// localization.js +/* global debounce */// toolbox.js /* exported StyleSettings */ 'use strict'; function StyleSettings() { - const {style} = editor; + const AUTOSAVE_DELAY = 500; // same as config-dialog.js const ui = t.template.styleSettings.cloneNode(true); - const inputs = [ - createInput('.style-update-url input', () => style.updateUrl || '', - e => API.styles.config(style.id, 'updateUrl', e.target.value)), - createRadio('.style-prefer-scheme input', () => style.preferScheme || 'none', - e => API.styles.config(style.id, 'preferScheme', e.target.value)), - ...[ - ['.style-include', 'inclusions'], - ['.style-exclude', 'exclusions'], - ].map(createArea), + const elAuto = $('[id="config.autosave"]', ui); + const elSave = $('#ss-save', ui); + const pendingSetters = new Map(); + const {style} = editor; + const updaters = [ + initInput('#ss-update-url', () => style.updateUrl || '', + val => API.styles.config(style.id, 'updateUrl', val)), + initRadio('ss-scheme', () => style.preferScheme || 'none', + val => API.styles.config(style.id, 'preferScheme', val)), + initArea('inclusions'), + initArea('exclusions'), ]; (editor.updateSettings = () => { - inputs.forEach(i => i.update()); + updaters.forEach(fn => fn()); })(); - helpPopup.show(t('styleSettings'), $create([ - ui, - $create('.buttons', [ - $create('button', { - onclick: helpPopup.close, - title: t('autosaveNotice'), - }, t('confirmClose')), - ]), - ]), { + helpPopup.show(t('styleSettings'), ui, { className: 'style-settings-popup', }); + elSave.onclick = save; + $('#ss-close', ui).onclick = helpPopup.close; + setupLivePrefs([elAuto.id]); moveFocus(ui, 0); - function textToList(text) { - const list = text.split(/\s*\r?\n\s*/g); - return list.filter(Boolean); + function autosave(el, setter) { + pendingSetters.set(el, setter); + helpPopup.div.classList.add('dirty'); + elSave.disabled = false; + if (elAuto.checked) debounce(save, AUTOSAVE_DELAY); } - function createArea([parentSel, type]) { - const sel = parentSel + ' textarea'; - const el = $(sel, ui); - el.on('input', () => { + function initArea(type) { + const selector = `#ss-${type}`; + const el = $(selector, ui); + el.oninput = () => { const val = el.value; el.rows = val.match(/^/gm).length + !val.endsWith('\n'); - }); - return createInput(sel, + }; + return initInput(selector, () => { const list = style[type] || []; const text = list.join('\n'); el.rows = (list.length || 1) + 1; return text; }, - () => API.styles.config(style.id, type, textToList(el.value)) + val => API.styles.config(style.id, type, textToList(val)) ); } - function createRadio(selector, getter, setter) { - const els = $$(selector, ui); - for (const el of els) { - el.addEventListener('change', e => { - if (el.checked) { - setter(e); - } - }); - } - return { - update() { - for (const el of els) { - if (el.value === getter()) { - el.checked = true; - } - } - }, + function initInput(selector, getter, setter) { + const el = $(selector, ui); + el.oninput = () => autosave(el, setter); + return () => { + const val = getter(); + // Skipping if unchanged to preserve the Undo history of the input + if (el.value !== val) el.value = val; }; } - function createInput(selector, getter, setter) { - const el = $(selector, ui); - el.addEventListener('change', setter); - return { - update() { - el.value = getter(); - }, + function initRadio(name, getter, setter) { + for (const el of $$(`[name="${name}"]`, ui)) { + el.onchange = () => { + if (el.checked) autosave(el, setter); + }; + } + return () => { + $(`[name="${name}"][value="${getter()}"]`, ui).checked = true; }; } + + function save() { + pendingSetters.forEach((fn, el) => fn(el.value)); + helpPopup.div.classList.remove('dirty'); + elSave.disabled = true; + } + + function textToList(text) { + return text.split(/\n/).map(s => s.trim()).filter(Boolean); + } }