theme logic reused

This commit is contained in:
Jan Prochazka
2021-02-17 19:26:48 +01:00
parent 4ffc5842bb
commit ba6abd1e64
13 changed files with 381 additions and 19 deletions

View File

@@ -24,6 +24,7 @@
"typescript": "^3.9.3"
},
"dependencies": {
"@ant-design/colors": "^6.0.0",
"@mdi/font": "^5.9.55",
"rollup-plugin-copy": "^3.3.0",
"sirv-cli": "^1.0.0"

View File

@@ -8,11 +8,8 @@
<link rel='icon' type='image/png' href='/favicon.png'>
<link rel='stylesheet' href='/global.css'>
<link rel='stylesheet' href='/theme-light.css'>
<link rel='stylesheet' href='/theme-dark.css'>
<link rel='stylesheet' href='/build/bundle.css'>
<link rel='stylesheet' href='/build/fonts/materialdesignicons.css'>
<script defer src='/build/bundle.js'></script>
</head>

View File

@@ -1,5 +0,0 @@
.theme-dark .statusbar { background-color: blue }
.theme-dark .iconbar {
background-color: #222;
color: #b3b3b3;
}

View File

@@ -1,5 +0,0 @@
.theme-light .statusbar { background-color: blue }
.theme-dark .iconbar {
background-color: #222;
color: #b3b3b3;
}

View File

@@ -20,9 +20,11 @@
top: 0;
bottom: var(--statusbar-height);
width: var(--widget-icon-size);
background: var(--theme-widget_background);
}
.statusbar {
position: fixed;
background: var(--theme-statusbar_background);
height: var(--statusbar-height);
left: 0;
right: 0;

View File

@@ -1,10 +1,14 @@
import App from './App.svelte';
import changeTheme from './theme/changeTheme';
import light from './theme/light';
changeTheme(light);
const app = new App({
target: document.body,
props: {
name: 'world'
}
target: document.body,
props: {
name: 'world',
},
});
export default app;
export default app;

View File

@@ -0,0 +1,13 @@
export default function changeTheme(theme) {
for (const [prop, color] of Object.entries(theme)) {
if (Array.isArray(color)) {
for (let index = 0; index < color.length; index++) {
const varString = `--theme-${prop}_${index}`;
document.documentElement.style.setProperty(varString, color[index]);
}
} else {
const varString = `--theme-${prop}`;
document.documentElement.style.setProperty(varString, color);
}
}
}

View File

@@ -0,0 +1,163 @@
// https://css-tricks.com/using-javascript-to-adjust-saturation-and-brightness-of-rgb-colors/
export function hexToRgb(rgb) {
if (!rgb) throw new Error(`Ivalid RGB color: ${rgb}`);
if (rgb.match(/^#[0-9a-fA-F][0-9a-fA-F][0-9a-fA-F]$/)) {
rgb = `#${rgb[1]}${rgb[1]}${rgb[2]}${rgb[2]}${rgb[3]}${rgb[3]}`;
}
if (!rgb.match(/^#[0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F]$/)) {
throw new Error(`Ivalid RGB color: ${rgb}`);
}
return [rgb.substring(1, 3), rgb.substring(3, 5), rgb.substring(5, 7)].map(x => parseInt(x, 16));
}
function componentToHex(c) {
let num = Math.round(c);
if (num < 0) num = 0;
if (num > 255) num = 255;
var hex = num.toString(16);
return hex.length == 1 ? '0' + hex : hex;
}
export function rgbToHex(r, g, b) {
return '#' + componentToHex(r) + componentToHex(g) + componentToHex(b);
}
export function getLightnessOfRGB(rgb) {
// First convert to an array of integers by removing the whitespace, taking the 3rd char to the 2nd last then splitting by ','
const rgbIntArray = hexToRgb(rgb);
// Get the highest and lowest out of red green and blue
const highest = Math.max(...rgbIntArray);
const lowest = Math.min(...rgbIntArray);
// Return the average divided by 255
return (highest + lowest) / 2 / 255;
}
export function getColorType(rgb) {
return getLightnessOfRGB(rgb) > 0.5 ? 'light' : 'dark';
}
export function saturateByTenth(rgb) {
const rgbIntArray = hexToRgb(rgb);
const grayVal = getLightnessOfRGB(rgb) * 255;
const [lowest, middle, highest] = getLowestMiddleHighest(rgbIntArray);
if (lowest.val === highest.val) {
return rgb;
}
const saturationRange = Math.round(Math.min(255 - grayVal, grayVal));
const maxChange = Math.min(255 - highest.val, lowest.val);
const changeAmount = Math.min(saturationRange / 10, maxChange);
const middleValueRatio = (grayVal - middle.val) / (grayVal - highest.val);
const returnArray = [];
returnArray[highest.index] = Math.round(highest.val + changeAmount);
returnArray[lowest.index] = Math.round(lowest.val - changeAmount);
returnArray[middle.index] = Math.round(grayVal + (returnArray[highest.index] - grayVal) * middleValueRatio);
return `rgb(${[returnArray].join()})`;
}
function getLowestMiddleHighest(rgbIntArray) {
let highest = { val: -1, index: -1 };
let lowest = { val: Infinity, index: -1 };
rgbIntArray.map((val, index) => {
if (val > highest.val) {
highest = { val: val, index: index };
}
if (val < lowest.val) {
lowest = { val: val, index: index };
}
});
if (lowest.index === highest.index) {
lowest.index = highest.index + 1;
}
let middle = { index: 3 - highest.index - lowest.index };
middle.val = rgbIntArray[middle.index];
return [lowest, middle, highest];
}
export function lightenByTenth(rgb, ratio = 0.1) {
const rgbIntArray = hexToRgb(rgb);
// Grab the values in order of magnitude
// This uses the getLowestMiddleHighest function from the saturate section
const [lowest, middle, highest] = getLowestMiddleHighest(rgbIntArray);
if (lowest.val === 255) {
return rgb;
}
const returnArray = [];
// First work out increase on lower value
returnArray[lowest.index] = Math.round(lowest.val + Math.min(255 - lowest.val, 255 * ratio));
// Then apply to the middle and higher values
const increaseFraction = (returnArray[lowest.index] - lowest.val) / (255 - lowest.val);
returnArray[middle.index] = middle.val + (255 - middle.val) * increaseFraction;
returnArray[highest.index] = highest.val + (255 - highest.val) * increaseFraction;
// Convert the array back into an rgb string
return rgbToHex(...returnArray);
}
export function darkenByTenth(rgb, ratio = 0.1) {
// Our rgb to int array function again
const rgbIntArray = hexToRgb(rgb);
//grab the values in order of magnitude
//this uses the function from the saturate function
const [lowest, middle, highest] = getLowestMiddleHighest(rgbIntArray);
if (highest.val === 0) {
return rgb;
}
const returnArray = [];
returnArray[highest.index] = highest.val - Math.min(highest.val, 255 * ratio);
const decreaseFraction = (highest.val - returnArray[highest.index]) / highest.val;
returnArray[middle.index] = middle.val - middle.val * decreaseFraction;
returnArray[lowest.index] = lowest.val - lowest.val * decreaseFraction;
// Convert the array back into an rgb string
return rgbToHex(...returnArray);
}
export function desaturateByTenth(rgb) {
const rgbIntArray = hexToRgb(rgb);
//grab the values in order of magnitude
//this uses the getLowestMiddleHighest function from the saturate section
const [lowest, middle, highest] = getLowestMiddleHighest(rgbIntArray);
const grayVal = getLightnessOfRGB(rgb) * 255;
if (lowest.val === highest.val) {
return rgb;
}
const saturationRange = Math.round(Math.min(255 - grayVal, grayVal));
const maxChange = grayVal - lowest.val;
const changeAmount = Math.min(saturationRange / 10, maxChange);
const middleValueRatio = (grayVal - middle.val) / (grayVal - highest.val);
const returnArray = [];
returnArray[highest.index] = Math.round(highest.val - changeAmount);
returnArray[lowest.index] = Math.round(lowest.val + changeAmount);
returnArray[middle.index] = Math.round(grayVal + (returnArray[highest.index] - grayVal) * middleValueRatio);
return rgbToHex(...returnArray);
}
export function accentColor(rgb, index, ratio = 0.1) {
const rgbIntArray = hexToRgb(rgb);
const returnArray = rgbIntArray.map((v, i) => {
if (i == index) return v + 255 * ratio;
return v - 128 * ratio;
});
return rgbToHex(...returnArray);
}

View File

@@ -0,0 +1,38 @@
import fillTheme from './fillTheme';
const theme = {
main_type: 'dark',
main_background: '#444',
fontWhite1: '#ddd',
selectionAntName: 'blue',
aceEditorTheme: 'twilight',
jsonViewerTheme: 'monokai',
border: '#555',
border_background: '#555',
toolbar_background: '#333',
content_background: '#333',
left_background: '#333',
widget_background: '#222',
title_background: '#555',
manager_background: '#222',
tabs_background: '#111',
gridheader_background: '#333',
gridbody_background: '#1a1a1a',
scrollbar_background: '#444',
input_background: '#222',
modal_background: '#222',
modalheader_background: '#555',
button_background: '#004488',
statusbar_background: '#00c',
inlinebtn_background: '#222',
designer_background: '#333',
designtable_background: '#000',
designer_line: '#bbb',
};
export default fillTheme(theme);

View File

@@ -0,0 +1,95 @@
import _ from 'lodash';
import { accentColor, darkenByTenth, getColorType, lightenByTenth } from './colorUtil';
import { generate, presetPalettes, presetDarkPalettes, presetPrimaryColors } from '@ant-design/colors';
function fillOne(theme, name, type, add, background, fontName, invFontName, changeLightFunc, fontPalettes) {
add[`${name}_font1`] = add[`${fontName}1`];
add[`${name}_font2`] = add[`${fontName}2`];
add[`${name}_font3`] = add[`${fontName}3`];
add[`${name}_font4`] = add[`${fontName}4`];
add[`${name}_invfont1`] = add[`${invFontName}1`];
add[`${name}_invfont2`] = add[`${invFontName}2`];
add[`${name}_invfont3`] = add[`${invFontName}3`];
add[`${name}_invfont4`] = add[`${invFontName}4`];
// add[`${name}_fontDisabled`] = add.fontBlack3;
if (background) {
add[`${name}_background1`] = background;
add[`${name}_background2`] = changeLightFunc(add[`${name}_background1`]);
add[`${name}_background3`] = changeLightFunc(add[`${name}_background2`]);
add[`${name}_background4`] = changeLightFunc(add[`${name}_background3`]);
}
for (const colorName in presetPrimaryColors) {
add[`${name}_font_${colorName}`] = fontPalettes[colorName];
if (background) {
add[`${name}_background_${colorName}`] = generate(presetPrimaryColors[colorName], {
theme: type,
backgroundColor: background,
});
add[`${name}_selection`] = generate(
theme.selectionAntName ? presetPrimaryColors[theme.selectionAntName] : theme.selectionBaseColor,
{
theme: type,
backgroundColor: background,
}
);
}
}
add[`${name}_font_hover`] = add[`${name}_font_geekblue`][8];
add[`${name}_font_link`] = add[`${name}_font_geekblue`][7];
if (background) {
add[`${name}_background_alt2`] = changeLightFunc(add[`${name}_background1`], type == 'light' ? 0.05 : 0.1);
add[`${name}_background_alt3`] = add[`${name}_background_geekblue`][type == 'light' ? 0 : 1];
}
}
function fillThemeCore(theme) {
const add = { ...theme };
add.fontWhite1 = add.fontWhite1 || '#FFFFFF';
add.fontWhite2 = add.fontWhite2 || darkenByTenth(add.fontWhite1, 0.3);
add.fontWhite3 = add.fontWhite3 || darkenByTenth(add.fontWhite2, 0.2);
add.fontWhite4 = add.fontWhite4 || darkenByTenth(add.fontWhite3, 0.2);
add.fontBlack1 = add.fontBlack1 || '#000000';
add.fontBlack2 = add.fontBlack2 || lightenByTenth(add.fontBlack1, 0.3);
add.fontBlack3 = add.fontBlack3 || lightenByTenth(add.fontBlack2, 0.2);
add.fontBlack4 = add.fontBlack4 || lightenByTenth(add.fontBlack3, 0.2);
for (const key of _.keys(theme)) {
const matchType = key.match(/^(.*)_type$/);
const matchBg = key.match(/^(.*)_background$/);
if (!matchType && !matchBg) continue;
const name = matchType ? matchType[1] : matchBg[1];
if (matchBg && theme[`${name}_type`]) continue;
const type = matchType ? theme[key] : getColorType(theme[key]);
if (type != 'light' && type != 'dark') continue;
const background = theme[`${name}_background`];
if (type == 'light') {
fillOne(theme, name, type, add, background, 'fontBlack', 'fontWhite', darkenByTenth, presetPalettes);
}
if (type == 'dark') {
fillOne(theme, name, type, add, background, 'fontWhite', 'fontBlack', lightenByTenth, presetDarkPalettes);
}
}
if (add.main_type == 'dark') add.main_palettes = presetDarkPalettes;
else add.main_palettes = presetPalettes;
return {
...add,
...theme,
};
}
export default function fillTheme(theme) {
theme = fillThemeCore(theme);
console.log('THEME', theme);
return theme;
}

View File

@@ -0,0 +1,37 @@
import fillTheme from './fillTheme';
const theme = {
main_type: 'light',
main_background: '#fff',
selectionAntName: 'blue',
aceEditorTheme: 'github',
jsonViewerTheme: 'rjv-default',
border: '#ccc',
border_background: '#ccc',
toolbar_background: '#eee',
content_background: '#eee',
left_background: '#ccc',
widget_background: '#222',
title_background: '#888',
manager_background: '#fff',
tabs_background: '#eee',
gridheader_background: '#eee',
gridheader_type: 'light',
gridbody_background: '#fff',
scrollbar_background: '#ddd',
input_background: '#fff',
modal_background: '#fff',
modalheader_background: '#eff',
button_background: '#337ab7',
statusbar_background: '#00c',
inlinebtn_background: '#ededed',
designer_background: '#eee',
designtable_background: '#fff',
designer_line: '#666',
};
export default fillTheme(theme);

View File

@@ -1,6 +1,8 @@
<script>
import FontIcon from '../icons/FontIcon.svelte';
let selectedWidget = 'database';
const widgets = [
{
icon: 'icon database',
@@ -43,7 +45,7 @@
</script>
{#each widgets as item}
<div class="wrapper">
<div class="wrapper" class:selected={item.name == selectedWidget} on:click={e => (selectedWidget = item.name)}>
<FontIcon icon={item.icon} />
</div>
{/each}
@@ -55,5 +57,13 @@
display: flex;
align-items: center;
justify-content: center;
color: var(--theme-widget_font2);
}
.wrapper:hover {
color: var(--theme-widget_font1);
}
.wrapper.selected {
color: var(--theme-widget_font1);
background: var(--theme-widget_background3);
}
</style>

View File

@@ -9,6 +9,13 @@
dependencies:
"@ctrl/tinycolor" "^3.1.6"
"@ant-design/colors@^6.0.0":
version "6.0.0"
resolved "https://registry.yarnpkg.com/@ant-design/colors/-/colors-6.0.0.tgz#9b9366257cffcc47db42b9d0203bb592c13c0298"
integrity sha512-qAZRvPzfdWHtfameEGP2Qvuf838NhergR35o+EuVyB5XvSA98xod5r4utvi4TJ3ywmevm290g9nsCG5MryrdWQ==
dependencies:
"@ctrl/tinycolor" "^3.4.0"
"@babel/code-frame@7.8.3", "@babel/code-frame@^7.0.0", "@babel/code-frame@^7.5.5", "@babel/code-frame@^7.8.3":
version "7.8.3"
resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.8.3.tgz#33e25903d7481181534e12ec0a25f16b6fcf419e"
@@ -967,6 +974,11 @@
resolved "https://registry.yarnpkg.com/@ctrl/tinycolor/-/tinycolor-3.1.6.tgz#2d0ea7d433a34b1682e2e312e8a04812210fcc60"
integrity sha512-9RUTT3omv+5mSYFVsX143R7cTDQmT1FibCzoUVmO294mRIT0Sc8dk5srN27BTH0JKzQDWKkNCKh6q/+EkNfpkA==
"@ctrl/tinycolor@^3.4.0":
version "3.4.0"
resolved "https://registry.yarnpkg.com/@ctrl/tinycolor/-/tinycolor-3.4.0.tgz#c3c5ae543c897caa9c2a68630bed355be5f9990f"
integrity sha512-JZButFdZ1+/xAfpguQHoabIXkcqRRKpMrWKBkpEZZyxfY9C1DpADFB8PEqGSTeFr135SaTRfKqGKx5xSCLI7ZQ==
"@emotion/cache@^10.0.27", "@emotion/cache@^10.0.9":
version "10.0.29"
resolved "https://registry.yarnpkg.com/@emotion/cache/-/cache-10.0.29.tgz#87e7e64f412c060102d589fe7c6dc042e6f9d1e0"