テーマに関して強化
This commit is contained in:
		| @@ -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: { | ||||
|   | ||||
| @@ -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()); | ||||
| 		}); | ||||
| 	} | ||||
|   | ||||
| @@ -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 | ||||
|   | ||||
| @@ -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); | ||||
| 				} | ||||
|   | ||||
| @@ -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 | ||||
| ]; | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 syuilo
					syuilo