simplify css + save + autosave

This commit is contained in:
tophf 2021-12-29 13:26:10 +03:00
parent bbb3585f2f
commit b553325d7c
5 changed files with 102 additions and 127 deletions

View File

@ -88,9 +88,6 @@
"message": "Author", "message": "Author",
"description": "Label for the style author" "description": "Label for the style author"
}, },
"autosaveNotice": {
"message": "Changes are saved automatically"
},
"backupButtons": { "backupButtons": {
"message": "Backup", "message": "Backup",
"description": "Heading for backup" "description": "Heading for backup"

View File

@ -231,44 +231,41 @@
</template> </template>
<template data-id="styleSettings"> <template data-id="styleSettings">
<fieldset class="style-settings can-close-on-esc"> <div>
<!-- <label class="style-origin"> <fieldset class="style-settings can-close-on-esc">
<span class="form-label" i18n-text="styleOriginLabel"></span> <label i18n-text="styleUpdateUrlLabel">
<input id="styleOrigin" type="text"> <input id="ss-update-url" type="text">
</label> -->
<label class="form-group style-update-url">
<span class="form-label" i18n-text="styleUpdateUrlLabel"></span>
<input type="text">
</label>
<div class="form-group style-prefer-scheme radio-group">
<!-- FIXME: should we use a different message from install page? -->
<span class="form-label" i18n-text="installPreferSchemeLabel"></span>
<label class="radio-item">
<input type="radio" name="preferScheme" value="none">
<span class="radio-label" i18n-text="installPreferSchemeNone"></span>
</label> </label>
<label class="radio-item"> <div>
<input type="radio" name="preferScheme" value="dark"> <div i18n-text="installPreferSchemeLabel"></div>
<span class="radio-label" i18n-text="installPreferSchemeDark"></span> <label i18n-text-append="installPreferSchemeNone">
<input name="ss-scheme" type="radio" value="none">
</label>
<label i18n-text-append="installPreferSchemeDark">
<input name="ss-scheme" type="radio" value="dark">
</label>
<label i18n-text-append="installPreferSchemeLight">
<input name="ss-scheme" type="radio" value="light">
</label>
</div>
<label i18n-text="styleIncludeLabel">
<textarea id="ss-inclusions" spellcheck="false"
placeholder="*://site1.com/*&#10;*://site2.com/*"></textarea>
</label> </label>
<label class="radio-item"> <label i18n-text="styleExcludeLabel">
<input type="radio" name="preferScheme" value="light"> <textarea id="ss-exclusions" spellcheck="false"
<span class="radio-label" i18n-text="installPreferSchemeLight"></span> placeholder="*://site1.com/*&#10;*://site2.com/*"></textarea>
</label> </label>
</fieldset>
<div class="buttons">
<button id="ss-save" i18n-text="confirmSave" disabled></button>
<label i18n-title="configOnChangeTooltip" i18n-text-append="configOnChange">
<input id="config.autosave" type="checkbox">
<svg class="svg-icon checked"><use xlink:href="#svg-icon-checked"/></svg>
</label>
<button id="ss-close" i18n-text="confirmClose"></button>
</div> </div>
<label class="form-group style-include"> </div>
<span class="form-label" i18n-text="styleIncludeLabel"></span>
<textarea spellcheck="false" placeholder="*://site1.com/*&#10;*://site2.com/*"></textarea>
</label>
<label class="form-group style-exclude">
<span class="form-label" i18n-text="styleExcludeLabel"></span>
<textarea spellcheck="false" placeholder="*://site1.com/*&#10;*://site2.com/*"></textarea>
</label>
<!-- <label class="style-always-important">
<input type="checkbox">
<span class="form-label" i18n-text="styleAlwaysImportantLabel"></span>
</label> -->
</fieldset>
</template> </template>
<link href="vendor/codemirror/lib/codemirror.css" rel="stylesheet"> <link href="vendor/codemirror/lib/codemirror.css" rel="stylesheet">

View File

@ -411,10 +411,6 @@ input:invalid {
.edit-actions button { .edit-actions button {
margin-right: .2rem; margin-right: .2rem;
} }
.dirty > label::before {
content: "*";
font-weight: bold;
}
#sections { #sections {
counter-reset: codebox; counter-reset: codebox;
} }

View File

@ -1,3 +1,6 @@
#help-popup.style-settings-popup.dirty .title::after {
content: ' *';
}
.compact-layout #help-popup.style-settings-popup { .compact-layout #help-popup.style-settings-popup {
width: 90%; width: 90%;
} }
@ -6,48 +9,29 @@
border: 0; border: 0;
margin: 0; margin: 0;
} }
.style-settings > * {
display: block;
margin: 1rem 0;
padding: 0;
}
.style-settings > :first-child { .style-settings > :first-child {
margin-top: 0; margin-top: 0;
} }
.style-settings > :last-child { .style-settings > :last-child {
margin-bottom: 0; margin-bottom: 0;
} }
.form-group { .style-settings input[type=radio] {
display: block; margin-left: -.5em; /* compensate for label's 16px margin in edit.css */
margin: .6em 0;
padding: 0;
} }
.form-label { .style-settings input[type=text],
display: inline-block; .style-settings input[type=number],
margin: .3em 0; .style-settings select,
} .style-settings textarea {
[disabled] .form-label {
opacity: 0.4;
}
.form-group input[type=text],
.form-group input[type=number],
.form-group select,
.form-group textarea {
display: block; display: block;
width: 100%; width: 100%;
margin-top: .25em;
box-sizing: border-box; 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 { .style-settings textarea {
resize: vertical; resize: vertical;
min-width: 33vw; min-width: 33vw;

View File

@ -1,90 +1,91 @@
/* global $ $$ $create moveFocus */// dom.js /* global $ $$ moveFocus setupLivePrefs */// dom.js
/* global API */// msg.js /* global API */// msg.js
/* global editor */ /* global editor */
/* global helpPopup */// util.js /* global helpPopup */// util.js
/* global t */// localization.js /* global t */// localization.js
/* global debounce */// toolbox.js
/* exported StyleSettings */ /* exported StyleSettings */
'use strict'; 'use strict';
function StyleSettings() { function StyleSettings() {
const {style} = editor; const AUTOSAVE_DELAY = 500; // same as config-dialog.js
const ui = t.template.styleSettings.cloneNode(true); const ui = t.template.styleSettings.cloneNode(true);
const inputs = [ const elAuto = $('[id="config.autosave"]', ui);
createInput('.style-update-url input', () => style.updateUrl || '', const elSave = $('#ss-save', ui);
e => API.styles.config(style.id, 'updateUrl', e.target.value)), const pendingSetters = new Map();
createRadio('.style-prefer-scheme input', () => style.preferScheme || 'none', const {style} = editor;
e => API.styles.config(style.id, 'preferScheme', e.target.value)), const updaters = [
...[ initInput('#ss-update-url', () => style.updateUrl || '',
['.style-include', 'inclusions'], val => API.styles.config(style.id, 'updateUrl', val)),
['.style-exclude', 'exclusions'], initRadio('ss-scheme', () => style.preferScheme || 'none',
].map(createArea), val => API.styles.config(style.id, 'preferScheme', val)),
initArea('inclusions'),
initArea('exclusions'),
]; ];
(editor.updateSettings = () => { (editor.updateSettings = () => {
inputs.forEach(i => i.update()); updaters.forEach(fn => fn());
})(); })();
helpPopup.show(t('styleSettings'), $create([ helpPopup.show(t('styleSettings'), ui, {
ui,
$create('.buttons', [
$create('button', {
onclick: helpPopup.close,
title: t('autosaveNotice'),
}, t('confirmClose')),
]),
]), {
className: 'style-settings-popup', className: 'style-settings-popup',
}); });
elSave.onclick = save;
$('#ss-close', ui).onclick = helpPopup.close;
setupLivePrefs([elAuto.id]);
moveFocus(ui, 0); moveFocus(ui, 0);
function textToList(text) { function autosave(el, setter) {
const list = text.split(/\s*\r?\n\s*/g); pendingSetters.set(el, setter);
return list.filter(Boolean); helpPopup.div.classList.add('dirty');
elSave.disabled = false;
if (elAuto.checked) debounce(save, AUTOSAVE_DELAY);
} }
function createArea([parentSel, type]) { function initArea(type) {
const sel = parentSel + ' textarea'; const selector = `#ss-${type}`;
const el = $(sel, ui); const el = $(selector, ui);
el.on('input', () => { el.oninput = () => {
const val = el.value; const val = el.value;
el.rows = val.match(/^/gm).length + !val.endsWith('\n'); el.rows = val.match(/^/gm).length + !val.endsWith('\n');
}); };
return createInput(sel, return initInput(selector,
() => { () => {
const list = style[type] || []; const list = style[type] || [];
const text = list.join('\n'); const text = list.join('\n');
el.rows = (list.length || 1) + 1; el.rows = (list.length || 1) + 1;
return text; 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) { function initInput(selector, getter, setter) {
const els = $$(selector, ui); const el = $(selector, ui);
for (const el of els) { el.oninput = () => autosave(el, setter);
el.addEventListener('change', e => { return () => {
if (el.checked) { const val = getter();
setter(e); // Skipping if unchanged to preserve the Undo history of the input
} if (el.value !== val) el.value = val;
});
}
return {
update() {
for (const el of els) {
if (el.value === getter()) {
el.checked = true;
}
}
},
}; };
} }
function createInput(selector, getter, setter) { function initRadio(name, getter, setter) {
const el = $(selector, ui); for (const el of $$(`[name="${name}"]`, ui)) {
el.addEventListener('change', setter); el.onchange = () => {
return { if (el.checked) autosave(el, setter);
update() { };
el.value = getter(); }
}, 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);
}
} }