usercss editor: use real theme color for inputs in widgets

This commit is contained in:
tophf 2017-12-05 20:41:08 +03:00
parent 69736ef5a1
commit bd0f460c56
3 changed files with 111 additions and 39 deletions

View File

@ -74,7 +74,7 @@
<link rel="stylesheet" href="/edit/codemirror-default.css"> <link rel="stylesheet" href="/edit/codemirror-default.css">
<template data-id="appliesTo"> <template data-id="appliesTo">
<li> <li class="applies-to-item">
<div class="select-resizer"> <div class="select-resizer">
<select name="applies-type" class="applies-type style-contributor"> <select name="applies-type" class="applies-type style-contributor">
<option value="url" i18n-text="appliesUrlOption"></option> <option value="url" i18n-text="appliesUrlOption"></option>

View File

@ -5,7 +5,7 @@ function createAppliesToLineWidget(cm) {
const THROTTLE_DELAY = 400; const THROTTLE_DELAY = 400;
let TPL, EVENTS, CLICK_ROUTE; let TPL, EVENTS, CLICK_ROUTE;
let widgets = []; let widgets = [];
let fromLine, toLine, styleVariables; let fromLine, toLine, actualStyle;
let initialized = false; let initialized = false;
return {toggle}; return {toggle};
@ -29,23 +29,14 @@ function createAppliesToLineWidget(cm) {
$create('label', t('appliesLabel')), $create('label', t('appliesLabel')),
$create('ul.applies-to-list'), $create('ul.applies-to-list'),
]), ]),
listItem: listItem: template.appliesTo,
$create('li.applies-to-item', [
$create('select.applies-type', [
$create('option', {value: 'url'}, t('appliesUrlOption')),
$create('option', {value: 'url-prefix'}, t('appliesUrlPrefixOption')),
$create('option', {value: 'domain'}, t('appliesDomainOption')),
$create('option', {value: 'regexp'}, t('appliesRegexpOption')),
]),
$create('input.applies-value', {spellcheck: false}),
$create('button.test-regexp', t('styleRegexpTestButton')),
$create('button.remove-applies-to', t('appliesRemove')),
$create('button.add-applies-to', t('appliesAdd')),
]),
appliesToEverything: appliesToEverything:
$create('li.applies-to-everything', t('appliesToEverything')), $create('li.applies-to-everything', t('appliesToEverything')),
}; };
$('button', TPL.listItem).insertAdjacentElement('afterend',
$create('button.test-regexp', t('styleRegexpTestButton')));
CLICK_ROUTE = { CLICK_ROUTE = {
'.test-regexp': (item, apply) => { '.test-regexp': (item, apply) => {
regExpTester.toggle(); regExpTester.toggle();
@ -136,15 +127,13 @@ function createAppliesToLineWidget(cm) {
} }
}; };
styleVariables = $create('style'); actualStyle = $create('style');
fromLine = 0; fromLine = 0;
toLine = cm.doc.size; toLine = cm.doc.size;
cm.on('change', onChange); cm.on('change', onChange);
cm.on('optionChange', onOptionChange); cm.on('optionChange', onOptionChange);
// is it possible to avoid flickering?
window.addEventListener('load', updateWidgetStyle);
chrome.runtime.onMessage.addListener(onRuntimeMessage); chrome.runtime.onMessage.addListener(onRuntimeMessage);
updateWidgetStyle(); updateWidgetStyle();
@ -158,9 +147,8 @@ function createAppliesToLineWidget(cm) {
widgets.length = 0; widgets.length = 0;
cm.off('change', onChange); cm.off('change', onChange);
cm.off('optionChange', onOptionChange); cm.off('optionChange', onOptionChange);
window.removeEventListener('load', updateWidgetStyle);
chrome.runtime.onMessage.removeListener(onRuntimeMessage); chrome.runtime.onMessage.removeListener(onRuntimeMessage);
styleVariables.remove(); actualStyle.remove();
} }
function onChange(cm, event) { function onChange(cm, event) {
@ -229,24 +217,112 @@ function createAppliesToLineWidget(cm) {
} }
function updateWidgetStyle() { function updateWidgetStyle() {
const gutterStyle = getComputedStyle(cm.getGutterElement()); const MIN_LUMA = .05;
const borderStyle = gutterStyle.borderRightWidth !== '0px' ? const MIN_LUMA_DIFF = .4;
`${gutterStyle.borderRightWidth} ${gutterStyle.borderRightStyle} ${gutterStyle.borderRightColor}` : const color = {
`1px solid ${gutterStyle.color}`; wrapper: getRealColors(cm.display.wrapper),
const id = Date.now(); gutter: getRealColors(cm.display.gutters, {
styleVariables.textContent = ` bg: 'backgroundColor',
.single-editor { border: 'borderRightColor',
--at-background-color-${id}: ${gutterStyle.backgroundColor}; }),
--at-border-top-${id}: ${borderStyle}; line: getRealColors('.CodeMirror-linenumber'),
--at-border-bottom-${id}: ${borderStyle}; comment: getRealColors('span.cm-comment'),
} };
const hasBorder =
color.gutter.style.borderRightWidth !== '0px' &&
!/transparent|\b0\)/g.test(color.gutter.style.borderRightColor);
const diff = {
wrapper: Math.abs(color.gutter.bgLuma - color.wrapper.foreLuma),
border: hasBorder ? Math.abs(color.gutter.bgLuma - color.gutter.borderLuma) : 0,
line: Math.abs(color.gutter.bgLuma - color.line.foreLuma),
};
const preferLine = diff.line > diff.wrapper || diff.line > MIN_LUMA_DIFF;
const fore = preferLine ? color.line.fore : color.wrapper.fore;
const border = fore.replace(/[\d.]+(?=\))/, MIN_LUMA_DIFF / 2);
const borderStyleForced = `1px ${hasBorder ? color.gutter.style.borderRightStyle : 'solid'} ${border}`;
actualStyle.textContent = `
.applies-to { .applies-to {
background-color: var(--at-background-color-${id}); background-color: ${color.gutter.bg};
border-top: var(--at-border-top-${id}); border-top: ${borderStyleForced};
border-bottom: var(--at-border-bottom-${id}); border-bottom: ${borderStyleForced};
}
.applies-to label {
color: ${fore};
}
.applies-to input,
.applies-to select {
background-color: rgba(255, 255, 255, ${
Math.max(MIN_LUMA, Math.pow(Math.max(0, color.gutter.bgLuma - MIN_LUMA * 2), 2)).toFixed(2)
});
border: ${borderStyleForced};
transition: none;
color: ${fore};
}
.applies-to .svg-icon.select-arrow {
fill: ${fore};
transition: none;
} }
`; `;
document.documentElement.appendChild(styleVariables); document.documentElement.appendChild(actualStyle);
function getRealColors(el, targets = {}) {
targets.fore = 'color';
const colors = {};
const done = {};
let numDone = 0;
let numTotal = 0;
for (const k in targets) {
colors[k] = {r: 255, g: 255, b: 255, a: 1};
numTotal++;
}
const isDummy = typeof el === 'string';
el = isDummy ? cm.display.lineDiv.appendChild($create(el, {style: 'display: none'})) : el;
for (let current = el; current; current = current && current.parentElement) {
const style = getComputedStyle(current);
for (const k in targets) {
if (!done[k]) {
done[k] = blend(colors[k], style[targets[k]]);
numDone += done[k] ? 1 : 0;
if (numDone === numTotal) {
current = null;
break;
}
}
}
colors.style = colors.style || style;
}
if (isDummy) {
el.remove();
}
for (const k in targets) {
const {r, g, b, a} = colors[k];
colors[k] = `rgba(${r}, ${g}, ${b}, ${a})`;
// https://www.w3.org/TR/AERT#color-contrast
colors[k + 'Luma'] = (r * .299 + g * .587 + b * .114) / 256;
}
return colors;
}
function blend(base, color) {
const [r, g, b, a = 255] = (color.match(/\d+/g) || []).map(Number);
if (a === 255) {
base.r = r;
base.g = g;
base.b = b;
base.a = 1;
} else if (a) {
const mixedA = 1 - (1 - a / 255) * (1 - base.a);
const q1 = a / 255 / mixedA;
const q2 = base.a * (1 - mixedA) / mixedA;
base.r = Math.round(r * q1 + base.r * q2);
base.g = Math.round(g * q1 + base.g * q2);
base.b = Math.round(b * q1 + base.b * q2);
base.a = mixedA;
}
return Math.abs(base.a - 1) < 1e-3;
}
} }
function doUpdate() { function doUpdate() {

View File

@ -56,7 +56,3 @@
position: absolute; position: absolute;
pointer-events: none; pointer-events: none;
} }
.CodeMirror-activeline .applies-to ul {
z-index: 2;
}