mirror of https://github.com/Lissy93/dashy.git
🚧 WIP - working on the custom theme feature
This commit is contained in:
parent
c7d91bed94
commit
65aa971099
File diff suppressed because one or more lines are too long
Before Width: | Height: | Size: 741 KiB After Width: | Height: | Size: 818 KiB |
|
@ -41,6 +41,13 @@
|
|||
|
||||
---
|
||||
|
||||
### Ground Control
|
||||
> By [@dtctek](https://github.com/dtctek)
|
||||
|
||||
![screenshot-ground-control](https://raw.githubusercontent.com/Lissy93/dashy/master/docs/showcase/7-ground-control-dtctek.png)
|
||||
|
||||
---
|
||||
|
||||
## Submitting your Dashboard
|
||||
|
||||
#### How to Submit
|
||||
|
@ -69,4 +76,4 @@ If you're submitting a pull request, please use a format similar to this:
|
|||
|
||||
---
|
||||
|
||||
```
|
||||
```
|
||||
|
|
Binary file not shown.
After Width: | Height: | Size: 191 KiB |
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "Dashy",
|
||||
"version": "1.3.7",
|
||||
"version": "1.3.8",
|
||||
"license": "MIT",
|
||||
"main": "server",
|
||||
"scripts": {
|
||||
|
@ -32,7 +32,7 @@
|
|||
"vue": "^2.6.10",
|
||||
"vue-cli-plugin-yaml": "^1.0.2",
|
||||
"vue-js-modal": "^2.0.0-rc.6",
|
||||
"vue-material-tabs": "^0.0.7",
|
||||
"vue-material-tabs": "^0.1.2",
|
||||
"vue-prism-editor": "^1.2.2",
|
||||
"vue-router": "^3.0.3",
|
||||
"vue-select": "^3.11.2",
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
<svg aria-hidden="true" focusable="false" data-prefix="far" data-icon="palette" class="svg-inline--fa fa-palette fa-w-16" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path fill="currentColor" d="M128 224c-17.7 0-32 14.3-32 32s14.3 32 32 32 32-14.3 32-32-14.4-32-32-32zM418.6 58.1C359.2 9.3 281.3-10 204.6 5 104.9 24.4 24.7 104.2 5.1 203.7c-16.7 84.2 8.1 168.3 67.8 230.6 47.3 49.4 109.7 77.8 167.9 77.8 8.8 0 17.5-.6 26.1-2 24.2-3.7 44.6-18.7 56.1-41.1 12.3-24 12.3-52.7.2-76.6-6.1-12-5.5-26.2 1.8-38 7-11.8 18.7-18.4 32-18.4h72.2c46.4 0 82.8-35.7 82.8-81.3-.2-76.4-34.3-148.1-93.4-196.6zM429.2 288H357c-29.9 0-57.2 15.4-73 41.3-16 26.1-17.3 57.8-3.6 84.9 5.1 10.1 5.1 22.7-.2 32.9-2.6 5-8.7 13.7-20.6 15.6-49.3 7.7-108.9-16.6-152-61.6-48.8-50.9-69-119.4-55.4-188 15.9-80.6 80.8-145.3 161.6-161 62.6-12.3 126.1 3.5 174.3 43.1 48.1 39.5 75.7 97.6 75.9 159.6 0 18.6-15.3 33.2-34.8 33.2zM160 128c-17.7 0-32 14.3-32 32s14.3 32 32 32 32-14.3 32-32-14.4-32-32-32zm96-32.1c-17.7 0-32 14.3-32 32s14.3 32 32 32 32-14.3 32-32c0-17.6-14.3-32-32-32zm96 32.1c-17.7 0-32 14.3-32 32s14.3 32 32 32 32-14.3 32-32-14.3-32-32-32z"></path></svg>
|
After Width: | Height: | Size: 1.1 KiB |
|
@ -1,5 +1,6 @@
|
|||
<template>
|
||||
<div class="theme-selector-section" v-if="themes" >
|
||||
<div class="theme-selector-section" v-if="themes" v-click-outside="closeThemeConfigurator">
|
||||
<div>
|
||||
<span class="theme-label">Theme</span>
|
||||
<v-select
|
||||
:options="themeNames"
|
||||
|
@ -7,13 +8,26 @@
|
|||
class="theme-dropdown"
|
||||
:tabindex="-2"
|
||||
/>
|
||||
</div>
|
||||
<IconPalette
|
||||
class="color-button"
|
||||
v-if="selectedTheme === 'custom'"
|
||||
@click="openThemeConfigurator"
|
||||
/>
|
||||
<div
|
||||
v-if="themeConfiguratorOpen"
|
||||
class="theme-configurator-wrapper"
|
||||
>
|
||||
<p>Custom Theme Configurator</p>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
||||
import ThemeHelper from '@/utils/ThemeHelper';
|
||||
import { LoadExternalTheme, ApplyLocalTheme, ApplyCustomTheme } from '@/utils/ThemeHelper';
|
||||
import Defaults, { localStorageKeys } from '@/utils/defaults';
|
||||
import IconPalette from '@/assets/interface-icons/config-color-palette.svg';
|
||||
|
||||
export default {
|
||||
name: 'ThemeSelector',
|
||||
|
@ -22,21 +36,28 @@ export default {
|
|||
confTheme: String,
|
||||
userThemes: Array,
|
||||
},
|
||||
components: {
|
||||
IconPalette,
|
||||
},
|
||||
watch: {
|
||||
selectedTheme(newTheme) { this.updateTheme(newTheme); },
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
selectedTheme: this.getInitialTheme(),
|
||||
themeHelper: new ThemeHelper(),
|
||||
loading: true,
|
||||
builtInThemes: this.userThemes.concat(Defaults.builtInThemes),
|
||||
builtInThemes: [...Defaults.builtInThemes, ...this.userThemes],
|
||||
themeHelper: new LoadExternalTheme(),
|
||||
// modalName: modalNames.THEME_MAKER,
|
||||
themeConfiguratorOpen: false,
|
||||
ApplyLocalTheme,
|
||||
ApplyCustomTheme,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
themeNames: function themeNames() {
|
||||
const externalThemeNames = Object.keys(this.themes);
|
||||
return externalThemeNames.concat(this.builtInThemes);
|
||||
const specialThemes = ['custom'];
|
||||
return [...specialThemes, ...externalThemeNames, ...this.builtInThemes];
|
||||
},
|
||||
},
|
||||
created() {
|
||||
|
@ -54,12 +75,6 @@ export default {
|
|||
}
|
||||
},
|
||||
methods: {
|
||||
/* Sets the theme, by updating data-theme attribute on the html tag */
|
||||
setLocalTheme(newTheme) {
|
||||
const htmlTag = document.getElementsByTagName('html')[0];
|
||||
if (htmlTag.hasAttribute('data-theme')) htmlTag.removeAttribute('data-theme');
|
||||
htmlTag.setAttribute('data-theme', newTheme);
|
||||
},
|
||||
/* Get default theme */
|
||||
getInitialTheme() {
|
||||
return localStorage[localStorageKeys.THEME] || this.confTheme || Defaults.theme;
|
||||
|
@ -67,14 +82,22 @@ export default {
|
|||
isThemeLocal(themeToCheck) {
|
||||
return this.builtInThemes.includes(themeToCheck);
|
||||
},
|
||||
openThemeConfigurator() {
|
||||
this.themeConfiguratorOpen = true;
|
||||
},
|
||||
closeThemeConfigurator() {
|
||||
this.themeConfiguratorOpen = false;
|
||||
},
|
||||
/* Updates theme. Checks if the new theme is local or external,
|
||||
and calls appropirate updating function. Updates local storage */
|
||||
updateTheme(newTheme) {
|
||||
if (newTheme === 'Deafault') {
|
||||
if (newTheme === 'custom') {
|
||||
this.ApplyCustomTheme();
|
||||
} else if (newTheme === 'Deafault') {
|
||||
this.resetToDefault();
|
||||
this.themeHelper.theme = 'Deafault';
|
||||
} else if (this.isThemeLocal(newTheme)) {
|
||||
this.setLocalTheme(newTheme);
|
||||
this.ApplyLocalTheme(newTheme);
|
||||
} else {
|
||||
this.themeHelper.theme = newTheme;
|
||||
}
|
||||
|
@ -95,7 +118,7 @@ export default {
|
|||
div.vs__dropdown-toggle {
|
||||
border-color: var(--settings-text-color);
|
||||
border-radius: var(--curve-factor);
|
||||
min-width: 10rem;
|
||||
width: 8rem;
|
||||
height: 1.8rem;
|
||||
font-size: 0.85rem;
|
||||
cursor: pointer;
|
||||
|
@ -111,6 +134,8 @@ export default {
|
|||
width: auto;
|
||||
background: var(--background);
|
||||
z-index: 5;
|
||||
max-width: 13rem;
|
||||
overflow-x: hidden;
|
||||
}
|
||||
li.vs__dropdown-option--highlight {
|
||||
background: var(--settings-text-color);
|
||||
|
@ -123,7 +148,7 @@ export default {
|
|||
|
||||
.theme-selector-section {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex-direction: row;
|
||||
align-items: flex-start;
|
||||
height: 100%;
|
||||
span.theme-label {
|
||||
|
@ -133,4 +158,37 @@ export default {
|
|||
}
|
||||
}
|
||||
|
||||
svg.color-button {
|
||||
path {
|
||||
fill: var(--settings-text-color);
|
||||
}
|
||||
width: 1rem;
|
||||
height: 1rem;
|
||||
padding: 0.2rem;
|
||||
margin: 0.5rem;
|
||||
align-self: flex-end;
|
||||
text-align: center;
|
||||
background: var(--background);
|
||||
border: 1px solid var(--settings-text-color);;
|
||||
border-radius: var(--curve-factor);
|
||||
cursor: pointer;
|
||||
&:hover, &.selected {
|
||||
background: var(--settings-text-color);
|
||||
path { fill: var(--background); }
|
||||
}
|
||||
}
|
||||
|
||||
div.theme-configurator-wrapper {
|
||||
position: absolute;
|
||||
right: 2rem;
|
||||
top: 3rem;
|
||||
width: 30%;
|
||||
height: 50%;
|
||||
padding: 0.5rem;
|
||||
background: var(--config-settings-background);
|
||||
color: var(--config-settings-color);
|
||||
border-radius: var(--curve-factor);
|
||||
box-shadow: 0 8px 10px -2px rgba(0, 0, 0, 0.6), 1px 1px 6px var(--primary);
|
||||
}
|
||||
|
||||
</style>
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import ConfigAccumulator from '@/utils/ConfigAccumalator';
|
||||
|
||||
import { visibleComponents } from '@/utils/defaults';
|
||||
import { visibleComponents, localStorageKeys, theme as defaultTheme } from '@/utils/defaults';
|
||||
|
||||
/**
|
||||
* Initiates the Accumulator class and generates a complete config object
|
||||
|
@ -40,3 +40,14 @@ export const componentVisibility = (appConfig) => {
|
|||
? !usersChoice.hideSplashScreen : visibleComponents.splashScreen,
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Gets the users saved theme, first looks for local storage theme,
|
||||
* then looks at user's appConfig, and finally checks the defaults
|
||||
* @returns {string} Name of theme to apply
|
||||
*/
|
||||
export const getTheme = () => {
|
||||
const localTheme = localStorage[localStorageKeys.THEME];
|
||||
const appConfigTheme = config.appConfig.theme;
|
||||
return localTheme || appConfigTheme || defaultTheme;
|
||||
};
|
||||
|
|
|
@ -1,8 +1,25 @@
|
|||
import ErrorHandler from '@/utils/ErrorHandler';
|
||||
import { getTheme } from '@/utils/ConfigHelpers';
|
||||
|
||||
/* Returns users current theme */
|
||||
export const GetTheme = () => getTheme();
|
||||
|
||||
/* Sets the theme, by updating data-theme attribute on the html tag */
|
||||
export const ApplyLocalTheme = (newTheme) => {
|
||||
const htmlTag = document.getElementsByTagName('html')[0];
|
||||
if (htmlTag.hasAttribute('data-theme')) htmlTag.removeAttribute('data-theme');
|
||||
htmlTag.setAttribute('data-theme', newTheme);
|
||||
};
|
||||
|
||||
/* Sets specific CSS variables, for the users custom theme */
|
||||
export const ApplyCustomTheme = () => { };
|
||||
|
||||
/**
|
||||
* A function for pre-loading, and easy switching of external stylesheets
|
||||
* External CSS is preloaded to avoid FOUC
|
||||
*/
|
||||
const ThemeHelper = function th() {
|
||||
export const LoadExternalTheme = function th() {
|
||||
/* Preload selected external theme */
|
||||
const preloadTheme = (href) => {
|
||||
const link = document.createElement('link');
|
||||
link.rel = 'stylesheet';
|
||||
|
@ -18,10 +35,21 @@ const ThemeHelper = function th() {
|
|||
});
|
||||
};
|
||||
|
||||
/* Check theme is selected, and it exists */
|
||||
const checkTheme = (themes, name) => {
|
||||
if ((!name) || (name !== 'custom' && !themes[name])) {
|
||||
ErrorHandler(`Theme: '${name || '[not selected]'}' does not exist.`);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
/* Disable all but selected theme */
|
||||
const selectTheme = (themes, name) => {
|
||||
const t = themes; // To avoid ESLint complaining about mutating a param
|
||||
if (name && !themes[name]) throw new Error(`Theme: '${name}' does not exist.`);
|
||||
Object.keys(themes).forEach(n => { t[n].disabled = (n !== name); });
|
||||
if (checkTheme(themes, name)) {
|
||||
const t = themes; // To avoid ESLint complaining about mutating a param
|
||||
Object.keys(themes).forEach(n => { t[n].disabled = (n !== name); });
|
||||
}
|
||||
};
|
||||
|
||||
const themes = {};
|
||||
|
@ -32,5 +60,3 @@ const ThemeHelper = function th() {
|
|||
get theme() { return Object.keys(themes).find(n => !themes[n].disabled); },
|
||||
};
|
||||
};
|
||||
|
||||
export default ThemeHelper;
|
||||
|
|
|
@ -70,6 +70,7 @@ module.exports = {
|
|||
CONF_EDITOR: 'CONF_EDITOR',
|
||||
CLOUD_BACKUP: 'CLOUD_BACKUP',
|
||||
REBUILD_APP: 'REBUILD_APP',
|
||||
THEME_MAKER: 'THEME_MAKER',
|
||||
ABOUT_APP: 'ABOUT_APP',
|
||||
},
|
||||
topLevelConfKeys: {
|
||||
|
|
|
@ -9,7 +9,8 @@
|
|||
|
||||
import SideBar from '@/components/Workspace/SideBar';
|
||||
import WebContent from '@/components/Workspace/WebContent';
|
||||
import Defaults, { localStorageKeys } from '@/utils/defaults';
|
||||
import Defaults from '@/utils/defaults';
|
||||
import { ApplyLocalTheme, GetTheme } from '@/utils/ThemeHelper';
|
||||
|
||||
export default {
|
||||
name: 'Workspace',
|
||||
|
@ -19,6 +20,8 @@ export default {
|
|||
},
|
||||
data: () => ({
|
||||
url: '', // this.$route.query.url || '',
|
||||
GetTheme,
|
||||
ApplyLocalTheme,
|
||||
}),
|
||||
components: {
|
||||
SideBar,
|
||||
|
@ -29,10 +32,7 @@ export default {
|
|||
this.url = url;
|
||||
},
|
||||
setTheme() {
|
||||
const theme = localStorage[localStorageKeys.THEME] || this.confTheme || Defaults.theme;
|
||||
const htmlTag = document.getElementsByTagName('html')[0];
|
||||
if (htmlTag.hasAttribute('data-theme')) htmlTag.removeAttribute('data-theme');
|
||||
htmlTag.setAttribute('data-theme', theme);
|
||||
this.ApplyLocalTheme(this.GetTheme());
|
||||
},
|
||||
initiateFontAwesome() {
|
||||
const fontAwesomeScript = document.createElement('script');
|
||||
|
|
|
@ -8899,10 +8899,10 @@ vue-loader@^15.9.2:
|
|||
vue-hot-reload-api "^2.3.0"
|
||||
vue-style-loader "^4.1.0"
|
||||
|
||||
vue-material-tabs@^0.0.7:
|
||||
version "0.0.7"
|
||||
resolved "https://registry.yarnpkg.com/vue-material-tabs/-/vue-material-tabs-0.0.7.tgz#5f3fa04ad35384af68582f7c89ad4cecac89207b"
|
||||
integrity sha512-02X5paTksYKrGvSRpMdkctRO9qhvJFD5VEGxd0xjOX4sYz6mZSAez0Z/+aYf7Z5ziY+eJ9dMQmxaLn9DVKQRJw==
|
||||
vue-material-tabs@^0.1.2:
|
||||
version "0.1.5"
|
||||
resolved "https://registry.yarnpkg.com/vue-material-tabs/-/vue-material-tabs-0.1.5.tgz#255fc0beb27c005eaae61c1534d782a94c30c525"
|
||||
integrity sha512-ZLFRCxaCS3TM8IwnxQA4S2CVj+tBaILb8fQZDg+Ix+9Zu+k16udrdhLU2GWvJEVPLspuCzKhCEXW4cIg59YkVw==
|
||||
|
||||
vue-prism-editor@^1.2.2:
|
||||
version "1.2.2"
|
||||
|
|
Loading…
Reference in New Issue