preserve opacity in colorpicker for preprocessor uso config (#1200)
USO has always produced 6-digit #rrggbb so some styles rely on it and use /*[[color]]*/11 notation to specify the transparency. Now we will try to preserve the opacity customized by the user via colorpicker unless the style specifies it inline.
This commit is contained in:
parent
7d08fea5e1
commit
65ac351699
|
@ -5,6 +5,7 @@
|
|||
/* global db */
|
||||
/* global prefs */
|
||||
/* global tabMan */
|
||||
/* global usercssMan */
|
||||
'use strict';
|
||||
|
||||
/*
|
||||
|
@ -202,7 +203,12 @@ const styleMan = (() => {
|
|||
/** @returns {Promise<StyleObj[]>} */
|
||||
async importMany(items) {
|
||||
if (ready.then) await ready;
|
||||
items.forEach(beforeSave);
|
||||
for (const style of items) {
|
||||
beforeSave(style);
|
||||
if (style.sourceCode && style.usercssData) {
|
||||
await usercssMan.buildCode(style);
|
||||
}
|
||||
}
|
||||
const events = await db.exec('putMany', items);
|
||||
return Promise.all(items.map((item, i) => {
|
||||
afterSave(item, events[i]);
|
||||
|
@ -210,12 +216,6 @@ const styleMan = (() => {
|
|||
}));
|
||||
},
|
||||
|
||||
/** @returns {Promise<StyleObj>} */
|
||||
async import(data) {
|
||||
if (ready.then) await ready;
|
||||
return handleSave(await saveStyle(data), 'import');
|
||||
},
|
||||
|
||||
/** @returns {Promise<StyleObj>} */
|
||||
async install(style, reason = null) {
|
||||
if (ready.then) await ready;
|
||||
|
|
|
@ -16,29 +16,27 @@ const colorConverter = (() => {
|
|||
// NAMED_COLORS is added below
|
||||
};
|
||||
|
||||
function format(color = '', type = color.type, hexUppercase) {
|
||||
function format(color = '', type = color.type, hexUppercase, usoMode) {
|
||||
if (!color || !type) return typeof color === 'string' ? color : '';
|
||||
const a = formatAlpha(color.a);
|
||||
const hasA = Boolean(a);
|
||||
if (type === 'rgb' && color.type === 'hsl') {
|
||||
const {a} = color;
|
||||
let aStr = formatAlpha(a);
|
||||
if (aStr) aStr = ', ' + aStr;
|
||||
if (type !== 'hsl' && color.type === 'hsl') {
|
||||
color = HSVtoRGB(HSLtoHSV(color));
|
||||
}
|
||||
const {r, g, b, h, s, l} = color;
|
||||
switch (type) {
|
||||
case 'hex': {
|
||||
const rgbStr = (0x1000000 + (r << 16) + (g << 8) + (b | 0)).toString(16).slice(1);
|
||||
const aStr = hasA ? (0x100 + Math.round(a * 255)).toString(16).slice(1) : '';
|
||||
const hexStr = `#${rgbStr + aStr}`.replace(/^#(.)\1(.)\2(.)\3(?:(.)\4)?$/, '#$1$2$3$4');
|
||||
return hexUppercase ? hexStr.toUpperCase() : hexStr.toLowerCase();
|
||||
let res = '#' + hex2(r) + hex2(g) + hex2(b) + (aStr ? hex2(Math.round(a * 255)) : '');
|
||||
if (!usoMode) res = res.replace(/^#(.)\1(.)\2(.)\3(?:(.)\4)?$/, '#$1$2$3$4');
|
||||
return hexUppercase ? res.toUpperCase() : res;
|
||||
}
|
||||
case 'rgb': {
|
||||
const rgb = [r, g, b].map(Math.round).join(', ');
|
||||
return usoMode ? rgb : `rgb${aStr ? 'a' : ''}(${rgb}${aStr})`;
|
||||
}
|
||||
case 'rgb':
|
||||
return hasA ?
|
||||
`rgba(${Math.round(r)}, ${Math.round(g)}, ${Math.round(b)}, ${a})` :
|
||||
`rgb(${Math.round(r)}, ${Math.round(g)}, ${Math.round(b)})`;
|
||||
case 'hsl':
|
||||
return hasA ?
|
||||
`hsla(${h}, ${s}%, ${l}%, ${a})` :
|
||||
`hsl(${h}, ${s}%, ${l}%)`;
|
||||
return `hsl${aStr ? 'a' : ''}(${h}, ${s}%, ${l}%${aStr})`;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -215,6 +213,10 @@ const colorConverter = (() => {
|
|||
const int = Math.round(num);
|
||||
return Math.abs(int - num) < 1e-3 ? int : num;
|
||||
}
|
||||
|
||||
function hex2(val) {
|
||||
return (val < 16 ? '0' : '') + (val >> 0).toString(16);
|
||||
}
|
||||
})();
|
||||
|
||||
colorConverter.NAMED_COLORS = new Map([
|
||||
|
|
|
@ -47,53 +47,50 @@ const BUILDERS = Object.assign(Object.create(null), {
|
|||
uso: {
|
||||
pre(source, vars) {
|
||||
require(['/js/color/color-converter']); /* global colorConverter */
|
||||
const pool = new Map();
|
||||
const pool = Object.create(null);
|
||||
return doReplace(source);
|
||||
|
||||
function getValue(name, rgbName) {
|
||||
if (!vars.hasOwnProperty(name)) {
|
||||
if (name.endsWith('-rgb')) {
|
||||
return getValue(name.slice(0, -4), name);
|
||||
function doReplace(text) {
|
||||
return text.replace(/(\/\*\[\[([\w-]+)]]\*\/)([0-9a-f]{2}(?=\W))?/gi, (_, cmt, name, alpha) => {
|
||||
const key = alpha ? name + '[A]' : name;
|
||||
let val = pool[key];
|
||||
if (val === undefined) {
|
||||
val = pool[key] = getValue(name, null, alpha);
|
||||
}
|
||||
return null;
|
||||
return (val != null ? val : cmt) + (alpha || '');
|
||||
});
|
||||
}
|
||||
const {type, value} = vars[name];
|
||||
switch (type) {
|
||||
case 'color': {
|
||||
let color = pool.get(rgbName || name);
|
||||
if (color == null) {
|
||||
color = colorConverter.parse(value);
|
||||
if (color) {
|
||||
if (color.type === 'hsl') {
|
||||
color = colorConverter.HSVtoRGB(colorConverter.HSLtoHSV(color));
|
||||
|
||||
function getValue(name, isUsoRgb, alpha) {
|
||||
const v = vars[name];
|
||||
if (!v) {
|
||||
return name.endsWith('-rgb')
|
||||
? getValue(name.slice(0, -4), true)
|
||||
: null;
|
||||
}
|
||||
const {r, g, b} = color;
|
||||
color = rgbName
|
||||
? `${r}, ${g}, ${b}`
|
||||
: `#${(0x1000000 + (r << 16) + (g << 8) + b).toString(16).slice(1)}`;
|
||||
}
|
||||
// the pool stores `false` for bad colors to differentiate from a yet unknown color
|
||||
pool.set(rgbName || name, color || false);
|
||||
}
|
||||
return color || null;
|
||||
let {value} = v;
|
||||
switch (v.type) {
|
||||
case 'color':
|
||||
value = colorConverter.parse(value) || null;
|
||||
if (value) {
|
||||
/* #rrggbb - inline alpha is present; an opaque hsl/a; #rrggbb originally
|
||||
* rgba(r, g, b, a) - transparency <1 is present (Chrome pre-66 compatibility)
|
||||
* rgb(r, g, b) - if color is rgb/a with a=1, note: r/g/b will be rounded
|
||||
* r, g, b - if the var has `-rgb` suffix per USO specification
|
||||
* TODO: when minimum_chrome_version >= 66 try to keep `value` intact */
|
||||
if (alpha) delete value.a;
|
||||
const isRgb = isUsoRgb || value.type === 'rgb' || value.a != null && value.a !== 1;
|
||||
const usoMode = isUsoRgb || !isRgb;
|
||||
value = colorConverter.format(value, isRgb ? 'rgb' : 'hex', undefined, usoMode);
|
||||
}
|
||||
return value;
|
||||
case 'dropdown':
|
||||
case 'select': // prevent infinite recursion
|
||||
pool.set(name, '');
|
||||
case 'select':
|
||||
pool[name] = ''; // prevent infinite recursion
|
||||
return doReplace(value);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
function doReplace(text) {
|
||||
return text.replace(/\/\*\[\[([\w-]+)\]\]\*\//g, (match, name) => {
|
||||
if (!pool.has(name)) {
|
||||
const value = getValue(name);
|
||||
pool.set(name, value === null ? match : value);
|
||||
}
|
||||
return pool.get(name);
|
||||
});
|
||||
}
|
||||
},
|
||||
},
|
||||
});
|
||||
|
|
|
@ -278,7 +278,7 @@ async function importFromString(jsonString) {
|
|||
tasks = tasks.then(() => API.styles.delete(id));
|
||||
const oldStyle = oldStylesById.get(id);
|
||||
if (oldStyle) {
|
||||
tasks = tasks.then(() => API.styles.import(oldStyle));
|
||||
tasks = tasks.then(() => API.styles.importMany([oldStyle]));
|
||||
}
|
||||
}
|
||||
// taskUI is superfast and updates style list only in this page,
|
||||
|
|
Loading…
Reference in New Issue
Block a user