diff --git a/packages/web-svelte/package.json b/packages/web-svelte/package.json index 35d4227b3..28fd6b456 100644 --- a/packages/web-svelte/package.json +++ b/packages/web-svelte/package.json @@ -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" diff --git a/packages/web-svelte/public/index.html b/packages/web-svelte/public/index.html index b346846a3..9827a3621 100644 --- a/packages/web-svelte/public/index.html +++ b/packages/web-svelte/public/index.html @@ -8,11 +8,8 @@ - - - diff --git a/packages/web-svelte/public/theme-dark.css b/packages/web-svelte/public/theme-dark.css deleted file mode 100644 index 19d025c18..000000000 --- a/packages/web-svelte/public/theme-dark.css +++ /dev/null @@ -1,5 +0,0 @@ -.theme-dark .statusbar { background-color: blue } -.theme-dark .iconbar { - background-color: #222; - color: #b3b3b3; -} diff --git a/packages/web-svelte/public/theme-light.css b/packages/web-svelte/public/theme-light.css deleted file mode 100644 index 20d4ce51b..000000000 --- a/packages/web-svelte/public/theme-light.css +++ /dev/null @@ -1,5 +0,0 @@ -.theme-light .statusbar { background-color: blue } -.theme-dark .iconbar { - background-color: #222; - color: #b3b3b3; -} diff --git a/packages/web-svelte/src/Screen.svelte b/packages/web-svelte/src/Screen.svelte index e35093446..8f91b1702 100644 --- a/packages/web-svelte/src/Screen.svelte +++ b/packages/web-svelte/src/Screen.svelte @@ -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; diff --git a/packages/web-svelte/src/main.ts b/packages/web-svelte/src/main.ts index d6cacbbb0..325b5e836 100644 --- a/packages/web-svelte/src/main.ts +++ b/packages/web-svelte/src/main.ts @@ -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; \ No newline at end of file +export default app; diff --git a/packages/web-svelte/src/theme/changeTheme.js b/packages/web-svelte/src/theme/changeTheme.js new file mode 100644 index 000000000..bf36135ad --- /dev/null +++ b/packages/web-svelte/src/theme/changeTheme.js @@ -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); + } + } +} diff --git a/packages/web-svelte/src/theme/colorUtil.js b/packages/web-svelte/src/theme/colorUtil.js new file mode 100644 index 000000000..cfddd70b8 --- /dev/null +++ b/packages/web-svelte/src/theme/colorUtil.js @@ -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); +} diff --git a/packages/web-svelte/src/theme/dark.js b/packages/web-svelte/src/theme/dark.js new file mode 100644 index 000000000..a39ba49e5 --- /dev/null +++ b/packages/web-svelte/src/theme/dark.js @@ -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); diff --git a/packages/web-svelte/src/theme/fillTheme.js b/packages/web-svelte/src/theme/fillTheme.js new file mode 100644 index 000000000..728597148 --- /dev/null +++ b/packages/web-svelte/src/theme/fillTheme.js @@ -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; +} diff --git a/packages/web-svelte/src/theme/light.js b/packages/web-svelte/src/theme/light.js new file mode 100644 index 000000000..6324946ff --- /dev/null +++ b/packages/web-svelte/src/theme/light.js @@ -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); diff --git a/packages/web-svelte/src/widgets/WidgetIconPanel.svelte b/packages/web-svelte/src/widgets/WidgetIconPanel.svelte index e81fca3f5..9ce82ff8c 100644 --- a/packages/web-svelte/src/widgets/WidgetIconPanel.svelte +++ b/packages/web-svelte/src/widgets/WidgetIconPanel.svelte @@ -1,6 +1,8 @@ {#each widgets as item} -
+
(selectedWidget = item.name)}>
{/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); } diff --git a/yarn.lock b/yarn.lock index ad5247fef..a7671eedd 100644 --- a/yarn.lock +++ b/yarn.lock @@ -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"