allow continuous tweaking in usercss config dialog
* don't close on clicking "save" to allow continuous tweaking * dirty item's label is marked with * and switches to italic * "save" button is enabled when some value differs from saved * "use default" button is enabled when some value differs from its default * "close" becomes "cancel" when there are unsaved changed values
This commit is contained in:
parent
de74c70778
commit
e776a45dfa
|
@ -41,6 +41,16 @@
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.config-dialog .dirty:after {
|
||||||
|
content: "*";
|
||||||
|
position: absolute;
|
||||||
|
left: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.config-dialog .dirty {
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
|
||||||
.config-dialog input,
|
.config-dialog input,
|
||||||
.config-dialog select,
|
.config-dialog select,
|
||||||
.config-dialog .onoffswitch {
|
.config-dialog .onoffswitch {
|
||||||
|
|
|
@ -6,35 +6,77 @@ function configDialog(style) {
|
||||||
const varsHash = deepCopy(data.vars) || {};
|
const varsHash = deepCopy(data.vars) || {};
|
||||||
const varNames = Object.keys(varsHash);
|
const varNames = Object.keys(varsHash);
|
||||||
const vars = varNames.map(name => varsHash[name]);
|
const vars = varNames.map(name => varsHash[name]);
|
||||||
|
let varsInitial = getInitialValues(varsHash);
|
||||||
|
|
||||||
const elements = [];
|
const elements = [];
|
||||||
const colorpicker = window.colorpicker();
|
const colorpicker = window.colorpicker();
|
||||||
|
const isPopup = location.href.includes('popup.html');
|
||||||
|
const buttons = {};
|
||||||
|
|
||||||
buildConfigForm();
|
buildConfigForm();
|
||||||
renderValues();
|
renderValues();
|
||||||
|
|
||||||
return messageBox({
|
return messageBox({
|
||||||
title: `${style.name} v${data.version}`,
|
title: `${style.name} v${data.version}`,
|
||||||
className: 'config-dialog',
|
className: 'config-dialog' + (isPopup ? ' stylus-popup' : ''),
|
||||||
contents: [
|
contents: [
|
||||||
$create('.config-heading', data.supportURL &&
|
$create('.config-heading', data.supportURL &&
|
||||||
$createLink({className: '.external-support', href: data.supportURL}, t('externalFeedback'))),
|
$createLink({className: '.external-support', href: data.supportURL}, t('externalFeedback'))),
|
||||||
$create('.config-body', elements)
|
$create('.config-body', elements)
|
||||||
],
|
],
|
||||||
buttons: [
|
buttons: [
|
||||||
t('confirmSave'),
|
{textContent: t('confirmSave'), dataset: {cmd: 'save'}, disabled: true, onclick: save},
|
||||||
{
|
{textContent: t('confirmDefault'), dataset: {cmd: 'default'}, onclick: useDefault},
|
||||||
textContent: t('confirmDefault'),
|
{textContent: t('confirmClose'), dataset: {cmd: 'close'}},
|
||||||
onclick: useDefault
|
],
|
||||||
},
|
onshow,
|
||||||
t('confirmCancel')
|
}).then(() => {
|
||||||
]
|
document.body.style.minWidth = '';
|
||||||
}).then(({button, esc}) => {
|
document.body.style.minHeight = '';
|
||||||
if (button !== 1) {
|
|
||||||
colorpicker.hide();
|
colorpicker.hide();
|
||||||
|
});
|
||||||
|
|
||||||
|
function getInitialValues(source) {
|
||||||
|
const data = {};
|
||||||
|
for (const name of varNames) {
|
||||||
|
const va = source[name];
|
||||||
|
data[name] = isDefault(va) ? va.default : va.value;
|
||||||
}
|
}
|
||||||
if (button > 0 || esc || !vars.length || !vars.some(va => va.dirty)) {
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
function onshow(box) {
|
||||||
|
if (isPopup) {
|
||||||
|
adjustSizeForPopup(box);
|
||||||
|
}
|
||||||
|
box.addEventListener('change', onchange);
|
||||||
|
buttons.save = $('[data-cmd="save"]', box);
|
||||||
|
buttons.default = $('[data-cmd="default"]', box);
|
||||||
|
buttons.close = $('[data-cmd="close"]', box);
|
||||||
|
}
|
||||||
|
|
||||||
|
function onchange({target}) {
|
||||||
|
// invoked after element's own onchange so 'va' contains the updated value
|
||||||
|
const va = target.va;
|
||||||
|
if (va) {
|
||||||
|
va.dirty = varsInitial[va.name] !== (isDefault(va) ? va.default : va.value);
|
||||||
|
target.closest('label').classList.toggle('dirty', va.dirty);
|
||||||
|
updateButtons();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateButtons() {
|
||||||
|
const someDirty = vars.some(va => va.dirty);
|
||||||
|
buttons.save.disabled = !someDirty;
|
||||||
|
buttons.default.disabled = vars.every(isDefault);
|
||||||
|
buttons.close.textContent = t(someDirty ? 'confirmCancel' : 'confirmClose');
|
||||||
|
}
|
||||||
|
|
||||||
|
function save() {
|
||||||
|
if (!vars.length || !vars.some(va => va.dirty)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
style.enabled = true;
|
||||||
style.reason = 'config';
|
style.reason = 'config';
|
||||||
const styleVars = style.usercssData.vars;
|
const styleVars = style.usercssData.vars;
|
||||||
const bgStyle = BG.cachedStyles.byId.get(style.id);
|
const bgStyle = BG.cachedStyles.byId.get(style.id);
|
||||||
|
@ -52,7 +94,7 @@ function configDialog(style) {
|
||||||
error = ['type ', '*' + va.type, ' != ', '*' + bgva.type];
|
error = ['type ', '*' + va.type, ' != ', '*' + bgva.type];
|
||||||
} else
|
} else
|
||||||
if ((va.type === 'select' || va.type === 'dropdown') &&
|
if ((va.type === 'select' || va.type === 'dropdown') &&
|
||||||
va.value !== null && va.value !== undefined &&
|
!isDefault(va) &&
|
||||||
bgva.options.every(o => o.name !== va.value)) {
|
bgva.options.every(o => o.name !== va.value)) {
|
||||||
error = `'${va.value}' not in the updated '${va.type}' list`;
|
error = `'${va.value}' not in the updated '${va.type}' list`;
|
||||||
} else if (!va.dirty) {
|
} else if (!va.dirty) {
|
||||||
|
@ -76,8 +118,24 @@ function configDialog(style) {
|
||||||
$create({tag: 'li', appendChild: msg}))),
|
$create({tag: 'li', appendChild: msg}))),
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
return numValid && BG.usercssHelper.save(style);
|
return numValid && BG.usercssHelper.save(style).then(saved => {
|
||||||
|
varsInitial = getInitialValues(deepCopy(saved.usercssData.vars));
|
||||||
|
vars.forEach(va => onchange({target: va.input}));
|
||||||
|
updateButtons();
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function useDefault() {
|
||||||
|
for (const va of vars) {
|
||||||
|
va.value = null;
|
||||||
|
onchange({target: va.input});
|
||||||
|
}
|
||||||
|
renderValues();
|
||||||
|
}
|
||||||
|
|
||||||
|
function isDefault(va) {
|
||||||
|
return va.value === null || va.value === undefined || va.value === va.default;
|
||||||
|
}
|
||||||
|
|
||||||
function buildConfigForm() {
|
function buildConfigForm() {
|
||||||
for (const va of vars) {
|
for (const va of vars) {
|
||||||
|
@ -86,7 +144,7 @@ function configDialog(style) {
|
||||||
case 'color':
|
case 'color':
|
||||||
children = [
|
children = [
|
||||||
$create('.cm-colorview', [
|
$create('.cm-colorview', [
|
||||||
va.inputColor = $create('.color-swatch', {
|
va.input = $create('.color-swatch', {
|
||||||
va,
|
va,
|
||||||
onclick: showColorpicker
|
onclick: showColorpicker
|
||||||
}),
|
}),
|
||||||
|
@ -98,10 +156,10 @@ function configDialog(style) {
|
||||||
children = [
|
children = [
|
||||||
$create('span.onoffswitch', [
|
$create('span.onoffswitch', [
|
||||||
va.input = $create('input.slider', {
|
va.input = $create('input.slider', {
|
||||||
|
va,
|
||||||
type: 'checkbox',
|
type: 'checkbox',
|
||||||
onchange() {
|
onchange() {
|
||||||
va.dirty = true;
|
va.value = va.input.checked ? '1' : '0';
|
||||||
va.value = String(Number(va.input.checked));
|
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
$create('span'),
|
$create('span'),
|
||||||
|
@ -116,8 +174,8 @@ function configDialog(style) {
|
||||||
children = [
|
children = [
|
||||||
$create('.select-resizer', [
|
$create('.select-resizer', [
|
||||||
va.input = $create('select', {
|
va.input = $create('select', {
|
||||||
|
va,
|
||||||
onchange() {
|
onchange() {
|
||||||
va.dirty = true;
|
|
||||||
va.value = this.value;
|
va.value = this.value;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -132,10 +190,11 @@ function configDialog(style) {
|
||||||
default:
|
default:
|
||||||
children = [
|
children = [
|
||||||
va.input = $create('input', {
|
va.input = $create('input', {
|
||||||
|
va,
|
||||||
type: 'text',
|
type: 'text',
|
||||||
oninput() {
|
oninput() {
|
||||||
va.dirty = true;
|
va.value = this.value;
|
||||||
va.value = va.input.value;
|
this.dispatchEvent(new Event('change', {bubbles: true}));
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
];
|
];
|
||||||
|
@ -151,10 +210,9 @@ function configDialog(style) {
|
||||||
|
|
||||||
function renderValues() {
|
function renderValues() {
|
||||||
for (const va of vars) {
|
for (const va of vars) {
|
||||||
const useDefault = va.value === null || va.value === undefined;
|
const value = isDefault(va) ? va.default : va.value;
|
||||||
const value = useDefault ? va.default : va.value;
|
|
||||||
if (va.type === 'color') {
|
if (va.type === 'color') {
|
||||||
va.inputColor.style.backgroundColor = value;
|
va.input.style.backgroundColor = value;
|
||||||
if (colorpicker.options.va === va) {
|
if (colorpicker.options.va === va) {
|
||||||
colorpicker.setColor(value);
|
colorpicker.setColor(value);
|
||||||
}
|
}
|
||||||
|
@ -166,15 +224,6 @@ function configDialog(style) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function useDefault() {
|
|
||||||
for (const va of vars) {
|
|
||||||
const hasValue = va.value !== null && va.value !== undefined;
|
|
||||||
va.dirty = hasValue && va.value !== va.default;
|
|
||||||
va.value = null;
|
|
||||||
}
|
|
||||||
renderValues();
|
|
||||||
}
|
|
||||||
|
|
||||||
function showColorpicker() {
|
function showColorpicker() {
|
||||||
window.removeEventListener('keydown', messageBox.listeners.key, true);
|
window.removeEventListener('keydown', messageBox.listeners.key, true);
|
||||||
const box = $('#message-box-contents');
|
const box = $('#message-box-contents');
|
||||||
|
@ -191,9 +240,9 @@ function configDialog(style) {
|
||||||
|
|
||||||
function onColorChanged(newColor) {
|
function onColorChanged(newColor) {
|
||||||
if (newColor) {
|
if (newColor) {
|
||||||
this.va.dirty = true;
|
|
||||||
this.va.value = newColor;
|
this.va.value = newColor;
|
||||||
this.va.inputColor.style.backgroundColor = newColor;
|
this.va.input.style.backgroundColor = newColor;
|
||||||
|
this.va.input.dispatchEvent(new Event('change', {bubbles: true}));
|
||||||
}
|
}
|
||||||
debounce(restoreEscInDialog);
|
debounce(restoreEscInDialog);
|
||||||
}
|
}
|
||||||
|
@ -203,4 +252,22 @@ function configDialog(style) {
|
||||||
window.addEventListener('keydown', messageBox.listeners.key, true);
|
window.addEventListener('keydown', messageBox.listeners.key, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function adjustSizeForPopup(box) {
|
||||||
|
box.style = 'white-space: nowrap !important';
|
||||||
|
box.firstElementChild.style = 'max-width: none; max-height: none;'.replace(/;/g, '!important;');
|
||||||
|
const {offsetWidth, offsetHeight} = box.firstElementChild;
|
||||||
|
box.style = box.firstElementChild.style = '';
|
||||||
|
|
||||||
|
const colorpicker = document.body.appendChild(
|
||||||
|
$create('.colorpicker-popup', {style: 'display: none!important'}));
|
||||||
|
const MIN_WIDTH = parseFloat(getComputedStyle(colorpicker).width) || 350;
|
||||||
|
const MIN_HEIGHT = 250;
|
||||||
|
colorpicker.remove();
|
||||||
|
|
||||||
|
const width = Math.max(Math.min(offsetWidth / 0.9 + 2, 800), MIN_WIDTH);
|
||||||
|
const height = Math.max(Math.min(offsetHeight / 0.9 + 2, 600), MIN_HEIGHT);
|
||||||
|
document.body.style.setProperty('min-width', width + 'px', 'important');
|
||||||
|
document.body.style.setProperty('min-height', height + 'px', 'important');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -143,13 +143,6 @@
|
||||||
top: auto;
|
top: auto;
|
||||||
right: auto;
|
right: auto;
|
||||||
}
|
}
|
||||||
#message-box.calculate-size {
|
|
||||||
white-space: nowrap;
|
|
||||||
}
|
|
||||||
#message-box.calculate-size > div {
|
|
||||||
max-width: none;
|
|
||||||
max-height: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
@keyframes fadein {
|
@keyframes fadein {
|
||||||
from {
|
from {
|
||||||
|
|
|
@ -12,25 +12,6 @@ function messageBox({
|
||||||
bindGlobalListeners();
|
bindGlobalListeners();
|
||||||
createElement();
|
createElement();
|
||||||
document.body.appendChild(messageBox.element);
|
document.body.appendChild(messageBox.element);
|
||||||
if (location.href.includes('popup.html')) {
|
|
||||||
messageBox.isPopup = true;
|
|
||||||
messageBox.element.classList.add('stylus-popup');
|
|
||||||
|
|
||||||
// calculate size
|
|
||||||
messageBox.element.classList.add('calculate-size');
|
|
||||||
const {offsetWidth, offsetHeight} = messageBox.element.children[0];
|
|
||||||
messageBox.element.classList.remove('calculate-size');
|
|
||||||
|
|
||||||
// for colorpicker
|
|
||||||
const MIN_WIDTH = 350;
|
|
||||||
const MIN_HEIGHT = 250;
|
|
||||||
|
|
||||||
const width = Math.max(Math.min(offsetWidth / 0.9 + 2, 800), MIN_WIDTH);
|
|
||||||
const height = Math.max(Math.min(offsetHeight / 0.9 + 2, 600), MIN_HEIGHT);
|
|
||||||
|
|
||||||
document.body.style.minWidth = `${width}px`;
|
|
||||||
document.body.style.minHeight = `${height}px`;
|
|
||||||
}
|
|
||||||
if (onshow) {
|
if (onshow) {
|
||||||
onshow(messageBox.element);
|
onshow(messageBox.element);
|
||||||
}
|
}
|
||||||
|
@ -89,11 +70,11 @@ function messageBox({
|
||||||
$create(`#${id}-contents`, tHTML(contents)),
|
$create(`#${id}-contents`, tHTML(contents)),
|
||||||
$create(`#${id}-buttons`,
|
$create(`#${id}-buttons`,
|
||||||
buttons.map((content, buttonIndex) => content &&
|
buttons.map((content, buttonIndex) => content &&
|
||||||
$create('button', {
|
$create('button', Object.assign({
|
||||||
buttonIndex,
|
buttonIndex,
|
||||||
textContent: content.textContent || content,
|
textContent: typeof content === 'object' ? '' : content,
|
||||||
onclick: content.onclick || messageBox.listeners.button,
|
onclick: messageBox.listeners.button,
|
||||||
}))),
|
}, typeof content === 'object' && content)))),
|
||||||
]),
|
]),
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
@ -115,10 +96,6 @@ function messageBox({
|
||||||
messageBox.element.remove();
|
messageBox.element.remove();
|
||||||
messageBox.element = null;
|
messageBox.element = null;
|
||||||
messageBox.resolve = null;
|
messageBox.resolve = null;
|
||||||
if (messageBox.isPopup) {
|
|
||||||
document.body.style.minWidth = '';
|
|
||||||
document.body.style.minHeight = '';
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user