rewrite the tinter to be ES6, and correctly cache fixups per theme
This commit is contained in:
parent
e729bc431d
commit
58ee4d0a76
2 changed files with 144 additions and 125 deletions
267
src/Tinter.js
267
src/Tinter.js
|
@ -15,69 +15,9 @@ See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// FIXME: these vars should be bundled up and attached to
|
|
||||||
// module.exports otherwise this will break when included by both
|
|
||||||
// react-sdk and apps layered on top.
|
|
||||||
|
|
||||||
const DEBUG = 0;
|
const DEBUG = 0;
|
||||||
|
|
||||||
// The default colour keys to be replaced as referred to in CSS
|
// utility to turn #rrggbb into [red,green,blue]
|
||||||
// (should be overridden by .mx_theme_accentColor and .mx_theme_secondaryAccentColor)
|
|
||||||
const keyRgb = [
|
|
||||||
"rgb(118, 207, 166)", // Vector Green
|
|
||||||
"rgb(234, 245, 240)", // Vector Light Green
|
|
||||||
"rgb(211, 239, 225)", // Unused: BottomLeftMenu (20% Green overlaid on Light Green)
|
|
||||||
];
|
|
||||||
|
|
||||||
// Some algebra workings for calculating the tint % of Vector Green & Light Green
|
|
||||||
// x * 118 + (1 - x) * 255 = 234
|
|
||||||
// x * 118 + 255 - 255 * x = 234
|
|
||||||
// x * 118 - x * 255 = 234 - 255
|
|
||||||
// (255 - 118) x = 255 - 234
|
|
||||||
// x = (255 - 234) / (255 - 118) = 0.16
|
|
||||||
|
|
||||||
// The colour keys to be replaced as referred to in SVGs
|
|
||||||
const keyHex = [
|
|
||||||
"#76CFA6", // Vector Green
|
|
||||||
"#EAF5F0", // Vector Light Green
|
|
||||||
"#D3EFE1", // Unused: BottomLeftMenu (20% Green overlaid on Light Green)
|
|
||||||
"#FFFFFF", // white highlights of the SVGs (for switching to dark theme)
|
|
||||||
];
|
|
||||||
|
|
||||||
// cache of our replacement colours
|
|
||||||
// defaults to our keys.
|
|
||||||
const colors = [
|
|
||||||
keyHex[0],
|
|
||||||
keyHex[1],
|
|
||||||
keyHex[2],
|
|
||||||
keyHex[3],
|
|
||||||
];
|
|
||||||
|
|
||||||
const cssFixups = [
|
|
||||||
// {
|
|
||||||
// style: a style object that should be fixed up taken from a stylesheet
|
|
||||||
// attr: name of the attribute to be clobbered, e.g. 'color'
|
|
||||||
// index: ordinal of primary, secondary or tertiary
|
|
||||||
// }
|
|
||||||
];
|
|
||||||
|
|
||||||
// CSS attributes to be fixed up
|
|
||||||
const cssAttrs = [
|
|
||||||
"color",
|
|
||||||
"backgroundColor",
|
|
||||||
"borderColor",
|
|
||||||
"borderTopColor",
|
|
||||||
"borderBottomColor",
|
|
||||||
"borderLeftColor",
|
|
||||||
];
|
|
||||||
|
|
||||||
const svgAttrs = [
|
|
||||||
"fill",
|
|
||||||
"stroke",
|
|
||||||
];
|
|
||||||
|
|
||||||
let cached = false;
|
|
||||||
|
|
||||||
function hexToRgb(color) {
|
function hexToRgb(color) {
|
||||||
if (color[0] === '#') color = color.slice(1);
|
if (color[0] === '#') color = color.slice(1);
|
||||||
if (color.length === 3) {
|
if (color.length === 3) {
|
||||||
|
@ -92,15 +32,77 @@ function hexToRgb(color) {
|
||||||
return [r, g, b];
|
return [r, g, b];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// utility to turn [red,green,blue] into #rrggbb
|
||||||
function rgbToHex(rgb) {
|
function rgbToHex(rgb) {
|
||||||
const val = (rgb[0] << 16) | (rgb[1] << 8) | rgb[2];
|
const val = (rgb[0] << 16) | (rgb[1] << 8) | rgb[2];
|
||||||
return '#' + (0x1000000 + val).toString(16).slice(1);
|
return '#' + (0x1000000 + val).toString(16).slice(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
// List of functions to call when the tint changes.
|
class Tinter {
|
||||||
const tintables = [];
|
constructor() {
|
||||||
|
// The default colour keys to be replaced as referred to in CSS
|
||||||
|
// (should be overridden by .mx_theme_accentColor and .mx_theme_secondaryAccentColor)
|
||||||
|
this.keyRgb = [
|
||||||
|
"rgb(118, 207, 166)", // Vector Green
|
||||||
|
"rgb(234, 245, 240)", // Vector Light Green
|
||||||
|
"rgb(211, 239, 225)", // Unused: BottomLeftMenu (20% Green overlaid on Light Green)
|
||||||
|
];
|
||||||
|
|
||||||
|
// Some algebra workings for calculating the tint % of Vector Green & Light Green
|
||||||
|
// x * 118 + (1 - x) * 255 = 234
|
||||||
|
// x * 118 + 255 - 255 * x = 234
|
||||||
|
// x * 118 - x * 255 = 234 - 255
|
||||||
|
// (255 - 118) x = 255 - 234
|
||||||
|
// x = (255 - 234) / (255 - 118) = 0.16
|
||||||
|
|
||||||
|
// The colour keys to be replaced as referred to in SVGs
|
||||||
|
this.keyHex = [
|
||||||
|
"#76CFA6", // Vector Green
|
||||||
|
"#EAF5F0", // Vector Light Green
|
||||||
|
"#D3EFE1", // Unused: BottomLeftMenu (20% Green overlaid on Light Green)
|
||||||
|
"#FFFFFF", // white highlights of the SVGs (for switching to dark theme)
|
||||||
|
];
|
||||||
|
|
||||||
|
// cache of our replacement colours
|
||||||
|
// defaults to our keys.
|
||||||
|
this.colors = [
|
||||||
|
this.keyHex[0],
|
||||||
|
this.keyHex[1],
|
||||||
|
this.keyHex[2],
|
||||||
|
this.keyHex[3],
|
||||||
|
];
|
||||||
|
|
||||||
|
this.cssFixups = [
|
||||||
|
// { theme: {
|
||||||
|
// style: a style object that should be fixed up taken from a stylesheet
|
||||||
|
// attr: name of the attribute to be clobbered, e.g. 'color'
|
||||||
|
// index: ordinal of primary, secondary or tertiary
|
||||||
|
// },
|
||||||
|
// }
|
||||||
|
];
|
||||||
|
|
||||||
|
// CSS attributes to be fixed up
|
||||||
|
this.cssAttrs = [
|
||||||
|
"color",
|
||||||
|
"backgroundColor",
|
||||||
|
"borderColor",
|
||||||
|
"borderTopColor",
|
||||||
|
"borderBottomColor",
|
||||||
|
"borderLeftColor",
|
||||||
|
];
|
||||||
|
|
||||||
|
this.svgAttrs = [
|
||||||
|
"fill",
|
||||||
|
"stroke",
|
||||||
|
];
|
||||||
|
|
||||||
|
// List of functions to call when the tint changes.
|
||||||
|
this.tintables = [];
|
||||||
|
|
||||||
|
// the currently loaded theme (if any)
|
||||||
|
this.theme = undefined;
|
||||||
|
}
|
||||||
|
|
||||||
module.exports = {
|
|
||||||
/**
|
/**
|
||||||
* Register a callback to fire when the tint changes.
|
* Register a callback to fire when the tint changes.
|
||||||
* This is used to rewrite the tintable SVGs with the new tint.
|
* This is used to rewrite the tintable SVGs with the new tint.
|
||||||
|
@ -112,27 +114,24 @@ module.exports = {
|
||||||
*
|
*
|
||||||
* @param {Function} tintable Function to call when the tint changes.
|
* @param {Function} tintable Function to call when the tint changes.
|
||||||
*/
|
*/
|
||||||
registerTintable: function(tintable) {
|
registerTintable(tintable) {
|
||||||
tintables.push(tintable);
|
this.tintables.push(tintable);
|
||||||
},
|
}
|
||||||
|
|
||||||
getKeyRgb: function() {
|
getKeyRgb() {
|
||||||
return keyRgb;
|
return this.keyRgb;
|
||||||
},
|
}
|
||||||
|
|
||||||
getCurrentColors: function() {
|
getCurrentColors() {
|
||||||
return colors;
|
return this.colors;
|
||||||
},
|
}
|
||||||
|
|
||||||
tint: function(primaryColor, secondaryColor, tertiaryColor) {
|
tint(primaryColor, secondaryColor, tertiaryColor) {
|
||||||
if (!cached) {
|
this.calcCssFixups();
|
||||||
this.calcCssFixups();
|
|
||||||
cached = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!primaryColor) {
|
if (!primaryColor) {
|
||||||
primaryColor = keyRgb[0];
|
primaryColor = this.keyRgb[0];
|
||||||
secondaryColor = keyRgb[1];
|
secondaryColor = this.keyRgb[1];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!secondaryColor) {
|
if (!secondaryColor) {
|
||||||
|
@ -154,15 +153,15 @@ module.exports = {
|
||||||
tertiaryColor = rgbToHex(rgb1);
|
tertiaryColor = rgbToHex(rgb1);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (colors[0] === primaryColor &&
|
if (this.colors[0] === primaryColor &&
|
||||||
colors[1] === secondaryColor &&
|
this.colors[1] === secondaryColor &&
|
||||||
colors[2] === tertiaryColor) {
|
this.colors[2] === tertiaryColor) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
colors[0] = primaryColor;
|
this.colors[0] = primaryColor;
|
||||||
colors[1] = secondaryColor;
|
this.colors[1] = secondaryColor;
|
||||||
colors[2] = tertiaryColor;
|
this.colors[2] = tertiaryColor;
|
||||||
|
|
||||||
if (DEBUG) console.log("Tinter.tint");
|
if (DEBUG) console.log("Tinter.tint");
|
||||||
|
|
||||||
|
@ -171,41 +170,52 @@ module.exports = {
|
||||||
|
|
||||||
// tell all the SVGs to go fix themselves up
|
// tell all the SVGs to go fix themselves up
|
||||||
// we don't do this as a dispatch otherwise it will visually lag
|
// we don't do this as a dispatch otherwise it will visually lag
|
||||||
tintables.forEach(function(tintable) {
|
this.tintables.forEach(function(tintable) {
|
||||||
tintable();
|
tintable();
|
||||||
});
|
});
|
||||||
},
|
}
|
||||||
|
|
||||||
tintSvgWhite: function(whiteColor) {
|
tintSvgWhite(whiteColor) {
|
||||||
if (!whiteColor) {
|
if (!whiteColor) {
|
||||||
whiteColor = colors[3];
|
whiteColor = this.colors[3];
|
||||||
}
|
}
|
||||||
if (colors[3] === whiteColor) {
|
if (this.colors[3] === whiteColor) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
colors[3] = whiteColor;
|
this.colors[3] = whiteColor;
|
||||||
tintables.forEach(function(tintable) {
|
this.tintables.forEach(function(tintable) {
|
||||||
tintable();
|
tintable();
|
||||||
});
|
});
|
||||||
},
|
}
|
||||||
|
|
||||||
calcCssFixups: function() {
|
setTheme(theme) {
|
||||||
if (DEBUG) console.log("calcCssFixups start");
|
this.theme = theme;
|
||||||
|
|
||||||
cssFixups.length = 0;
|
|
||||||
|
|
||||||
// update keyRgb from the current theme CSS itself, if it defines it
|
// update keyRgb from the current theme CSS itself, if it defines it
|
||||||
if (document.getElementById('mx_theme_accentColor')) {
|
if (document.getElementById('mx_theme_accentColor')) {
|
||||||
keyRgb[0] = window.getComputedStyle(
|
this.keyRgb[0] = window.getComputedStyle(
|
||||||
document.getElementById('mx_theme_accentColor')
|
document.getElementById('mx_theme_accentColor')
|
||||||
).color;
|
).color;
|
||||||
}
|
}
|
||||||
if (document.getElementById('mx_theme_secondaryAccentColor')) {
|
if (document.getElementById('mx_theme_secondaryAccentColor')) {
|
||||||
keyRgb[1] = window.getComputedStyle(
|
this.keyRgb[1] = window.getComputedStyle(
|
||||||
document.getElementById('mx_theme_secondaryAccentColor')
|
document.getElementById('mx_theme_secondaryAccentColor')
|
||||||
).color;
|
).color;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.calcCssFixups();
|
||||||
|
}
|
||||||
|
|
||||||
|
calcCssFixups() {
|
||||||
|
// cache our fixups
|
||||||
|
if (this.cssFixups[this.theme]) return;
|
||||||
|
|
||||||
|
if (DEBUG) console.trace("calcCssFixups start for " + this.theme + " (checking " +
|
||||||
|
document.styleSheets.length +
|
||||||
|
" stylesheets)");
|
||||||
|
|
||||||
|
this.cssFixups[this.theme] = [];
|
||||||
|
|
||||||
for (let i = 0; i < document.styleSheets.length; i++) {
|
for (let i = 0; i < document.styleSheets.length; i++) {
|
||||||
const ss = document.styleSheets[i];
|
const ss = document.styleSheets[i];
|
||||||
if (!ss) continue; // well done safari >:(
|
if (!ss) continue; // well done safari >:(
|
||||||
|
@ -236,7 +246,7 @@ module.exports = {
|
||||||
// Vector Green as any other colour.
|
// Vector Green as any other colour.
|
||||||
// --matthew
|
// --matthew
|
||||||
|
|
||||||
if (ss.href && !ss.href.match(/\/bundle.*\.css$/)) continue;
|
if (ss.href && !ss.href.match(new RegExp('/theme-' + this.theme + '.css$'))) continue;
|
||||||
if (ss.disabled) continue;
|
if (ss.disabled) continue;
|
||||||
if (!ss.cssRules) continue;
|
if (!ss.cssRules) continue;
|
||||||
|
|
||||||
|
@ -244,11 +254,11 @@ module.exports = {
|
||||||
const rule = ss.cssRules[j];
|
const rule = ss.cssRules[j];
|
||||||
if (!rule.style) continue;
|
if (!rule.style) continue;
|
||||||
if (rule.selectorText && rule.selectorText.match(/#mx_theme/)) continue;
|
if (rule.selectorText && rule.selectorText.match(/#mx_theme/)) continue;
|
||||||
for (let k = 0; k < cssAttrs.length; k++) {
|
for (let k = 0; k < this.cssAttrs.length; k++) {
|
||||||
const attr = cssAttrs[k];
|
const attr = this.cssAttrs[k];
|
||||||
for (let l = 0; l < keyRgb.length; l++) {
|
for (let l = 0; l < this.keyRgb.length; l++) {
|
||||||
if (rule.style[attr] === keyRgb[l]) {
|
if (rule.style[attr] === this.keyRgb[l]) {
|
||||||
cssFixups.push({
|
this.cssFixups[this.theme].push({
|
||||||
style: rule.style,
|
style: rule.style,
|
||||||
attr: attr,
|
attr: attr,
|
||||||
index: l,
|
index: l,
|
||||||
|
@ -258,22 +268,26 @@ module.exports = {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (DEBUG) console.log("calcCssFixups end");
|
if (DEBUG) console.log("calcCssFixups end (" +
|
||||||
},
|
this.cssFixups[this.theme].length +
|
||||||
|
" fixups)");
|
||||||
|
}
|
||||||
|
|
||||||
applyCssFixups: function() {
|
applyCssFixups() {
|
||||||
if (DEBUG) console.log("applyCssFixups start");
|
if (DEBUG) console.log("applyCssFixups start (" +
|
||||||
for (let i = 0; i < cssFixups.length; i++) {
|
this.cssFixups[this.theme].length +
|
||||||
const cssFixup = cssFixups[i];
|
" fixups)");
|
||||||
cssFixup.style[cssFixup.attr] = colors[cssFixup.index];
|
for (let i = 0; i < this.cssFixups[this.theme].length; i++) {
|
||||||
|
const cssFixup = this.cssFixups[this.theme][i];
|
||||||
|
cssFixup.style[cssFixup.attr] = this.colors[cssFixup.index];
|
||||||
}
|
}
|
||||||
if (DEBUG) console.log("applyCssFixups end");
|
if (DEBUG) console.log("applyCssFixups end");
|
||||||
},
|
}
|
||||||
|
|
||||||
// XXX: we could just move this all into TintableSvg, but as it's so similar
|
// XXX: we could just move this all into TintableSvg, but as it's so similar
|
||||||
// to the CSS fixup stuff in Tinter (just that the fixups are stored in TintableSvg)
|
// to the CSS fixup stuff in Tinter (just that the fixups are stored in TintableSvg)
|
||||||
// keeping it here for now.
|
// keeping it here for now.
|
||||||
calcSvgFixups: function(svgs) {
|
calcSvgFixups(svgs) {
|
||||||
// go through manually fixing up SVG colours.
|
// go through manually fixing up SVG colours.
|
||||||
// we could do this by stylesheets, but keeping the stylesheets
|
// we could do this by stylesheets, but keeping the stylesheets
|
||||||
// updated would be a PITA, so just brute-force search for the
|
// updated would be a PITA, so just brute-force search for the
|
||||||
|
@ -299,11 +313,11 @@ module.exports = {
|
||||||
const tags = svgDoc.getElementsByTagName("*");
|
const tags = svgDoc.getElementsByTagName("*");
|
||||||
for (let j = 0; j < tags.length; j++) {
|
for (let j = 0; j < tags.length; j++) {
|
||||||
const tag = tags[j];
|
const tag = tags[j];
|
||||||
for (let k = 0; k < svgAttrs.length; k++) {
|
for (let k = 0; k < this.svgAttrs.length; k++) {
|
||||||
const attr = svgAttrs[k];
|
const attr = this.svgAttrs[k];
|
||||||
for (let l = 0; l < keyHex.length; l++) {
|
for (let l = 0; l < this.keyHex.length; l++) {
|
||||||
if (tag.getAttribute(attr) &&
|
if (tag.getAttribute(attr) &&
|
||||||
tag.getAttribute(attr).toUpperCase() === keyHex[l])
|
tag.getAttribute(attr).toUpperCase() === this.keyHex[l])
|
||||||
{
|
{
|
||||||
fixups.push({
|
fixups.push({
|
||||||
node: tag,
|
node: tag,
|
||||||
|
@ -318,14 +332,19 @@ module.exports = {
|
||||||
if (DEBUG) console.log("calcSvgFixups end");
|
if (DEBUG) console.log("calcSvgFixups end");
|
||||||
|
|
||||||
return fixups;
|
return fixups;
|
||||||
},
|
}
|
||||||
|
|
||||||
applySvgFixups: function(fixups) {
|
applySvgFixups(fixups) {
|
||||||
if (DEBUG) console.log("applySvgFixups start for " + fixups);
|
if (DEBUG) console.log("applySvgFixups start for " + fixups);
|
||||||
for (let i = 0; i < fixups.length; i++) {
|
for (let i = 0; i < fixups.length; i++) {
|
||||||
const svgFixup = fixups[i];
|
const svgFixup = fixups[i];
|
||||||
svgFixup.node.setAttribute(svgFixup.attr, colors[svgFixup.index]);
|
svgFixup.node.setAttribute(svgFixup.attr, this.colors[svgFixup.index]);
|
||||||
}
|
}
|
||||||
if (DEBUG) console.log("applySvgFixups end");
|
if (DEBUG) console.log("applySvgFixups end");
|
||||||
},
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
|
if (global.singletonTinter === undefined) {
|
||||||
|
global.singletonTinter = new Tinter();
|
||||||
|
}
|
||||||
|
export default global.singletonTinter;
|
||||||
|
|
|
@ -922,7 +922,7 @@ module.exports = React.createClass({
|
||||||
});
|
});
|
||||||
styleElements[theme].disabled = false;
|
styleElements[theme].disabled = false;
|
||||||
|
|
||||||
Tinter.calcCssFixups();
|
Tinter.setTheme(theme);
|
||||||
const colors = Tinter.getCurrentColors();
|
const colors = Tinter.getCurrentColors();
|
||||||
Tinter.tint(colors[0], colors[1]);
|
Tinter.tint(colors[0], colors[1]);
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue