mirror of
https://github.com/freqtrade/frequi.git
synced 2024-11-11 02:33:51 +00:00
Extract theme selection to shared js file
introduce simple theme mode (only dark / light selections)
This commit is contained in:
parent
ea5e135a69
commit
e352dff2e3
|
@ -8,13 +8,13 @@
|
|||
right
|
||||
lazy
|
||||
>
|
||||
<b-dropdown-item v-if="themes.length === 0">
|
||||
<b-dropdown-item v-if="themeList.length === 0">
|
||||
<b-spinner small></b-spinner> Loading Themes...
|
||||
</b-dropdown-item>
|
||||
|
||||
<!-- TODO Add v-b-tooltip.hover.right=="{ variant: 'className' }" for tooltip class rendered from bootswatch-->
|
||||
<b-dropdown-item-button
|
||||
v-for="(theme, key) in themes"
|
||||
v-for="(theme, key) in themeList"
|
||||
:key="key"
|
||||
v-b-tooltip.hover.right
|
||||
:active="activeTheme === theme.name"
|
||||
|
@ -34,6 +34,8 @@
|
|||
import Vue from 'vue';
|
||||
import axios from 'axios';
|
||||
import ThemeLightDark from 'vue-material-design-icons/Brightness6.vue';
|
||||
import { themeList } from '@/shared/themes';
|
||||
import { mapActions } from 'vuex';
|
||||
|
||||
export default Vue.extend({
|
||||
name: 'BootswatchThemeSelect',
|
||||
|
@ -47,123 +49,7 @@ export default Vue.extend({
|
|||
data() {
|
||||
return {
|
||||
activeTheme: '',
|
||||
themes: [
|
||||
{
|
||||
name: 'Bootstrap',
|
||||
description: 'Plain bootstrap default theme',
|
||||
dark: false,
|
||||
},
|
||||
{
|
||||
name: 'Bootstrap_dark',
|
||||
description: 'Plain dark bootstrap default theme',
|
||||
dark: true,
|
||||
},
|
||||
{
|
||||
name: 'Cerulean',
|
||||
description: 'A calm blue sky',
|
||||
dark: false,
|
||||
},
|
||||
{
|
||||
name: 'Cosmo',
|
||||
description: 'An ode to Metro',
|
||||
dark: false,
|
||||
},
|
||||
{
|
||||
name: 'Cyborg',
|
||||
description: 'Jet black and electric blue',
|
||||
dark: true,
|
||||
},
|
||||
{
|
||||
name: 'Darkly',
|
||||
description: 'Flatly in night mode',
|
||||
dark: true,
|
||||
},
|
||||
{
|
||||
name: 'Flatly',
|
||||
description: 'Flat and modern',
|
||||
dark: false,
|
||||
},
|
||||
{
|
||||
name: 'Journal',
|
||||
description: 'Crisp like a new sheet of paper',
|
||||
dark: false,
|
||||
},
|
||||
{
|
||||
name: 'Litera',
|
||||
description: 'The medium is the message',
|
||||
dark: false,
|
||||
},
|
||||
{
|
||||
name: 'Lumen',
|
||||
description: 'Light and shadow',
|
||||
dark: false,
|
||||
},
|
||||
{
|
||||
name: 'Lux',
|
||||
description: 'A touch of class',
|
||||
dark: false,
|
||||
},
|
||||
{
|
||||
name: 'Materia',
|
||||
description: 'Material is the metaphor',
|
||||
dark: false,
|
||||
},
|
||||
{
|
||||
name: 'Minty',
|
||||
description: 'A fresh feel',
|
||||
dark: false,
|
||||
},
|
||||
{
|
||||
name: 'Pulse',
|
||||
description: 'A trace of purple',
|
||||
dark: false,
|
||||
},
|
||||
{
|
||||
name: 'Sandstone',
|
||||
description: 'A touch of warmth',
|
||||
dark: false,
|
||||
},
|
||||
{
|
||||
name: 'Simplex',
|
||||
description: 'Mini and minimalist',
|
||||
dark: false,
|
||||
},
|
||||
{
|
||||
name: 'Sketchy',
|
||||
description: 'A hand-drawn look for mockups and mirth',
|
||||
dark: false,
|
||||
},
|
||||
{
|
||||
name: 'Slate',
|
||||
description: 'Shades of gunmetal gray',
|
||||
dark: true,
|
||||
},
|
||||
{
|
||||
name: 'Solar',
|
||||
description: 'A spin on Solarized',
|
||||
dark: true,
|
||||
},
|
||||
{
|
||||
name: 'Spacelab',
|
||||
description: 'Silvery and sleek',
|
||||
dark: false,
|
||||
},
|
||||
{
|
||||
name: 'Superhero',
|
||||
description: 'The brave and the blue',
|
||||
dark: true,
|
||||
},
|
||||
{
|
||||
name: 'United',
|
||||
description: 'Ubuntu orange and unique font',
|
||||
dark: false,
|
||||
},
|
||||
{
|
||||
name: 'Yeti',
|
||||
description: 'A friendly foundation',
|
||||
dark: false,
|
||||
},
|
||||
],
|
||||
themeList,
|
||||
};
|
||||
},
|
||||
mounted() {
|
||||
|
@ -171,6 +57,7 @@ export default Vue.extend({
|
|||
if (window.localStorage.theme) this.setTheme(window.localStorage.theme);
|
||||
},
|
||||
methods: {
|
||||
...mapActions(['setCurrentTheme']),
|
||||
handleClick(e) {
|
||||
this.setTheme(e.target.name.trim());
|
||||
},
|
||||
|
@ -193,10 +80,13 @@ export default Vue.extend({
|
|||
bw.forEach((style, index) => {
|
||||
(bw[index] as any).disabled = true;
|
||||
});
|
||||
document.documentElement.classList.add('ft-theme-transition');
|
||||
window.setTimeout(() => {
|
||||
document.documentElement.classList.remove('ft-theme-transition');
|
||||
}, 1000);
|
||||
if (this.simple) {
|
||||
// Only transition if simple mode is active
|
||||
document.documentElement.classList.add('ft-theme-transition');
|
||||
window.setTimeout(() => {
|
||||
document.documentElement.classList.remove('ft-theme-transition');
|
||||
}, 1000);
|
||||
}
|
||||
} else {
|
||||
// Dynamic import for a different theme, to avoid loading ALL themes.
|
||||
import(`bootswatch/dist/${themeName.toLowerCase()}/bootstrap.min.css`).then((mod) => {
|
||||
|
@ -219,7 +109,7 @@ export default Vue.extend({
|
|||
});
|
||||
}
|
||||
// Save the theme as localstorage
|
||||
window.localStorage.theme = themeName;
|
||||
this.setCurrentTheme(themeName);
|
||||
this.activeTheme = themeName;
|
||||
},
|
||||
fetchApi() {
|
||||
|
@ -227,17 +117,17 @@ export default Vue.extend({
|
|||
// Not used, but useful for updating the static array of themes if bootswatch dependency is outdated.
|
||||
axios
|
||||
.get('https://bootswatch.com/api/4.json')
|
||||
.then((res) => {
|
||||
const { themes } = res.data;
|
||||
this.themes = themes;
|
||||
// .then((res) => {
|
||||
|
||||
// Use this code in the browser console and copy and paste the filteredThemes into this.themes
|
||||
// console.log(themes);
|
||||
// const filteredThemes = [];
|
||||
// themes.forEach((item) =>
|
||||
// filteredThemes.push({ name: item.name, description: item.description }),
|
||||
// );
|
||||
})
|
||||
// const { themes } = res.data;
|
||||
// this.themes = themes;
|
||||
// Use this code in the browser console and copy and paste the filteredThemes into this.themes
|
||||
// console.log(themes);
|
||||
// const filteredThemes = [];
|
||||
// themes.forEach((item) =>
|
||||
// filteredThemes.push({ name: item.name, description: item.description }),
|
||||
// );
|
||||
// })
|
||||
.catch((error) => {
|
||||
console.error(error);
|
||||
});
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
<template>
|
||||
<v-chart v-if="trades.length > 0" :options="chartOptions" autoresize />
|
||||
<v-chart v-if="trades.length > 0" :options="chartOptions" autoresize :theme="getChartTheme" />
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { Component, Vue, Prop } from 'vue-property-decorator';
|
||||
import { Getter } from 'vuex-class';
|
||||
|
||||
import ECharts from 'vue-echarts';
|
||||
import { EChartOption } from 'echarts';
|
||||
|
@ -35,6 +36,8 @@ export default class CumProfitChart extends Vue {
|
|||
|
||||
@Prop({ default: 'close_profit_abs' }) profitColumn!: string;
|
||||
|
||||
@Getter getChartTheme!: string;
|
||||
|
||||
get cumulativeData() {
|
||||
const res: CumProfitData[] = [];
|
||||
const closedTrades = this.trades; // .filter((t) => t.close_timestamp);
|
||||
|
@ -122,10 +125,10 @@ export default class CumProfitChart extends Vue {
|
|||
name: CHART_PROFIT,
|
||||
animation: false,
|
||||
lineStyle: {
|
||||
color: 'black',
|
||||
color: this.getChartTheme === 'dark' ? '#c2c2c2' : 'black',
|
||||
},
|
||||
itemStyle: {
|
||||
color: 'black',
|
||||
color: this.getChartTheme === 'dark' ? '#c2c2c2' : 'black',
|
||||
},
|
||||
// symbol: 'none',
|
||||
},
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
<template>
|
||||
<v-chart v-if="dailyStats.data" :options="dailyChartOptions" autoresize />
|
||||
<v-chart v-if="dailyStats.data" :options="dailyChartOptions" :theme="getChartTheme" autoresize />
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { Component, Vue, Prop } from 'vue-property-decorator';
|
||||
|
||||
import { Getter } from 'vuex-class';
|
||||
import ECharts from 'vue-echarts';
|
||||
import { EChartOption } from 'echarts';
|
||||
|
||||
|
@ -30,6 +30,8 @@ export default class DailyChart extends Vue {
|
|||
|
||||
@Prop({ default: true, type: Boolean }) showTitle!: boolean;
|
||||
|
||||
@Getter getChartTheme!: string;
|
||||
|
||||
get absoluteMin() {
|
||||
return Number(
|
||||
this.dailyStats.data.reduce(
|
||||
|
|
|
@ -1,9 +1,15 @@
|
|||
<template>
|
||||
<v-chart v-if="trades.length > 0" :options="hourlyChartOptions" autoresize />
|
||||
<v-chart
|
||||
v-if="trades.length > 0"
|
||||
:options="hourlyChartOptions"
|
||||
autoresize
|
||||
:theme="getChartTheme"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { Component, Vue, Prop } from 'vue-property-decorator';
|
||||
import { Getter } from 'vuex-class';
|
||||
|
||||
import ECharts from 'vue-echarts';
|
||||
|
||||
|
@ -33,6 +39,8 @@ export default class HourlyChart extends Vue {
|
|||
|
||||
@Prop({ default: true, type: Boolean }) showTitle!: boolean;
|
||||
|
||||
@Getter getChartTheme!: string;
|
||||
|
||||
get hourlyData() {
|
||||
const res = new Array(24);
|
||||
for (let i = 0; i < 24; i += 1) {
|
||||
|
|
|
@ -123,6 +123,7 @@ export default class NavBar extends Vue {
|
|||
.nav-link:active {
|
||||
color: white;
|
||||
}
|
||||
|
||||
.verticalCenter {
|
||||
align-items: center;
|
||||
display: inline-flex;
|
||||
|
|
181
src/shared/themes.ts
Normal file
181
src/shared/themes.ts
Normal file
|
@ -0,0 +1,181 @@
|
|||
export interface ThemeType {
|
||||
name: string;
|
||||
description: string;
|
||||
dark: boolean;
|
||||
bootswatch: boolean;
|
||||
}
|
||||
export const themeList: ThemeType[] = [
|
||||
{
|
||||
name: 'Bootstrap',
|
||||
description: 'Plain bootstrap default theme',
|
||||
dark: false,
|
||||
bootswatch: false,
|
||||
},
|
||||
{
|
||||
name: 'Bootstrap_dark',
|
||||
description: 'Plain dark bootstrap default theme',
|
||||
dark: true,
|
||||
bootswatch: false,
|
||||
},
|
||||
{
|
||||
name: 'Cerulean',
|
||||
description: 'A calm blue sky',
|
||||
dark: false,
|
||||
bootswatch: true,
|
||||
},
|
||||
{
|
||||
name: 'Cosmo',
|
||||
description: 'An ode to Metro',
|
||||
dark: false,
|
||||
bootswatch: true,
|
||||
},
|
||||
|
||||
{
|
||||
name: 'Cyborg',
|
||||
description: 'Jet black and electric blue',
|
||||
dark: true,
|
||||
bootswatch: true,
|
||||
},
|
||||
|
||||
{
|
||||
name: 'Darkly',
|
||||
description: 'Flatly in night mode',
|
||||
dark: true,
|
||||
bootswatch: true,
|
||||
},
|
||||
|
||||
{
|
||||
name: 'Flatly',
|
||||
description: 'Flat and modern',
|
||||
dark: false,
|
||||
bootswatch: true,
|
||||
},
|
||||
|
||||
{
|
||||
name: 'Journal',
|
||||
description: 'Crisp like a new sheet of paper',
|
||||
dark: false,
|
||||
bootswatch: true,
|
||||
},
|
||||
|
||||
{
|
||||
name: 'Litera',
|
||||
description: 'The medium is the message',
|
||||
dark: false,
|
||||
bootswatch: true,
|
||||
},
|
||||
|
||||
{
|
||||
name: 'Lumen',
|
||||
description: 'Light and shadow',
|
||||
dark: false,
|
||||
bootswatch: true,
|
||||
},
|
||||
|
||||
{
|
||||
name: 'Lux',
|
||||
description: 'A touch of class',
|
||||
dark: false,
|
||||
bootswatch: true,
|
||||
},
|
||||
|
||||
{
|
||||
name: 'Materia',
|
||||
description: 'Material is the metaphor',
|
||||
dark: false,
|
||||
bootswatch: true,
|
||||
},
|
||||
|
||||
{
|
||||
name: 'Minty',
|
||||
description: 'A fresh feel',
|
||||
dark: false,
|
||||
bootswatch: true,
|
||||
},
|
||||
|
||||
{
|
||||
name: 'Pulse',
|
||||
description: 'A trace of purple',
|
||||
dark: false,
|
||||
bootswatch: true,
|
||||
},
|
||||
|
||||
{
|
||||
name: 'Sandstone',
|
||||
description: 'A touch of warmth',
|
||||
dark: false,
|
||||
bootswatch: true,
|
||||
},
|
||||
|
||||
{
|
||||
name: 'Simplex',
|
||||
description: 'Mini and minimalist',
|
||||
dark: false,
|
||||
bootswatch: true,
|
||||
},
|
||||
|
||||
{
|
||||
name: 'Sketchy',
|
||||
description: 'A hand-drawn look for mockups and mirth',
|
||||
dark: false,
|
||||
bootswatch: true,
|
||||
},
|
||||
|
||||
{
|
||||
name: 'Slate',
|
||||
description: 'Shades of gunmetal gray',
|
||||
dark: true,
|
||||
bootswatch: true,
|
||||
},
|
||||
|
||||
{
|
||||
name: 'Solar',
|
||||
description: 'A spin on Solarized',
|
||||
dark: true,
|
||||
bootswatch: true,
|
||||
},
|
||||
|
||||
{
|
||||
name: 'Spacelab',
|
||||
description: 'Silvery and sleek',
|
||||
dark: false,
|
||||
bootswatch: true,
|
||||
},
|
||||
|
||||
{
|
||||
name: 'Superhero',
|
||||
description: 'The brave and the blue',
|
||||
dark: true,
|
||||
bootswatch: true,
|
||||
},
|
||||
|
||||
{
|
||||
name: 'United',
|
||||
description: 'Ubuntu orange and unique font',
|
||||
dark: false,
|
||||
bootswatch: true,
|
||||
},
|
||||
|
||||
{
|
||||
name: 'Yeti',
|
||||
description: 'A friendly foundation',
|
||||
dark: false,
|
||||
bootswatch: true,
|
||||
},
|
||||
];
|
||||
|
||||
export function storeCurrentTheme(themeName: string) {
|
||||
window.localStorage.theme = themeName;
|
||||
}
|
||||
|
||||
export function getTheme(theme: string): ThemeType | undefined {
|
||||
if (theme !== undefined) {
|
||||
return themeList.find((item) => item.name.toLowerCase() === theme.toLowerCase());
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
export function getCurrentTheme(): string {
|
||||
const { theme } = window.localStorage;
|
||||
return theme;
|
||||
}
|
|
@ -2,6 +2,7 @@ import Vue from 'vue';
|
|||
import Vuex from 'vuex';
|
||||
|
||||
import userService from '@/shared/userService';
|
||||
import { getCurrentTheme, getTheme, storeCurrentTheme } from '@/shared/themes';
|
||||
import ftbotModule, { BotStoreGetters } from './modules/ftbot';
|
||||
import alertsModule from './modules/alerts';
|
||||
import layoutModule from './modules/layout';
|
||||
|
@ -9,6 +10,7 @@ import layoutModule from './modules/layout';
|
|||
const AUTO_REFRESH = 'ft_auto_refresh';
|
||||
|
||||
Vue.use(Vuex);
|
||||
const initCurrentTheme = getCurrentTheme();
|
||||
|
||||
export default new Vuex.Store({
|
||||
state: {
|
||||
|
@ -16,6 +18,19 @@ export default new Vuex.Store({
|
|||
loggedIn: userService.loggedIn(),
|
||||
autoRefresh: JSON.parse(localStorage.getItem(AUTO_REFRESH) || '{}'),
|
||||
isBotOnline: false,
|
||||
currentTheme: initCurrentTheme,
|
||||
},
|
||||
getters: {
|
||||
isDarkTheme(state) {
|
||||
const theme = getTheme(state.currentTheme);
|
||||
if (theme) {
|
||||
return theme.dark;
|
||||
}
|
||||
return false;
|
||||
},
|
||||
getChartTheme(state, getters) {
|
||||
return getters.isDarkTheme ? 'dark' : 'light';
|
||||
},
|
||||
},
|
||||
modules: {
|
||||
ftbot: ftbotModule,
|
||||
|
@ -37,8 +52,15 @@ export default new Vuex.Store({
|
|||
setIsBotOnline(state, isBotOnline: boolean) {
|
||||
state.isBotOnline = isBotOnline;
|
||||
},
|
||||
mutateCurrentTheme(state, newTheme: string) {
|
||||
storeCurrentTheme(newTheme);
|
||||
state.currentTheme = newTheme;
|
||||
},
|
||||
},
|
||||
actions: {
|
||||
setCurrentTheme({ commit }, newTheme: string) {
|
||||
commit('mutateCurrentTheme', newTheme);
|
||||
},
|
||||
setAutoRefresh({ commit }, newRefreshValue) {
|
||||
commit('setAutoRefresh', newRefreshValue);
|
||||
localStorage.setItem(AUTO_REFRESH, JSON.stringify(newRefreshValue));
|
||||
|
|
Loading…
Reference in New Issue
Block a user