テーマに関して強化

This commit is contained in:
syuilo
2018-10-02 16:04:31 +09:00
parent f2b9863eea
commit 6b96bd0185
15 changed files with 569 additions and 513 deletions

View File

@@ -5,9 +5,6 @@
<script lang="ts">
import Vue from 'vue';
import { url, lang } from './config';
import applyTheme from './common/scripts/theme';
const darkTheme = require('../theme/dark');
const halloweenTheme = require('../theme/halloween');
export default Vue.extend({
computed: {

View File

@@ -24,7 +24,6 @@
const theme = localStorage.getItem('theme');
if (theme) {
Object.entries(JSON.parse(theme)).forEach(([k, v]) => {
if (k == 'meta') return;
document.documentElement.style.setProperty(`--${k}`, v.toString());
});
}

View File

@@ -3,14 +3,14 @@
<label>
<span>%i18n:@light-theme%</span>
<ui-select v-model="light" placeholder="%i18n:@light-theme%">
<option v-for="x in themes" :value="x.meta.id" :key="x.meta.id">{{ x.meta.name }}</option>
<option v-for="x in themes" :value="x.id" :key="x.id">{{ x.name }}</option>
</ui-select>
</label>
<label>
<span>%i18n:@dark-theme%</span>
<ui-select v-model="dark" placeholder="%i18n:@dark-theme%">
<option v-for="x in themes" :value="x.meta.id" :key="x.meta.id">{{ x.meta.name }}</option>
<option v-for="x in themes" :value="x.id" :key="x.id">{{ x.name }}</option>
</ui-select>
</label>
@@ -53,7 +53,7 @@
<details>
<summary>%i18n:@installed-themes%</summary>
<ui-select v-model="selectedInstalledTheme" placeholder="%i18n:@select-theme%">
<option v-for="x in installedThemes" :value="x.meta.id" :key="x.meta.id">{{ x.meta.name }}</option>
<option v-for="x in installedThemes" :value="x.id" :key="x.id">{{ x.name }}</option>
</ui-select>
<ui-textarea readonly :value="selectedInstalledThemeCode">
<span>%i18n:@theme-code%</span>
@@ -65,10 +65,25 @@
<script lang="ts">
import Vue from 'vue';
import { lightTheme, darkTheme, builtinThemes, applyTheme } from '../../../theme';
import { lightTheme, darkTheme, builtinThemes, applyTheme, Theme } from '../../../theme';
import { Chrome } from 'vue-color';
import * as uuid from 'uuid';
import * as tinycolor from 'tinycolor2';
import * as JSON5 from 'json5';
// 後方互換性のため
function convertOldThemedefinition(t) {
const t2 = {
id: t.meta.id,
name: t.meta.name,
author: t.meta.author,
base: t.meta.base,
vars: t.meta.vars,
props: t
};
delete t2.props.meta;
return t2;
}
export default Vue.extend({
components: {
@@ -81,18 +96,18 @@ export default Vue.extend({
selectedInstalledTheme: null,
myThemeBase: 'light',
myThemeName: '',
myThemePrimary: lightTheme.meta.vars.primary,
myThemeSecondary: lightTheme.meta.vars.secondary,
myThemeText: lightTheme.meta.vars.text
myThemePrimary: lightTheme.vars.primary,
myThemeSecondary: lightTheme.vars.secondary,
myThemeText: lightTheme.vars.text
};
},
computed: {
themes(): any {
themes(): Theme[] {
return this.$store.state.device.themes.concat(builtinThemes);
},
installedThemes(): any {
installedThemes(): Theme[] {
return this.$store.state.device.themes;
},
@@ -108,20 +123,18 @@ export default Vue.extend({
selectedInstalledThemeCode() {
if (this.selectedInstalledTheme == null) return null;
return JSON.stringify(this.installedThemes.find(x => x.meta.id == this.selectedInstalledTheme));
return JSON5.stringify(this.installedThemes.find(x => x.id == this.selectedInstalledTheme), null, '\t');
},
myTheme(): any {
return {
meta: {
name: this.myThemeName,
author: this.$store.state.i.name,
base: this.myThemeBase,
vars: {
primary: tinycolor(typeof this.myThemePrimary == 'string' ? this.myThemePrimary : this.myThemePrimary.rgba).toRgbString(),
secondary: tinycolor(typeof this.myThemeSecondary == 'string' ? this.myThemeSecondary : this.myThemeSecondary.rgba).toRgbString(),
text: tinycolor(typeof this.myThemeText == 'string' ? this.myThemeText : this.myThemeText.rgba).toRgbString()
}
name: this.myThemeName,
author: this.$store.state.i.name,
base: this.myThemeBase,
vars: {
primary: tinycolor(typeof this.myThemePrimary == 'string' ? this.myThemePrimary : this.myThemePrimary.rgba).toRgbString(),
secondary: tinycolor(typeof this.myThemeSecondary == 'string' ? this.myThemeSecondary : this.myThemeSecondary.rgba).toRgbString(),
text: tinycolor(typeof this.myThemeText == 'string' ? this.myThemeText : this.myThemeText.rgba).toRgbString()
}
};
}
@@ -130,37 +143,67 @@ export default Vue.extend({
watch: {
myThemeBase(v) {
const theme = v == 'light' ? lightTheme : darkTheme;
this.myThemePrimary = theme.meta.vars.primary;
this.myThemeSecondary = theme.meta.vars.secondary;
this.myThemeText = theme.meta.vars.text;
this.myThemePrimary = theme.vars.primary;
this.myThemeSecondary = theme.vars.secondary;
this.myThemeText = theme.vars.text;
}
},
beforeCreate() {
// migrate old theme definitions
// 後方互換性のため
this.$store.commit('device/set', {
key: 'themes', value: this.$store.state.device.themes.map(t => {
if (t.id == null) {
return convertOldThemedefinition(t);
} else {
return t;
}
})
});
},
methods: {
install() {
const theme = JSON.parse(this.installThemeCode);
if (theme.meta == null || theme.meta.id == null) {
let theme;
try {
theme = JSON5.parse(this.installThemeCode);
} catch (e) {
alert('%i18n:@invalid-theme%');
return;
}
if (this.$store.state.device.themes.some(t => t.meta.id == theme.meta.id)) {
// 後方互換性のため
if (theme.id == null && theme.meta != null) {
theme = convertOldThemedefinition(theme);
}
if (theme.id == null) {
alert('%i18n:@invalid-theme%');
return;
}
if (this.$store.state.device.themes.some(t => t.id == theme.id)) {
alert('%i18n:@already-installed%');
return;
}
const themes = this.$store.state.device.themes.concat(theme);
this.$store.commit('device/set', {
key: 'themes', value: themes
});
alert('%i18n:@installed%'.replace('{}', theme.meta.name));
alert('%i18n:@installed%'.replace('{}', theme.name));
},
uninstall() {
const theme = this.installedThemes.find(x => x.meta.id == this.selectedInstalledTheme);
const themes = this.$store.state.device.themes.filter(t => t.meta.id != theme.meta.id);
const theme = this.installedThemes.find(x => x.id == this.selectedInstalledTheme);
const themes = this.$store.state.device.themes.filter(t => t.id != theme.id);
this.$store.commit('device/set', {
key: 'themes', value: themes
});
alert('%i18n:@uninstalled%'.replace('{}', theme.meta.name));
alert('%i18n:@uninstalled%'.replace('{}', theme.name));
},
preview() {
@@ -169,7 +212,7 @@ export default Vue.extend({
gen() {
const theme = this.myTheme;
theme.meta.id = uuid();
theme.id = uuid();
const themes = this.$store.state.device.themes.concat(theme);
this.$store.commit('device/set', {
key: 'themes', value: themes

View File

@@ -14,8 +14,7 @@ import App from './app.vue';
import checkForUpdate from './common/scripts/check-for-update';
import MiOS, { API } from './mios';
import { version, codename, lang } from './config';
import { builtinThemes, applyTheme } from './theme';
const lightTheme = require('../theme/light.json');
import { builtinThemes, lightTheme, applyTheme } from './theme';
if (localStorage.getItem('theme') == null) {
applyTheme(lightTheme);
@@ -97,15 +96,15 @@ export default (callback: (launch: (router: VueRouter, api?: (os: MiOS) => API)
return s.device.darkmode;
}, v => {
const themes = os.store.state.device.themes.concat(builtinThemes);
const dark = themes.find(t => t.meta.id == os.store.state.device.darkTheme);
const light = themes.find(t => t.meta.id == os.store.state.device.lightTheme);
const dark = themes.find(t => t.id == os.store.state.device.darkTheme);
const light = themes.find(t => t.id == os.store.state.device.lightTheme);
applyTheme(v ? dark : light);
});
os.store.watch(s => {
return s.device.lightTheme;
}, v => {
const themes = os.store.state.device.themes.concat(builtinThemes);
const theme = themes.find(t => t.meta.id == v);
const theme = themes.find(t => t.id == v);
if (!os.store.state.device.darkmode) {
applyTheme(theme);
}
@@ -114,7 +113,7 @@ export default (callback: (launch: (router: VueRouter, api?: (os: MiOS) => API)
return s.device.darkTheme;
}, v => {
const themes = os.store.state.device.themes.concat(builtinThemes);
const theme = themes.find(t => t.meta.id == v);
const theme = themes.find(t => t.id == v);
if (os.store.state.device.darkmode) {
applyTheme(theme);
}

View File

@@ -1,27 +1,40 @@
import * as tinycolor from 'tinycolor2';
type Theme = {
meta: {
id: string;
name: string;
author: string;
base?: string;
vars: any;
};
} & {
[key: string]: string;
export type Theme = {
id: string;
name: string;
author: string;
desc?: string;
base?: 'dark' | 'light';
vars: { [key: string]: string };
props: { [key: string]: string };
};
export const lightTheme: Theme = require('../theme/light.json5');
export const darkTheme: Theme = require('../theme/dark.json5');
export const pinkTheme: Theme = require('../theme/pink.json5');
export const halloweenTheme: Theme = require('../theme/halloween.json5');
export const builtinThemes = [
lightTheme,
darkTheme,
pinkTheme,
halloweenTheme
];
export function applyTheme(theme: Theme, persisted = true) {
if (theme.meta.base) {
const base = [lightTheme, darkTheme].find(x => x.meta.id == theme.meta.base);
theme = Object.assign({}, base, theme);
// Deep copy
const _theme = JSON.parse(JSON.stringify(theme));
if (_theme.base) {
const base = [lightTheme, darkTheme].find(x => x.id == _theme.base);
_theme.vars = Object.assign({}, base.vars, _theme.vars);
_theme.props = Object.assign({}, base.props, _theme.props);
}
const props = compile(theme);
const props = compile(_theme);
Object.entries(props).forEach(([k, v]) => {
if (k == 'meta') return;
document.documentElement.style.setProperty(`--${k}`, v.toString());
});
@@ -34,10 +47,10 @@ function compile(theme: Theme): { [key: string]: string } {
function getColor(code: string): tinycolor.Instance {
// ref
if (code[0] == '@') {
return getColor(theme[code.substr(1)]);
return getColor(theme.props[code.substr(1)]);
}
if (code[0] == '$') {
return getColor(theme.meta.vars[code.substr(1)]);
return getColor(theme.vars[code.substr(1)]);
}
// func
@@ -59,8 +72,7 @@ function compile(theme: Theme): { [key: string]: string } {
const props = {};
Object.entries(theme).forEach(([k, v]) => {
if (k == 'meta') return;
Object.entries(theme.props).forEach(([k, v]) => {
const c = getColor(v);
props[k] = genValue(c);
});
@@ -88,15 +100,3 @@ function compile(theme: Theme): { [key: string]: string } {
function genValue(c: tinycolor.Instance): string {
return c.toRgbString();
}
export const lightTheme = require('../theme/light.json');
export const darkTheme = require('../theme/dark.json');
export const pinkTheme = require('../theme/pink.json');
export const halloweenTheme = require('../theme/halloween.json');
export const builtinThemes = [
lightTheme,
darkTheme,
pinkTheme,
halloweenTheme
];