mirror of https://github.com/keeweb/keeweb.git
set css plugin theme properties
This commit is contained in:
parent
a9e51b75ee
commit
c4adde0560
|
@ -98,7 +98,8 @@ module.exports = function(grunt) {
|
|||
{ test: /\.json$/, loader: 'json-loader' },
|
||||
{ test: /argon2-asm\.min\.js$/, loader: 'raw-loader' },
|
||||
{ test: /argon2\.wasm$/, loader: 'base64-loader' },
|
||||
{ test: /argon2\.min\.js/, loader: 'raw-loader' }
|
||||
{ test: /argon2\.min\.js/, loader: 'raw-loader' },
|
||||
{ test: /\.scss$/, loader: 'raw-loader' }
|
||||
]
|
||||
},
|
||||
plugins: [
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
const kdbxweb = require('kdbxweb');
|
||||
const Backbone = require('backbone');
|
||||
const PluginApi = require('./plugin-api');
|
||||
const ThemeVars = require('./theme-vars');
|
||||
const Logger = require('../util/logger');
|
||||
const SettingsManager = require('../comp/settings-manager');
|
||||
const IoCache = require('../storage/io-cache');
|
||||
|
@ -205,7 +206,7 @@ const Plugin = Backbone.Model.extend(_.extend({}, PluginStatus, {
|
|||
},
|
||||
|
||||
installWithResources() {
|
||||
this.logger.info('Installing plugin code');
|
||||
this.logger.info('Installing plugin resources');
|
||||
const manifest = this.get('manifest');
|
||||
const promises = [];
|
||||
if (this.resources.css) {
|
||||
|
@ -271,16 +272,37 @@ const Plugin = Backbone.Model.extend(_.extend({}, PluginStatus, {
|
|||
applyCss(name, data, theme) {
|
||||
return Promise.resolve().then(() => {
|
||||
const text = kdbxweb.ByteUtils.bytesToString(data);
|
||||
this.createElementInHead('style', 'plugin-css-' + name, 'text/css', text);
|
||||
const id = 'plugin-css-' + name;
|
||||
this.createElementInHead('style', id, 'text/css', text);
|
||||
if (theme) {
|
||||
const locKey = this.getThemeLocaleKey(theme.name);
|
||||
SettingsManager.allThemes[theme.name] = locKey;
|
||||
BaseLocale[locKey] = theme.title;
|
||||
for (const styleSheet of document.styleSheets) {
|
||||
if (styleSheet.ownerNode.id === id) {
|
||||
this.processThemeStyleSheet(styleSheet, theme);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
this.logger.debug('Plugin style installed');
|
||||
});
|
||||
},
|
||||
|
||||
processThemeStyleSheet(styleSheet, theme) {
|
||||
const themeSelector = '.th-' + theme.name;
|
||||
for (const rule of styleSheet.cssRules) {
|
||||
if (rule.selectorText === themeSelector) {
|
||||
this.addThemeVariables(rule);
|
||||
break;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
addThemeVariables(rule) {
|
||||
ThemeVars.apply(rule.style);
|
||||
},
|
||||
|
||||
applyJs(name, data) {
|
||||
return Promise.resolve().then(() => {
|
||||
let text = kdbxweb.ByteUtils.bytesToString(data);
|
||||
|
|
|
@ -0,0 +1,117 @@
|
|||
const ThemeVarsScss = require('../../styles/base/_theme-vars.scss');
|
||||
const ThemeDefaults = require('../../styles/themes/_theme-defaults.scss');
|
||||
const Color = require('../util/color');
|
||||
|
||||
const ThemeVars = {
|
||||
themeDefaults: null,
|
||||
|
||||
init() {
|
||||
if (this.themeDefaults) {
|
||||
return;
|
||||
}
|
||||
this.themeDefaults = {};
|
||||
const propRegex = /\s([\w\-]+):\s*([^,\s]+)/g;
|
||||
let match;
|
||||
do {
|
||||
match = propRegex.exec(ThemeDefaults);
|
||||
if (match) {
|
||||
const [, name, value] = match;
|
||||
this.themeDefaults['--' + name] = value;
|
||||
}
|
||||
} while (match);
|
||||
},
|
||||
|
||||
apply(cssStyle) {
|
||||
this.init();
|
||||
const lines = ThemeVarsScss.split('\n');
|
||||
for (const line of lines) {
|
||||
const match = line.match(/\s*([^:]+):\s*(.*?),?\s*$/);
|
||||
if (!match) {
|
||||
continue;
|
||||
}
|
||||
const [, name, def] = match;
|
||||
const propName = '--' + name;
|
||||
const currentValue = cssStyle.getPropertyValue(propName);
|
||||
if (currentValue) {
|
||||
continue;
|
||||
}
|
||||
let result = def.replace(/map-get\(\$t,\s*([\w\-]+)\)/g, '--$1');
|
||||
let replaced = true;
|
||||
const locals = [];
|
||||
while (replaced) {
|
||||
replaced = false;
|
||||
result = result.replace(/([\w\-]+)\([^()]+\)/, fnText => {
|
||||
replaced = true;
|
||||
const [, name, argsStr] = fnText.match(/([\w\-]+)\((.*)\)/);
|
||||
const args = argsStr.trim().split(/\s*,\s*/).filter(arg => arg).map(arg => this.resolveArg(arg, cssStyle, locals));
|
||||
locals.push(this.fn[name](...args));
|
||||
return 'L' + (locals.length - 1);
|
||||
});
|
||||
}
|
||||
result = locals[locals.length - 1];
|
||||
cssStyle.setProperty(propName, result);
|
||||
}
|
||||
},
|
||||
|
||||
resolveArg(arg, cssStyle, locals) {
|
||||
if (/^--/.test(arg)) {
|
||||
let cssProp = cssStyle.getPropertyValue(arg);
|
||||
if (cssProp) {
|
||||
cssProp = cssProp.trim();
|
||||
}
|
||||
if (cssProp) {
|
||||
arg = cssProp;
|
||||
} else {
|
||||
if (this.themeDefaults[arg]) {
|
||||
arg = this.themeDefaults[arg];
|
||||
} else {
|
||||
throw new Error('Css property missing: ' + arg);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (/^L/.test(arg)) {
|
||||
return locals[arg.substr(1)];
|
||||
}
|
||||
if (/%$/.test(arg)) {
|
||||
return arg.replace(/%$/, '') / 100;
|
||||
}
|
||||
if (/^-?[\d.]+?$/.test(arg)) {
|
||||
return +arg;
|
||||
}
|
||||
if (/^(#|rgb)/.test(arg)) {
|
||||
return new Color(arg);
|
||||
}
|
||||
throw new Error('Bad css arg: ' + arg);
|
||||
},
|
||||
|
||||
fn: {
|
||||
'mix'(color1, color2, percent) {
|
||||
return color1.mix(color2, percent).toRgba();
|
||||
},
|
||||
'semi-mute-percent'(mutePercent) {
|
||||
return mutePercent / 2;
|
||||
},
|
||||
'rgba'(color, alpha) {
|
||||
const res = new Color(color);
|
||||
res.a = alpha;
|
||||
return res.toRgba();
|
||||
},
|
||||
'text-contrast-color'(color, lshift, thBg, thText) {
|
||||
if (color.l - lshift >= thBg.l) {
|
||||
return thText.toRgba();
|
||||
}
|
||||
return thBg.toRgba();
|
||||
},
|
||||
'lightness-alpha'(color, lightness, alpha) {
|
||||
const res = new Color(color);
|
||||
res.l += Math.min(0, Math.max(1, lightness));
|
||||
res.a += Math.min(0, Math.max(1, alpha));
|
||||
return res.toHsla();
|
||||
},
|
||||
'shade'(color, percent) {
|
||||
return Color.black.mix(color, percent).toRgba();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = ThemeVars;
|
|
@ -2,13 +2,37 @@ const Colors = require('../const/colors');
|
|||
|
||||
const KnownColors = {};
|
||||
|
||||
const Color = function(str) {
|
||||
const start = str[0] === '#' ? 1 : 0;
|
||||
const len = str.length === 3 ? 1 : 2;
|
||||
this.r = parseInt(str.substr(start, len), 16);
|
||||
this.g = parseInt(str.substr(start + len, len), 16);
|
||||
this.b = parseInt(str.substr(start + len * 2, len), 16);
|
||||
this.setHsl();
|
||||
const Color = function(arg) {
|
||||
const rgbaMatch = /^rgba?\((\d+),\s*(\d+),\s*(\d+)(,\s*([\d.]+))?\)$/.exec(arg);
|
||||
if (rgbaMatch) {
|
||||
this.r = +rgbaMatch[1];
|
||||
this.g = +rgbaMatch[2];
|
||||
this.b = +rgbaMatch[2];
|
||||
this.a = rgbaMatch[4] ? rgbaMatch[4] : 1;
|
||||
this.setHsl();
|
||||
} else {
|
||||
const hexMatch = /^#?([0-9a-f]{3,6})$/i.exec(arg);
|
||||
if (hexMatch) {
|
||||
const digits = hexMatch[1];
|
||||
const len = digits.length === 3 ? 1 : 2;
|
||||
this.r = parseInt(digits.substr(0, len), 16);
|
||||
this.g = parseInt(digits.substr(len, len), 16);
|
||||
this.b = parseInt(digits.substr(len * 2, len), 16);
|
||||
this.a = 1;
|
||||
this.setHsl();
|
||||
} else if (arg instanceof Color) {
|
||||
this.r = arg.r;
|
||||
this.g = arg.g;
|
||||
this.b = arg.b;
|
||||
this.h = arg.h;
|
||||
this.s = arg.s;
|
||||
this.l = arg.l;
|
||||
this.a = arg.a;
|
||||
} else {
|
||||
this.r = this.g = this.b = this.h = this.s = this.l = 0;
|
||||
this.a = 1;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Color.prototype.setHsl = function() {
|
||||
|
@ -44,10 +68,28 @@ Color.prototype.toHex = function() {
|
|||
return '#' + hex(this.r) + hex(this.g) + hex(this.b);
|
||||
};
|
||||
|
||||
Color.prototype.toRgba = function() {
|
||||
return `rgba(${Math.round(this.r)},${Math.round(this.g)},${Math.round(this.b)},${this.a})`;
|
||||
};
|
||||
|
||||
Color.prototype.toHsla = function() {
|
||||
return `hsla(${Math.round(this.h * 100)},${Math.round(this.s * 100)}%,${Math.round(this.l * 100)}%,${this.a})`;
|
||||
};
|
||||
|
||||
Color.prototype.distanceTo = function(color) {
|
||||
return Math.abs(this.h - color.h);
|
||||
};
|
||||
|
||||
Color.prototype.mix = function(another, weight) {
|
||||
const res = new Color(this);
|
||||
const anotherWeight = 1 - weight;
|
||||
res.r = this.r * weight + another.r * anotherWeight;
|
||||
res.g = this.g * weight + another.g * anotherWeight;
|
||||
res.b = this.b * weight + another.b * anotherWeight;
|
||||
res.a = this.a * weight + another.a * anotherWeight;
|
||||
return res;
|
||||
};
|
||||
|
||||
Color.getNearest = function(colorStr) {
|
||||
const color = new Color(colorStr);
|
||||
if (!color.s) {
|
||||
|
@ -73,6 +115,8 @@ _.forEach(Colors.ColorsValues, (val, name) => {
|
|||
KnownColors[name] = new Color(val);
|
||||
});
|
||||
|
||||
Color.black = new Color('#000');
|
||||
|
||||
function hex(num) {
|
||||
const str = (num || 0).toString(16);
|
||||
return str.length < 2 ? '0' + str : str;
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
"licence": "MIT",
|
||||
"url": "https://keeweb.info",
|
||||
"resources": {
|
||||
"css": "wBU9HL+b7PPRUxzjRdSJnPCP+zHpmY7Uv/mSgLHDDKMwUilWws+Gh6d8ldtfRDPx3DLIQ4dU3QZ0UrJz7f9rnC0n7RXEzOY7TPgU9lsK5f/kDPBEIIAB/VaVy40Ii2G72sZVyS6oTB67Rw1MQO0OHEgreYhnD/IyWi7r2iPi8jEDmmyLIDx/oS6HNxU/30KqcFQYkqysfxpqqo6kQ5WJ4O7a9rrRnJZezU9IwnCKxmPgQSA3FtuUh0qTrK7aiFndWn/COxklKhMiaocvB5oZcoHYvlZYPFKYVAaDWVfSUW4+OsAaTbutjNtCuLSFLm0MCa6keVpFYgdRplDTYWlLyA=="
|
||||
"css": "bW0s1nSdHaQvJS/EzLLrkIS14un5uXQ3MVWqQ+RuOP5/ZvtlOERvI8jPLtpyUuHfMOQItUVmult/jTI5/1ya0P1VHPYewo89Wwgy9s9umxnkQ3RfKVUZi0Kq/qhamV8dH37rTW7NUrrCayUBSu+LT36sl03+cixNRoRKTkaLayqFP/YZxyQFzbswiGMWkFE9PrP8vHJZMFa/4Bim1w85jZksheMRoODRLSs9TExTHe+Tteh9LxlswUAdL0CVfzI4NlnBbX++ua1p76MBs0DQOb1mVhkCfNRI0xE4vD7djcCqNZrMPgFPj/4d2SBY3NC4WG4Kg6Up286mS1LfdONyLg=="
|
||||
},
|
||||
"publicKey": "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA1QB+yQofkqIHbHFVAtWhjEFjaxvNekyQx/aK7nEpZzqM8ReoVWbJGVA+7z7MhymwZanbL8uAUrSpNTp5eFWltDksxqHqmXT4UcFU+4reLjYfgwjIaA3c9Q+2JA1Iowqbv3NDcDKm6Ug+dROr04VDCfYE4WRYgGdTYHDbJs5svxUgQ25uc0KKUWAvhYbSKsw43AwmbPbKkHdfZHiS5ZST99HVEJWxn3Kd2zLY9Kk70nu9MzypMLDqxUqjKgdeRCIyZeAYzB75miH3B5vMKhFcdmA8+D6WU2N+6gzKsY5BfqF729uFKSTo4JUKQ5fMU0lKSDHG4qGrkgnURfAUuj9YMQIDAQAB",
|
||||
"theme": {
|
||||
|
|
|
@ -1,39 +1,11 @@
|
|||
.th-rainbow {
|
||||
/* basic theme style variables */
|
||||
/* theme style variables */
|
||||
--background-color: #FAFAFA;
|
||||
--medium-color: #050505;
|
||||
--text-color: #424243;
|
||||
--action-color: #475FD7;
|
||||
--error-color: #E75675;
|
||||
--muted-color: #b1b1b1;
|
||||
|
||||
/* dependent theme style variables */
|
||||
--muted-color-border: #d5d5d5;
|
||||
--text-selection-bg-color: rgba(71, 95, 215, 0.3);
|
||||
--text-selection-bg-color-error: rgba(231, 86, 117, 0.8);
|
||||
--text-semi-muted-color: #424244;
|
||||
--text-contrast-action-color: #FAFAFA;
|
||||
--text-contrast-error-color: #FAFAFA;
|
||||
--base-border-color: gray;
|
||||
--accent-border-color: #5b5b5b;
|
||||
--light-border-color: #e2e2e2;
|
||||
--form-box-shadow-color-focus: rgba(50, 77, 210, 0.7);
|
||||
--form-box-shadow-color-focus-error: rgba(228, 64, 99, 0.7);
|
||||
--dropdown-box-shadow-color: rgba(5, 5, 5, 0.05);
|
||||
--secondary-background-color: #e2e2e2;
|
||||
--intermediate-background-color: #f3f3f3;
|
||||
--intermediate-pressed-background-color: #f4f4f4;
|
||||
--disabled-background-color: #eeeeee;
|
||||
--action-background-color-focus: #394cac;
|
||||
--action-background-color-focus-tr: rgba(57, 76, 172, 0.1);
|
||||
--error-background-color-focus: #b9455e;
|
||||
--error-background-color-focus-tr: rgba(185, 69, 94, 0.1);
|
||||
--action-background-color-active: #3547a1;
|
||||
--action-background-color-active-tr: rgba(53, 71, 161, 0.15);
|
||||
--error-background-color-active: #ad4158;
|
||||
--error-background-color-active-tr: rgba(173, 65, 88, 0.15);
|
||||
--modal-background-color: rgba(250, 250, 250, 0.9);
|
||||
--modal-background-color-tr: rgba(250, 250, 250, 0);
|
||||
}
|
||||
|
||||
/* any other custom css goes here */
|
||||
|
|
Loading…
Reference in New Issue