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",
"description": "Label for the style author"
},
"autosaveNotice": {
"message": "Changes are saved automatically"
},
"backupButtons": {
"message": "Backup",
"description": "Heading for backup"

View File

@ -231,44 +231,41 @@
</template>
<template data-id="styleSettings">
<fieldset class="style-settings can-close-on-esc">
<!-- <label class="style-origin">
<span class="form-label" i18n-text="styleOriginLabel"></span>
<input id="styleOrigin" 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>
<div>
<fieldset class="style-settings can-close-on-esc">
<label i18n-text="styleUpdateUrlLabel">
<input id="ss-update-url" type="text">
</label>
<label class="radio-item">
<input type="radio" name="preferScheme" value="dark">
<span class="radio-label" i18n-text="installPreferSchemeDark"></span>
<div>
<div i18n-text="installPreferSchemeLabel"></div>
<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 class="radio-item">
<input type="radio" name="preferScheme" value="light">
<span class="radio-label" i18n-text="installPreferSchemeLight"></span>
<label i18n-text="styleExcludeLabel">
<textarea id="ss-exclusions" spellcheck="false"
placeholder="*://site1.com/*&#10;*://site2.com/*"></textarea>
</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>
<label class="form-group style-include">
<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>
</div>
</template>
<link href="vendor/codemirror/lib/codemirror.css" rel="stylesheet">

View File

@ -411,10 +411,6 @@ input:invalid {
.edit-actions button {
margin-right: .2rem;
}
.dirty > label::before {
content: "*";
font-weight: bold;
}
#sections {
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 {
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;

View File

@ -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);
}
}