This commit is contained in:
syuilo
2018-09-29 00:01:11 +09:00
parent 1fa4d0d3f8
commit 6a82e94c54
15 changed files with 287 additions and 31 deletions

View File

@@ -1,89 +0,0 @@
import * as tinycolor from 'tinycolor2';
const lightTheme = require('../../../theme/light');
const darkTheme = require('../../../theme/dark');
type Theme = {
meta: {
id: string;
name: string;
inherit: string;
vars: any;
};
} & {
[key: string]: string;
};
export default function(theme: Theme) {
if (theme.meta.inherit) {
const inherit = [lightTheme, darkTheme].find(x => x.meta.id == theme.meta.inherit);
theme = Object.assign({}, inherit, theme);
}
const props = compile(theme);
Object.entries(props).forEach(([k, v]) => {
if (k == 'meta') return;
document.documentElement.style.setProperty(`--${k}`, v.toString());
});
localStorage.setItem('theme', JSON.stringify(props));
}
function compile(theme: Theme): { [key: string]: string } {
function getColor(code: string): tinycolor.Instance {
// ref
if (code[0] == '@') {
return getColor(theme[code.substr(1)]);
}
if (code[0] == '$') {
return getColor(theme.meta.vars[code.substr(1)]);
}
// func
if (code[0] == ':') {
const parts = code.split('<');
const func = parts.shift().substr(1);
const arg = parseFloat(parts.shift());
const color = getColor(parts.join('<'));
switch (func) {
case 'darken': return color.darken(arg);
case 'lighten': return color.lighten(arg);
case 'alpha': return color.setAlpha(arg);
}
}
return tinycolor(code);
}
const props = {};
Object.entries(theme).forEach(([k, v]) => {
if (k == 'meta') return;
const c = getColor(v);
props[k] = genValue(c);
});
const primary = getColor(props['primary']);
for (let i = 1; i < 10; i++) {
const color = primary.clone().setAlpha(i / 10);
props['primaryAlpha0' + i] = genValue(color);
}
for (let i = 1; i < 100; i++) {
const color = primary.clone().lighten(i);
props['primaryLighten' + i] = genValue(color);
}
for (let i = 1; i < 100; i++) {
const color = primary.clone().darken(i);
props['primaryDarken' + i] = genValue(color);
}
return props;
}
function genValue(c: tinycolor.Instance): string {
return c.toRgbString();
}

View File

@@ -59,7 +59,9 @@ export default Vue.extend({
}
},
mounted() {
this.$el.style.color = `rgb(${this.user.avatarColor.slice(0, 3).join(',')})`;
if (this.user.avatarColor) {
this.$el.style.color = `rgb(${this.user.avatarColor.slice(0, 3).join(',')})`;
}
},
methods: {
onClick(e) {

View File

@@ -1,5 +1,6 @@
import Vue from 'vue';
import theme from './theme.vue';
import instance from './instance.vue';
import cwButton from './cw-button.vue';
import tagCloud from './tag-cloud.vue';
@@ -43,6 +44,7 @@ import uiSelect from './ui/select.vue';
import formButton from './ui/form/button.vue';
import formRadio from './ui/form/radio.vue';
Vue.component('mk-theme', theme);
Vue.component('mk-instance', instance);
Vue.component('mk-cw-button', cwButton);
Vue.component('mk-tag-cloud', tagCloud);

View File

@@ -0,0 +1,179 @@
<template>
<div class="nicnklzforebnpfgasiypmpdaaglujqm">
<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>
</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>
</ui-select>
</label>
<details class="creator">
<summary>%i18n:@create-a-theme%</summary>
<div>
<span>%i18n:@base-theme%:</span>
<ui-radio v-model="myThemeBase" value="light">%i18n:@base-theme-light%</ui-radio>
<ui-radio v-model="myThemeBase" value="dark">%i18n:@base-theme-dark%</ui-radio>
</div>
<div>
<ui-input v-model="myThemeName">
<span>%i18n:@theme-name%</span>
</ui-input>
</div>
<div>
<div style="padding-bottom:8px;">%i18n:@primary-color%:</div>
<color-picker v-model="myThemePrimary"/>
</div>
<div>
<div style="padding-bottom:8px;">%i18n:@secondary-color%:</div>
<color-picker v-model="myThemeSecondary"/>
</div>
<div>
<div style="padding-bottom:8px;">%i18n:@text-color%:</div>
<color-picker v-model="myThemeText"/>
</div>
<ui-button @click="preview()">%i18n:@preview-created-theme%</ui-button>
<ui-button primary @click="gen()">%i18n:@save-created-theme%</ui-button>
</details>
<details>
<summary>%i18n:@install-a-theme%</summary>
<ui-textarea v-model="installThemeCode">
<span>%i18n:@theme-code%</span>
</ui-textarea>
<ui-button @click="install()">%i18n:@install%</ui-button>
</details>
<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>
</ui-select>
<ui-textarea readonly :value="selectedInstalledThemeCode">
<span>%i18n:@theme-code%</span>
</ui-textarea>
</details>
</div>
</template>
<script lang="ts">
import Vue from 'vue';
import { apiUrl, docsUrl } from '../../../config';
import { lightTheme, darkTheme, builtinThemes, applyTheme } from '../../../theme';
import { Chrome } from 'vue-color';
import * as uuid from 'uuid';
import * as tinycolor from 'tinycolor2';
export default Vue.extend({
components: {
ColorPicker: Chrome
},
data() {
return {
installThemeCode: null,
selectedInstalledTheme: null,
myThemeBase: 'light',
myThemeName: '',
myThemePrimary: lightTheme.meta.vars.primary,
myThemeSecondary: lightTheme.meta.vars.secondary,
myThemeText: lightTheme.meta.vars.text
};
},
computed: {
themes(): any {
return this.$store.state.device.themes.concat(builtinThemes);
},
installedThemes(): any {
return this.$store.state.device.themes;
},
light: {
get() { return this.$store.state.device.lightTheme; },
set(value) { this.$store.commit('device/set', { key: 'lightTheme', value }); }
},
dark: {
get() { return this.$store.state.device.darkTheme; },
set(value) { this.$store.commit('device/set', { key: 'darkTheme', value }); }
},
selectedInstalledThemeCode() {
if (this.selectedInstalledTheme == null) return null;
return JSON.stringify(this.installedThemes.find(x => x.meta.id == this.selectedInstalledTheme));
},
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()
}
}
};
}
},
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;
}
},
methods: {
install() {
const theme = JSON.parse(this.installThemeCode);
if (theme.meta == null || theme.meta.id == null) {
alert('%i18n:@invalid-theme%');
return;
}
if (this.$store.state.device.themes.some(t => t.meta.id == theme.meta.id)) {
alert('%i18n:@already-installed%');
return;
}
const themes = this.$store.state.device.themes.concat(theme);
this.$store.commit('device/set', {
key: 'themes', value: themes
});
},
preview() {
applyTheme(this.myTheme, false);
},
gen() {
const theme = this.myTheme;
theme.meta.id = uuid();
const themes = this.$store.state.device.themes.concat(theme);
this.$store.commit('device/set', {
key: 'themes', value: themes
});
alert('%i18n:@saved%');
}
}
});
</script>
<style lang="stylus" scoped>
.nicnklzforebnpfgasiypmpdaaglujqm
> .creator
> div
padding 16px 0
border-bottom solid 1px var(--faceDivider)
</style>

View File

@@ -27,14 +27,6 @@ export default Vue.extend({
return {
styl: 'fill'
};
},
inject: {
isCardChild: { default: false }
},
created() {
if (this.isCardChild) {
this.styl = 'line';
}
}
});
</script>
@@ -43,6 +35,7 @@ export default Vue.extend({
.dmtdnykelhudezerjlfpbhgovrgnqqgr
display block
width 100%
min-height 40px
margin 0
padding 0
font-weight normal
@@ -52,6 +45,9 @@ export default Vue.extend({
outline none
box-shadow none
&:not(.inline) + .dmtdnykelhudezerjlfpbhgovrgnqqgr
margin-top 16px
&.inline
display inline-block
width auto