From f78a5158e160fce03b333ad0aea6b2e136d42f67 Mon Sep 17 00:00:00 2001 From: Henry Jameson <me@hjkos.com> Date: Tue, 2 Oct 2018 21:43:58 +0300 Subject: [PATCH 01/96] something works without exploding and i'm tired already --- package.json | 1 + src/App.scss | 10 +- src/components/settings/settings.vue | 2 + .../style_switcher/style_switcher.js | 141 +++++++++----- .../style_switcher/style_switcher.vue | 174 ++++++++---------- src/lib/persisted_state.js | 1 + src/services/color_convert/color_convert.js | 12 ++ src/services/style_setter/style_setter.js | 134 +++++++++----- yarn.lock | 4 + 9 files changed, 297 insertions(+), 182 deletions(-) diff --git a/package.json b/package.json index b716e7c6..1149d200 100644 --- a/package.json +++ b/package.json @@ -16,6 +16,7 @@ "dependencies": { "babel-plugin-add-module-exports": "^0.2.1", "babel-plugin-lodash": "^3.2.11", + "chromatism": "^3.0.0", "diff": "^3.0.1", "karma-mocha-reporter": "^2.2.1", "localforage": "^1.5.0", diff --git a/src/App.scss b/src/App.scss index 056a235e..1119caf2 100644 --- a/src/App.scss +++ b/src/App.scss @@ -51,7 +51,7 @@ a { button { user-select: none; color: $fallback--fg; - color: var(--fg, $fallback--fg); + color: var(--btnText, $fallback--fg); background-color: $fallback--btn; background-color: var(--btn, $fallback--btn); border: none; @@ -254,7 +254,7 @@ nav { mask-position: center; mask-size: contain; background-color: $fallback--fg; - background-color: var(--fg, $fallback--fg); + background-color: var(--topBarText, $fallback--fg); position: absolute; top: 0; bottom: 0; @@ -330,8 +330,9 @@ main-router { padding: .6em .6em; text-align: left; line-height: 28px; + color: var(--panelText); background-color: $fallback--btn; - background-color: var(--btn, $fallback--btn); + background-color: var(--panel, $fallback--btn); align-items: baseline; .title { @@ -387,8 +388,9 @@ main-router { nav { z-index: 1000; + color: var(--topBarText); background-color: $fallback--btn; - background-color: var(--btn, $fallback--btn); + background-color: var(--topBar, $fallback--btn); color: $fallback--faint; color: var(--faint, $fallback--faint); box-shadow: 0px 0px 4px rgba(0,0,0,.6); diff --git a/src/components/settings/settings.vue b/src/components/settings/settings.vue index 42c660a3..eebb2cb7 100644 --- a/src/components/settings/settings.vue +++ b/src/components/settings/settings.vue @@ -18,6 +18,7 @@ </transition> </div> <div class="panel-body"> +<keep-alive> <tab-switcher> <div :label="$t('settings.general')" > <div class="setting-item"> @@ -146,6 +147,7 @@ </div> </tab-switcher> +</keep-alive> </div> </div> </template> diff --git a/src/components/style_switcher/style_switcher.js b/src/components/style_switcher/style_switcher.js index 95c15b49..5f76c038 100644 --- a/src/components/style_switcher/style_switcher.js +++ b/src/components/style_switcher/style_switcher.js @@ -1,4 +1,7 @@ -import { rgbstr2hex } from '../../services/color_convert/color_convert.js' +import { rgb2hex } from '../../services/color_convert/color_convert.js' +import ColorInput from '../color_input/color_input.vue' +import OpacityInput from '../opacity_input/opacity_input.vue' +import StyleSetter from '../../services/style_setter/style_setter.js' export default { data () { @@ -7,13 +10,23 @@ export default { selected: this.$store.state.config.theme, invalidThemeImported: false, bgColorLocal: '', + bgOpacityLocal: 0, btnColorLocal: '', + btnOpacityLocal: '', + textColorLocal: '', linkColorLocal: '', + + panelColorLocal: undefined, + panelOpacityLocal: undefined, + topBarColorLocal: undefined, + topBarOpacityLocal: undefined, + redColorLocal: '', blueColorLocal: '', greenColorLocal: '', orangeColorLocal: '', + btnRadiusLocal: '', inputRadiusLocal: '', panelRadiusLocal: '', @@ -33,7 +46,48 @@ export default { }) }, mounted () { - this.normalizeLocalState(this.$store.state.config.colors, this.$store.state.config.radii) + this.normalizeLocalState(this.$store.state.config.customTheme) + }, + computed: { + currentTheme () { + return { + colors: { + bg: this.bgColorLocal, + fg: this.textColorLocal, + panel: this.panelColorLocal, + topBar: this.topBarColorLocal, + btn: this.btnColorLocal, + link: this.linkColorLocal, + cRed: this.redColorLocal, + cBlue: this.blueColorLocal, + cGreen: this.greenColorLocal, + cOrange: this.orangeColorLocal + }, + radii: { + btnRadius: this.btnRadiusLocal, + inputRadius: this.inputRadiusLocal, + panelRadius: this.panelRadiusLocal, + avatarRadius: this.avatarRadiusLocal, + avatarAltRadius: this.avatarAltRadiusLocal, + tooltipRadius: this.tooltipRadiusLocal, + attachmentRadius: this.attachmentRadiusLocal + } + } + }, + previewRules () { + try { + const generated = StyleSetter.generatePreset(this.currentTheme.colors) + return [generated.colorRules, generated.radiiRules].join(';') + } catch (e) { + console.error('CATCH') + console.error(e) + return '' + } + } + }, + components: { + ColorInput, + OpacityInput }, methods: { exportCurrentTheme () { @@ -101,57 +155,62 @@ export default { b: parseInt(result[3], 16) } : null } - const bgRgb = rgb(this.bgColorLocal) - const btnRgb = rgb(this.btnColorLocal) - const textRgb = rgb(this.textColorLocal) - const linkRgb = rgb(this.linkColorLocal) - const redRgb = rgb(this.redColorLocal) - const blueRgb = rgb(this.blueColorLocal) - const greenRgb = rgb(this.greenColorLocal) - const orangeRgb = rgb(this.orangeColorLocal) - - if (bgRgb && btnRgb && linkRgb) { - this.$store.dispatch('setOption', { - name: 'customTheme', - value: { - fg: btnRgb, - bg: bgRgb, - text: textRgb, - link: linkRgb, - cRed: redRgb, - cBlue: blueRgb, - cGreen: greenRgb, - cOrange: orangeRgb, - btnRadius: this.btnRadiusLocal, - inputRadius: this.inputRadiusLocal, - panelRadius: this.panelRadiusLocal, - avatarRadius: this.avatarRadiusLocal, - avatarAltRadius: this.avatarAltRadiusLocal, - tooltipRadius: this.tooltipRadiusLocal, - attachmentRadius: this.attachmentRadiusLocal - }}) - } + this.$store.dispatch('setOption', { + name: 'customTheme', + value: this.currentTheme + }) }, - normalizeLocalState (colors, radii) { - this.bgColorLocal = rgbstr2hex(colors.bg) - this.btnColorLocal = rgbstr2hex(colors.btn) - this.textColorLocal = rgbstr2hex(colors.fg) - this.linkColorLocal = rgbstr2hex(colors.link) + normalizeLocalState (input) { + const colors = input.colors || input + const radii = input.radii || input + let i = 0 + console.log('BENIS') + console.log(colors) - this.redColorLocal = rgbstr2hex(colors.cRed) - this.blueColorLocal = rgbstr2hex(colors.cBlue) - this.greenColorLocal = rgbstr2hex(colors.cGreen) - this.orangeColorLocal = rgbstr2hex(colors.cOrange) + console.log(i++) + this.bgColorLocal = rgb2hex(colors.bg) + console.log(i++) + this.btnColorLocal = rgb2hex(colors.btn) + console.log(i++) + this.textColorLocal = rgb2hex(colors.text || colors.fg) + console.log(i++) + this.linkColorLocal = rgb2hex(colors.link) + console.log(i++) + + this.panelColorLocal = colors.panel ? rgb2hex(colors.panel) : undefined + console.log(i++) + this.topBarColorLocal = colors.topBad ? rgb2hex(colors.topBar) : undefined + console.log(i++) + + this.redColorLocal = rgb2hex(colors.cRed) + console.log(i++) + console.log('red') + console.log(colors.cRed) + console.log(this.redColorLocal) + this.blueColorLocal = rgb2hex(colors.cBlue) + console.log(i++) + console.log('blue', this.blueColorLocal, colors.cBlue) + this.greenColorLocal = rgb2hex(colors.cGreen) + console.log(i++) + this.orangeColorLocal = rgb2hex(colors.cOrange) + console.log(i++) this.btnRadiusLocal = radii.btnRadius || 4 + console.log(i++) this.inputRadiusLocal = radii.inputRadius || 4 + console.log(i++) this.panelRadiusLocal = radii.panelRadius || 10 + console.log(i++) this.avatarRadiusLocal = radii.avatarRadius || 5 + console.log(i++) this.avatarAltRadiusLocal = radii.avatarAltRadius || 50 + console.log(i++) this.tooltipRadiusLocal = radii.tooltipRadius || 2 + console.log(i++) this.attachmentRadiusLocal = radii.attachmentRadius || 5 + console.log(i++) } }, watch: { diff --git a/src/components/style_switcher/style_switcher.vue b/src/components/style_switcher/style_switcher.vue index 72a338bd..339d7c3d 100644 --- a/src/components/style_switcher/style_switcher.vue +++ b/src/components/style_switcher/style_switcher.vue @@ -24,80 +24,73 @@ </div> </div> - <div class="preview-container"> - <div :style="{ - '--btnRadius': btnRadiusLocal + 'px', - '--inputRadius': inputRadiusLocal + 'px', - '--panelRadius': panelRadiusLocal + 'px', - '--avatarRadius': avatarRadiusLocal + 'px', - '--avatarAltRadius': avatarAltRadiusLocal + 'px', - '--tooltipRadius': tooltipRadiusLocal + 'px', - '--attachmentRadius': attachmentRadiusLocal + 'px' - }"> - <div class="panel dummy"> - <div class="panel-heading" :style="{ 'background-color': btnColorLocal, 'color': textColorLocal }">Preview</div> - <div class="panel-body theme-preview-content" :style="{ 'background-color': bgColorLocal, 'color': textColorLocal }"> - <div class="avatar" :style="{ - 'border-radius': avatarRadiusLocal + 'px' - }"> - ( ͡° ͜ʖ ͡°) - </div> - <h4>Content</h4> - <br> - A bunch of more content and - <a :style="{ color: linkColorLocal }">a nice lil' link</a> - <i :style="{ color: blueColorLocal }" class="icon-reply"/> - <i :style="{ color: greenColorLocal }" class="icon-retweet"/> - <i :style="{ color: redColorLocal }" class="icon-cancel"/> - <i :style="{ color: orangeColorLocal }" class="icon-star"/> - <br> - <button class="btn" :style="{ 'background-color': btnColorLocal, 'color': textColorLocal }">Button</button> + <div class="preview-container" :style="previewRules"> + <div class="panel dummy"> + <div class="panel-heading">Preview</div> + <div class="panel-body theme-preview-content"> + <div class="avatar"> + ( ͡° ͜ʖ ͡°) </div> + <h4>Content</h4> + <br> + A bunch of more content and + <a style="color: var(--link)">a nice lil' link</a> + <i style="color: var(--cBlue)" class="icon-reply"/> + <i style="color: var(--cGreen)" class="icon-retweet"/> + <i style="color: var(--cRed)" class="icon-cancel"/> + <i style="color: var(--cOrange)" class="icon-star"/> + <br> + <button class="btn">Button</button> </div> </div> </div> <div class="color-container"> <p>{{$t('settings.theme_help')}}</p> - <div class="color-item"> - <label for="bgcolor" class="theme-color-lb">{{$t('settings.background')}}</label> - <input id="bgcolor" class="theme-color-cl" type="color" v-model="bgColorLocal"> - <input id="bgcolor-t" class="theme-color-in" type="text" v-model="bgColorLocal"> + <h3>Basic colors!!</h3> + <div> + <div class="color-item"> + <ColorInput name="bgColor" v-model="bgColorLocal" :label="$t('settings.background')"/> + <OpacityInput name="bgOpacity" v-model="bgOpacityLocal" fallback="1"/> + </div> + <div class="color-item"> + <ColorInput name="fgColor" v-model="btnColorLocal" :label="$t('settings.foreground')"/> + <OpacityInput name="fgOpacity" v-model="btnOpacityLocal" fallback="1"/> + </div> + <div class="color-item"> + <ColorInput name="textColor" v-model="textColorLocal" :label="$t('settings.text')"/> + </div> + <div class="color-item"> + <ColorInput name="linkColor" v-model="linkColorLocal" :label="$t('settings.links')"/> + </div> </div> - <div class="color-item"> - <label for="fgcolor" class="theme-color-lb">{{$t('settings.foreground')}}</label> - <input id="fgcolor" class="theme-color-cl" type="color" v-model="btnColorLocal"> - <input id="fgcolor-t" class="theme-color-in" type="text" v-model="btnColorLocal"> + + <h3>More customs!</h3> + <div> + <div class="color-item"> + <ColorInput name="panelColor" v-model="panelColorLocal" :fallback="btnColorLocal" label="Panel header"/> + <OpacityInput name="panelOpacity" v-model="panelOpacityLocal" fallback="1"/> + </div> + <div class="color-item"> + <ColorInput name="topBarColor" v-model="topBarColorLocal" :fallback="btnColorLocal" label="Top bar"/> + <OpacityInput name="topBarOpacity" v-model="topBarOpacityLocal" fallback="1"/> + </div> </div> - <div class="color-item"> - <label for="textcolor" class="theme-color-lb">{{$t('settings.text')}}</label> - <input id="textcolor" class="theme-color-cl" type="color" v-model="textColorLocal"> - <input id="textcolor-t" class="theme-color-in" type="text" v-model="textColorLocal"> - </div> - <div class="color-item"> - <label for="linkcolor" class="theme-color-lb">{{$t('settings.links')}}</label> - <input id="linkcolor" class="theme-color-cl" type="color" v-model="linkColorLocal"> - <input id="linkcolor-t" class="theme-color-in" type="text" v-model="linkColorLocal"> - </div> - <div class="color-item"> - <label for="redcolor" class="theme-color-lb">{{$t('settings.cRed')}}</label> - <input id="redcolor" class="theme-color-cl" type="color" v-model="redColorLocal"> - <input id="redcolor-t" class="theme-color-in" type="text" v-model="redColorLocal"> - </div> - <div class="color-item"> - <label for="bluecolor" class="theme-color-lb">{{$t('settings.cBlue')}}</label> - <input id="bluecolor" class="theme-color-cl" type="color" v-model="blueColorLocal"> - <input id="bluecolor-t" class="theme-color-in" type="text" v-model="blueColorLocal"> - </div> - <div class="color-item"> - <label for="greencolor" class="theme-color-lb">{{$t('settings.cGreen')}}</label> - <input id="greencolor" class="theme-color-cl" type="color" v-model="greenColorLocal"> - <input id="greencolor-t" class="theme-color-in" type="green" v-model="greenColorLocal"> - </div> - <div class="color-item"> - <label for="orangecolor" class="theme-color-lb">{{$t('settings.cOrange')}}</label> - <input id="orangecolor" class="theme-color-cl" type="color" v-model="orangeColorLocal"> - <input id="orangecolor-t" class="theme-color-in" type="text" v-model="orangeColorLocal"> + + <h3>Rainbows!!!</h3> + <div> + <div class="color-item"> + <ColorInput name="cRedColor" v-model="redColorLocal" :label="$t('settings.cRed')"/> + </div> + <div class="color-item"> + <ColorInput name="cBlueColor" v-model="blueColorLocal" :label="$t('settings.cBlue')"/> + </div> + <div class="color-item"> + <ColorInput name="cGreenColor" v-model="greenColorLocal" :label="$t('settings.cGreen')"/> + </div> + <div class="color-item"> + <ColorInput name="cOrangeColor" v-model="orangeColorLocal" :label="$t('settings.cOrange')"/> + </div> </div> </div> @@ -161,7 +154,7 @@ .apply-container, .radius-container, -.color-container, +.color-container > div, .presets-container { display: flex; @@ -176,7 +169,7 @@ flex-direction: column; } -.color-container { +.color-container > div{ flex-wrap: wrap; justify-content: space-between; } @@ -214,14 +207,30 @@ .radius-item, .color-item { min-width: 20em; + margin: 5px 6px 0 0; display:flex; + flex-direction: column; flex: 1 1 0; - align-items: baseline; - margin: 5px 6px 5px 0; + + &:nth-child(2n+1) { + margin-right: 7px; + + } + + .color, .opacity { + display:flex; + align-items: baseline; + } label { color: var(--faint, $fallback--faint); } + .opacity-control { + margin-top: 5px; + height: 12px; + line-height: 12px; + font-size: 12px; + } } .radius-item { @@ -243,44 +252,19 @@ margin-left: 4px; } -.theme-color-in { - min-width: 4em; -} - .theme-radius-in { min-width: 1em; } -.theme-radius-in, -.theme-color-in { +.theme-radius-in { max-width: 7em; flex: 1; } -.theme-radius-lb, -.theme-color-lb { - flex: 2; - min-width: 7em; -} - .theme-radius-lb{ max-width: 50em; } -.theme-color-lb { - max-width: 10em; -} - -.theme-color-cl { - padding: 1px; - max-width: 8em; - height: 100%; - flex: 0; - min-width: 2em; - cursor: pointer; - max-height: 29px; -} - .theme-preview-content { padding: 20px; } diff --git a/src/lib/persisted_state.js b/src/lib/persisted_state.js index 006107e2..e55b3b79 100644 --- a/src/lib/persisted_state.js +++ b/src/lib/persisted_state.js @@ -73,6 +73,7 @@ export default function createPersistedState ({ loaded = true } catch (e) { console.log("Couldn't load state") + console.error(e) loaded = true } }) diff --git a/src/services/color_convert/color_convert.js b/src/services/color_convert/color_convert.js index 13dd8979..efb43327 100644 --- a/src/services/color_convert/color_convert.js +++ b/src/services/color_convert/color_convert.js @@ -1,6 +1,10 @@ import { map } from 'lodash' const rgb2hex = (r, g, b) => { + console.log(r) + if (typeof r === 'object') { + ({ r, g, b } = r) + } [r, g, b] = map([r, g, b], (val) => { val = Math.ceil(val) val = val < 0 ? 0 : val @@ -27,8 +31,16 @@ const rgbstr2hex = (rgb) => { return `#${((Number(rgb[0]) << 16) + (Number(rgb[1]) << 8) + Number(rgb[2])).toString(16)}` } +const mixrgb = (a, b) => { + return Object.keys(a).reduce((acc, k) => { + acc[k] = (a[k] + b[k]) / 2 + return acc + }, {}) +} + export { rgb2hex, hex2rgb, + mixrgb, rgbstr2hex } diff --git a/src/services/style_setter/style_setter.js b/src/services/style_setter/style_setter.js index 493d444e..72782594 100644 --- a/src/services/style_setter/style_setter.js +++ b/src/services/style_setter/style_setter.js @@ -1,5 +1,6 @@ import { times } from 'lodash' -import { rgb2hex, hex2rgb } from '../color_convert/color_convert.js' +import { brightness, invertLightness, convert } from 'chromatism' +import { rgb2hex, hex2rgb, mixrgb } from '../color_convert/color_convert.js' // While this is not used anymore right now, I left it in if we want to do custom // styles that aren't just colors, so user can pick from a few different distinct @@ -53,7 +54,23 @@ const setStyle = (href, commit) => { cssEl.addEventListener('load', setDynamic) } -const setColors = (col, commit) => { +const rgb2rgba = function (rgba) { + return `rgba(${rgba.r}, ${rgba.g}, ${rgba.b}, ${rgba.a})` +} + +const getTextColor = function (bg, text) { + const bgIsLight = convert(bg).hsl.l > 50 + const textIsLight = convert(text).hsl.l > 50 + + if ((bgIsLight && textIsLight) || (!bgIsLight && !textIsLight)) { + const base = typeof text.a !== 'undefined' ? { a: text.a } : {} + return Object.assign(base, invertLightness(text).rgb) + } + return text +} + +const setColors = (input, commit) => { + const { colorRules, radiiRules, col } = generatePreset(input) const head = document.head const body = document.body body.style.display = 'none' @@ -62,51 +79,83 @@ const setColors = (col, commit) => { head.appendChild(styleEl) const styleSheet = styleEl.sheet - const isDark = (col.text.r + col.text.g + col.text.b) > (col.bg.r + col.bg.g + col.bg.b) - let colors = {} - let radii = {} - - const mod = isDark ? -10 : 10 - - colors.bg = rgb2hex(col.bg.r, col.bg.g, col.bg.b) // background - colors.lightBg = rgb2hex((col.bg.r + col.fg.r) / 2, (col.bg.g + col.fg.g) / 2, (col.bg.b + col.fg.b) / 2) // hilighted bg - colors.btn = rgb2hex(col.fg.r, col.fg.g, col.fg.b) // panels & buttons - colors.input = `rgba(${col.fg.r}, ${col.fg.g}, ${col.fg.b}, .5)` - colors.border = rgb2hex(col.fg.r - mod, col.fg.g - mod, col.fg.b - mod) // borders - colors.faint = `rgba(${col.text.r}, ${col.text.g}, ${col.text.b}, .5)` - colors.fg = rgb2hex(col.text.r, col.text.g, col.text.b) // text - colors.lightFg = rgb2hex(col.text.r - mod * 5, col.text.g - mod * 5, col.text.b - mod * 5) // strong text - - colors['base07'] = rgb2hex(col.text.r - mod * 2, col.text.g - mod * 2, col.text.b - mod * 2) - - colors.link = rgb2hex(col.link.r, col.link.g, col.link.b) // links - colors.icon = rgb2hex((col.bg.r + col.text.r) / 2, (col.bg.g + col.text.g) / 2, (col.bg.b + col.text.b) / 2) // icons - - colors.cBlue = col.cBlue && rgb2hex(col.cBlue.r, col.cBlue.g, col.cBlue.b) - colors.cRed = col.cRed && rgb2hex(col.cRed.r, col.cRed.g, col.cRed.b) - colors.cGreen = col.cGreen && rgb2hex(col.cGreen.r, col.cGreen.g, col.cGreen.b) - colors.cOrange = col.cOrange && rgb2hex(col.cOrange.r, col.cOrange.g, col.cOrange.b) - - colors.cAlertRed = col.cRed && `rgba(${col.cRed.r}, ${col.cRed.g}, ${col.cRed.b}, .5)` - - radii.btnRadius = col.btnRadius - radii.inputRadius = col.inputRadius - radii.panelRadius = col.panelRadius - radii.avatarRadius = col.avatarRadius - radii.avatarAltRadius = col.avatarAltRadius - radii.tooltipRadius = col.tooltipRadius - radii.attachmentRadius = col.attachmentRadius - styleSheet.toString() - styleSheet.insertRule(`body { ${Object.entries(colors).filter(([k, v]) => v).map(([k, v]) => `--${k}: ${v}`).join(';')} }`, 'index-max') - styleSheet.insertRule(`body { ${Object.entries(radii).filter(([k, v]) => v).map(([k, v]) => `--${k}: ${v}px`).join(';')} }`, 'index-max') + styleSheet.insertRule(`body { ${colorRules} }`, 'index-max') + styleSheet.insertRule(`body { ${radiiRules} }`, 'index-max') body.style.display = 'initial' - commit('setOption', { name: 'colors', value: colors }) - commit('setOption', { name: 'radii', value: radii }) + // commit('setOption', { name: 'colors', value: htmlColors }) + // commit('setOption', { name: 'radii', value: radii }) commit('setOption', { name: 'customTheme', value: col }) } +const generatePreset = (input) => { + const radii = input.radii || { + btnRadius: input.btnRadius, + inputRadius: input.inputRadius, + panelRadius: input.panelRadius, + avatarRadius: input.avatarRadius, + avatarAltRadius: input.avatarAltRadius, + tooltipRadius: input.tooltipRadius, + attachmentRadius: input.attachmentRadius + } + const colors = {} + + const col = Object.entries(input.colors || input).reduce((acc, [k, v]) => { + if (typeof v === 'object') { + acc[k] = v + } else { + acc[k] = hex2rgb(v) + } + return acc + }, {}) + + colors.fg = col.fg || col.text // text + colors.text = col.fg || col.text // text + colors.lightFg = col.fg || col.text // text + + colors.bg = col.bg // background + colors.lightBg = col.lightBg || brightness(5, colors.bg).rgb // hilighted bg + console.log(colors.bg) + console.log(colors.lightBg) + + colors.btn = col.btn || { r: 0, g: 0, b: 0 } + colors.btnText = getTextColor(colors.btn, colors.text) + + colors.panel = col.panel || col.btn + colors.panelText = getTextColor(colors.panel, colors.text) + + colors.topBar = col.topBar || col.btn + colors.topBarText = getTextColor(colors.topBar, colors.text) + + colors.input = col.input || Object.assign({ a: 0.5 }, col.btn) + colors.border = col.btn // borders + colors.faint = col.faint || Object.assign({ a: 0.5 }, col.text) + + colors.link = col.link // links + colors.icon = mixrgb(colors.bg, colors.text) // icons + + colors.cBlue = col.cBlue + colors.cRed = col.cRed + colors.cGreen = col.cGreen + colors.cOrange = col.cOrange + + colors.cAlertRed = col.cAlertRed || Object.assign({ a: 0.5 }, col.cRed) + + const htmlColors = Object.entries(colors) + .reduce((acc, [k, v]) => { + if (!v) return acc + acc[k] = typeof v.a === 'undefined' ? rgb2hex(v) : rgb2rgba(v) + return acc + }, {}) + + return { + colorRules: Object.entries(htmlColors).filter(([k, v]) => v).map(([k, v]) => `--${k}: ${v}`).join(';'), + radiiRules: Object.entries(radii).filter(([k, v]) => v).map(([k, v]) => `--${k}: ${v}px`).join(';'), + col + } +} + const setPreset = (val, commit) => { window.fetch('/static/styles.json') .then((data) => data.json()) @@ -148,7 +197,8 @@ const setPreset = (val, commit) => { const StyleSetter = { setStyle, setPreset, - setColors + setColors, + generatePreset } export default StyleSetter diff --git a/yarn.lock b/yarn.lock index fdad8b49..0139c714 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1205,6 +1205,10 @@ chokidar@^1.0.0, chokidar@^1.4.1: optionalDependencies: fsevents "^1.0.0" +chromatism@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/chromatism/-/chromatism-3.0.0.tgz#a7249d353c1e4f3577e444ac41171c4e2e624b12" + chromedriver@^2.21.2: version "2.35.0" resolved "https://registry.yarnpkg.com/chromedriver/-/chromedriver-2.35.0.tgz#c103ba2fb3d1671f666058159f5cbaa816902e4d" From fb29e7c73da9520d2d08bae0757bb4ff7803ca11 Mon Sep 17 00:00:00 2001 From: Henry Jameson <me@hjkos.com> Date: Wed, 3 Oct 2018 21:21:48 +0300 Subject: [PATCH 02/96] more workings and even less explosions. --- .../style_switcher/style_switcher.js | 110 ++++++++++-------- .../style_switcher/style_switcher.vue | 64 +++++++--- src/i18n/en.json | 1 + src/services/color_convert/color_convert.js | 7 +- src/services/style_setter/style_setter.js | 18 ++- 5 files changed, 124 insertions(+), 76 deletions(-) diff --git a/src/components/style_switcher/style_switcher.js b/src/components/style_switcher/style_switcher.js index 5f76c038..f74337fd 100644 --- a/src/components/style_switcher/style_switcher.js +++ b/src/components/style_switcher/style_switcher.js @@ -9,17 +9,27 @@ export default { availableStyles: [], selected: this.$store.state.config.theme, invalidThemeImported: false, - bgColorLocal: '', - bgOpacityLocal: 0, - btnColorLocal: '', - btnOpacityLocal: '', textColorLocal: '', linkColorLocal: '', + bgColorLocal: '', + bgOpacityLocal: undefined, + + btnColorLocal: '', + btnTextColorLocal: undefined, + btnOpacityLocal: undefined, + + inputColorLocal: undefined, + inputTextColorLocal: undefined, + inputOpacityLocal: undefined, + panelColorLocal: undefined, + panelTextColorLocal: undefined, panelOpacityLocal: undefined, + topBarColorLocal: undefined, + topBarTextColorLocal: undefined, topBarOpacityLocal: undefined, redColorLocal: '', @@ -49,6 +59,9 @@ export default { this.normalizeLocalState(this.$store.state.config.customTheme) }, computed: { + selectedVersion () { + return Array.isArray(this.selected) ? 1 : 2 + }, currentTheme () { return { colors: { @@ -76,8 +89,11 @@ export default { }, previewRules () { try { - const generated = StyleSetter.generatePreset(this.currentTheme.colors) - return [generated.colorRules, generated.radiiRules].join(';') + if (!this.currentTheme.colors.bg) { + return '' + } + const generated = StyleSetter.generatePreset(this.currentTheme) + return [generated.colorRules, generated.radiiRules, 'color: var(--text)'].join(';') } catch (e) { console.error('CATCH') console.error(e) @@ -93,9 +109,8 @@ export default { exportCurrentTheme () { const stringified = JSON.stringify({ // To separate from other random JSON files and possible future theme formats - _pleroma_theme_version: 1, - colors: this.$store.state.config.colors, - radii: this.$store.state.config.radii + _pleroma_theme_version: 2, + theme: this.currentTheme }, null, 2) // Pretty-print and indent with 2 spaces // Create an invisible link with a data url and simulate a click @@ -123,7 +138,9 @@ export default { try { const parsed = JSON.parse(target.result) if (parsed._pleroma_theme_version === 1) { - this.normalizeLocalState(parsed.colors, parsed.radii) + this.normalizeLocalState(parsed, 1) + } else if (parsed._pleroma_theme_version === 2) { + this.normalizeLocalState(parsed.theme) } else { // A theme from the future, spooky this.invalidThemeImported = true @@ -162,67 +179,68 @@ export default { }) }, - normalizeLocalState (input) { + clearV1 () { + this.panelColorLocal = undefined + this.topBarColorLocal = undefined + this.btnTextColorLocal = undefined + this.btnOpacityLocal = undefined + + this.inputColorLocal = undefined + this.inputTextColorLocal = undefined + this.inputOpacityLocal = undefined + + this.panelColorLocal = undefined + this.panelTextColorLocal = undefined + this.panelOpacityLocal = undefined + + this.topBarColorLocal = undefined + this.topBarTextColorLocal = undefined + this.topBarOpacityLocal = undefined + }, + + normalizeLocalState (input, version = 2) { const colors = input.colors || input const radii = input.radii || input - let i = 0 - console.log('BENIS') - console.log(colors) - console.log(i++) this.bgColorLocal = rgb2hex(colors.bg) - console.log(i++) this.btnColorLocal = rgb2hex(colors.btn) - console.log(i++) this.textColorLocal = rgb2hex(colors.text || colors.fg) - console.log(i++) this.linkColorLocal = rgb2hex(colors.link) - console.log(i++) - this.panelColorLocal = colors.panel ? rgb2hex(colors.panel) : undefined - console.log(i++) - this.topBarColorLocal = colors.topBad ? rgb2hex(colors.topBar) : undefined - console.log(i++) + if (version === 1) { + this.clearV1() + } + + this.panelColorLocal = rgb2hex(colors.panel) + this.topBarColorLocal = rgb2hex(colors.topBar) this.redColorLocal = rgb2hex(colors.cRed) - console.log(i++) - console.log('red') - console.log(colors.cRed) - console.log(this.redColorLocal) this.blueColorLocal = rgb2hex(colors.cBlue) - console.log(i++) - console.log('blue', this.blueColorLocal, colors.cBlue) this.greenColorLocal = rgb2hex(colors.cGreen) - console.log(i++) this.orangeColorLocal = rgb2hex(colors.cOrange) - console.log(i++) this.btnRadiusLocal = radii.btnRadius || 4 - console.log(i++) this.inputRadiusLocal = radii.inputRadius || 4 - console.log(i++) this.panelRadiusLocal = radii.panelRadius || 10 - console.log(i++) this.avatarRadiusLocal = radii.avatarRadius || 5 - console.log(i++) this.avatarAltRadiusLocal = radii.avatarAltRadius || 50 - console.log(i++) this.tooltipRadiusLocal = radii.tooltipRadius || 2 - console.log(i++) this.attachmentRadiusLocal = radii.attachmentRadius || 5 - console.log(i++) } }, watch: { selected () { - this.bgColorLocal = this.selected[1] - this.btnColorLocal = this.selected[2] - this.textColorLocal = this.selected[3] - this.linkColorLocal = this.selected[4] - this.redColorLocal = this.selected[5] - this.greenColorLocal = this.selected[6] - this.blueColorLocal = this.selected[7] - this.orangeColorLocal = this.selected[8] + if (this.selectedVersion === 1) { + this.clearV1(); + this.bgColorLocal = this.selected[1] + this.btnColorLocal = this.selected[2] + this.textColorLocal = this.selected[3] + this.linkColorLocal = this.selected[4] + this.redColorLocal = this.selected[5] + this.greenColorLocal = this.selected[6] + this.blueColorLocal = this.selected[7] + this.orangeColorLocal = this.selected[8] + } } } } diff --git a/src/components/style_switcher/style_switcher.vue b/src/components/style_switcher/style_switcher.vue index 339d7c3d..521683be 100644 --- a/src/components/style_switcher/style_switcher.vue +++ b/src/components/style_switcher/style_switcher.vue @@ -52,44 +52,65 @@ <div class="color-item"> <ColorInput name="bgColor" v-model="bgColorLocal" :label="$t('settings.background')"/> <OpacityInput name="bgOpacity" v-model="bgOpacityLocal" fallback="1"/> + <ColorInput name="textColor" v-model="textColorLocal" :label="$t('settings.text')"/> + <ColorInput name="linkColor" v-model="linkColorLocal" :label="$t('settings.links')"/> </div> <div class="color-item"> <ColorInput name="fgColor" v-model="btnColorLocal" :label="$t('settings.foreground')"/> - <OpacityInput name="fgOpacity" v-model="btnOpacityLocal" fallback="1"/> + <OpacityInput name="fgOpacity" v-model="fgOpacityLocal" :fallback="bgOpacityLocal || 1"/> + <ColorInput name="fgTextColor" v-model="fgTextColorLocal" :label="$t('settings.text')" :fallback="textColorLocal"/> + <ColorInput name="fgLinkColor" v-model="fgLinkColorLocal" :label="$t('settings.links')" :fallback="linkColorLocal"/> </div> <div class="color-item"> - <ColorInput name="textColor" v-model="textColorLocal" :label="$t('settings.text')"/> + <ColorInput name="cRedColor" v-model="redColorLocal" :label="$t('settings.cRed')"/> + <ColorInput name="cBlueColor" v-model="blueColorLocal" :label="$t('settings.cBlue')"/> </div> <div class="color-item"> - <ColorInput name="linkColor" v-model="linkColorLocal" :label="$t('settings.links')"/> + <ColorInput name="cGreenColor" v-model="greenColorLocal" :label="$t('settings.cGreen')"/> + <ColorInput name="cOrangeColor" v-model="orangeColorLocal" :label="$t('settings.cOrange')"/> + </div> + <div class="color-item wide"> + <h4>Alert opacity</h4> + <OpacityInput name="alertOpacity" v-model="alertOpacityLocal" fallback="1"/> </div> </div> <h3>More customs!</h3> <div> <div class="color-item"> - <ColorInput name="panelColor" v-model="panelColorLocal" :fallback="btnColorLocal" label="Panel header"/> + <h4>Panel header</h4> + <ColorInput name="panelColor" v-model="panelColorLocal" :fallback="btnColorLocal" :label="$t('settings.background')"/> <OpacityInput name="panelOpacity" v-model="panelOpacityLocal" fallback="1"/> + <ColorInput name="panelTextColor" v-model="panelTextColorLocal" :fallback="textColorLocal" :label="$t('settings.links')"/> </div> <div class="color-item"> - <ColorInput name="topBarColor" v-model="topBarColorLocal" :fallback="btnColorLocal" label="Top bar"/> + <h4>Top bar</h4> + <ColorInput name="topBarColor" v-model="topBarColorLocal" :fallback="btnColorLocal" :label="$t('settings.background')"/> <OpacityInput name="topBarOpacity" v-model="topBarOpacityLocal" fallback="1"/> - </div> - </div> - - <h3>Rainbows!!!</h3> - <div> - <div class="color-item"> - <ColorInput name="cRedColor" v-model="redColorLocal" :label="$t('settings.cRed')"/> + <ColorInput name="topBarTextColor" v-model="topBarTextColorLocal" :fallback="textColorLocal" :label="$t('settings.text')"/> + <ColorInput name="topBarLinkColor" v-model="topBarLinkColorLocal" :fallback="linkColorLocal" :label="$t('settings.links')"/> </div> <div class="color-item"> - <ColorInput name="cBlueColor" v-model="blueColorLocal" :label="$t('settings.cBlue')"/> + <h4>Inputs</h4> + <ColorInput name="inputColor" v-model="inputColorLocal" :fallback="btnColorLocal" :label="$t('settings.background')"/> + <OpacityInput name="inputOpacity" v-model="inputOpacityLocal" fallback="0.5"/> + <ColorInput name="inputTextColor" v-model="inputTextColorLocal" :fallback="textColorLocal" :label="$t('settings.text')"/> </div> <div class="color-item"> - <ColorInput name="cGreenColor" v-model="greenColorLocal" :label="$t('settings.cGreen')"/> + <h4>Buttons</h4> + <ColorInput name="buttonColor" v-model="buttonColorLocal" :fallback="btnColorLocal" :label="$t('settings.background')"/> + <OpacityInput name="buttonOpacity" v-model="buttonOpacityLocal" fallback="0.5"/> + <ColorInput name="buttonTextColor" v-model="buttonTextColorLocal" :fallback="textColorLocal" :label="$t('settings.text')"/> </div> <div class="color-item"> - <ColorInput name="cOrangeColor" v-model="orangeColorLocal" :label="$t('settings.cOrange')"/> + <h4>Borders</h4> + <ColorInput name="buttonColor" v-model="buttonColorLocal" :fallback="btnColorLocal" label="Color"/> + <OpacityInput name="buttonOpacity" v-model="buttonOpacityLocal" fallback="0.5"/> + </div> + <div class="color-item"> + <h4>Faint text</h4> + <ColorInput name="buttonColor" v-model="buttonColorLocal" :fallback="btnColorLocal" :label="$t('settings.text')"/> + <OpacityInput name="buttonOpacity" v-model="buttonOpacityLocal" fallback="0.5"/> </div> </div> </div> @@ -212,7 +233,10 @@ flex-direction: column; flex: 1 1 0; - &:nth-child(2n+1) { + &.wide { + min-width: 60% + } + &:not(.wide):nth-child(2n+1) { margin-right: 7px; } @@ -222,14 +246,16 @@ align-items: baseline; } + h4 { + margin-top: 1em; + } + label { color: var(--faint, $fallback--faint); } .opacity-control { margin-top: 5px; - height: 12px; - line-height: 12px; - font-size: 12px; + margin-bottom: 5px; } } diff --git a/src/i18n/en.json b/src/i18n/en.json index 8c7360e9..d825dcc1 100644 --- a/src/i18n/en.json +++ b/src/i18n/en.json @@ -108,6 +108,7 @@ "follow_import_error": "Error importing followers", "follows_imported": "Follows imported! Processing them will take a while.", "foreground": "Foreground", + "opacity": "Opacity", "general": "General", "hide_attachments_in_convo": "Hide attachments in conversations", "hide_attachments_in_tl": "Hide attachments in timeline", diff --git a/src/services/color_convert/color_convert.js b/src/services/color_convert/color_convert.js index efb43327..ae5d5a31 100644 --- a/src/services/color_convert/color_convert.js +++ b/src/services/color_convert/color_convert.js @@ -1,7 +1,12 @@ import { map } from 'lodash' const rgb2hex = (r, g, b) => { - console.log(r) + if (r === null || typeof r === 'undefined') { + return undefined + } + if (r[0] === '#') { + return r + } if (typeof r === 'object') { ({ r, g, b } = r) } diff --git a/src/services/style_setter/style_setter.js b/src/services/style_setter/style_setter.js index 72782594..2a803a4f 100644 --- a/src/services/style_setter/style_setter.js +++ b/src/services/style_setter/style_setter.js @@ -70,7 +70,7 @@ const getTextColor = function (bg, text) { } const setColors = (input, commit) => { - const { colorRules, radiiRules, col } = generatePreset(input) + const { colorRules, radiiRules } = generatePreset(input) const head = document.head const body = document.body body.style.display = 'none' @@ -86,10 +86,11 @@ const setColors = (input, commit) => { // commit('setOption', { name: 'colors', value: htmlColors }) // commit('setOption', { name: 'radii', value: radii }) - commit('setOption', { name: 'customTheme', value: col }) + commit('setOption', { name: 'customTheme', value: input }) } const generatePreset = (input) => { + console.log(input) const radii = input.radii || { btnRadius: input.btnRadius, inputRadius: input.inputRadius, @@ -116,8 +117,6 @@ const generatePreset = (input) => { colors.bg = col.bg // background colors.lightBg = col.lightBg || brightness(5, colors.bg).rgb // hilighted bg - console.log(colors.bg) - console.log(colors.lightBg) colors.btn = col.btn || { r: 0, g: 0, b: 0 } colors.btnText = getTextColor(colors.btn, colors.text) @@ -151,8 +150,7 @@ const generatePreset = (input) => { return { colorRules: Object.entries(htmlColors).filter(([k, v]) => v).map(([k, v]) => `--${k}: ${v}`).join(';'), - radiiRules: Object.entries(radii).filter(([k, v]) => v).map(([k, v]) => `--${k}: ${v}px`).join(';'), - col + radiiRules: Object.entries(radii).filter(([k, v]) => v).map(([k, v]) => `--${k}: ${v}px`).join(';') } } @@ -162,7 +160,7 @@ const setPreset = (val, commit) => { .then((themes) => { const theme = themes[val] ? themes[val] : themes['pleroma-dark'] const bgRgb = hex2rgb(theme[1]) - const fgRgb = hex2rgb(theme[2]) + const btnRgb = hex2rgb(theme[2]) const textRgb = hex2rgb(theme[3]) const linkRgb = hex2rgb(theme[4]) @@ -171,9 +169,9 @@ const setPreset = (val, commit) => { const cBlueRgb = hex2rgb(theme[7] || '#0000FF') const cOrangeRgb = hex2rgb(theme[8] || '#E3FF00') - const col = { + const colors = { bg: bgRgb, - fg: fgRgb, + btn: btnRgb, text: textRgb, link: linkRgb, cRed: cRedRgb, @@ -189,7 +187,7 @@ const setPreset = (val, commit) => { // load config -> set preset -> wait for styles.json to load -> // load persisted state -> set colors -> styles.json loaded -> set colors if (!window.themeLoaded) { - setColors(col, commit) + setColors({ colors }, commit) } }) } From 0a4b07652aa24ea5fcda8f62838ea37f8e8168ef Mon Sep 17 00:00:00 2001 From: Henry Jameson <me@hjkos.com> Date: Thu, 4 Oct 2018 18:16:14 +0300 Subject: [PATCH 03/96] trying to fix transition --- .../style_switcher/style_switcher.js | 67 +++++++++++++++---- .../style_switcher/style_switcher.vue | 18 ++--- src/services/style_setter/style_setter.js | 44 +++++++++--- 3 files changed, 98 insertions(+), 31 deletions(-) diff --git a/src/components/style_switcher/style_switcher.js b/src/components/style_switcher/style_switcher.js index f74337fd..7c204bdb 100644 --- a/src/components/style_switcher/style_switcher.js +++ b/src/components/style_switcher/style_switcher.js @@ -16,7 +16,12 @@ export default { bgColorLocal: '', bgOpacityLocal: undefined, - btnColorLocal: '', + fgColorLocal: '', + fgOpacityLocal: undefined, + fgTextColorLocal: undefined, + fgLinkColorLocal: undefined, + + btnColorLocal: undefined, btnTextColorLocal: undefined, btnOpacityLocal: undefined, @@ -30,8 +35,11 @@ export default { topBarColorLocal: undefined, topBarTextColorLocal: undefined, + topBarLinkColorLocal: undefined, topBarOpacityLocal: undefined, + alertOpacityLocal: undefined, + redColorLocal: '', blueColorLocal: '', greenColorLocal: '', @@ -66,7 +74,8 @@ export default { return { colors: { bg: this.bgColorLocal, - fg: this.textColorLocal, + fg: this.fgColorLocal, + text: this.textColorLocal, panel: this.panelColorLocal, topBar: this.topBarColorLocal, btn: this.btnColorLocal, @@ -87,18 +96,26 @@ export default { } } }, - previewRules () { + preview () { try { if (!this.currentTheme.colors.bg) { - return '' + return {} } - const generated = StyleSetter.generatePreset(this.currentTheme) - return [generated.colorRules, generated.radiiRules, 'color: var(--text)'].join(';') + return StyleSetter.generatePreset(this.currentTheme) } catch (e) { console.error('CATCH') console.error(e) - return '' + return {} } + }, + previewTheme () { + if (!this.preview.theme) return { colors: {}, radii: {} } + console.log(this.preview.theme) + return this.preview.theme + }, + previewRules () { + if (!this.preview.colorRules) return '' + return [this.preview.colorRules, this.preview.radiiRules, 'color: var(--text)'].join(';') } }, components: { @@ -140,7 +157,7 @@ export default { if (parsed._pleroma_theme_version === 1) { this.normalizeLocalState(parsed, 1) } else if (parsed._pleroma_theme_version === 2) { - this.normalizeLocalState(parsed.theme) + this.normalizeLocalState(parsed.theme, 2) } else { // A theme from the future, spooky this.invalidThemeImported = true @@ -180,6 +197,10 @@ export default { }, clearV1 () { + this.fgOpacityLocal = undefined + this.fgTextColorLocal = undefined + this.fgLinkColorLocal = undefined + this.panelColorLocal = undefined this.topBarColorLocal = undefined this.btnTextColorLocal = undefined @@ -198,13 +219,35 @@ export default { this.topBarOpacityLocal = undefined }, - normalizeLocalState (input, version = 2) { + /** + * This applies stored theme data onto form. + * @param {Object} input - input data + * @param {Number} version - version of data. 0 means try to guess based on data. + */ + normalizeLocalState (input, version = 0) { const colors = input.colors || input const radii = input.radii || input + if (version === 0) { + if (input.version) version = input.version + // Old v1 naming: fg is text, btn is foreground + if (typeof input.text === 'undefined' && typeof input.fg !== 'undefined') { + version = 1 + } + // New v2 naming: text is text, fg is foreground + if (typeof input.text !== 'undefined' && typeof input.fg !== 'undefined') { + version = 2 + } + } + this.bgColorLocal = rgb2hex(colors.bg) - this.btnColorLocal = rgb2hex(colors.btn) - this.textColorLocal = rgb2hex(colors.text || colors.fg) + if (version === 1) { + this.fgColorLocal = rgb2hex(colors.btn) + this.textColorLocal = rgb2hex(colors.fg) + } else { + this.fgColorLocal = rgb2hex(colors.fg) + this.textColorLocal = rgb2hex(colors.text) + } this.linkColorLocal = rgb2hex(colors.link) if (version === 1) { @@ -231,7 +274,7 @@ export default { watch: { selected () { if (this.selectedVersion === 1) { - this.clearV1(); + this.clearV1() this.bgColorLocal = this.selected[1] this.btnColorLocal = this.selected[2] this.textColorLocal = this.selected[3] diff --git a/src/components/style_switcher/style_switcher.vue b/src/components/style_switcher/style_switcher.vue index 521683be..cf1fac92 100644 --- a/src/components/style_switcher/style_switcher.vue +++ b/src/components/style_switcher/style_switcher.vue @@ -56,9 +56,9 @@ <ColorInput name="linkColor" v-model="linkColorLocal" :label="$t('settings.links')"/> </div> <div class="color-item"> - <ColorInput name="fgColor" v-model="btnColorLocal" :label="$t('settings.foreground')"/> + <ColorInput name="fgColor" v-model="fgColorLocal" :label="$t('settings.foreground')"/> <OpacityInput name="fgOpacity" v-model="fgOpacityLocal" :fallback="bgOpacityLocal || 1"/> - <ColorInput name="fgTextColor" v-model="fgTextColorLocal" :label="$t('settings.text')" :fallback="textColorLocal"/> + <ColorInput name="fgTextColor" v-model="fgTextColorLocal" :label="$t('settings.text')" :fallback="previewTheme.colors.btnText"/> <ColorInput name="fgLinkColor" v-model="fgLinkColorLocal" :label="$t('settings.links')" :fallback="linkColorLocal"/> </div> <div class="color-item"> @@ -98,19 +98,19 @@ </div> <div class="color-item"> <h4>Buttons</h4> - <ColorInput name="buttonColor" v-model="buttonColorLocal" :fallback="btnColorLocal" :label="$t('settings.background')"/> - <OpacityInput name="buttonOpacity" v-model="buttonOpacityLocal" fallback="0.5"/> - <ColorInput name="buttonTextColor" v-model="buttonTextColorLocal" :fallback="textColorLocal" :label="$t('settings.text')"/> + <ColorInput name="btnColor" v-model="btnColorLocal" :fallback="btnColorLocal" :label="$t('settings.background')"/> + <OpacityInput name="btnOpacity" v-model="btnOpacityLocal" fallback="0.5"/> + <ColorInput name="btnTextColor" v-model="btnTextColorLocal" :fallback="textColorLocal" :label="$t('settings.text')"/> </div> <div class="color-item"> <h4>Borders</h4> - <ColorInput name="buttonColor" v-model="buttonColorLocal" :fallback="btnColorLocal" label="Color"/> - <OpacityInput name="buttonOpacity" v-model="buttonOpacityLocal" fallback="0.5"/> + <ColorInput name="btnColor" v-model="btnColorLocal" :fallback="btnColorLocal" label="Color"/> + <OpacityInput name="btnOpacity" v-model="btnOpacityLocal" fallback="0.5"/> </div> <div class="color-item"> <h4>Faint text</h4> - <ColorInput name="buttonColor" v-model="buttonColorLocal" :fallback="btnColorLocal" :label="$t('settings.text')"/> - <OpacityInput name="buttonOpacity" v-model="buttonOpacityLocal" fallback="0.5"/> + <ColorInput name="btnColor" v-model="btnColorLocal" :fallback="btnColorLocal" :label="$t('settings.text')"/> + <OpacityInput name="btnOpacity" v-model="btnOpacityLocal" fallback="0.5"/> </div> </div> </div> diff --git a/src/services/style_setter/style_setter.js b/src/services/style_setter/style_setter.js index 2a803a4f..54f54b4e 100644 --- a/src/services/style_setter/style_setter.js +++ b/src/services/style_setter/style_setter.js @@ -101,7 +101,6 @@ const generatePreset = (input) => { attachmentRadius: input.attachmentRadius } const colors = {} - const col = Object.entries(input.colors || input).reduce((acc, [k, v]) => { if (typeof v === 'object') { acc[k] = v @@ -111,12 +110,32 @@ const generatePreset = (input) => { return acc }, {}) - colors.fg = col.fg || col.text // text - colors.text = col.fg || col.text // text - colors.lightFg = col.fg || col.text // text + let version = 0 - colors.bg = col.bg // background - colors.lightBg = col.lightBg || brightness(5, colors.bg).rgb // hilighted bg + if (input.version) { + version = input.version + } + // Old v1 naming: fg is text, btn is foreground + if (typeof col.text === 'undefined' && typeof col.fg !== 'undefined') { + version = 1 + } + // New v2 naming: text is text, fg is foreground + if (typeof col.text !== 'undefined' && typeof col.fg !== 'undefined') { + version = 2 + } + + colors.text = version === 1 ? col.fg : col.text + colors.lightText = colors.text + + colors.bg = col.bg + colors.lightBg = col.lightBg || brightness(5, colors.bg).rgb + + colors.fg = version === 1 ? col.btn : col.fg + console.log('BENIN') + console.log(version) + console.log(col) + console.log(colors.text) + colors.fgText = getTextColor(colors.fg, colors.text) colors.btn = col.btn || { r: 0, g: 0, b: 0 } colors.btnText = getTextColor(colors.btn, colors.text) @@ -128,11 +147,11 @@ const generatePreset = (input) => { colors.topBarText = getTextColor(colors.topBar, colors.text) colors.input = col.input || Object.assign({ a: 0.5 }, col.btn) - colors.border = col.btn // borders + colors.border = col.btn colors.faint = col.faint || Object.assign({ a: 0.5 }, col.text) - colors.link = col.link // links - colors.icon = mixrgb(colors.bg, colors.text) // icons + colors.link = col.link + colors.icon = mixrgb(colors.bg, colors.text) colors.cBlue = col.cBlue colors.cRed = col.cRed @@ -150,7 +169,11 @@ const generatePreset = (input) => { return { colorRules: Object.entries(htmlColors).filter(([k, v]) => v).map(([k, v]) => `--${k}: ${v}`).join(';'), - radiiRules: Object.entries(radii).filter(([k, v]) => v).map(([k, v]) => `--${k}: ${v}px`).join(';') + radiiRules: Object.entries(radii).filter(([k, v]) => v).map(([k, v]) => `--${k}: ${v}px`).join(';'), + theme: { + colors, + radii + } } } @@ -196,6 +219,7 @@ const StyleSetter = { setStyle, setPreset, setColors, + getTextColor, generatePreset } From 5441766c3cba46682227202e91e08c4fbb3e3ac3 Mon Sep 17 00:00:00 2001 From: Henry Jameson <me@hjkos.com> Date: Thu, 4 Oct 2018 18:27:27 +0300 Subject: [PATCH 04/96] fix --- .../style_switcher/style_switcher.js | 21 ++------------ src/services/style_setter/style_setter.js | 28 ++++--------------- 2 files changed, 7 insertions(+), 42 deletions(-) diff --git a/src/components/style_switcher/style_switcher.js b/src/components/style_switcher/style_switcher.js index 7c204bdb..a1c44be3 100644 --- a/src/components/style_switcher/style_switcher.js +++ b/src/components/style_switcher/style_switcher.js @@ -228,26 +228,9 @@ export default { const colors = input.colors || input const radii = input.radii || input - if (version === 0) { - if (input.version) version = input.version - // Old v1 naming: fg is text, btn is foreground - if (typeof input.text === 'undefined' && typeof input.fg !== 'undefined') { - version = 1 - } - // New v2 naming: text is text, fg is foreground - if (typeof input.text !== 'undefined' && typeof input.fg !== 'undefined') { - version = 2 - } - } - this.bgColorLocal = rgb2hex(colors.bg) - if (version === 1) { - this.fgColorLocal = rgb2hex(colors.btn) - this.textColorLocal = rgb2hex(colors.fg) - } else { - this.fgColorLocal = rgb2hex(colors.fg) - this.textColorLocal = rgb2hex(colors.text) - } + this.fgColorLocal = rgb2hex(colors.fg) + this.textColorLocal = rgb2hex(colors.text) this.linkColorLocal = rgb2hex(colors.link) if (version === 1) { diff --git a/src/services/style_setter/style_setter.js b/src/services/style_setter/style_setter.js index 54f54b4e..cfa41b11 100644 --- a/src/services/style_setter/style_setter.js +++ b/src/services/style_setter/style_setter.js @@ -110,40 +110,22 @@ const generatePreset = (input) => { return acc }, {}) - let version = 0 - - if (input.version) { - version = input.version - } - // Old v1 naming: fg is text, btn is foreground - if (typeof col.text === 'undefined' && typeof col.fg !== 'undefined') { - version = 1 - } - // New v2 naming: text is text, fg is foreground - if (typeof col.text !== 'undefined' && typeof col.fg !== 'undefined') { - version = 2 - } - - colors.text = version === 1 ? col.fg : col.text + colors.text = col.text colors.lightText = colors.text colors.bg = col.bg colors.lightBg = col.lightBg || brightness(5, colors.bg).rgb - colors.fg = version === 1 ? col.btn : col.fg - console.log('BENIN') - console.log(version) - console.log(col) - console.log(colors.text) + colors.fg = col.fg colors.fgText = getTextColor(colors.fg, colors.text) - colors.btn = col.btn || { r: 0, g: 0, b: 0 } + colors.btn = col.btn || col.fg colors.btnText = getTextColor(colors.btn, colors.text) - colors.panel = col.panel || col.btn + colors.panel = col.panel || col.fg colors.panelText = getTextColor(colors.panel, colors.text) - colors.topBar = col.topBar || col.btn + colors.topBar = col.topBar || col.fg colors.topBarText = getTextColor(colors.topBar, colors.text) colors.input = col.input || Object.assign({ a: 0.5 }, col.btn) From 96804d42f0f6aa6af85295933af6fd267b19e473 Mon Sep 17 00:00:00 2001 From: Henry Jameson <me@hjkos.com> Date: Sun, 7 Oct 2018 19:59:22 +0300 Subject: [PATCH 05/96] Some themeing is working!! --- src/App.scss | 56 ++++----- src/_variables.scss | 7 +- src/components/chat_panel/chat_panel.vue | 4 +- src/components/color_input/color_input.vue | 84 +++++++++++++ .../notifications/notifications.scss | 7 +- .../opacity_input/opacity_input.vue | 75 ++++++++++++ .../post_status_form/post_status_form.vue | 12 +- src/components/settings/settings.vue | 2 +- src/components/status/status.vue | 4 +- .../style_switcher/style_switcher.js | 113 +++++++++++++----- .../style_switcher/style_switcher.vue | 40 ++++--- src/components/tab_switcher/tab_switcher.scss | 8 +- src/components/timeline/timeline.vue | 10 +- .../user_card_content/user_card_content.vue | 16 +-- src/services/style_setter/style_setter.js | 28 +++-- 15 files changed, 346 insertions(+), 120 deletions(-) create mode 100644 src/components/color_input/color_input.vue create mode 100644 src/components/opacity_input/opacity_input.vue diff --git a/src/App.scss b/src/App.scss index 1119caf2..c91b6a61 100644 --- a/src/App.scss +++ b/src/App.scss @@ -36,8 +36,8 @@ body { font-family: sans-serif; font-size: 14px; margin: 0; - color: $fallback--fg; - color: var(--fg, $fallback--fg); + color: $fallback--text; + color: var(--text, $fallback--text); max-width: 100vw; overflow-x: hidden; } @@ -50,10 +50,10 @@ a { button { user-select: none; - color: $fallback--fg; - color: var(--btnText, $fallback--fg); - background-color: $fallback--btn; - background-color: var(--btn, $fallback--btn); + color: $fallback--text; + color: var(--btnText, $fallback--text); + background-color: $fallback--fg; + background-color: var(--btn, $fallback--fg); border: none; border-radius: $fallback--btnRadius; border-radius: var(--btnRadius, $fallback--btnRadius); @@ -102,10 +102,10 @@ input, textarea, .select { border-bottom: 1px solid rgba(255, 255, 255, 0.2); border-top: 1px solid rgba(0, 0, 0, 0.2); box-shadow: 0px 0px 2px black inset; - background-color: $fallback--input; - background-color: var(--input, $fallback--input); - color: $fallback--lightFg; - color: var(--lightFg, $fallback--lightFg); + background-color: $fallback--fg; + background-color: var(--input, $fallback--fg); + color: $fallback--lightText; + color: var(--inputText, $fallback--lightText); font-family: sans-serif; font-size: 14px; padding: 8px 7px; @@ -122,8 +122,8 @@ input, textarea, .select { bottom: 0; right: 5px; height: 100%; - color: $fallback--fg; - color: var(--fg, $fallback--fg); + color: $fallback--text; + color: var(--text, $fallback--text); line-height: 29px; z-index: 0; pointer-events: none; @@ -136,8 +136,8 @@ input, textarea, .select { background: transparent; border: none; margin: 0; - color: $fallback--fg; - color: var(--fg, $fallback--fg); + color: $fallback--text; + color: var(--text, $fallback--text); padding: 4px 2em 3px 3px; width: 100%; z-index: 1; @@ -149,8 +149,8 @@ input, textarea, .select { &[type=checkbox] { display: none; &:checked + label::before { - color: $fallback--fg; - color: var(--fg, $fallback--fg); + color: $fallback--text; + color: var(--text, $fallback--text); } &:disabled, { @@ -172,8 +172,8 @@ input, textarea, .select { border-top: 1px solid rgba(0, 0, 0, 0.2); box-shadow: 0px 0px 2px black inset; margin-right: .5em; - background-color: $fallback--input; - background-color: var(--input, $fallback--input); + background-color: $fallback--fg; + background-color: var(--input, $fallback--fg); vertical-align: top; text-align: center; line-height: 1.1em; @@ -187,8 +187,8 @@ input, textarea, .select { } option { - color: $fallback--fg; - color: var(--fg, $fallback--fg); + color: $fallback--text; + color: var(--text, $fallback--text); background-color: $fallback--bg; background-color: var(--bg, $fallback--bg); } @@ -279,9 +279,9 @@ nav { margin: auto; height: 50px; - a i { + a, a i { color: $fallback--link; - color: var(--link, $fallback--link); + color: var(--topBarLink, $fallback--link); } } } @@ -331,8 +331,8 @@ main-router { text-align: left; line-height: 28px; color: var(--panelText); - background-color: $fallback--btn; - background-color: var(--panel, $fallback--btn); + background-color: $fallback--fg; + background-color: var(--panel, $fallback--fg); align-items: baseline; .title { @@ -389,8 +389,8 @@ main-router { nav { z-index: 1000; color: var(--topBarText); - background-color: $fallback--btn; - background-color: var(--topBar, $fallback--btn); + background-color: $fallback--fg; + background-color: var(--topBar, $fallback--fg); color: $fallback--faint; color: var(--faint, $fallback--faint); box-shadow: 0px 0px 4px rgba(0,0,0,.6); @@ -518,8 +518,8 @@ nav { cursor: pointer; .selected { - color: $fallback--lightFg; - color: var(--lightFg, $fallback--lightFg); + color: $fallback--lightText; + color: var(--lightText, $fallback--lightText); } .text-format { diff --git a/src/_variables.scss b/src/_variables.scss index b5222a6a..0f73e929 100644 --- a/src/_variables.scss +++ b/src/_variables.scss @@ -3,14 +3,13 @@ $main-background: white; $darkened-background: whitesmoke; $fallback--bg: #121a24; -$fallback--btn: #182230; -$fallback--input: #182230; +$fallback--fg: #182230; $fallback--faint: rgba(185, 185, 186, .5); -$fallback--fg: #b9b9ba; +$fallback--text: #b9b9ba; $fallback--link: #d8a070; $fallback--icon: #666; $fallback--lightBg: rgb(21, 30, 42); -$fallback--lightFg: #b9b9ba; +$fallback--lightText: #b9b9ba; $fallback--border: #222; $fallback--cRed: #ff0000; $fallback--cBlue: #0095ff; diff --git a/src/components/chat_panel/chat_panel.vue b/src/components/chat_panel/chat_panel.vue index 30070d3e..f174319a 100644 --- a/src/components/chat_panel/chat_panel.vue +++ b/src/components/chat_panel/chat_panel.vue @@ -55,8 +55,8 @@ .chat-heading { cursor: pointer; .icon-comment-empty { - color: $fallback--fg; - color: var(--fg, $fallback--fg); + color: $fallback--text; + color: var(--text, $fallback--text); } } diff --git a/src/components/color_input/color_input.vue b/src/components/color_input/color_input.vue new file mode 100644 index 00000000..49d9bed7 --- /dev/null +++ b/src/components/color_input/color_input.vue @@ -0,0 +1,84 @@ +<template> +<div class="color-control" :class="{ disabled: !present }"> + <label :for="name" class="label"> + {{label}} + </label> + <input + v-if="typeof fallback !== 'undefined'" + class="opt" + :id="name + '-o'" + type="checkbox" + :checked="present" + @input="$emit('input', typeof value === 'undefined' ? fallback : undefined)" + > + <label v-if="typeof fallback !== 'undefined'" class="opt-l" :for="name + '-o'"></label> + <input + :id="name" + class="color-input" + type="color" + :value="value || fallback" + :disabled="!present" + @input="$emit('input', $event.target.value)" + > + <input + :id="name + '-t'" + class="text-input" + type="text" + :value="value || fallback" + :disabled="!present" + @input="$emit('input', $event.target.value)" + > +</div> +</template> + +<script> +export default { + props: [ + 'name', 'label', 'value', 'fallback' + ], + computed: { + present () { + return typeof this.value !== 'undefined' + } + } +} +</script> + +<style lang="scss"> +.color-control { + display: flex; + + &.disabled *:not(.opt-l){ + opacity: .5 + } + + .label { + flex: 2; + min-width: 7em; + } + + .opt-l { + align-self: center; + flex: 0; + &::before { + width: 14px; + height: 14px; + } + } + + .text-input { + max-width: 7em; + flex: 1; + } + + .color-input { + flex: 0; + padding: 1px; + cursor: pointer; + height: 100%; + max-height: 29px; + min-width: 2em; + border: none; + } +} +</style> diff --git a/src/components/notifications/notifications.scss b/src/components/notifications/notifications.scss index a137ccd5..a98c2549 100644 --- a/src/components/notifications/notifications.scss +++ b/src/components/notifications/notifications.scss @@ -22,8 +22,8 @@ } .loadmore-error { - color: $fallback--fg; - color: var(--fg, $fallback--fg); + color: $fallback--text; + color: var(--text, $fallback--text); } .unseen { @@ -90,6 +90,9 @@ padding: 0.25em 0; color: $fallback--faint; color: var(--faint, $fallback--faint); + a { + color: var(--faintLink); + } } padding: 0; .media-body { diff --git a/src/components/opacity_input/opacity_input.vue b/src/components/opacity_input/opacity_input.vue new file mode 100644 index 00000000..cfe6de21 --- /dev/null +++ b/src/components/opacity_input/opacity_input.vue @@ -0,0 +1,75 @@ +<template> +<div class="opacity-control" :class="{ disabled: !present }"> + <label :for="name" class="label"> + {{$t('settings.opacity')}} + </label> + <input + v-if="typeof fallback !== 'undefined'" + class="opt" + :id="name + '-o'" + type="checkbox" + :checked="present" + @input="$emit('input', !present ? fallback : undefined)" + > + <label v-if="typeof fallback !== 'undefined'" class="opt-l" :for="name + '-o'"></label> + <input + :id="name" + class="input-range" + type="range" + :value="value || fallback" + :disabled="!present" + @input="$emit('input', $event.target.value)" + max="1" + min="0" + step=".05"> +</div> +</template> + +<script> +export default { + props: [ + 'name', 'value', 'fallback' + ], + computed: { + present () { + return typeof this.value !== 'undefined' + } + } +} +</script> + +<style lang="scss"> +.opacity-control { + display: flex; + + &.disabled *:not(.opt-l) { + opacity: .5 + } + + .opt-l { + align-self: center; + line-height: 0; + &::before { + width: 14px; + height: 14px; + } + } + + .label { + flex: 2; + min-width: 7em; + } + + .input-range { + align-self: center; + background: none; + border: none; + padding: 0; + margin: 0; + height: auto; + box-shadow: none; + min-width: 9em; + flex: 1; + } +} +</style> diff --git a/src/components/post_status_form/post_status_form.vue b/src/components/post_status_form/post_status_form.vue index 42e9c65c..4514e79f 100644 --- a/src/components/post_status_form/post_status_form.vue +++ b/src/components/post_status_form/post_status_form.vue @@ -153,8 +153,8 @@ padding-bottom: 0; margin-left: $fallback--attachmentRadius; margin-left: var(--attachmentRadius, $fallback--attachmentRadius); - background-color: $fallback--btn; - background-color: var(--btn, $fallback--btn); + background-color: $fallback--fg; + background-color: var(--fg, $fallback--fg); border-bottom-left-radius: 0; border-bottom-right-radius: 0; } @@ -261,8 +261,8 @@ min-width: 75%; background: $fallback--bg; background: var(--bg, $fallback--bg); - color: $fallback--lightFg; - color: var(--lightFg, $fallback--lightFg); + color: $fallback--lightText; + color: var(--lightText, $fallback--lightText); } .autocomplete { @@ -291,8 +291,8 @@ } &.highlighted { - background-color: $fallback--btn; - background-color: var(--btn, $fallback--btn); + background-color: $fallback--fg; + background-color: var(--fg, $fallback--fg); } } } diff --git a/src/components/settings/settings.vue b/src/components/settings/settings.vue index eebb2cb7..990d1f0d 100644 --- a/src/components/settings/settings.vue +++ b/src/components/settings/settings.vue @@ -159,7 +159,7 @@ @import '../../_variables.scss'; .setting-item { - border-bottom: 2px solid var(--btn, $fallback--btn); + border-bottom: 2px solid var(--fg, $fallback--fg); margin: 1em 1em 1.4em; padding-bottom: 1.4em; diff --git a/src/components/status/status.vue b/src/components/status/status.vue index eb521280..57a007d9 100644 --- a/src/components/status/status.vue +++ b/src/components/status/status.vue @@ -284,8 +284,8 @@ margin-left: 0.2em; } a:hover i { - color: $fallback--fg; - color: var(--fg, $fallback--fg); + color: $fallback--text; + color: var(--text, $fallback--text); } } diff --git a/src/components/style_switcher/style_switcher.js b/src/components/style_switcher/style_switcher.js index a1c44be3..203ca18a 100644 --- a/src/components/style_switcher/style_switcher.js +++ b/src/components/style_switcher/style_switcher.js @@ -31,6 +31,7 @@ export default { panelColorLocal: undefined, panelTextColorLocal: undefined, + panelFaintColorLocal: undefined, panelOpacityLocal: undefined, topBarColorLocal: undefined, @@ -40,10 +41,17 @@ export default { alertOpacityLocal: undefined, - redColorLocal: '', - blueColorLocal: '', - greenColorLocal: '', - orangeColorLocal: '', + borderColorLocal: undefined, + borderOpacityLocal: undefined, + + faintColorLocal: undefined, + faintOpacityLocal: undefined, + faintLinkColorLocal: undefined, + + cRedColorLocal: '', + cBlueColorLocal: '', + cGreenColorLocal: '', + cOrangeColorLocal: '', btnRadiusLocal: '', inputRadiusLocal: '', @@ -74,16 +82,35 @@ export default { return { colors: { bg: this.bgColorLocal, - fg: this.fgColorLocal, text: this.textColorLocal, - panel: this.panelColorLocal, - topBar: this.topBarColorLocal, - btn: this.btnColorLocal, link: this.linkColorLocal, - cRed: this.redColorLocal, - cBlue: this.blueColorLocal, - cGreen: this.greenColorLocal, - cOrange: this.orangeColorLocal + + fg: this.fgColorLocal, + fgText: this.fgTextColorLocal, + fgLink: this.fgLinkColorLocal, + + panel: this.panelColorLocal, + panelText: this.panelTextColorLocal, + panelFaint: this.panelFaintColorLocal, + + input: this.inputColorLocal, + inputText: this.inputTextColorLocal, + + topBar: this.topBarColorLocal, + topBarText: this.topBarTextColorLocal, + topBarLink: this.topBarLinkColorLocal, + + btn: this.btnColorLocal, + btnText: this.btnTextColorLocal, + + faint: this.faintColorLocal, + faintLink: this.faintLinkColorLocal, + border: this.borderColorLocal, + + cRed: this.cRedColorLocal, + cBlue: this.cBlueColorLocal, + cGreen: this.cGreenColorLocal, + cOrange: this.cOrangeColorLocal }, radii: { btnRadius: this.btnRadiusLocal, @@ -197,12 +224,12 @@ export default { }, clearV1 () { + this.bgOpacityLocal = undefined this.fgOpacityLocal = undefined this.fgTextColorLocal = undefined this.fgLinkColorLocal = undefined - this.panelColorLocal = undefined - this.topBarColorLocal = undefined + this.btnColorLocal = undefined this.btnTextColorLocal = undefined this.btnOpacityLocal = undefined @@ -216,7 +243,17 @@ export default { this.topBarColorLocal = undefined this.topBarTextColorLocal = undefined + this.topBarLinkColorLocal = undefined this.topBarOpacityLocal = undefined + + this.alertOpacityLocal = undefined + + this.borderColorLocal = undefined + this.borderOpacityLocal = undefined + + this.faintColorLocal = undefined + this.faintOpacityLocal = undefined + this.faintLinkColorLocal = undefined }, /** @@ -228,22 +265,42 @@ export default { const colors = input.colors || input const radii = input.radii || input - this.bgColorLocal = rgb2hex(colors.bg) - this.fgColorLocal = rgb2hex(colors.fg) - this.textColorLocal = rgb2hex(colors.text) - this.linkColorLocal = rgb2hex(colors.link) - - if (version === 1) { - this.clearV1() + if (version === 0) { + if (input.version) version = input.version + // Old v1 naming: fg is text, btn is foreground + if (typeof colors.text === 'undefined' && typeof colors.fg !== 'undefined') { + version = 1 + } + // New v2 naming: text is text, fg is foreground + if (typeof colors.text !== 'undefined' && typeof colors.fg !== 'undefined') { + version = 2 + } } - this.panelColorLocal = rgb2hex(colors.panel) - this.topBarColorLocal = rgb2hex(colors.topBar) + console.log('BENIS') + console.log(version) + // Stuff that differs between V1 and V2 + if (version === 1) { + console.log(colors) + this.fgColorLocal = rgb2hex(colors.btn) + this.textColorLocal = rgb2hex(colors.fg) + } - this.redColorLocal = rgb2hex(colors.cRed) - this.blueColorLocal = rgb2hex(colors.cBlue) - this.greenColorLocal = rgb2hex(colors.cGreen) - this.orangeColorLocal = rgb2hex(colors.cOrange) + const keys = new Set(version !== 1 ? Object.keys(colors) : []) + if (version === 1) { + // V1 ignores the rest + this.clearV1() + keys + .add('bg') + .add('link') + .add('cRed') + .add('cBlue') + .add('cGreen') + .add('cOrange') + } + keys.forEach(key => { + this[key + 'ColorLocal'] = rgb2hex(colors[key]) + }) this.btnRadiusLocal = radii.btnRadius || 4 this.inputRadiusLocal = radii.inputRadius || 4 @@ -259,7 +316,7 @@ export default { if (this.selectedVersion === 1) { this.clearV1() this.bgColorLocal = this.selected[1] - this.btnColorLocal = this.selected[2] + this.fgColorLocal = this.selected[2] this.textColorLocal = this.selected[3] this.linkColorLocal = this.selected[4] this.redColorLocal = this.selected[5] diff --git a/src/components/style_switcher/style_switcher.vue b/src/components/style_switcher/style_switcher.vue index cf1fac92..7ddc2d1c 100644 --- a/src/components/style_switcher/style_switcher.vue +++ b/src/components/style_switcher/style_switcher.vue @@ -58,16 +58,16 @@ <div class="color-item"> <ColorInput name="fgColor" v-model="fgColorLocal" :label="$t('settings.foreground')"/> <OpacityInput name="fgOpacity" v-model="fgOpacityLocal" :fallback="bgOpacityLocal || 1"/> - <ColorInput name="fgTextColor" v-model="fgTextColorLocal" :label="$t('settings.text')" :fallback="previewTheme.colors.btnText"/> - <ColorInput name="fgLinkColor" v-model="fgLinkColorLocal" :label="$t('settings.links')" :fallback="linkColorLocal"/> + <ColorInput name="fgTextColor" v-model="fgTextColorLocal" :label="$t('settings.text')" :fallback="previewTheme.colors.fgText"/> + <ColorInput name="fgLinkColor" v-model="fgLinkColorLocal" :label="$t('settings.links')" :fallback="previewTheme.colors.fgLink"/> </div> <div class="color-item"> - <ColorInput name="cRedColor" v-model="redColorLocal" :label="$t('settings.cRed')"/> - <ColorInput name="cBlueColor" v-model="blueColorLocal" :label="$t('settings.cBlue')"/> + <ColorInput name="cRedColor" v-model="cRedColorLocal" :label="$t('settings.cRed')"/> + <ColorInput name="cBlueColor" v-model="cBlueColorLocal" :label="$t('settings.cBlue')"/> </div> <div class="color-item"> - <ColorInput name="cGreenColor" v-model="greenColorLocal" :label="$t('settings.cGreen')"/> - <ColorInput name="cOrangeColor" v-model="orangeColorLocal" :label="$t('settings.cOrange')"/> + <ColorInput name="cGreenColor" v-model="cGreenColorLocal" :label="$t('settings.cGreen')"/> + <ColorInput name="cOrangeColor" v-model="cOrangeColorLocal" :label="$t('settings.cOrange')"/> </div> <div class="color-item wide"> <h4>Alert opacity</h4> @@ -79,38 +79,40 @@ <div> <div class="color-item"> <h4>Panel header</h4> - <ColorInput name="panelColor" v-model="panelColorLocal" :fallback="btnColorLocal" :label="$t('settings.background')"/> + <ColorInput name="panelColor" v-model="panelColorLocal" :fallback="fgColorLocal" :label="$t('settings.background')"/> <OpacityInput name="panelOpacity" v-model="panelOpacityLocal" fallback="1"/> - <ColorInput name="panelTextColor" v-model="panelTextColorLocal" :fallback="textColorLocal" :label="$t('settings.links')"/> + <ColorInput name="panelTextColor" v-model="panelTextColorLocal" :fallback="previewTheme.colors.panelText" :label="$t('settings.links')"/> + <ColorInput name="panelFaintColor" v-model="panelFaintColorLocal" :fallback="previewTheme.colors.panelFaint" :label="$t('settings.faint')"/> </div> <div class="color-item"> <h4>Top bar</h4> - <ColorInput name="topBarColor" v-model="topBarColorLocal" :fallback="btnColorLocal" :label="$t('settings.background')"/> + <ColorInput name="topBarColor" v-model="topBarColorLocal" :fallback="fgColorLocal" :label="$t('settings.background')"/> <OpacityInput name="topBarOpacity" v-model="topBarOpacityLocal" fallback="1"/> - <ColorInput name="topBarTextColor" v-model="topBarTextColorLocal" :fallback="textColorLocal" :label="$t('settings.text')"/> - <ColorInput name="topBarLinkColor" v-model="topBarLinkColorLocal" :fallback="linkColorLocal" :label="$t('settings.links')"/> + <ColorInput name="topBarTextColor" v-model="topBarTextColorLocal" :fallback="previewTheme.colors.topBarText" :label="$t('settings.text')"/> + <ColorInput name="topBarLinkColor" v-model="topBarLinkColorLocal" :fallback="previewTheme.colors.topBarLink" :label="$t('settings.links')"/> </div> <div class="color-item"> <h4>Inputs</h4> - <ColorInput name="inputColor" v-model="inputColorLocal" :fallback="btnColorLocal" :label="$t('settings.background')"/> + <ColorInput name="inputColor" v-model="inputColorLocal" :fallback="fgColorLocal" :label="$t('settings.background')"/> <OpacityInput name="inputOpacity" v-model="inputOpacityLocal" fallback="0.5"/> - <ColorInput name="inputTextColor" v-model="inputTextColorLocal" :fallback="textColorLocal" :label="$t('settings.text')"/> + <ColorInput name="inputTextColor" v-model="inputTextColorLocal" :fallback="previewTheme.colors.inputText" :label="$t('settings.text')"/> </div> <div class="color-item"> <h4>Buttons</h4> - <ColorInput name="btnColor" v-model="btnColorLocal" :fallback="btnColorLocal" :label="$t('settings.background')"/> + <ColorInput name="btnColor" v-model="btnColorLocal" :fallback="fgColorLocal" :label="$t('settings.background')"/> <OpacityInput name="btnOpacity" v-model="btnOpacityLocal" fallback="0.5"/> - <ColorInput name="btnTextColor" v-model="btnTextColorLocal" :fallback="textColorLocal" :label="$t('settings.text')"/> + <ColorInput name="btnTextColor" v-model="btnTextColorLocal" :fallback="previewTheme.colors.btnText" :label="$t('settings.text')"/> </div> <div class="color-item"> <h4>Borders</h4> - <ColorInput name="btnColor" v-model="btnColorLocal" :fallback="btnColorLocal" label="Color"/> - <OpacityInput name="btnOpacity" v-model="btnOpacityLocal" fallback="0.5"/> + <ColorInput name="borderColor" v-model="borderColorLocal" :fallback="previewTheme.colors.border" label="Color"/> + <OpacityInput name="borderOpacity" v-model="borderOpacityLocal" fallback="0.5"/> </div> <div class="color-item"> <h4>Faint text</h4> - <ColorInput name="btnColor" v-model="btnColorLocal" :fallback="btnColorLocal" :label="$t('settings.text')"/> - <OpacityInput name="btnOpacity" v-model="btnOpacityLocal" fallback="0.5"/> + <ColorInput name="faintColor" v-model="faintColorLocal" :fallback="previewTheme.colors.faint" :label="$t('settings.text')"/> + <OpacityInput name="faintOpacity" v-model="faintOpacityLocal" fallback="0.5"/> + <ColorInput name="faintLinkColor" v-model="faintLinkColorLocal" :fallback="previewTheme.colors.faintLink" :label="$t('settings.link')"/> </div> </div> </div> diff --git a/src/components/tab_switcher/tab_switcher.scss b/src/components/tab_switcher/tab_switcher.scss index 374a19c5..578caec2 100644 --- a/src/components/tab_switcher/tab_switcher.scss +++ b/src/components/tab_switcher/tab_switcher.scss @@ -17,8 +17,8 @@ .tab, &::after, &::before { border-bottom: 1px solid; - border-bottom-color: $fallback--btn; - border-bottom-color: var(--btn, $fallback--btn); + border-bottom-color: $fallback--fg; + border-bottom-color: var(--fg, $fallback--fg); } .tab { @@ -28,8 +28,8 @@ &:not(.active) { border-bottom: 1px solid; - border-bottom-color: $fallback--btn; - border-bottom-color: var(--btn, $fallback--btn); + border-bottom-color: $fallback--fg; + border-bottom-color: var(--fg, $fallback--fg); z-index: 4; } diff --git a/src/components/timeline/timeline.vue b/src/components/timeline/timeline.vue index 2dd4376a..77a9a2af 100644 --- a/src/components/timeline/timeline.vue +++ b/src/components/timeline/timeline.vue @@ -61,12 +61,12 @@ opacity: 0.8; background-color: transparent; color: $fallback--faint; - color: var(--faint, $fallback--faint); + color: var(--panelFaint, $fallback--faint); } .loadmore-error { - color: $fallback--fg; - color: var(--fg, $fallback--fg); + color: $fallback--text; + color: var(--text, $fallback--text); } } @@ -79,7 +79,7 @@ border-color: var(--border, $fallback--border); padding: 10px; z-index: 1; - background-color: $fallback--btn; - background-color: var(--btn, $fallback--btn); + background-color: $fallback--fg; + background-color: var(--fg, $fallback--fg); } </style> diff --git a/src/components/user_card_content/user_card_content.vue b/src/components/user_card_content/user_card_content.vue index 59358040..f1b54fad 100644 --- a/src/components/user_card_content/user_card_content.vue +++ b/src/components/user_card_content/user_card_content.vue @@ -138,8 +138,8 @@ } .user-info { - color: $fallback--lightFg; - color: var(--lightFg, $fallback--lightFg); + color: $fallback--lightText; + color: var(--lightText, $fallback--lightText); padding: 0 16px; .container { @@ -173,8 +173,8 @@ } .usersettings { - color: $fallback--lightFg; - color: var(--lightFg, $fallback--lightFg); + color: $fallback--lightText; + color: var(--lightText, $fallback--lightText); opacity: .8; } @@ -193,8 +193,8 @@ } .user-screen-name { - color: $fallback--lightFg; - color: var(--lightFg, $fallback--lightFg); + color: $fallback--lightText; + color: var(--lightText, $fallback--lightText); display: inline-block; font-weight: light; font-size: 15px; @@ -269,8 +269,8 @@ padding: .5em 1.5em 0em 1.5em; text-align: center; justify-content: space-between; - color: $fallback--lightFg; - color: var(--lightFg, $fallback--lightFg); + color: $fallback--lightText; + color: var(--lightText, $fallback--lightText); &.clickable { .user-count { diff --git a/src/services/style_setter/style_setter.js b/src/services/style_setter/style_setter.js index cfa41b11..cc408933 100644 --- a/src/services/style_setter/style_setter.js +++ b/src/services/style_setter/style_setter.js @@ -112,27 +112,33 @@ const generatePreset = (input) => { colors.text = col.text colors.lightText = colors.text + colors.link = col.link + colors.border = col.border || col.fg + colors.faint = col.faint || col.text colors.bg = col.bg colors.lightBg = col.lightBg || brightness(5, colors.bg).rgb colors.fg = col.fg - colors.fgText = getTextColor(colors.fg, colors.text) + colors.fgText = col.fgText || getTextColor(colors.fg, colors.text) + colors.fgLink = col.fgLink || getTextColor(colors.fg, colors.link) colors.btn = col.btn || col.fg - colors.btnText = getTextColor(colors.btn, colors.text) + colors.btnText = col.btnText || getTextColor(colors.btn, colors.fgText) + + colors.input = col.input || col.fg + colors.inputText = col.inputText || getTextColor(colors.input, colors.fgText) colors.panel = col.panel || col.fg - colors.panelText = getTextColor(colors.panel, colors.text) + colors.panelText = col.panelText || getTextColor(colors.panel, colors.fgText) + colors.panelFaint = col.panelFaint || getTextColor(colors.panel, colors.faint) colors.topBar = col.topBar || col.fg - colors.topBarText = getTextColor(colors.topBar, colors.text) + colors.topBarText = col.topBarText || getTextColor(colors.topBar, colors.fgText) + colors.topBarLink = col.topBarLink || getTextColor(colors.topBar, colors.fgLink) - colors.input = col.input || Object.assign({ a: 0.5 }, col.btn) - colors.border = col.btn - colors.faint = col.faint || Object.assign({ a: 0.5 }, col.text) + colors.faintLink = col.faintLink || col.link - colors.link = col.link colors.icon = mixrgb(colors.bg, colors.text) colors.cBlue = col.cBlue @@ -153,7 +159,7 @@ const generatePreset = (input) => { colorRules: Object.entries(htmlColors).filter(([k, v]) => v).map(([k, v]) => `--${k}: ${v}`).join(';'), radiiRules: Object.entries(radii).filter(([k, v]) => v).map(([k, v]) => `--${k}: ${v}px`).join(';'), theme: { - colors, + colors: htmlColors, radii } } @@ -165,7 +171,7 @@ const setPreset = (val, commit) => { .then((themes) => { const theme = themes[val] ? themes[val] : themes['pleroma-dark'] const bgRgb = hex2rgb(theme[1]) - const btnRgb = hex2rgb(theme[2]) + const fgRgb = hex2rgb(theme[2]) const textRgb = hex2rgb(theme[3]) const linkRgb = hex2rgb(theme[4]) @@ -176,7 +182,7 @@ const setPreset = (val, commit) => { const colors = { bg: bgRgb, - btn: btnRgb, + fg: fgRgb, text: textRgb, link: linkRgb, cRed: cRedRgb, From 4d77b0c86bbd711e76e8ed23b6b227332bbea3cf Mon Sep 17 00:00:00 2001 From: Henry Jameson <me@hjkos.com> Date: Sun, 7 Oct 2018 22:03:34 +0300 Subject: [PATCH 06/96] Transparency works without exploding now. All nice. --- .../opacity_input/opacity_input.vue | 19 ++++++- .../style_switcher/style_switcher.js | 28 ++++++---- .../style_switcher/style_switcher.vue | 24 ++++----- src/components/tab_switcher/tab_switcher.scss | 8 +-- src/services/style_setter/style_setter.js | 54 +++++++++++++------ 5 files changed, 87 insertions(+), 46 deletions(-) diff --git a/src/components/opacity_input/opacity_input.vue b/src/components/opacity_input/opacity_input.vue index cfe6de21..09972868 100644 --- a/src/components/opacity_input/opacity_input.vue +++ b/src/components/opacity_input/opacity_input.vue @@ -22,6 +22,16 @@ max="1" min="0" step=".05"> + <input + :id="name" + class="input-number" + type="number" + :value="value || fallback" + :disabled="!present" + @input="$emit('input', $event.target.value)" + max="1" + min="0" + step=".05"> </div> </template> @@ -64,12 +74,17 @@ export default { align-self: center; background: none; border: none; - padding: 0; margin: 0; height: auto; box-shadow: none; - min-width: 9em; + min-width: 7em; flex: 1; } + .input-number { + align-self: center; + margin: 0; + min-width: 4em; + flex: 0; + } } </style> diff --git a/src/components/style_switcher/style_switcher.js b/src/components/style_switcher/style_switcher.js index 203ca18a..c419a9ce 100644 --- a/src/components/style_switcher/style_switcher.js +++ b/src/components/style_switcher/style_switcher.js @@ -17,7 +17,6 @@ export default { bgOpacityLocal: undefined, fgColorLocal: '', - fgOpacityLocal: undefined, fgTextColorLocal: undefined, fgLinkColorLocal: undefined, @@ -37,7 +36,6 @@ export default { topBarColorLocal: undefined, topBarTextColorLocal: undefined, topBarLinkColorLocal: undefined, - topBarOpacityLocal: undefined, alertOpacityLocal: undefined, @@ -112,6 +110,15 @@ export default { cGreen: this.cGreenColorLocal, cOrange: this.cOrangeColorLocal }, + opacity: { + bg: this.bgOpacityLocal, + btn: this.btnOpacityLocal, + input: this.inputOpacityLocal, + panel: this.panelOpacityLocal, + topBar: this.topBarOpacityLocal, + border: this.borderOpacityLocal, + faint: this.faintOpacityLocal + }, radii: { btnRadius: this.btnRadiusLocal, inputRadius: this.inputRadiusLocal, @@ -136,8 +143,7 @@ export default { } }, previewTheme () { - if (!this.preview.theme) return { colors: {}, radii: {} } - console.log(this.preview.theme) + if (!this.preview.theme) return { colors: {}, opacity: {}, radii: {} } return this.preview.theme }, previewRules () { @@ -226,7 +232,6 @@ export default { clearV1 () { this.bgOpacityLocal = undefined this.fgOpacityLocal = undefined - this.fgTextColorLocal = undefined this.fgLinkColorLocal = undefined this.btnColorLocal = undefined @@ -239,6 +244,7 @@ export default { this.panelColorLocal = undefined this.panelTextColorLocal = undefined + this.panelFaintColorLocal = undefined this.panelOpacityLocal = undefined this.topBarColorLocal = undefined @@ -246,8 +252,6 @@ export default { this.topBarLinkColorLocal = undefined this.topBarOpacityLocal = undefined - this.alertOpacityLocal = undefined - this.borderColorLocal = undefined this.borderOpacityLocal = undefined @@ -264,6 +268,7 @@ export default { normalizeLocalState (input, version = 0) { const colors = input.colors || input const radii = input.radii || input + const opacity = input.opacity || input if (version === 0) { if (input.version) version = input.version @@ -277,11 +282,8 @@ export default { } } - console.log('BENIS') - console.log(version) // Stuff that differs between V1 and V2 if (version === 1) { - console.log(colors) this.fgColorLocal = rgb2hex(colors.btn) this.textColorLocal = rgb2hex(colors.fg) } @@ -302,6 +304,7 @@ export default { this[key + 'ColorLocal'] = rgb2hex(colors[key]) }) + // TODO optimize this this.btnRadiusLocal = radii.btnRadius || 4 this.inputRadiusLocal = radii.inputRadius || 4 this.panelRadiusLocal = radii.panelRadius || 10 @@ -309,6 +312,11 @@ export default { this.avatarAltRadiusLocal = radii.avatarAltRadius || 50 this.tooltipRadiusLocal = radii.tooltipRadius || 2 this.attachmentRadiusLocal = radii.attachmentRadius || 5 + + Object.entries(opacity).forEach(([k, v]) => { + if (typeof v === 'undefined' || v === null || Number.isNaN(v)) return + this[k + 'OpacityLocal'] = v + }) } }, watch: { diff --git a/src/components/style_switcher/style_switcher.vue b/src/components/style_switcher/style_switcher.vue index 7ddc2d1c..1b00603c 100644 --- a/src/components/style_switcher/style_switcher.vue +++ b/src/components/style_switcher/style_switcher.vue @@ -51,13 +51,12 @@ <div> <div class="color-item"> <ColorInput name="bgColor" v-model="bgColorLocal" :label="$t('settings.background')"/> - <OpacityInput name="bgOpacity" v-model="bgOpacityLocal" fallback="1"/> + <OpacityInput name="bgOpacity" v-model="bgOpacityLocal" :fallback="previewTheme.opacity.bg || 1"/> <ColorInput name="textColor" v-model="textColorLocal" :label="$t('settings.text')"/> <ColorInput name="linkColor" v-model="linkColorLocal" :label="$t('settings.links')"/> </div> <div class="color-item"> <ColorInput name="fgColor" v-model="fgColorLocal" :label="$t('settings.foreground')"/> - <OpacityInput name="fgOpacity" v-model="fgOpacityLocal" :fallback="bgOpacityLocal || 1"/> <ColorInput name="fgTextColor" v-model="fgTextColorLocal" :label="$t('settings.text')" :fallback="previewTheme.colors.fgText"/> <ColorInput name="fgLinkColor" v-model="fgLinkColorLocal" :label="$t('settings.links')" :fallback="previewTheme.colors.fgLink"/> </div> @@ -71,7 +70,7 @@ </div> <div class="color-item wide"> <h4>Alert opacity</h4> - <OpacityInput name="alertOpacity" v-model="alertOpacityLocal" fallback="1"/> + <OpacityInput name="alertOpacity" v-model="alertOpacityLocal" fallback="previewTheme.opacity.alert || 1"/> </div> </div> @@ -80,39 +79,38 @@ <div class="color-item"> <h4>Panel header</h4> <ColorInput name="panelColor" v-model="panelColorLocal" :fallback="fgColorLocal" :label="$t('settings.background')"/> - <OpacityInput name="panelOpacity" v-model="panelOpacityLocal" fallback="1"/> + <OpacityInput name="panelOpacity" v-model="panelOpacityLocal" :fallback="previewTheme.opacity.panel || 1"/> <ColorInput name="panelTextColor" v-model="panelTextColorLocal" :fallback="previewTheme.colors.panelText" :label="$t('settings.links')"/> <ColorInput name="panelFaintColor" v-model="panelFaintColorLocal" :fallback="previewTheme.colors.panelFaint" :label="$t('settings.faint')"/> </div> <div class="color-item"> <h4>Top bar</h4> <ColorInput name="topBarColor" v-model="topBarColorLocal" :fallback="fgColorLocal" :label="$t('settings.background')"/> - <OpacityInput name="topBarOpacity" v-model="topBarOpacityLocal" fallback="1"/> <ColorInput name="topBarTextColor" v-model="topBarTextColorLocal" :fallback="previewTheme.colors.topBarText" :label="$t('settings.text')"/> <ColorInput name="topBarLinkColor" v-model="topBarLinkColorLocal" :fallback="previewTheme.colors.topBarLink" :label="$t('settings.links')"/> </div> <div class="color-item"> <h4>Inputs</h4> <ColorInput name="inputColor" v-model="inputColorLocal" :fallback="fgColorLocal" :label="$t('settings.background')"/> - <OpacityInput name="inputOpacity" v-model="inputOpacityLocal" fallback="0.5"/> + <OpacityInput name="inputOpacity" v-model="inputOpacityLocal" :fallback="previewTheme.opacity.input || 1"/> <ColorInput name="inputTextColor" v-model="inputTextColorLocal" :fallback="previewTheme.colors.inputText" :label="$t('settings.text')"/> </div> <div class="color-item"> <h4>Buttons</h4> <ColorInput name="btnColor" v-model="btnColorLocal" :fallback="fgColorLocal" :label="$t('settings.background')"/> - <OpacityInput name="btnOpacity" v-model="btnOpacityLocal" fallback="0.5"/> + <OpacityInput name="btnOpacity" v-model="btnOpacityLocal" :fallback="previewTheme.opacity.btn || 1"/> <ColorInput name="btnTextColor" v-model="btnTextColorLocal" :fallback="previewTheme.colors.btnText" :label="$t('settings.text')"/> </div> <div class="color-item"> <h4>Borders</h4> <ColorInput name="borderColor" v-model="borderColorLocal" :fallback="previewTheme.colors.border" label="Color"/> - <OpacityInput name="borderOpacity" v-model="borderOpacityLocal" fallback="0.5"/> + <OpacityInput name="borderOpacity" v-model="borderOpacityLocal" :fallback="previewTheme.opacity.border || 1"/> </div> <div class="color-item"> <h4>Faint text</h4> - <ColorInput name="faintColor" v-model="faintColorLocal" :fallback="previewTheme.colors.faint" :label="$t('settings.text')"/> - <OpacityInput name="faintOpacity" v-model="faintOpacityLocal" fallback="0.5"/> - <ColorInput name="faintLinkColor" v-model="faintLinkColorLocal" :fallback="previewTheme.colors.faintLink" :label="$t('settings.link')"/> + <ColorInput name="faintColor" v-model="faintColorLocal" :fallback="previewTheme.colors.faint || 1" :label="$t('settings.text')"/> + <ColorInput name="faintLinkColor" v-model="faintLinkColorLocal" :fallback="previewTheme.colors.faintLink" :label="$t('settings.links')"/> + <OpacityInput name="faintOpacity" v-model="faintOpacityLocal" fallback="previewTheme.opacity.faint"/> </div> </div> </div> @@ -255,10 +253,6 @@ label { color: var(--faint, $fallback--faint); } - .opacity-control { - margin-top: 5px; - margin-bottom: 5px; - } } .radius-item { diff --git a/src/components/tab_switcher/tab_switcher.scss b/src/components/tab_switcher/tab_switcher.scss index 578caec2..6f3f9f27 100644 --- a/src/components/tab_switcher/tab_switcher.scss +++ b/src/components/tab_switcher/tab_switcher.scss @@ -17,8 +17,8 @@ .tab, &::after, &::before { border-bottom: 1px solid; - border-bottom-color: $fallback--fg; - border-bottom-color: var(--fg, $fallback--fg); + border-bottom-color: $fallback--border; + border-bottom-color: var(--border, $fallback--border); } .tab { @@ -28,8 +28,8 @@ &:not(.active) { border-bottom: 1px solid; - border-bottom-color: $fallback--fg; - border-bottom-color: var(--fg, $fallback--fg); + border-bottom-color: $fallback--border; + border-bottom-color: var(--border, $fallback--border); z-index: 4; } diff --git a/src/services/style_setter/style_setter.js b/src/services/style_setter/style_setter.js index cc408933..4de39f79 100644 --- a/src/services/style_setter/style_setter.js +++ b/src/services/style_setter/style_setter.js @@ -90,7 +90,6 @@ const setColors = (input, commit) => { } const generatePreset = (input) => { - console.log(input) const radii = input.radii || { btnRadius: input.btnRadius, inputRadius: input.inputRadius, @@ -101,6 +100,12 @@ const generatePreset = (input) => { attachmentRadius: input.attachmentRadius } const colors = {} + const opacity = Object.assign({ + alert: 0.5, + input: 0.5, + faint: 0.5 + }, input.opacity) + const col = Object.entries(input.colors || input).reduce((acc, [k, v]) => { if (typeof v === 'object') { acc[k] = v @@ -110,11 +115,13 @@ const generatePreset = (input) => { return acc }, {}) + const isLightOnDark = convert(col.bg).hsl.l < convert(col.text).hsl.l + const mod = isLightOnDark ? 1 : -1 + colors.text = col.text - colors.lightText = colors.text + colors.lightText = brightness(20 * mod, colors.text).rgb colors.link = col.link - colors.border = col.border || col.fg - colors.faint = col.faint || col.text + colors.faint = col.faint || Object.assign({}, col.text) colors.bg = col.bg colors.lightBg = col.lightBg || brightness(5, colors.bg).rgb @@ -123,21 +130,23 @@ const generatePreset = (input) => { colors.fgText = col.fgText || getTextColor(colors.fg, colors.text) colors.fgLink = col.fgLink || getTextColor(colors.fg, colors.link) - colors.btn = col.btn || col.fg + colors.border = col.border || brightness(20 * mod, colors.fg).rgb + + colors.btn = col.btn || Object.assign({}, col.fg) colors.btnText = col.btnText || getTextColor(colors.btn, colors.fgText) - colors.input = col.input || col.fg - colors.inputText = col.inputText || getTextColor(colors.input, colors.fgText) + colors.input = col.input || Object.assign({}, col.fg) + colors.inputText = col.inputText || getTextColor(colors.input, colors.lightText) - colors.panel = col.panel || col.fg + colors.panel = col.panel || Object.assign({}, col.fg) colors.panelText = col.panelText || getTextColor(colors.panel, colors.fgText) colors.panelFaint = col.panelFaint || getTextColor(colors.panel, colors.faint) - colors.topBar = col.topBar || col.fg + colors.topBar = col.topBar || Object.assign({}, col.fg) colors.topBarText = col.topBarText || getTextColor(colors.topBar, colors.fgText) colors.topBarLink = col.topBarLink || getTextColor(colors.topBar, colors.fgLink) - colors.faintLink = col.faintLink || col.link + colors.faintLink = col.faintLink || Object.assign({}, col.link) colors.icon = mixrgb(colors.bg, colors.text) @@ -146,20 +155,35 @@ const generatePreset = (input) => { colors.cGreen = col.cGreen colors.cOrange = col.cOrange - colors.cAlertRed = col.cAlertRed || Object.assign({ a: 0.5 }, col.cRed) + colors.cAlertRed = col.cAlertRed || Object.assign({}, col.cRed) + + Object.entries(opacity).forEach(([ k, v ]) => { + if (typeof v === 'undefined') return + if (k === 'alert') { + colors.cAlertRed.a = v + return + } + if (k === 'faint') { + colors[k + 'Link'].a = v + colors['panelFaint'].a = v + } + colors[k].a = v + }) const htmlColors = Object.entries(colors) .reduce((acc, [k, v]) => { if (!v) return acc - acc[k] = typeof v.a === 'undefined' ? rgb2hex(v) : rgb2rgba(v) + acc.solid[k] = rgb2hex(v) + acc.complete[k] = typeof v.a === 'undefined' ? rgb2hex(v) : rgb2rgba(v) return acc - }, {}) + }, { complete: {}, solid: {} }) return { - colorRules: Object.entries(htmlColors).filter(([k, v]) => v).map(([k, v]) => `--${k}: ${v}`).join(';'), + colorRules: Object.entries(htmlColors.complete).filter(([k, v]) => v).map(([k, v]) => `--${k}: ${v}`).join(';'), radiiRules: Object.entries(radii).filter(([k, v]) => v).map(([k, v]) => `--${k}: ${v}px`).join(';'), theme: { - colors: htmlColors, + colors: htmlColors.solid, + opacity, radii } } From 87e98772b09b9b317dee020d22115517635a29c8 Mon Sep 17 00:00:00 2001 From: Henry Jameson <me@hjkos.com> Date: Wed, 10 Oct 2018 00:07:28 +0300 Subject: [PATCH 07/96] initial contrast display support --- .../contrast_ratio/contrast_ratio.vue | 64 +++++++++++++++++++ .../style_switcher/style_switcher.js | 37 +++++++++-- .../style_switcher/style_switcher.vue | 6 ++ src/services/color_convert/color_convert.js | 39 ++++++++++- 4 files changed, 141 insertions(+), 5 deletions(-) create mode 100644 src/components/contrast_ratio/contrast_ratio.vue diff --git a/src/components/contrast_ratio/contrast_ratio.vue b/src/components/contrast_ratio/contrast_ratio.vue new file mode 100644 index 00000000..6c4a59b6 --- /dev/null +++ b/src/components/contrast_ratio/contrast_ratio.vue @@ -0,0 +1,64 @@ +<template> +<div class="contrast-ratio"> + <span class="label"> + Contrast: + </span> + <span> + <span> + {{contrast.text}} + </span> + <span class="rating"> + <span v-if="contrast.aaa"> + AAA + </span> + <span v-if="!contrast.aaa && contrast.aa"> + AA + </span> + <span v-if="!contrast.aaa && !contrast.aa"> + bad + </span> + </span> + </span> + <span v-if="large"> + <span> + 18pt+: + </span> + <span class="rating"> + <span v-if="contrast.aaa"> + AAA + </span> + <span v-if="!contrast.aaa && contrast.aa"> + AA + </span> + <span v-if="!contrast.aaa && !contrast.aa"> + bad + </span> + </span> + </span> +</div> +</template> + +<script> +export default { + props: [ + 'large', 'contrast' + ] +} +</script> + +<style lang="scss"> +.contrast-ratio { + display: flex; + justify-content: end; + + .label { + margin-right: 1em; + } + + .rating { + display: inline-block; + min-width: 3em; + text-align: center; + } +} +</style> diff --git a/src/components/style_switcher/style_switcher.js b/src/components/style_switcher/style_switcher.js index c419a9ce..27efa230 100644 --- a/src/components/style_switcher/style_switcher.js +++ b/src/components/style_switcher/style_switcher.js @@ -1,5 +1,6 @@ -import { rgb2hex } from '../../services/color_convert/color_convert.js' +import { rgb2hex, hex2rgb, getContrastRatio } from '../../services/color_convert/color_convert.js' import ColorInput from '../color_input/color_input.vue' +import ContrastRatio from '../contrast_ratio/contrast_ratio.vue' import OpacityInput from '../opacity_input/opacity_input.vue' import StyleSetter from '../../services/style_setter/style_setter.js' @@ -127,7 +128,7 @@ export default { avatarAltRadius: this.avatarAltRadiusLocal, tooltipRadius: this.tooltipRadiusLocal, attachmentRadius: this.attachmentRadiusLocal - } + }, } }, preview () { @@ -143,9 +144,36 @@ export default { } }, previewTheme () { - if (!this.preview.theme) return { colors: {}, opacity: {}, radii: {} } + if (!this.preview.theme) return { colors: {}, opacity: {}, radii: {}, contrast: {} } return this.preview.theme }, + previewContrast () { + if (!this.previewTheme.colors) return {} + const colors = this.previewTheme.colors + const hints = (ratio) => ({ + text: ratio.toPrecision(3) + ':1', + // AA level, AAA level + aa: ratio >= 4.5, + aaa: ratio >= 7, + // same but for 18pt+ texts + laa: ratio >= 3, + laaa: ratio >= 4.5 + }) + + const ratios = { + bgText: getContrastRatio(hex2rgb(colors.bg), hex2rgb(colors.text)), + bgLink: getContrastRatio(hex2rgb(colors.bg), hex2rgb(colors.link)), + + panelText: getContrastRatio(hex2rgb(colors.panel), hex2rgb(colors.panelText)), + + btnText: getContrastRatio(hex2rgb(colors.btn), hex2rgb(colors.btnText)), + + topBarText: getContrastRatio(hex2rgb(colors.topBar), hex2rgb(colors.topBarText)), + topBarLink: getContrastRatio(hex2rgb(colors.topBar), hex2rgb(colors.topBarLink)), + } + + return Object.entries(ratios).reduce((acc, [k, v]) => { acc[k] = hints(v); return acc }, {}) + }, previewRules () { if (!this.preview.colorRules) return '' return [this.preview.colorRules, this.preview.radiiRules, 'color: var(--text)'].join(';') @@ -153,7 +181,8 @@ export default { }, components: { ColorInput, - OpacityInput + OpacityInput, + ContrastRatio }, methods: { exportCurrentTheme () { diff --git a/src/components/style_switcher/style_switcher.vue b/src/components/style_switcher/style_switcher.vue index 1b00603c..4235d65c 100644 --- a/src/components/style_switcher/style_switcher.vue +++ b/src/components/style_switcher/style_switcher.vue @@ -53,7 +53,9 @@ <ColorInput name="bgColor" v-model="bgColorLocal" :label="$t('settings.background')"/> <OpacityInput name="bgOpacity" v-model="bgOpacityLocal" :fallback="previewTheme.opacity.bg || 1"/> <ColorInput name="textColor" v-model="textColorLocal" :label="$t('settings.text')"/> + <ContrastRatio :contrast="previewContrast.bgText"/> <ColorInput name="linkColor" v-model="linkColorLocal" :label="$t('settings.links')"/> + <ContrastRatio :contrast="previewContrast.bgLink"/> </div> <div class="color-item"> <ColorInput name="fgColor" v-model="fgColorLocal" :label="$t('settings.foreground')"/> @@ -81,13 +83,16 @@ <ColorInput name="panelColor" v-model="panelColorLocal" :fallback="fgColorLocal" :label="$t('settings.background')"/> <OpacityInput name="panelOpacity" v-model="panelOpacityLocal" :fallback="previewTheme.opacity.panel || 1"/> <ColorInput name="panelTextColor" v-model="panelTextColorLocal" :fallback="previewTheme.colors.panelText" :label="$t('settings.links')"/> + <ContrastRatio :contrast="previewContrast.panelText" large="1"/> <ColorInput name="panelFaintColor" v-model="panelFaintColorLocal" :fallback="previewTheme.colors.panelFaint" :label="$t('settings.faint')"/> </div> <div class="color-item"> <h4>Top bar</h4> <ColorInput name="topBarColor" v-model="topBarColorLocal" :fallback="fgColorLocal" :label="$t('settings.background')"/> <ColorInput name="topBarTextColor" v-model="topBarTextColorLocal" :fallback="previewTheme.colors.topBarText" :label="$t('settings.text')"/> + <ContrastRatio :contrast="previewContrast.topBarText"/> <ColorInput name="topBarLinkColor" v-model="topBarLinkColorLocal" :fallback="previewTheme.colors.topBarLink" :label="$t('settings.links')"/> + <ContrastRatio :contrast="previewContrast.topBarLink"/> </div> <div class="color-item"> <h4>Inputs</h4> @@ -100,6 +105,7 @@ <ColorInput name="btnColor" v-model="btnColorLocal" :fallback="fgColorLocal" :label="$t('settings.background')"/> <OpacityInput name="btnOpacity" v-model="btnOpacityLocal" :fallback="previewTheme.opacity.btn || 1"/> <ColorInput name="btnTextColor" v-model="btnTextColorLocal" :fallback="previewTheme.colors.btnText" :label="$t('settings.text')"/> + <ContrastRatio :contrast="previewContrast.btnText"/> </div> <div class="color-item"> <h4>Borders</h4> diff --git a/src/services/color_convert/color_convert.js b/src/services/color_convert/color_convert.js index ae5d5a31..31ee3a6b 100644 --- a/src/services/color_convert/color_convert.js +++ b/src/services/color_convert/color_convert.js @@ -19,6 +19,42 @@ const rgb2hex = (r, g, b) => { return `#${((1 << 24) + (r << 16) + (g << 8) + b).toString(16).slice(1)}` } +// https://www.w3.org/TR/2008/REC-WCAG20-20081211/#relativeluminancedef +// https://www.w3.org/TR/2008/REC-WCAG20-20081211/relative-luminance.xml +// https://en.wikipedia.org/wiki/SRGB#The_reverse_transformation +const c2linear = (b) => { + // W3C gives 0.03928 while wikipedia states 0.04045 + // what those magical numbers mean - I don't know. + // something about gamma-correction, i suppose. + // Sticking with W3C example. + const c = b / 255 + if (c < 0.03928) { + return c / 12.92 + } else { + return Math.pow((c + 0.055) / 1.055, 2.4) + } +} + +const srgbToLinear = (srgb) => { + return 'rgb'.split('').reduce((acc, c) => { acc[c] = c2linear(srgb[c]); return acc }, {}) +} + +// https://www.w3.org/TR/2008/REC-WCAG20-20081211/#relativeluminancedef +// https://www.w3.org/TR/2008/REC-WCAG20-20081211/relative-luminance.xml +const relativeLuminance = (srgb) => { + const {r, g, b} = srgbToLinear(srgb) + return 0.2126 * r + 0.7152 * g + 0.0722 * b +} + +// https://www.w3.org/TR/2008/REC-WCAG20-20081211/#contrast-ratiodef +const getContrastRatio = (a, b) => { + const la = relativeLuminance(a) + const lb = relativeLuminance(b) + const [l1, l2] = la > lb ? [la, lb] : [lb, la] + + return (l1 + 0.05) / (l2 + 0.05) +} + const hex2rgb = (hex) => { const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex) return result ? { @@ -47,5 +83,6 @@ export { rgb2hex, hex2rgb, mixrgb, - rgbstr2hex + rgbstr2hex, + getContrastRatio } From 4b7b7d9905b965c225fd42fb68682b9602254c82 Mon Sep 17 00:00:00 2001 From: Henry Jameson <me@hjkos.com> Date: Wed, 10 Oct 2018 05:39:02 +0300 Subject: [PATCH 08/96] cleanup, documentation, contrast taking alpha into account. --- .../style_switcher/style_switcher.js | 38 +++++++--- src/services/color_convert/color_convert.js | 70 ++++++++++++++++--- 2 files changed, 91 insertions(+), 17 deletions(-) diff --git a/src/components/style_switcher/style_switcher.js b/src/components/style_switcher/style_switcher.js index 27efa230..d4381202 100644 --- a/src/components/style_switcher/style_switcher.js +++ b/src/components/style_switcher/style_switcher.js @@ -1,4 +1,4 @@ -import { rgb2hex, hex2rgb, getContrastRatio } from '../../services/color_convert/color_convert.js' +import { rgb2hex, hex2rgb, getContrastRatio, worstCase } from '../../services/color_convert/color_convert.js' import ColorInput from '../color_input/color_input.vue' import ContrastRatio from '../contrast_ratio/contrast_ratio.vue' import OpacityInput from '../opacity_input/opacity_input.vue' @@ -144,12 +144,13 @@ export default { } }, previewTheme () { - if (!this.preview.theme) return { colors: {}, opacity: {}, radii: {}, contrast: {} } + if (!this.preview.theme) return { colors: {}, opacity: {}, radii: {} } return this.preview.theme }, previewContrast () { if (!this.previewTheme.colors) return {} const colors = this.previewTheme.colors + const opacity = this.previewTheme.opacity const hints = (ratio) => ({ text: ratio.toPrecision(3) + ':1', // AA level, AAA level @@ -160,16 +161,37 @@ export default { laaa: ratio >= 4.5 }) + // fgsfds :DDDD + const fgs = { + text: hex2rgb(colors.text), + panelText: hex2rgb(colors.panelText), + btnText: hex2rgb(colors.btnText), + topBarText: hex2rgb(colors.topBarText), + + link: hex2rgb(colors.link), + topBarLink: hex2rgb(colors.topBarLink), + } + + const bgs = { + bg: hex2rgb(colors.bg), + btn: hex2rgb(colors.btn), + panel: hex2rgb(colors.panel), + topBar: hex2rgb(colors.topBar) + } + const ratios = { - bgText: getContrastRatio(hex2rgb(colors.bg), hex2rgb(colors.text)), - bgLink: getContrastRatio(hex2rgb(colors.bg), hex2rgb(colors.link)), + bgText: getContrastRatio(worstCase(bgs.bg, opacity.bg, fgs.text), fgs.text), + bgLink: getContrastRatio(worstCase(bgs.bg, opacity.bg, fgs.link), fgs.link), - panelText: getContrastRatio(hex2rgb(colors.panel), hex2rgb(colors.panelText)), + // User Profile + tintText: getContrastRatio(worstCase(bgs.bg, 0.5, fgs.panelText), fgs.text), - btnText: getContrastRatio(hex2rgb(colors.btn), hex2rgb(colors.btnText)), + panelText: getContrastRatio(worstCase(bgs.panel, opacity.panel, fgs.panelText), fgs.panelText), - topBarText: getContrastRatio(hex2rgb(colors.topBar), hex2rgb(colors.topBarText)), - topBarLink: getContrastRatio(hex2rgb(colors.topBar), hex2rgb(colors.topBarLink)), + btnText: getContrastRatio(worstCase(bgs.btn, opacity.btn, fgs.btnText), fgs.btnText), + + topBarText: getContrastRatio(worstCase(bgs.topBar, opacity.topBar, fgs.topBarText), fgs.topBarText), + topBarLink: getContrastRatio(worstCase(bgs.topBar, opacity.topBar, fgs.topBarLink), fgs.topBarLink) } return Object.entries(ratios).reduce((acc, [k, v]) => { acc[k] = hints(v); return acc }, {}) diff --git a/src/services/color_convert/color_convert.js b/src/services/color_convert/color_convert.js index 31ee3a6b..0acc7e7c 100644 --- a/src/services/color_convert/color_convert.js +++ b/src/services/color_convert/color_convert.js @@ -19,15 +19,21 @@ const rgb2hex = (r, g, b) => { return `#${((1 << 24) + (r << 16) + (g << 8) + b).toString(16).slice(1)}` } -// https://www.w3.org/TR/2008/REC-WCAG20-20081211/#relativeluminancedef -// https://www.w3.org/TR/2008/REC-WCAG20-20081211/relative-luminance.xml -// https://en.wikipedia.org/wiki/SRGB#The_reverse_transformation -const c2linear = (b) => { +/** + * Converts 8-bit RGB component into linear component + * https://www.w3.org/TR/2008/REC-WCAG20-20081211/#relativeluminancedef + * https://www.w3.org/TR/2008/REC-WCAG20-20081211/relative-luminance.xml + * https://en.wikipedia.org/wiki/SRGB#The_reverse_transformation + * + * @param {Number} bit - color component [0..255] + * @returns {Number} linear component [0..1] + */ +const c2linear = (bit) => { // W3C gives 0.03928 while wikipedia states 0.04045 // what those magical numbers mean - I don't know. // something about gamma-correction, i suppose. // Sticking with W3C example. - const c = b / 255 + const c = bit / 255 if (c < 0.03928) { return c / 12.92 } else { @@ -35,18 +41,36 @@ const c2linear = (b) => { } } +/** + * Converts sRGB into linear RGB + * @param {Object} srgb - sRGB color + * @returns {Object} linear rgb color + */ const srgbToLinear = (srgb) => { return 'rgb'.split('').reduce((acc, c) => { acc[c] = c2linear(srgb[c]); return acc }, {}) } -// https://www.w3.org/TR/2008/REC-WCAG20-20081211/#relativeluminancedef -// https://www.w3.org/TR/2008/REC-WCAG20-20081211/relative-luminance.xml +/** + * Calculates relative luminance for given color + * https://www.w3.org/TR/2008/REC-WCAG20-20081211/#relativeluminancedef + * https://www.w3.org/TR/2008/REC-WCAG20-20081211/relative-luminance.xml + * + * @param {Object} srgb - sRGB color + * @returns {Number} relative luminance + */ const relativeLuminance = (srgb) => { const {r, g, b} = srgbToLinear(srgb) return 0.2126 * r + 0.7152 * g + 0.0722 * b } -// https://www.w3.org/TR/2008/REC-WCAG20-20081211/#contrast-ratiodef +/** + * Generates color ratio between two colors. Order is unimporant + * https://www.w3.org/TR/2008/REC-WCAG20-20081211/#contrast-ratiodef + * + * @param {Object} a - sRGB color + * @param {Object} b - sRGB color + * @returns {Number} color ratio + */ const getContrastRatio = (a, b) => { const la = relativeLuminance(a) const lb = relativeLuminance(b) @@ -55,6 +79,33 @@ const getContrastRatio = (a, b) => { return (l1 + 0.05) / (l2 + 0.05) } +/** + * This generates what "worst case" color would look like for transparent + * segments. I.e. black with .2 alpha and pure-white background image + * could make white text unreadable + * + * @param {Object} srgb - transparent color + * @param {Number} alpha - color's opacity/alpha channel + * @param {Boolean} white - use white "background" if true, black otherwise + * @returns {Object} sRGB of resulting color + */ +const transparentWorstCase = (srgb, alpha, white = false) => { + const bg = 'rgb'.split('').reduce((acc, c) => { acc[c] = Number(white) * 255; return acc }, {}) + return 'rgb'.split('').reduce((acc, c) => { + // Simplified https://en.wikipedia.org/wiki/Alpha_compositing#Alpha_blending + // for opaque bg and transparent fg + acc[c] = (srgb[c] * alpha + bg[c] * (1 - alpha)) + return acc + }, {}) +} + +const worstCase = (bg, bga, text) => { + if (bga === 1 || typeof bga === 'undefined') return bg + // taken from https://github.com/toish/chromatism/blob/master/src/operations/contrastRatio.js + const blackWorse = ((text.r * 299) + (text.g * 587) + (text.b * 114)) / 1000 <= 128 + return transparentWorstCase(bg, bga, !blackWorse) +} + const hex2rgb = (hex) => { const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex) return result ? { @@ -84,5 +135,6 @@ export { hex2rgb, mixrgb, rgbstr2hex, - getContrastRatio + getContrastRatio, + worstCase } From 7b657fcccd3524aba552cab4ee1005057fd83d41 Mon Sep 17 00:00:00 2001 From: Henry Jameson <me@hjkos.com> Date: Sun, 21 Oct 2018 15:25:21 +0300 Subject: [PATCH 09/96] added contrasts for rgbo --- .../style_switcher/style_switcher.js | 20 +++++++++++++------ .../style_switcher/style_switcher.vue | 4 ++++ 2 files changed, 18 insertions(+), 6 deletions(-) diff --git a/src/components/style_switcher/style_switcher.js b/src/components/style_switcher/style_switcher.js index d4381202..7f794608 100644 --- a/src/components/style_switcher/style_switcher.js +++ b/src/components/style_switcher/style_switcher.js @@ -128,7 +128,7 @@ export default { avatarAltRadius: this.avatarAltRadiusLocal, tooltipRadius: this.tooltipRadiusLocal, attachmentRadius: this.attachmentRadiusLocal - }, + } } }, preview () { @@ -170,6 +170,11 @@ export default { link: hex2rgb(colors.link), topBarLink: hex2rgb(colors.topBarLink), + + red: hex2rgb(colors.cRed), + green: hex2rgb(colors.cGreen), + blue: hex2rgb(colors.cBlue), + orange: hex2rgb(colors.cOrange) } const bgs = { @@ -182,8 +187,11 @@ export default { const ratios = { bgText: getContrastRatio(worstCase(bgs.bg, opacity.bg, fgs.text), fgs.text), bgLink: getContrastRatio(worstCase(bgs.bg, opacity.bg, fgs.link), fgs.link), + bgRed: getContrastRatio(worstCase(bgs.bg, opacity.bg, fgs.red), fgs.red), + bgGreen: getContrastRatio(worstCase(bgs.bg, opacity.bg, fgs.green), fgs.green), + bgBlue: getContrastRatio(worstCase(bgs.bg, opacity.bg, fgs.blue), fgs.blue), + bgOrange: getContrastRatio(worstCase(bgs.bg, opacity.bg, fgs.orange), fgs.orange), - // User Profile tintText: getContrastRatio(worstCase(bgs.bg, 0.5, fgs.panelText), fgs.text), panelText: getContrastRatio(worstCase(bgs.panel, opacity.panel, fgs.panelText), fgs.panelText), @@ -378,10 +386,10 @@ export default { this.fgColorLocal = this.selected[2] this.textColorLocal = this.selected[3] this.linkColorLocal = this.selected[4] - this.redColorLocal = this.selected[5] - this.greenColorLocal = this.selected[6] - this.blueColorLocal = this.selected[7] - this.orangeColorLocal = this.selected[8] + this.cRedColorLocal = this.selected[5] + this.cGreenColorLocal = this.selected[6] + this.cBlueColorLocal = this.selected[7] + this.cOrangeColorLocal = this.selected[8] } } } diff --git a/src/components/style_switcher/style_switcher.vue b/src/components/style_switcher/style_switcher.vue index 4235d65c..cecd6bc0 100644 --- a/src/components/style_switcher/style_switcher.vue +++ b/src/components/style_switcher/style_switcher.vue @@ -64,11 +64,15 @@ </div> <div class="color-item"> <ColorInput name="cRedColor" v-model="cRedColorLocal" :label="$t('settings.cRed')"/> + <ContrastRatio :contrast="previewContrast.bgRed"/> <ColorInput name="cBlueColor" v-model="cBlueColorLocal" :label="$t('settings.cBlue')"/> + <ContrastRatio :contrast="previewContrast.bgBlue"/> </div> <div class="color-item"> <ColorInput name="cGreenColor" v-model="cGreenColorLocal" :label="$t('settings.cGreen')"/> + <ContrastRatio :contrast="previewContrast.bgGreen"/> <ColorInput name="cOrangeColor" v-model="cOrangeColorLocal" :label="$t('settings.cOrange')"/> + <ContrastRatio :contrast="previewContrast.bgOrange"/> </div> <div class="color-item wide"> <h4>Alert opacity</h4> From 1723f427f59bb6bf62bb35de93c7226aef2e8727 Mon Sep 17 00:00:00 2001 From: Henry Jameson <me@hjkos.com> Date: Tue, 13 Nov 2018 16:30:01 +0300 Subject: [PATCH 10/96] updates --- src/App.scss | 4 +- src/_variables.scss | 2 +- .../contrast_ratio/contrast_ratio.vue | 50 +++++++----------- .../notifications/notifications.scss | 8 +-- .../opacity_input/opacity_input.vue | 10 ---- .../style_switcher/style_switcher.js | 24 ++++++++- .../style_switcher/style_switcher.vue | 26 +++++---- src/services/color_convert/color_convert.js | 16 +++--- src/services/style_setter/style_setter.js | 6 ++- static/font/config.json | 26 ++++++--- static/font/css/fontello-codes.css | 6 ++- static/font/css/fontello-embedded.css | 18 ++++--- static/font/css/fontello-ie7-codes.css | 6 ++- static/font/css/fontello-ie7.css | 6 ++- static/font/css/fontello.css | 20 +++---- static/font/demo.html | 22 ++++---- static/font/font/fontello.eot | Bin 15552 -> 16124 bytes static/font/font/fontello.svg | 8 ++- static/font/font/fontello.ttf | Bin 15384 -> 15956 bytes static/font/font/fontello.woff | Bin 9432 -> 9848 bytes static/font/font/fontello.woff2 | Bin 8020 -> 8372 bytes 21 files changed, 145 insertions(+), 113 deletions(-) diff --git a/src/App.scss b/src/App.scss index c91b6a61..0a2ff5cc 100644 --- a/src/App.scss +++ b/src/App.scss @@ -479,8 +479,8 @@ nav { line-height: 28px; &.error { - background-color: $fallback--cAlertRed; - background-color: var(--cAlertRed, $fallback--cAlertRed); + background-color: $fallback--alertError; + background-color: var(--alertError, $fallback--alertError); } } diff --git a/src/_variables.scss b/src/_variables.scss index 0f73e929..d0d91efe 100644 --- a/src/_variables.scss +++ b/src/_variables.scss @@ -16,7 +16,7 @@ $fallback--cBlue: #0095ff; $fallback--cGreen: #0fa00f; $fallback--cOrange: orange; -$fallback--cAlertRed: rgba(211,16,20,.5); +$fallback--alertError: rgba(211,16,20,.5); $fallback--panelRadius: 10px; $fallback--checkBoxRadius: 2px; diff --git a/src/components/contrast_ratio/contrast_ratio.vue b/src/components/contrast_ratio/contrast_ratio.vue index 6c4a59b6..a428e75f 100644 --- a/src/components/contrast_ratio/contrast_ratio.vue +++ b/src/components/contrast_ratio/contrast_ratio.vue @@ -1,41 +1,28 @@ <template> -<div class="contrast-ratio"> - <span class="label"> - Contrast: - </span> - <span> - <span> - {{contrast.text}} +<span class="contrast-ratio"> + <span :title="`Contrast is ${contrast.text}`" class="rating"> + <span v-if="contrast.aaa"> + <i class="icon-thumbs-up-alt"/> </span> - <span class="rating"> - <span v-if="contrast.aaa"> - AAA - </span> - <span v-if="!contrast.aaa && contrast.aa"> - AA - </span> - <span v-if="!contrast.aaa && !contrast.aa"> - bad - </span> + <span v-if="!contrast.aaa && contrast.aa"> + <i class="icon-adjust"/> + </span> + <span v-if="!contrast.aaa && !contrast.aa"> + <i class="icon-attention"/> </span> </span> - <span v-if="large"> - <span> - 18pt+: + <span class="rating" v-if="large" :title="`Contrast is ${contrast.text} (18pt+)`"> + <span v-if="contrast.aaa"> + <i class="icon-thumbs-up-alt"/> </span> - <span class="rating"> - <span v-if="contrast.aaa"> - AAA - </span> - <span v-if="!contrast.aaa && contrast.aa"> - AA - </span> - <span v-if="!contrast.aaa && !contrast.aa"> - bad - </span> + <span v-if="!contrast.aaa && contrast.aa"> + <i class="icon-adjust"/> + </span> + <span v-if="!contrast.aaa && !contrast.aa"> + <i class="icon-attention"/> </span> </span> -</div> +</span> </template> <script> @@ -57,7 +44,6 @@ export default { .rating { display: inline-block; - min-width: 3em; text-align: center; } } diff --git a/src/components/notifications/notifications.scss b/src/components/notifications/notifications.scss index a98c2549..98fdd3f5 100644 --- a/src/components/notifications/notifications.scss +++ b/src/components/notifications/notifications.scss @@ -7,7 +7,7 @@ .unseen-count { display: inline-block; background-color: $fallback--cRed; - background-color: var(--cRed, $fallback--cRed); + background-color: var(--badgeNotification, $fallback--cRed); text-shadow: 0px 0px 3px rgba(0, 0, 0, 0.5); border-radius: 99px; min-width: 22px; @@ -27,7 +27,7 @@ } .unseen { - box-shadow: inset 4px 0 0 var(--cRed, $fallback--cRed); + box-shadow: inset 4px 0 0 var(--badgeNotification, $fallback--cRed); padding-left: 0; } } @@ -44,8 +44,8 @@ border-radius: var(--tooltipRadius, $fallback--tooltipRadius); color: $fallback--faint; color: var(--faint, $fallback--faint); - background-color: $fallback--cAlertRed; - background-color: var(--cAlertRed, $fallback--cAlertRed); + background-color: $fallback--alertError; + background-color: var(--alertError, $fallback--alertError); padding: 2px .5em } diff --git a/src/components/opacity_input/opacity_input.vue b/src/components/opacity_input/opacity_input.vue index 09972868..efa6c449 100644 --- a/src/components/opacity_input/opacity_input.vue +++ b/src/components/opacity_input/opacity_input.vue @@ -12,16 +12,6 @@ @input="$emit('input', !present ? fallback : undefined)" > <label v-if="typeof fallback !== 'undefined'" class="opt-l" :for="name + '-o'"></label> - <input - :id="name" - class="input-range" - type="range" - :value="value || fallback" - :disabled="!present" - @input="$emit('input', $event.target.value)" - max="1" - min="0" - step=".05"> <input :id="name" class="input-number" diff --git a/src/components/style_switcher/style_switcher.js b/src/components/style_switcher/style_switcher.js index 7f794608..8953dc49 100644 --- a/src/components/style_switcher/style_switcher.js +++ b/src/components/style_switcher/style_switcher.js @@ -39,6 +39,10 @@ export default { topBarLinkColorLocal: undefined, alertOpacityLocal: undefined, + alertErrorColorLocal: undefined, + + badgeOpacityLocal: undefined, + badgeNotificationColorLocal: undefined, borderColorLocal: undefined, borderOpacityLocal: undefined, @@ -102,6 +106,9 @@ export default { btn: this.btnColorLocal, btnText: this.btnTextColorLocal, + alertError: this.alertErrorColorLocal, + badgeNotification: this.badgeNotificationColorLocal, + faint: this.faintColorLocal, faintLink: this.faintLinkColorLocal, border: this.borderColorLocal, @@ -116,6 +123,8 @@ export default { btn: this.btnOpacityLocal, input: this.inputOpacityLocal, panel: this.panelOpacityLocal, + alert: this.alertOpacityLocal, + badge: this.badgeOpacityLocal, topBar: this.topBarOpacityLocal, border: this.borderOpacityLocal, faint: this.faintOpacityLocal @@ -167,6 +176,7 @@ export default { panelText: hex2rgb(colors.panelText), btnText: hex2rgb(colors.btnText), topBarText: hex2rgb(colors.topBarText), + inputText: hex2rgb(colors.inputText), link: hex2rgb(colors.link), topBarLink: hex2rgb(colors.topBarLink), @@ -181,7 +191,10 @@ export default { bg: hex2rgb(colors.bg), btn: hex2rgb(colors.btn), panel: hex2rgb(colors.panel), - topBar: hex2rgb(colors.topBar) + topBar: hex2rgb(colors.topBar), + input: hex2rgb(colors.input), + alertError: hex2rgb(colors.alertError), + badgeNotification: hex2rgb(colors.badgeNotification) } const ratios = { @@ -198,6 +211,10 @@ export default { btnText: getContrastRatio(worstCase(bgs.btn, opacity.btn, fgs.btnText), fgs.btnText), + inputText: getContrastRatio(worstCase(bgs.input, opacity.input, fgs.inputText), fgs.inputText), + + badgeNotification: getContrastRatio(worstCase(bgs.badgeNotification, opacity.badge, fgs.text), fgs.text), + topBarText: getContrastRatio(worstCase(bgs.topBar, opacity.topBar, fgs.topBarText), fgs.topBarText), topBarLink: getContrastRatio(worstCase(bgs.topBar, opacity.topBar, fgs.topBarLink), fgs.topBarLink) } @@ -317,6 +334,10 @@ export default { this.faintColorLocal = undefined this.faintOpacityLocal = undefined this.faintLinkColorLocal = undefined + + this.alertErrorColorLocal = undefined + + this.badgeNotificationColorLocal = undefined }, /** @@ -348,6 +369,7 @@ export default { } const keys = new Set(version !== 1 ? Object.keys(colors) : []) + console.log(keys) if (version === 1) { // V1 ignores the rest this.clearV1() diff --git a/src/components/style_switcher/style_switcher.vue b/src/components/style_switcher/style_switcher.vue index cecd6bc0..f4b4e88f 100644 --- a/src/components/style_switcher/style_switcher.vue +++ b/src/components/style_switcher/style_switcher.vue @@ -74,21 +74,27 @@ <ColorInput name="cOrangeColor" v-model="cOrangeColorLocal" :label="$t('settings.cOrange')"/> <ContrastRatio :contrast="previewContrast.bgOrange"/> </div> - <div class="color-item wide"> - <h4>Alert opacity</h4> - <OpacityInput name="alertOpacity" v-model="alertOpacityLocal" fallback="previewTheme.opacity.alert || 1"/> - </div> </div> <h3>More customs!</h3> <div> + <div class="color-item"> + <h4>Alerts</h4> + <ColorInput name="alertError" v-model="alertErrorColorLocal" :label="$t('settings.error')" :fallback="previewTheme.colors.alertError"/> + <OpacityInput name="alertOpacity" v-model="alertOpacityLocal" :fallback="previewTheme.opacity.alert || 1"/> + </div> + <div class="color-item"> + <h4>Alerts</h4> + <ColorInput name="badgeNotification" v-model="badgeNotificationColorLocal" :label="$t('settings.notification')" :fallback="previewTheme.colors.badgeNotification"/> + <ContrastRatio :contrast="previewContrast.badgeNotification"/> + <OpacityInput name="badgeOpacity" v-model="badgeOpacityLocal" :fallback="previewTheme.opacity.badge || 1"/> + </div> <div class="color-item"> <h4>Panel header</h4> <ColorInput name="panelColor" v-model="panelColorLocal" :fallback="fgColorLocal" :label="$t('settings.background')"/> <OpacityInput name="panelOpacity" v-model="panelOpacityLocal" :fallback="previewTheme.opacity.panel || 1"/> <ColorInput name="panelTextColor" v-model="panelTextColorLocal" :fallback="previewTheme.colors.panelText" :label="$t('settings.links')"/> <ContrastRatio :contrast="previewContrast.panelText" large="1"/> - <ColorInput name="panelFaintColor" v-model="panelFaintColorLocal" :fallback="previewTheme.colors.panelFaint" :label="$t('settings.faint')"/> </div> <div class="color-item"> <h4>Top bar</h4> @@ -99,10 +105,11 @@ <ContrastRatio :contrast="previewContrast.topBarLink"/> </div> <div class="color-item"> - <h4>Inputs</h4> + <h4>Text fields</h4> <ColorInput name="inputColor" v-model="inputColorLocal" :fallback="fgColorLocal" :label="$t('settings.background')"/> <OpacityInput name="inputOpacity" v-model="inputOpacityLocal" :fallback="previewTheme.opacity.input || 1"/> <ColorInput name="inputTextColor" v-model="inputTextColorLocal" :fallback="previewTheme.colors.inputText" :label="$t('settings.text')"/> + <ContrastRatio :contrast="previewContrast.inputText"/> </div> <div class="color-item"> <h4>Buttons</h4> @@ -120,7 +127,8 @@ <h4>Faint text</h4> <ColorInput name="faintColor" v-model="faintColorLocal" :fallback="previewTheme.colors.faint || 1" :label="$t('settings.text')"/> <ColorInput name="faintLinkColor" v-model="faintLinkColorLocal" :fallback="previewTheme.colors.faintLink" :label="$t('settings.links')"/> - <OpacityInput name="faintOpacity" v-model="faintOpacityLocal" fallback="previewTheme.opacity.faint"/> + <ColorInput name="panelFaintColor" v-model="panelFaintColorLocal" :fallback="previewTheme.colors.panelFaint" :label="$t('settings.panel')"/> + <OpacityInput name="faintOpacity" v-model="faintOpacityLocal" :fallback="previewTheme.opacity.faint || 0.5"/> </div> </div> </div> @@ -259,10 +267,6 @@ h4 { margin-top: 1em; } - - label { - color: var(--faint, $fallback--faint); - } } .radius-item { diff --git a/src/services/color_convert/color_convert.js b/src/services/color_convert/color_convert.js index 0acc7e7c..7a5254b9 100644 --- a/src/services/color_convert/color_convert.js +++ b/src/services/color_convert/color_convert.js @@ -81,29 +81,27 @@ const getContrastRatio = (a, b) => { /** * This generates what "worst case" color would look like for transparent - * segments. I.e. black with .2 alpha and pure-white background image - * could make white text unreadable + * segments. I.e. transparent black with yellow text over yellow background. * * @param {Object} srgb - transparent color * @param {Number} alpha - color's opacity/alpha channel - * @param {Boolean} white - use white "background" if true, black otherwise + * @param {Object} textSrgb - text color (considered as worst case scenario for transparent background) * @returns {Object} sRGB of resulting color */ -const transparentWorstCase = (srgb, alpha, white = false) => { - const bg = 'rgb'.split('').reduce((acc, c) => { acc[c] = Number(white) * 255; return acc }, {}) +const transparentWorstCase = (srgb, alpha, textSrgb) => { return 'rgb'.split('').reduce((acc, c) => { // Simplified https://en.wikipedia.org/wiki/Alpha_compositing#Alpha_blending // for opaque bg and transparent fg - acc[c] = (srgb[c] * alpha + bg[c] * (1 - alpha)) + acc[c] = (srgb[c] * alpha + textSrgb[c] * (1 - alpha)) return acc }, {}) } const worstCase = (bg, bga, text) => { + console.log(bg) + console.log(text) if (bga === 1 || typeof bga === 'undefined') return bg - // taken from https://github.com/toish/chromatism/blob/master/src/operations/contrastRatio.js - const blackWorse = ((text.r * 299) + (text.g * 587) + (text.b * 114)) / 1000 <= 128 - return transparentWorstCase(bg, bga, !blackWorse) + return transparentWorstCase(bg, bga, text) } const hex2rgb = (hex) => { diff --git a/src/services/style_setter/style_setter.js b/src/services/style_setter/style_setter.js index 4de39f79..666e74c1 100644 --- a/src/services/style_setter/style_setter.js +++ b/src/services/style_setter/style_setter.js @@ -155,12 +155,14 @@ const generatePreset = (input) => { colors.cGreen = col.cGreen colors.cOrange = col.cOrange - colors.cAlertRed = col.cAlertRed || Object.assign({}, col.cRed) + colors.alertError = col.alertError || Object.assign({}, col.cRed) + colors.badgeNotification = col.badgeNotification || Object.assign({}, col.cRed) + colors.badgeNotificationText = col.badgeNotification || Object.assign({}, col.cRed) Object.entries(opacity).forEach(([ k, v ]) => { if (typeof v === 'undefined') return if (k === 'alert') { - colors.cAlertRed.a = v + colors.alertError.a = v return } if (k === 'faint') { diff --git a/static/font/config.json b/static/font/config.json index 20cb3254..be809269 100644 --- a/static/font/config.json +++ b/static/font/config.json @@ -51,7 +51,7 @@ { "uid": "09feb4465d9bd1364f4e301c9ddbaa92", "css": "retweet", - "code": 59396, + "code": 59398, "src": "fontawesome" }, { @@ -66,12 +66,6 @@ "code": 61925, "src": "fontawesome" }, - { - "uid": "1a5cfa186647e8c929c2b17b9fc4dac1", - "css": "plus-squared", - "code": 59398, - "src": "font-awesome" - }, { "uid": "e99461abfef3923546da8d745372c995", "css": "cog", @@ -191,6 +185,24 @@ "css": "brush", "code": 59411, "src": "iconic" + }, + { + "uid": "ca90da02d2c6a3183f2458e4dc416285", + "css": "adjust", + "code": 59396, + "src": "fontawesome" + }, + { + "uid": "5e2ab018e3044337bcef5f7e94098ea1", + "css": "thumbs-up-alt", + "code": 61796, + "src": "fontawesome" + }, + { + "uid": "c76b7947c957c9b78b11741173c8349b", + "css": "attention", + "code": 59412, + "src": "fontawesome" } ] } \ No newline at end of file diff --git a/static/font/css/fontello-codes.css b/static/font/css/fontello-codes.css index b1c76c3f..21cf6f06 100644 --- a/static/font/css/fontello-codes.css +++ b/static/font/css/fontello-codes.css @@ -3,9 +3,9 @@ .icon-upload:before { content: '\e801'; } /* '' */ .icon-star:before { content: '\e802'; } /* '' */ .icon-star-empty:before { content: '\e803'; } /* '' */ -.icon-retweet:before { content: '\e804'; } /* '' */ +.icon-adjust:before { content: '\e804'; } /* '' */ .icon-eye-off:before { content: '\e805'; } /* '' */ -.icon-plus-squared:before { content: '\e806'; } /* '' */ +.icon-retweet:before { content: '\e806'; } /* '' */ .icon-cog:before { content: '\e807'; } /* '' */ .icon-logout:before { content: '\e808'; } /* '' */ .icon-down-open:before { content: '\e809'; } /* '' */ @@ -19,6 +19,7 @@ .icon-lock:before { content: '\e811'; } /* '' */ .icon-globe:before { content: '\e812'; } /* '' */ .icon-brush:before { content: '\e813'; } /* '' */ +.icon-attention:before { content: '\e814'; } /* '' */ .icon-spin3:before { content: '\e832'; } /* '' */ .icon-spin4:before { content: '\e834'; } /* '' */ .icon-link-ext:before { content: '\f08e'; } /* '' */ @@ -28,5 +29,6 @@ .icon-comment-empty:before { content: '\f0e5'; } /* '' */ .icon-reply:before { content: '\f112'; } /* '' */ .icon-lock-open-alt:before { content: '\f13e'; } /* '' */ +.icon-thumbs-up-alt:before { content: '\f164'; } /* '' */ .icon-binoculars:before { content: '\f1e5'; } /* '' */ .icon-user-plus:before { content: '\f234'; } /* '' */ \ No newline at end of file diff --git a/static/font/css/fontello-embedded.css b/static/font/css/fontello-embedded.css index bea63f38..a1b3e3d6 100644 --- a/static/font/css/fontello-embedded.css +++ b/static/font/css/fontello-embedded.css @@ -1,15 +1,15 @@ @font-face { font-family: 'fontello'; - src: url('../font/fontello.eot?99189355'); - src: url('../font/fontello.eot?99189355#iefix') format('embedded-opentype'), - url('../font/fontello.svg?99189355#fontello') format('svg'); + src: url('../font/fontello.eot?61520746'); + src: url('../font/fontello.eot?61520746#iefix') format('embedded-opentype'), + url('../font/fontello.svg?61520746#fontello') format('svg'); font-weight: normal; font-style: normal; } @font-face { font-family: 'fontello'; - src: url('data:application/octet-stream;base64,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') format('woff'), - url('data:application/octet-stream;base64,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') format('truetype'); + src: url('data:application/octet-stream;base64,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') format('woff'), + url('data:application/octet-stream;base64,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RCywABUgR7AAI0KyAAEBFRQTLrAxKi2wRSywABUgR7AAI0KyAAEBFRQTLrAxKi2wRiyxAAEUE7AyKi2wRyywNCotsEgssAAWRSMgLiBGiiNhOLErARQrLbBJLLAII0KwSCstsEossgAAQSstsEsssgABQSstsEwssgEAQSstsE0ssgEBQSstsE4ssgAAQistsE8ssgABQistsFAssgEAQistsFEssgEBQistsFIssgAAPistsFMssgABPistsFQssgEAPistsFUssgEBPistsFYssgAAQCstsFcssgABQCstsFgssgEAQCstsFkssgEBQCstsFossgAAQystsFsssgABQystsFwssgEAQystsF0ssgEBQystsF4ssgAAPystsF8ssgABPystsGAssgEAPystsGEssgEBPystsGIssDcrLrErARQrLbBjLLA3K7A7Ky2wZCywNyuwPCstsGUssAAWsDcrsD0rLbBmLLA4Ky6xKwEUKy2wZyywOCuwOystsGgssDgrsDwrLbBpLLA4K7A9Ky2waiywOSsusSsBFCstsGsssDkrsDsrLbBsLLA5K7A8Ky2wbSywOSuwPSstsG4ssDorLrErARQrLbBvLLA6K7A7Ky2wcCywOiuwPCstsHEssDorsD0rLbByLLMJBAIDRVghGyMhWUIrsAhlsAMkUHiwARUwLQBLuADIUlixAQGOWbABuQgACABjcLEABUKyAAEAKrEABUKzCgIBCCqxAAVCsw4AAQgqsQAGQroCwAABAAkqsQAHQroAQAABAAkqsQMARLEkAYhRWLBAiFixA2REsSYBiFFYugiAAAEEQIhjVFixAwBEWVlZWbMMAgEMKrgB/4WwBI2xAgBEAAA=') format('truetype'); } /* Chrome hack: SVG is rendered more smooth in Windozze. 100% magic, uncomment if you need it. */ /* Note, that will break hinting! In other OS-es font will be not as sharp as it could be */ @@ -17,7 +17,7 @@ @media screen and (-webkit-min-device-pixel-ratio:0) { @font-face { font-family: 'fontello'; - src: url('../font/fontello.svg?99189355#fontello') format('svg'); + src: url('../font/fontello.svg?61520746#fontello') format('svg'); } } */ @@ -56,9 +56,9 @@ .icon-upload:before { content: '\e801'; } /* '' */ .icon-star:before { content: '\e802'; } /* '' */ .icon-star-empty:before { content: '\e803'; } /* '' */ -.icon-retweet:before { content: '\e804'; } /* '' */ +.icon-adjust:before { content: '\e804'; } /* '' */ .icon-eye-off:before { content: '\e805'; } /* '' */ -.icon-plus-squared:before { content: '\e806'; } /* '' */ +.icon-retweet:before { content: '\e806'; } /* '' */ .icon-cog:before { content: '\e807'; } /* '' */ .icon-logout:before { content: '\e808'; } /* '' */ .icon-down-open:before { content: '\e809'; } /* '' */ @@ -72,6 +72,7 @@ .icon-lock:before { content: '\e811'; } /* '' */ .icon-globe:before { content: '\e812'; } /* '' */ .icon-brush:before { content: '\e813'; } /* '' */ +.icon-attention:before { content: '\e814'; } /* '' */ .icon-spin3:before { content: '\e832'; } /* '' */ .icon-spin4:before { content: '\e834'; } /* '' */ .icon-link-ext:before { content: '\f08e'; } /* '' */ @@ -81,5 +82,6 @@ .icon-comment-empty:before { content: '\f0e5'; } /* '' */ .icon-reply:before { content: '\f112'; } /* '' */ .icon-lock-open-alt:before { content: '\f13e'; } /* '' */ +.icon-thumbs-up-alt:before { content: '\f164'; } /* '' */ .icon-binoculars:before { content: '\f1e5'; } /* '' */ .icon-user-plus:before { content: '\f234'; } /* '' */ \ No newline at end of file diff --git a/static/font/css/fontello-ie7-codes.css b/static/font/css/fontello-ie7-codes.css index 5ba45f75..5e732ec9 100644 --- a/static/font/css/fontello-ie7-codes.css +++ b/static/font/css/fontello-ie7-codes.css @@ -3,9 +3,9 @@ .icon-upload { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } .icon-star { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } .icon-star-empty { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } -.icon-retweet { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-adjust { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } .icon-eye-off { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } -.icon-plus-squared { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-retweet { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } .icon-cog { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } .icon-logout { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } .icon-down-open { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } @@ -19,6 +19,7 @@ .icon-lock { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } .icon-globe { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } .icon-brush { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-attention { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } .icon-spin3 { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } .icon-spin4 { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } .icon-link-ext { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } @@ -28,5 +29,6 @@ .icon-comment-empty { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } .icon-reply { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } .icon-lock-open-alt { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-thumbs-up-alt { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } .icon-binoculars { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } .icon-user-plus { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } \ No newline at end of file diff --git a/static/font/css/fontello-ie7.css b/static/font/css/fontello-ie7.css index 6ab5e0ca..b4be6e9b 100644 --- a/static/font/css/fontello-ie7.css +++ b/static/font/css/fontello-ie7.css @@ -14,9 +14,9 @@ .icon-upload { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } .icon-star { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } .icon-star-empty { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } -.icon-retweet { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-adjust { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } .icon-eye-off { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } -.icon-plus-squared { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-retweet { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } .icon-cog { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } .icon-logout { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } .icon-down-open { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } @@ -30,6 +30,7 @@ .icon-lock { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } .icon-globe { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } .icon-brush { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-attention { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } .icon-spin3 { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } .icon-spin4 { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } .icon-link-ext { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } @@ -39,5 +40,6 @@ .icon-comment-empty { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } .icon-reply { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } .icon-lock-open-alt { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-thumbs-up-alt { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } .icon-binoculars { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } .icon-user-plus { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } \ No newline at end of file diff --git a/static/font/css/fontello.css b/static/font/css/fontello.css index 2a3d708d..d8419862 100644 --- a/static/font/css/fontello.css +++ b/static/font/css/fontello.css @@ -1,11 +1,11 @@ @font-face { font-family: 'fontello'; - src: url('../font/fontello.eot?60305294'); - src: url('../font/fontello.eot?60305294#iefix') format('embedded-opentype'), - url('../font/fontello.woff2?60305294') format('woff2'), - url('../font/fontello.woff?60305294') format('woff'), - url('../font/fontello.ttf?60305294') format('truetype'), - url('../font/fontello.svg?60305294#fontello') format('svg'); + src: url('../font/fontello.eot?91338570'); + src: url('../font/fontello.eot?91338570#iefix') format('embedded-opentype'), + url('../font/fontello.woff2?91338570') format('woff2'), + url('../font/fontello.woff?91338570') format('woff'), + url('../font/fontello.ttf?91338570') format('truetype'), + url('../font/fontello.svg?91338570#fontello') format('svg'); font-weight: normal; font-style: normal; } @@ -15,7 +15,7 @@ @media screen and (-webkit-min-device-pixel-ratio:0) { @font-face { font-family: 'fontello'; - src: url('../font/fontello.svg?60305294#fontello') format('svg'); + src: url('../font/fontello.svg?91338570#fontello') format('svg'); } } */ @@ -59,9 +59,9 @@ .icon-upload:before { content: '\e801'; } /* '' */ .icon-star:before { content: '\e802'; } /* '' */ .icon-star-empty:before { content: '\e803'; } /* '' */ -.icon-retweet:before { content: '\e804'; } /* '' */ +.icon-adjust:before { content: '\e804'; } /* '' */ .icon-eye-off:before { content: '\e805'; } /* '' */ -.icon-plus-squared:before { content: '\e806'; } /* '' */ +.icon-retweet:before { content: '\e806'; } /* '' */ .icon-cog:before { content: '\e807'; } /* '' */ .icon-logout:before { content: '\e808'; } /* '' */ .icon-down-open:before { content: '\e809'; } /* '' */ @@ -75,6 +75,7 @@ .icon-lock:before { content: '\e811'; } /* '' */ .icon-globe:before { content: '\e812'; } /* '' */ .icon-brush:before { content: '\e813'; } /* '' */ +.icon-attention:before { content: '\e814'; } /* '' */ .icon-spin3:before { content: '\e832'; } /* '' */ .icon-spin4:before { content: '\e834'; } /* '' */ .icon-link-ext:before { content: '\f08e'; } /* '' */ @@ -84,5 +85,6 @@ .icon-comment-empty:before { content: '\f0e5'; } /* '' */ .icon-reply:before { content: '\f112'; } /* '' */ .icon-lock-open-alt:before { content: '\f13e'; } /* '' */ +.icon-thumbs-up-alt:before { content: '\f164'; } /* '' */ .icon-binoculars:before { content: '\f1e5'; } /* '' */ .icon-user-plus:before { content: '\f234'; } /* '' */ \ No newline at end of file diff --git a/static/font/demo.html b/static/font/demo.html index 26a1875e..22bc0e92 100644 --- a/static/font/demo.html +++ b/static/font/demo.html @@ -229,11 +229,11 @@ body { } @font-face { font-family: 'fontello'; - src: url('./font/fontello.eot?32487936'); - src: url('./font/fontello.eot?32487936#iefix') format('embedded-opentype'), - url('./font/fontello.woff?32487936') format('woff'), - url('./font/fontello.ttf?32487936') format('truetype'), - url('./font/fontello.svg?32487936#fontello') format('svg'); + src: url('./font/fontello.eot?93659852'); + src: url('./font/fontello.eot?93659852#iefix') format('embedded-opentype'), + url('./font/fontello.woff?93659852') format('woff'), + url('./font/fontello.ttf?93659852') format('truetype'), + url('./font/fontello.svg?93659852#fontello') format('svg'); font-weight: normal; font-style: normal; } @@ -304,9 +304,9 @@ body { <div class="the-icons span3" title="Code: 0xe803"><i class="demo-icon icon-star-empty"></i> <span class="i-name">icon-star-empty</span><span class="i-code">0xe803</span></div> </div> <div class="row"> - <div class="the-icons span3" title="Code: 0xe804"><i class="demo-icon icon-retweet"></i> <span class="i-name">icon-retweet</span><span class="i-code">0xe804</span></div> + <div class="the-icons span3" title="Code: 0xe804"><i class="demo-icon icon-adjust"></i> <span class="i-name">icon-adjust</span><span class="i-code">0xe804</span></div> <div class="the-icons span3" title="Code: 0xe805"><i class="demo-icon icon-eye-off"></i> <span class="i-name">icon-eye-off</span><span class="i-code">0xe805</span></div> - <div class="the-icons span3" title="Code: 0xe806"><i class="demo-icon icon-plus-squared"></i> <span class="i-name">icon-plus-squared</span><span class="i-code">0xe806</span></div> + <div class="the-icons span3" title="Code: 0xe806"><i class="demo-icon icon-retweet"></i> <span class="i-name">icon-retweet</span><span class="i-code">0xe806</span></div> <div class="the-icons span3" title="Code: 0xe807"><i class="demo-icon icon-cog"></i> <span class="i-name">icon-cog</span><span class="i-code">0xe807</span></div> </div> <div class="row"> @@ -328,20 +328,24 @@ body { <div class="the-icons span3" title="Code: 0xe813"><i class="demo-icon icon-brush"></i> <span class="i-name">icon-brush</span><span class="i-code">0xe813</span></div> </div> <div class="row"> + <div class="the-icons span3" title="Code: 0xe814"><i class="demo-icon icon-attention"></i> <span class="i-name">icon-attention</span><span class="i-code">0xe814</span></div> <div class="the-icons span3" title="Code: 0xe832"><i class="demo-icon icon-spin3 animate-spin"></i> <span class="i-name">icon-spin3</span><span class="i-code">0xe832</span></div> <div class="the-icons span3" title="Code: 0xe834"><i class="demo-icon icon-spin4 animate-spin"></i> <span class="i-name">icon-spin4</span><span class="i-code">0xe834</span></div> <div class="the-icons span3" title="Code: 0xf08e"><i class="demo-icon icon-link-ext"></i> <span class="i-name">icon-link-ext</span><span class="i-code">0xf08e</span></div> - <div class="the-icons span3" title="Code: 0xf08f"><i class="demo-icon icon-link-ext-alt"></i> <span class="i-name">icon-link-ext-alt</span><span class="i-code">0xf08f</span></div> </div> <div class="row"> + <div class="the-icons span3" title="Code: 0xf08f"><i class="demo-icon icon-link-ext-alt"></i> <span class="i-name">icon-link-ext-alt</span><span class="i-code">0xf08f</span></div> <div class="the-icons span3" title="Code: 0xf0c9"><i class="demo-icon icon-menu"></i> <span class="i-name">icon-menu</span><span class="i-code">0xf0c9</span></div> <div class="the-icons span3" title="Code: 0xf0e0"><i class="demo-icon icon-mail-alt"></i> <span class="i-name">icon-mail-alt</span><span class="i-code">0xf0e0</span></div> <div class="the-icons span3" title="Code: 0xf0e5"><i class="demo-icon icon-comment-empty"></i> <span class="i-name">icon-comment-empty</span><span class="i-code">0xf0e5</span></div> - <div class="the-icons span3" title="Code: 0xf112"><i class="demo-icon icon-reply"></i> <span class="i-name">icon-reply</span><span class="i-code">0xf112</span></div> </div> <div class="row"> + <div class="the-icons span3" title="Code: 0xf112"><i class="demo-icon icon-reply"></i> <span class="i-name">icon-reply</span><span class="i-code">0xf112</span></div> <div class="the-icons span3" title="Code: 0xf13e"><i class="demo-icon icon-lock-open-alt"></i> <span class="i-name">icon-lock-open-alt</span><span class="i-code">0xf13e</span></div> + <div class="the-icons span3" title="Code: 0xf164"><i class="demo-icon icon-thumbs-up-alt"></i> <span class="i-name">icon-thumbs-up-alt</span><span class="i-code">0xf164</span></div> <div class="the-icons span3" title="Code: 0xf1e5"><i class="demo-icon icon-binoculars"></i> <span class="i-name">icon-binoculars</span><span class="i-code">0xf1e5</span></div> + </div> + <div class="row"> <div class="the-icons span3" title="Code: 0xf234"><i class="demo-icon icon-user-plus"></i> <span class="i-name">icon-user-plus</span><span class="i-code">0xf234</span></div> </div> </div> diff --git a/static/font/font/fontello.eot b/static/font/font/fontello.eot index 0533282b937801677597e6f61c7f0f2e66845d04..993381629cbd44410df7caee5dfa646e5ed19525 100644 GIT binary patch delta 1615 zcmY*ZZERCj7=GV#zq_s<x3{<ZU~6}~wS;bLD`hi+wV;T?VwO3{Hc=>Bw}I<gw-yEl zYy3r&085E~_{bCmG?JK96GBMz$3j5ypGHGS;Lk`yi0Sx9j2YDTb`s^>Jm)#@bKY~# z`=0mY{<weXptuwT=#S2dUHrMZ`CGHGPjVjtU@rh{8&1a4v7djqOZIsH?$Gewj87~- zc?b|*PxHRf^jPxa*<*ubZwIJKkHx3cq&vut5X)l|`$uni_Z9$z4uIgE@kD&Y_4Ngs zYCTC^*Ek8%HSRIlzmnZNp3Ka=|K+yw9TNT`;m|~CIDV%7QyRB@NcJnq_)Hr2Ri7pM zD%rltcrtPQRHKjVA^`g(otn;+PGvU%bX=sM52U9O>4goU%K$H2&(GmocqYGuW9#k# z(rSP^z*R=A#1`_u2RvLNy^c~KrKlCd#YpLB>00S_X<<=aj4qDkPqKPxWO3nsq+HH- za<#Qc-Wx05%ka#XzsojAK4_wS#WS58s<g4yJQdB{&DNBU@egtuK#>4$;_xy+C#D-T z0Um(Tk!3&)@zG^KE%CKwPzX@Ey$q-)URVaK09d4RO+W)NmD&U}5|1pS4!ks=1t!1; zaGwq}0ZoPPgc~eZeq6rD?ch|9K!Z?hCEW}l2RnI0#;~6V*#IzPJpiiHT19@6QxM#t z7N=ojjfk2JjnyoQVH#g}<8UwQKg4kAO7ZHXc;NEGOCPLdJNpiQakvjVvh$hD{9Uu7 z7XwiKi7T?gXUa}2%*rwreyiH<%5mjK6wd>05bSifYhWa1t#>kxDaa7m=B?j0_YTAw zfC-$yxa8Adsmwg`+d&Yl!fW+00|X|dfR?5H^Wb^x2Sk3rbnO|8DW2zo!GK>B>Ow7R zBY_YaVgu$h-Lod#6?8>{rXq=MkEVB%!l5qNP--#)!9X`swmMs9ktQTdr9+D#Ne4t( zP^{9JY?bjF&8-;f(pl9|+!|g`jDRt;N48jG>d^W-qR5LPFP8-wMdn!g)9-hxs5$(y zpHrQh22g1C>Pn7kLAP;`sw30mcoSZaZQX*h<gI6v7b0{zsYd)}kYp(ZGAE}*Djk=i z3PK8a#;2iDL~>;V0^K0%X>WBmzZh8SQWc>g)E!xkHf+Q$ms1lZKjr3(bW#4gph>!0 zmvXu!N+$fqY77Q~6^AJ6?&_v4%B=}E@}h_8FWHzxtB&do8WM&y$DEx;wmG}bj(ZiX zRTM=Re9c>4ZP^xHwaG=3TK)4Y0;<I(3cToW+iO?VXtKzdRkotcb0<Qvcgd-zvLUuU zIZ@dT6E+<+dxyVPU1fOvUUxJYLe*xkp`$8uF@L9OF|-=J#?Y;5U5!tzcDL#pZ?%ar zD7>_Ck>zg9&z;+0U;hZIIQpdk{9%uL^_1!9?c!&+F|uV~rb>-|CEQ^7Jpg_$%|GC0 zOy!wom^*P!!rcEbSbl8ke7m{+U%d$VLZ}rV<!^>sa4!EO<k@hZ4YAwY=X^6i#Lo*+ z@tAl;{9EdfdAY~hYdun}Ru^qEcHN$_-*9Yk+;oPWC)JHEi|cy|!3@MG^tD!TXyo5% zkMU@rKYzO2>k;E4d$Q9RDX~A%mKq(ErV^Qbi9{xUqy24*63=83lbPMA$$WQ5UrTjn qJe%A#-Ih(a#V0bBUAre!!`X@W)U=YFPE56>C$iJ@8c%l^)_(vKZCslG delta 961 zcmYjQUuaWT82_DnZ|;pvnxyINO}A=u({v8orLJj`B6eM48T#OZh=@guy|lM2+St1` z?ONDq5s}eBwL4DmVZ(up!C(yUgZm@WO;HpDx0elNBQKSdJuIzG1~$vS<ZiHY?(h80 z@B6;<efJ#B*?ezhPG0E&8he)Il=OHpvbNm!!TbWi4gfejlr^lkzKxbie-4PlLo+#D zF8}=uc%9tPN37B8;_~?c(mLR=MvW<p>;YisU&QXw@wt)NkEVYITwdUnnK9EC4&J%8 z6=*v{{?r&5lpn<xq_2~19n0osb4`cKRG=+ShK2EoA>+d@&JF_Y1nkKgvlg4K{fzV# z()uYQYu^93Stor95FS_)Q@JO`R|e2=fqr_}nl!D|bUP*AeV$)tN7<)*la21mgVAc> z5ya~3HR2kI&qKnOWN)M`h;SF`3-Lmtc&>Q2m@lrb2iJS~Cqk2Jbt6$O^Y4XFh*4y3 zwfEtFoo@(fN-XoVxXB+1ik~)5*(+PFv@qW<J)MsO1p*p~6AqvO#U%#_5ubBFRZzU^ z051{e9pGi+RR?GS)`Jc}2fW_v0NaQurUEnr8&s<T0b%<W*CWARs_`&;$CfcR|Ft+E z1`$OU(tW+P3@pqfQP?ZcOk@zVE+$I!orFS%rm#UsvMe2fB*{l0OY;6?GTNm@Tcd8* z)^?UmY*(T*yc%thm4@n|YTaE8s<xZOQ{Bm~c&ffmm6b@#_GDcm)g6f+)P#Gg-`0G4 z{VKak(0o@NVf(bDOn+&uKa=SfLj4(*-sjVpT@A5Se^vN2&1b9Z%~D}ch7G2L#tPXN zJ;&?qQdr$$A8Y$VXkLpfK0fa6`4b+oK*xcfFMV_EKQ5Q}iC8#PDnFHG|Ggd+m(z59 zIi}V~JLwV7LUDlKi0$GpVl6z<p}w&pToD7}PtqZ2QF`V&C9lZOl{RJ2op8VFUGUzh zz2Iy0{ovRAXZ_Cu?+1RX+ZPN3zoWdBJE+?;9UhVC{B&m@zug($rc9c-cT6*<m~&=q jVr0Z?jZaU-rrw@5Ce2~K*?Ei)#SikE@kaaCc-Z?79H96` diff --git a/static/font/font/fontello.svg b/static/font/font/fontello.svg index ec7c464d..c706fcad 100644 --- a/static/font/font/fontello.svg +++ b/static/font/font/fontello.svg @@ -14,11 +14,11 @@ <glyph glyph-name="star-empty" unicode="" d="M635 297l170 166-235 34-106 213-105-213-236-34 171-166-41-235 211 111 211-111z m294 199q0-12-15-27l-202-197 48-279q0-4 0-12 0-28-23-28-10 0-22 7l-251 132-250-132q-12-7-23-7-11 0-17 9t-6 19q0 4 1 12l48 279-203 197q-14 15-14 27 0 21 31 26l280 40 126 254q11 23 27 23t28-23l125-254 280-40q32-5 32-26z" horiz-adv-x="928.6" /> -<glyph glyph-name="retweet" unicode="" d="M714 18q0-7-5-13t-13-5h-535q-5 0-8 1t-5 4-3 4-2 7 0 6v335h-107q-15 0-25 11t-11 25q0 13 8 23l179 214q11 12 27 12t28-12l178-214q9-10 9-23 0-15-11-25t-25-11h-107v-214h321q9 0 14-6l89-108q4-5 4-11z m357 232q0-13-8-23l-178-214q-12-13-28-13t-27 13l-179 214q-8 10-8 23 0 14 11 25t25 11h107v214h-322q-9 0-14 7l-89 107q-4 5-4 11 0 7 5 12t13 6h536q4 0 7-1t5-4 3-5 2-6 1-7v-334h107q14 0 25-11t10-25z" horiz-adv-x="1071.4" /> +<glyph glyph-name="adjust" unicode="" d="M429 53v608q-83 0-153-41t-110-111-41-152 41-152 110-111 153-41z m428 304q0-117-57-215t-156-156-215-58-216 58-155 156-58 215 58 215 155 156 216 58 215-58 156-156 57-215z" horiz-adv-x="857.1" /> <glyph glyph-name="eye-off" unicode="" d="M310 112l43 79q-48 35-76 88t-27 114q0 67 34 125-128-65-213-197 94-144 239-209z m217 424q0 11-8 19t-19 7q-70 0-120-50t-50-119q0-11 8-19t19-8 19 8 8 19q0 48 34 82t82 34q11 0 19 8t8 19z m202 106q0-4 0-5-59-105-176-316t-176-316l-28-50q-5-9-15-9-7 0-75 39-9 6-9 16 0 7 25 49-80 36-147 96t-117 137q-11 17-11 38t11 39q86 131 212 207t277 76q50 0 100-10l31 54q5 9 15 9 3 0 10-3t18-9 18-10 18-10 10-7q9-5 9-15z m21-249q0-78-44-142t-117-91l157 280q4-25 4-47z m250-72q0-19-11-38-22-36-61-81-84-96-194-149t-234-53l41 74q119 10 219 76t169 171q-65 100-158 164l35 63q53-36 102-85t81-103q11-19 11-39z" horiz-adv-x="1000" /> -<glyph glyph-name="plus-squared" unicode="" d="M714 321v72q0 14-10 25t-25 10h-179v179q0 15-11 25t-25 11h-71q-15 0-25-11t-11-25v-179h-178q-15 0-25-10t-11-25v-72q0-14 11-25t25-10h178v-179q0-14 11-25t25-11h71q15 0 25 11t11 25v179h179q14 0 25 10t10 25z m143 304v-536q0-66-47-113t-114-48h-535q-67 0-114 48t-47 113v536q0 66 47 113t114 48h535q67 0 114-48t47-113z" horiz-adv-x="857.1" /> +<glyph glyph-name="retweet" unicode="" d="M714 18q0-7-5-13t-13-5h-535q-5 0-8 1t-5 4-3 4-2 7 0 6v335h-107q-15 0-25 11t-11 25q0 13 8 23l179 214q11 12 27 12t28-12l178-214q9-10 9-23 0-15-11-25t-25-11h-107v-214h321q9 0 14-6l89-108q4-5 4-11z m357 232q0-13-8-23l-178-214q-12-13-28-13t-27 13l-179 214q-8 10-8 23 0 14 11 25t25 11h107v214h-322q-9 0-14 7l-89 107q-4 5-4 11 0 7 5 12t13 6h536q4 0 7-1t5-4 3-5 2-6 1-7v-334h107q14 0 25-11t10-25z" horiz-adv-x="1071.4" /> <glyph glyph-name="cog" unicode="" d="M571 357q0 59-41 101t-101 42-101-42-42-101 42-101 101-42 101 42 41 101z m286 61v-124q0-7-4-13t-11-7l-104-16q-10-30-21-51 19-27 59-77 6-6 6-13t-5-13q-15-21-55-61t-53-39q-7 0-14 5l-77 60q-25-13-51-21-9-76-16-104-4-16-20-16h-124q-8 0-14 5t-6 12l-16 103q-27 9-50 21l-79-60q-6-5-14-5-8 0-14 6-70 64-92 94-4 5-4 13 0 6 5 12 8 12 28 37t30 40q-15 28-23 55l-102 15q-7 1-11 7t-5 13v124q0 7 5 13t10 7l104 16q8 25 22 51-23 32-60 77-6 7-6 14 0 5 5 12 15 20 55 60t53 40q7 0 15-5l77-60q24 13 50 21 9 76 17 104 3 16 20 16h124q7 0 13-5t7-12l15-103q28-9 51-20l79 59q5 5 13 5 7 0 14-5 72-67 92-95 4-5 4-12 0-7-4-13-9-12-29-37t-30-40q15-28 23-54l102-16q7-1 12-7t4-13z" horiz-adv-x="857.1" /> @@ -46,6 +46,8 @@ <glyph glyph-name="brush" unicode="" d="M464 209q0-124-87-212t-210-87q-81 0-149 40 68 39 109 108t40 151q0 61 44 105t105 44 105-44 43-105z m415 562q32-32 32-79t-33-79l-318-318q-20 55-61 97t-97 62l318 318q32 32 79 32t80-33z" horiz-adv-x="928" /> +<glyph glyph-name="attention" unicode="" d="M571 90v106q0 8-5 13t-12 5h-108q-7 0-12-5t-5-13v-106q0-8 5-13t12-6h108q7 0 12 6t5 13z m-1 208l10 257q0 6-5 10-7 6-14 6h-122q-6 0-14-6-5-4-5-12l9-255q0-5 6-9t13-3h103q8 0 14 3t5 9z m-7 522l428-786q20-35-1-70-9-17-26-26t-35-10h-858q-18 0-35 10t-26 26q-21 35-1 70l429 786q9 17 26 27t36 10 36-10 27-27z" horiz-adv-x="1000" /> + <glyph glyph-name="spin3" unicode="" d="M494 857c-266 0-483-210-494-472-1-19 13-20 13-20l84 0c16 0 19 10 19 18 10 199 176 358 378 358 107 0 205-45 273-118l-58-57c-11-12-11-27 5-31l247-50c21-5 46 11 37 44l-58 227c-2 9-16 22-29 13l-65-60c-89 91-214 148-352 148z m409-508c-16 0-19-10-19-18-10-199-176-358-377-358-108 0-205 45-274 118l59 57c10 12 10 27-5 31l-248 50c-21 5-46-11-37-44l58-227c2-9 16-22 30-13l64 60c89-91 214-148 353-148 265 0 482 210 493 473 1 18-13 19-13 19l-84 0z" horiz-adv-x="1000" /> <glyph glyph-name="spin4" unicode="" d="M498 857c-114 0-228-39-320-116l0 0c173 140 428 130 588-31 134-134 164-332 89-495-10-29-5-50 12-68 21-20 61-23 84 0 3 3 12 15 15 24 71 180 33 393-112 539-99 98-228 147-356 147z m-409-274c-14 0-29-5-39-16-3-3-13-15-15-24-71-180-34-393 112-539 185-185 479-195 676-31l0 0c-173-140-428-130-589 31-134 134-163 333-89 495 11 29 6 50-12 68-11 11-27 17-44 16z" horiz-adv-x="1001" /> @@ -64,6 +66,8 @@ <glyph glyph-name="lock-open-alt" unicode="" d="M589 428q23 0 38-15t16-38v-322q0-22-16-37t-38-16h-535q-23 0-38 16t-16 37v322q0 22 16 38t38 15h17v179q0 103 74 177t176 73 177-73 73-177q0-14-10-25t-25-11h-36q-14 0-25 11t-11 25q0 59-42 101t-101 42-101-42-41-101v-179h410z" horiz-adv-x="642.9" /> +<glyph glyph-name="thumbs-up-alt" unicode="" d="M143 107q0 15-11 25t-25 11q-15 0-25-11t-11-25q0-15 11-25t25-11q15 0 25 11t11 25z m89 286v-357q0-15-10-25t-26-11h-160q-15 0-25 11t-11 25v357q0 14 11 25t25 10h160q15 0 26-10t10-25z m661 0q0-48-31-83 9-25 9-43 1-42-24-76 9-31 0-66-9-31-31-52 5-62-27-101-36-43-110-44h-72q-37 0-80 9t-68 16-67 22q-69 24-88 25-15 0-25 11t-11 25v357q0 14 10 25t24 11q13 1 42 33t57 67q38 49 56 67 10 10 17 27t10 27 8 34q4 22 7 34t11 29 19 28q10 11 25 11 25 0 46-6t33-15 22-22 14-25 7-28 2-25 1-22q0-21-6-43t-10-33-16-31q-1-4-5-10t-6-13-5-13h155q43 0 75-32t32-75z" horiz-adv-x="928.6" /> + <glyph glyph-name="binoculars" unicode="" d="M393 678v-428q0-15-11-25t-25-11v-321q0-15-10-25t-26-11h-285q-15 0-25 11t-11 25v285l139 488q4 12 17 12h237z m178 0v-392h-142v392h142z m429-500v-285q0-15-11-25t-25-11h-285q-15 0-25 11t-11 25v321q-15 0-25 11t-11 25v428h237q13 0 17-12z m-589 661v-125h-197v125q0 8 5 13t13 5h161q8 0 13-5t5-13z m375 0v-125h-197v125q0 8 5 13t13 5h161q8 0 13-5t5-13z" horiz-adv-x="1000" /> <glyph glyph-name="user-plus" unicode="" d="M393 357q-89 0-152 63t-62 151 62 152 152 63 151-63 63-152-63-151-151-63z m536-71h196q7 0 13-6t5-12v-107q0-8-5-13t-13-5h-196v-197q0-7-6-12t-12-6h-107q-8 0-13 6t-5 12v197h-197q-7 0-12 5t-6 13v107q0 7 6 12t12 6h197v196q0 7 5 13t13 5h107q7 0 12-5t6-13v-196z m-411-125q0-29 21-51t50-21h143v-133q-38-28-95-28h-488q-67 0-108 39t-41 106q0 30 2 58t8 61 15 60 24 55 34 45 48 30 62 11q11 0 22-10 44-34 86-51t92-17 92 17 86 51q11 10 22 10 73 0 121-54h-125q-29 0-50-21t-21-50v-107z" horiz-adv-x="1142.9" /> diff --git a/static/font/font/fontello.ttf b/static/font/font/fontello.ttf index 3f91b6432ea9d6cad18c263b6370859b2802b07b..d2d2963e7db8232c1a10317db768f4c3fe1b0b33 100644 GIT binary patch delta 1558 zcmY*Zdu&r>6hG&?&vtix+}_^q!Pf3}Yt6bBrEEU179yf!F$+%ICI%|&HgR1W*1^C> zjsHjt*&x>#5+GSb5s4%wjfM~s|5*qq`WK0YXxKj^l@QYbQ#3$5Z6(UR`JMBf=Xbty zzI;b!uDpH7pM4(y`vG9<&SX3t`~KS-B%cD{4(!~M@rlI;cL2ic$nG0Wk0n1md}xT| zZ2(p2vG`P)^bV3E#O1MxeWTaBdvX9m7eH{=cp^UHI(>#rZO4dokCQ^W#N8+P3dzmm z$;`}KpKUE(C&gV-9GFP$jGw6gnAYv@k^EdTK9k11)h9{5NV0D-o=jXm-smGa55Rs% zr=~K6<I@`fy3SJ3`_sD<>816d^8im@K7998JYf#t*ef>yDHz~7aOG7SaVxps1Rl<j zzK%{nn!J_|=Ocx=!llAuVQE=ijxLWZFWrh1ivW2j+h+f7Gi|J)p3+eKhVuU`Jo1|x z*m}taP2?h)huK|oo>keEQXyFCM(JxZk8roM8s#T|n|Qba(1}w|O8^f*VYULOAwE|D z)Dpi`0o4G7#R`CmDlAn1s{oeO3ZQ{_v;t@(K2kv)cxgcaC4dj$7HwJrH02cGD$5q{ z7teCrITa+(AQXFyngz(g4jz#)93X~l5E!yP0M)5fk)Pxg1b3*#Y1mjJqGm&*gGDh+ z>oYGM>}LZ981BB1zc?xGKY!=kyKC8wEeAh6xCOhW7c!ZJ8zn+_2%z{KmuI=Z<Q<s% zO_DL!UbW4Y<%%m*&uwl9?9|*fFcMShos44^WC(22=C7Lj2V)Ju1WsUF@?o(wCm#82 zAP9=^LVe5tfe9&~u+)DZJdXo_$PboW`-Wl`&*Q;hz%L4Qp_a9gKnM-70kfL!SrhIK zx+1}nMiSi~P46KMhq`1#smTZg13gHm)!9>4X+pA=30eh7+Aqq2MUlp2MaC~Rx5ZHJ zom35rTf;LJBVY{glC4&m2r54zio7WDa#4^`WR82k`~6N8HHTmJbE;F*0CFER>Xt0c zg1YezO-H87@iM#^+uVz?<gI6PE<{-Eq#5y>K$4{t$ef%KX>?qwbZ=9@Gd>OFA(AT_ z6sUu&ud~hF{7hi2OSK3Mp`J(w+OQG3T~1Au{B$;Fq?^vK3!0?6bt$V$qEv$4=)hng zSayiAp6(tZQEpARkrzEQf62xqikj0KG$agbj(Iz+Z1Z-VedM)Zt;J%|1z&UT^DSG$ zts7lr)asvH6;Q1<QQ$>~+g{sRqsby;imaf_b4Nq5H_555oDka{94)8AgiS}y-sP`V zTMe(@>y8FPsM_o`v{l&_^RHH|hE}837`md?)%etEcbl&9icO3`?r3nCHC|ho|9rcB z-3nB3^vD4GY>#~LkQZ0X)2&|5O7S*7LmhndGt9+S&B_fG@0SAkPV+(Q6S!hVLM=n{ z^qiJ-d;X2+g&$GH2|UGy*&_D|-^>s53qn*pBwi5zmbzqK?o;}e*=n^qZ=12}_Kf|i zqu23^GweL3Zg5#$UsIma_D1e>sER|w{H7zuqk(?&j}EU#jF0S^p2|pxeTnwe=%}<i wk=dI_WXy2qYgS7<lSxcw-bhWFbDdkP)tT|><m*%I(`ob9&Q-ZTJDZe$041hYjQ{`u delta 1041 zcmZ8gZ)h839Dd$=FL!C1CTW^pr`wuquTE?i*QQDPVN#nF*%*G%tqzNr_10cfF==o4 zQ!^N}f(+@0w(}<F2f;xRK@iy?Qzs%rkjXYs1fhcDOGk#FNjJwpYW!WUqImB;@AE#t z=Y4<gy+7Xj@%%fbb!}-GV9x<KHJ#PXXTH$tq(21s%=BVGmFj=p2L{N7I%}r0D;L)% zNvlAMnbz|r#e=}1hs54==G<)Q?c%S1#|J#NIA`cHp>MD51Nz<|e|U}p^7s5t(qEG9 zoy!(Vg}tZhv_W5)0?V0&Y5lE_E=&Ra1RTlgC6g6fKOnt9TAkOk#?AM-RMOu7!Yy+l zU$~=xq61G}qK}?2bB4K<?5E_1*DpSNiCwlPSbDMyMvH+>@W$vMaWhRXL&V1v-$N(h z@K)L@u}ZwUR=rUzSGQ`R+GuTSJ6@-1RM2o%|L<6rh0ab!>qi@H4F74?aUp512|m{3 z9Nl>$)&{?~6r*$mbP&f~Kmn?&F3?H5=7JKVdcy@CAuhYXqr_V-uotL>T!0E)8+C#G z#5BhNx`1uk%YlH1J>c0C>^n^@%zoWF$Cf_hlRN|sLr9K|wlc5?6S;6ypb=-_Sr6kP zy{|&1>d9;hq9lnE5Jl-lNTT#?BB2c_TCe8y?CWQV_yJj??s2V0k~<n*SQ#1W2rGwK zY<MIw6dP`D3rli$&w)f+e0Zcgc0v)p3kPh)e>4zgR|ty#iYsitvYHy-xjUXpjSHRQ zDV99uSD4+HVuu3_;a3#D9cEAORF0(BR8rXEko{Zp>2~{tNVv)V^oievrMulLKc5af z^#@vbh01}S-u?XQKV09bpAkzuP1;M<aUZ%be6oHP5Ese6Czf_^(5_9SI+=JI;r)Lh z9-^@mr$3|t$E(27q6e*$(VpkY5*K%K9LH@IVi~qAZ15oeL7Wg*#QUCk>1*kM+$T?Y z<KFYWW#3n=m;Al{>j5=zA@CshX7HD`W1(Q^8s%`Vv})guws59en*(E3*I;D7oHGiq z8Ad@i&Kc2#*;${NDdwa3vqe2;%vckHFI(3JPjob&d1ZcKx|q>(`KDst$VHu5b}ZK8 F`x`(Z4-fzV diff --git a/static/font/font/fontello.woff b/static/font/font/fontello.woff index ec5cf4aa8b365ad6d24e0d3ac4e564cf4c873dd4..c1aa83bb3aeb19fbeb0626a27133e218ad0d0444 100644 GIT binary patch delta 7787 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 delta 7378 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 AJpcdz diff --git a/static/font/font/fontello.woff2 b/static/font/font/fontello.woff2 index 8eed845a60243dfe59560f1ee38e4350d2026dbe..cbd9b09f706d5320154cd4fb11140fac7e7c35ed 100644 GIT binary patch literal 8372 zcmV;lAWPqOPew8T0RR9103fsg4*&oF06tUz03cid0RR9100000000000000000000 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 K;pHEOfV%+u3jlop literal 8020 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 W^{O=oUnmQUMIYeMXb+49djOt)yc@Cr From e7fe2dc9f9e52771c2ffe8d0ee1c4e8b2e38ab2f Mon Sep 17 00:00:00 2001 From: Henry Jameson <me@hjkos.com> Date: Wed, 14 Nov 2018 19:39:17 +0300 Subject: [PATCH 11/96] collateral fixes, removed alpha control for alerts, added contrast text generation for alerts, updated getTextColor to also have fallback to black/white if resulting contrast isn't passable (only when inverting lightness!), updated UI to use tabs. --- src/App.js | 7 +- src/App.scss | 9 +- .../contrast_ratio/contrast_ratio.vue | 4 +- .../delete_button/delete_button.vue | 2 +- .../notifications/notifications.scss | 10 +- .../style_switcher/style_switcher.js | 40 +-- .../style_switcher/style_switcher.vue | 257 +++++++++--------- src/components/timeline/timeline.vue | 5 - src/services/color_convert/color_convert.js | 23 +- src/services/style_setter/style_setter.js | 28 +- .../user_highlighter/user_highlighter.js | 2 +- 11 files changed, 200 insertions(+), 187 deletions(-) diff --git a/src/App.js b/src/App.js index 05e3eda3..b06e8b5d 100644 --- a/src/App.js +++ b/src/App.js @@ -59,7 +59,12 @@ export default { }) }, logo () { return this.$store.state.instance.logo }, - style () { return { 'background-image': `url(${this.background})` } }, + style () { + return { + '--body-background-image': `url(${this.background})`, + 'background-image': `url(${this.background})` + } + }, sitename () { return this.$store.state.instance.name }, chat () { return this.$store.state.chat.channel.state === 'joined' }, suggestionsEnabled () { return this.$store.state.instance.suggestionsEnabled }, diff --git a/src/App.scss b/src/App.scss index 0a2ff5cc..8fb3c488 100644 --- a/src/App.scss +++ b/src/App.scss @@ -473,14 +473,19 @@ nav { padding: 0.25em; border-radius: $fallback--tooltipRadius; border-radius: var(--tooltipRadius, $fallback--tooltipRadius); - color: $fallback--faint; - color: var(--faint, $fallback--faint); min-height: 28px; line-height: 28px; &.error { background-color: $fallback--alertError; background-color: var(--alertError, $fallback--alertError); + color: $fallback--text; + color: var(--alertErrorText, $fallback--text); + + .panel-heading & { + color: $fallback--text; + color: var(--alertErrorPanelText, $fallback--text); + } } } diff --git a/src/components/contrast_ratio/contrast_ratio.vue b/src/components/contrast_ratio/contrast_ratio.vue index a428e75f..cb65c371 100644 --- a/src/components/contrast_ratio/contrast_ratio.vue +++ b/src/components/contrast_ratio/contrast_ratio.vue @@ -1,5 +1,5 @@ <template> -<span class="contrast-ratio"> +<span v-if="contrast" class="contrast-ratio"> <span :title="`Contrast is ${contrast.text}`" class="rating"> <span v-if="contrast.aaa"> <i class="icon-thumbs-up-alt"/> @@ -11,7 +11,7 @@ <i class="icon-attention"/> </span> </span> - <span class="rating" v-if="large" :title="`Contrast is ${contrast.text} (18pt+)`"> + <span class="rating" v-if="contrast && large" :title="`Contrast is ${contrast.text} (18pt+)`"> <span v-if="contrast.aaa"> <i class="icon-thumbs-up-alt"/> </span> diff --git a/src/components/delete_button/delete_button.vue b/src/components/delete_button/delete_button.vue index d13547e2..b458b0dc 100644 --- a/src/components/delete_button/delete_button.vue +++ b/src/components/delete_button/delete_button.vue @@ -14,8 +14,8 @@ .icon-cancel,.delete-status { cursor: pointer; &:hover { - color: var(--cRed, $fallback--cRed); color: $fallback--cRed; + color: var(--cRed, $fallback--cRed); } } </style> diff --git a/src/components/notifications/notifications.scss b/src/components/notifications/notifications.scss index 98fdd3f5..fcc0c3d4 100644 --- a/src/components/notifications/notifications.scss +++ b/src/components/notifications/notifications.scss @@ -8,13 +8,13 @@ display: inline-block; background-color: $fallback--cRed; background-color: var(--badgeNotification, $fallback--cRed); - text-shadow: 0px 0px 3px rgba(0, 0, 0, 0.5); border-radius: 99px; min-width: 22px; max-width: 22px; min-height: 22px; max-height: 22px; color: white; + color: var(--badgeNotificationText, white); font-size: 15px; line-height: 22px; text-align: center; @@ -27,8 +27,8 @@ } .unseen { - box-shadow: inset 4px 0 0 var(--badgeNotification, $fallback--cRed); - padding-left: 0; + + background-image: linear-gradient(135deg, var(--badgeNotification, $fallback--cRed) 4px, transparent 10px) } } @@ -42,8 +42,8 @@ .broken-favorite { border-radius: $fallback--tooltipRadius; border-radius: var(--tooltipRadius, $fallback--tooltipRadius); - color: $fallback--faint; - color: var(--faint, $fallback--faint); + color: $fallback--text; + color: var(--alertErrorText, $fallback--text); background-color: $fallback--alertError; background-color: var(--alertError, $fallback--alertError); padding: 2px .5em diff --git a/src/components/style_switcher/style_switcher.js b/src/components/style_switcher/style_switcher.js index 8953dc49..9e236488 100644 --- a/src/components/style_switcher/style_switcher.js +++ b/src/components/style_switcher/style_switcher.js @@ -1,8 +1,9 @@ -import { rgb2hex, hex2rgb, getContrastRatio, worstCase } from '../../services/color_convert/color_convert.js' +import { rgb2hex, hex2rgb, getContrastRatio, alphaBlend } from '../../services/color_convert/color_convert.js' import ColorInput from '../color_input/color_input.vue' import ContrastRatio from '../contrast_ratio/contrast_ratio.vue' import OpacityInput from '../opacity_input/opacity_input.vue' import StyleSetter from '../../services/style_setter/style_setter.js' +import TabSwitcher from '../tab_switcher/tab_switcher.jsx' export default { data () { @@ -38,7 +39,6 @@ export default { topBarTextColorLocal: undefined, topBarLinkColorLocal: undefined, - alertOpacityLocal: undefined, alertErrorColorLocal: undefined, badgeOpacityLocal: undefined, @@ -123,8 +123,6 @@ export default { btn: this.btnOpacityLocal, input: this.inputOpacityLocal, panel: this.panelOpacityLocal, - alert: this.alertOpacityLocal, - badge: this.badgeOpacityLocal, topBar: this.topBarOpacityLocal, border: this.borderOpacityLocal, faint: this.faintOpacityLocal @@ -160,6 +158,7 @@ export default { if (!this.previewTheme.colors) return {} const colors = this.previewTheme.colors const opacity = this.previewTheme.opacity + if (!colors.bg) return {} const hints = (ratio) => ({ text: ratio.toPrecision(3) + ':1', // AA level, AAA level @@ -197,26 +196,28 @@ export default { badgeNotification: hex2rgb(colors.badgeNotification) } + /* This is a bit confusing because "bottom layer" used is text color + * This is done to get worst case scenario when background below transparent + * layer matches text color, making it harder to read the lower alpha is. + */ const ratios = { - bgText: getContrastRatio(worstCase(bgs.bg, opacity.bg, fgs.text), fgs.text), - bgLink: getContrastRatio(worstCase(bgs.bg, opacity.bg, fgs.link), fgs.link), - bgRed: getContrastRatio(worstCase(bgs.bg, opacity.bg, fgs.red), fgs.red), - bgGreen: getContrastRatio(worstCase(bgs.bg, opacity.bg, fgs.green), fgs.green), - bgBlue: getContrastRatio(worstCase(bgs.bg, opacity.bg, fgs.blue), fgs.blue), - bgOrange: getContrastRatio(worstCase(bgs.bg, opacity.bg, fgs.orange), fgs.orange), + bgText: getContrastRatio(alphaBlend(bgs.bg, opacity.bg, fgs.text), fgs.text), + bgLink: getContrastRatio(alphaBlend(bgs.bg, opacity.bg, fgs.link), fgs.link), + bgRed: getContrastRatio(alphaBlend(bgs.bg, opacity.bg, fgs.red), fgs.red), + bgGreen: getContrastRatio(alphaBlend(bgs.bg, opacity.bg, fgs.green), fgs.green), + bgBlue: getContrastRatio(alphaBlend(bgs.bg, opacity.bg, fgs.blue), fgs.blue), + bgOrange: getContrastRatio(alphaBlend(bgs.bg, opacity.bg, fgs.orange), fgs.orange), - tintText: getContrastRatio(worstCase(bgs.bg, 0.5, fgs.panelText), fgs.text), + tintText: getContrastRatio(alphaBlend(bgs.bg, 0.5, fgs.panelText), fgs.text), - panelText: getContrastRatio(worstCase(bgs.panel, opacity.panel, fgs.panelText), fgs.panelText), + panelText: getContrastRatio(alphaBlend(bgs.panel, opacity.panel, fgs.panelText), fgs.panelText), - btnText: getContrastRatio(worstCase(bgs.btn, opacity.btn, fgs.btnText), fgs.btnText), + btnText: getContrastRatio(alphaBlend(bgs.btn, opacity.btn, fgs.btnText), fgs.btnText), - inputText: getContrastRatio(worstCase(bgs.input, opacity.input, fgs.inputText), fgs.inputText), + inputText: getContrastRatio(alphaBlend(bgs.input, opacity.input, fgs.inputText), fgs.inputText), - badgeNotification: getContrastRatio(worstCase(bgs.badgeNotification, opacity.badge, fgs.text), fgs.text), - - topBarText: getContrastRatio(worstCase(bgs.topBar, opacity.topBar, fgs.topBarText), fgs.topBarText), - topBarLink: getContrastRatio(worstCase(bgs.topBar, opacity.topBar, fgs.topBarLink), fgs.topBarLink) + topBarText: getContrastRatio(alphaBlend(bgs.topBar, opacity.topBar, fgs.topBarText), fgs.topBarText), + topBarLink: getContrastRatio(alphaBlend(bgs.topBar, opacity.topBar, fgs.topBarLink), fgs.topBarLink) } return Object.entries(ratios).reduce((acc, [k, v]) => { acc[k] = hints(v); return acc }, {}) @@ -229,7 +230,8 @@ export default { components: { ColorInput, OpacityInput, - ContrastRatio + ContrastRatio, + TabSwitcher }, methods: { exportCurrentTheme () { diff --git a/src/components/style_switcher/style_switcher.vue b/src/components/style_switcher/style_switcher.vue index f4b4e88f..5bc38318 100644 --- a/src/components/style_switcher/style_switcher.vue +++ b/src/components/style_switcher/style_switcher.vue @@ -24,8 +24,8 @@ </div> </div> - <div class="preview-container" :style="previewRules"> - <div class="panel dummy"> + <div class="preview-container"> + <div class="panel dummy" :style="previewRules"> <div class="panel-heading">Preview</div> <div class="panel-body theme-preview-content"> <div class="avatar"> @@ -45,132 +45,132 @@ </div> </div> - <div class="color-container"> <p>{{$t('settings.theme_help')}}</p> - <h3>Basic colors!!</h3> - <div> - <div class="color-item"> - <ColorInput name="bgColor" v-model="bgColorLocal" :label="$t('settings.background')"/> - <OpacityInput name="bgOpacity" v-model="bgOpacityLocal" :fallback="previewTheme.opacity.bg || 1"/> - <ColorInput name="textColor" v-model="textColorLocal" :label="$t('settings.text')"/> - <ContrastRatio :contrast="previewContrast.bgText"/> - <ColorInput name="linkColor" v-model="linkColorLocal" :label="$t('settings.links')"/> - <ContrastRatio :contrast="previewContrast.bgLink"/> + <tab-switcher> + <div label="Basic" class="color-container"> + <div class="color-item"> + <h4>Main colors</h4> + <ColorInput name="bgColor" v-model="bgColorLocal" :label="$t('settings.background')"/> + <OpacityInput name="bgOpacity" v-model="bgOpacityLocal" :fallback="previewTheme.opacity.bg || 1"/> + <ColorInput name="textColor" v-model="textColorLocal" :label="$t('settings.text')"/> + <ContrastRatio :contrast="previewContrast.bgText"/> + <ColorInput name="linkColor" v-model="linkColorLocal" :label="$t('settings.links')"/> + <ContrastRatio :contrast="previewContrast.bgLink"/> + </div> + <div class="color-item"> + <h4>Panel header, top bar, buttons, text fields</h4> + <ColorInput name="fgColor" v-model="fgColorLocal" :label="$t('settings.foreground')"/> + <ColorInput name="fgTextColor" v-model="fgTextColorLocal" :label="$t('settings.text')" :fallback="previewTheme.colors.fgText"/> + <ColorInput name="fgLinkColor" v-model="fgLinkColorLocal" :label="$t('settings.links')" :fallback="previewTheme.colors.fgLink"/> + <p>See "Advanced" tab for more detailed control</p> + </div> + <div class="color-item"> + <h4>Icons, alerts, etc.</h4> + <ColorInput name="cRedColor" v-model="cRedColorLocal" :label="$t('settings.cRed')"/> + <ContrastRatio :contrast="previewContrast.bgRed"/> + <ColorInput name="cBlueColor" v-model="cBlueColorLocal" :label="$t('settings.cBlue')"/> + <ContrastRatio :contrast="previewContrast.bgBlue"/> + </div> + <div class="color-item"> + <h4>.</h4> + <ColorInput name="cGreenColor" v-model="cGreenColorLocal" :label="$t('settings.cGreen')"/> + <ContrastRatio :contrast="previewContrast.bgGreen"/> + <ColorInput name="cOrangeColor" v-model="cOrangeColorLocal" :label="$t('settings.cOrange')"/> + <ContrastRatio :contrast="previewContrast.bgOrange"/> + </div> </div> - <div class="color-item"> - <ColorInput name="fgColor" v-model="fgColorLocal" :label="$t('settings.foreground')"/> - <ColorInput name="fgTextColor" v-model="fgTextColorLocal" :label="$t('settings.text')" :fallback="previewTheme.colors.fgText"/> - <ColorInput name="fgLinkColor" v-model="fgLinkColorLocal" :label="$t('settings.links')" :fallback="previewTheme.colors.fgLink"/> - </div> - <div class="color-item"> - <ColorInput name="cRedColor" v-model="cRedColorLocal" :label="$t('settings.cRed')"/> - <ContrastRatio :contrast="previewContrast.bgRed"/> - <ColorInput name="cBlueColor" v-model="cBlueColorLocal" :label="$t('settings.cBlue')"/> - <ContrastRatio :contrast="previewContrast.bgBlue"/> - </div> - <div class="color-item"> - <ColorInput name="cGreenColor" v-model="cGreenColorLocal" :label="$t('settings.cGreen')"/> - <ContrastRatio :contrast="previewContrast.bgGreen"/> - <ColorInput name="cOrangeColor" v-model="cOrangeColorLocal" :label="$t('settings.cOrange')"/> - <ContrastRatio :contrast="previewContrast.bgOrange"/> - </div> - </div> - <h3>More customs!</h3> - <div> - <div class="color-item"> - <h4>Alerts</h4> - <ColorInput name="alertError" v-model="alertErrorColorLocal" :label="$t('settings.error')" :fallback="previewTheme.colors.alertError"/> - <OpacityInput name="alertOpacity" v-model="alertOpacityLocal" :fallback="previewTheme.opacity.alert || 1"/> + <div label="Advanced" class="color-container"> + <div class="color-item"> + <h4>Alerts</h4> + <ColorInput name="alertError" v-model="alertErrorColorLocal" :label="$t('settings.error')" :fallback="previewTheme.colors.alertError"/> + <ContrastRatio :contrast="previewContrast.alertError"/> + </div> + <div class="color-item"> + <h4>Badges</h4> + <ColorInput name="badgeNotification" v-model="badgeNotificationColorLocal" :label="$t('settings.notification')" :fallback="previewTheme.colors.badgeNotification"/> + </div> + <div class="color-item"> + <h4>Panel header</h4> + <ColorInput name="panelColor" v-model="panelColorLocal" :fallback="fgColorLocal" :label="$t('settings.background')"/> + <OpacityInput name="panelOpacity" v-model="panelOpacityLocal" :fallback="previewTheme.opacity.panel || 1"/> + <ColorInput name="panelTextColor" v-model="panelTextColorLocal" :fallback="previewTheme.colors.panelText" :label="$t('settings.links')"/> + <ContrastRatio :contrast="previewContrast.panelText" large="1"/> + </div> + <div class="color-item"> + <h4>Top bar</h4> + <ColorInput name="topBarColor" v-model="topBarColorLocal" :fallback="fgColorLocal" :label="$t('settings.background')"/> + <ColorInput name="topBarTextColor" v-model="topBarTextColorLocal" :fallback="previewTheme.colors.topBarText" :label="$t('settings.text')"/> + <ContrastRatio :contrast="previewContrast.topBarText"/> + <ColorInput name="topBarLinkColor" v-model="topBarLinkColorLocal" :fallback="previewTheme.colors.topBarLink" :label="$t('settings.links')"/> + <ContrastRatio :contrast="previewContrast.topBarLink"/> + </div> + <div class="color-item"> + <h4>Text fields</h4> + <ColorInput name="inputColor" v-model="inputColorLocal" :fallback="fgColorLocal" :label="$t('settings.background')"/> + <OpacityInput name="inputOpacity" v-model="inputOpacityLocal" :fallback="previewTheme.opacity.input || 1"/> + <ColorInput name="inputTextColor" v-model="inputTextColorLocal" :fallback="previewTheme.colors.inputText" :label="$t('settings.text')"/> + <ContrastRatio :contrast="previewContrast.inputText"/> + </div> + <div class="color-item"> + <h4>Buttons</h4> + <ColorInput name="btnColor" v-model="btnColorLocal" :fallback="fgColorLocal" :label="$t('settings.background')"/> + <OpacityInput name="btnOpacity" v-model="btnOpacityLocal" :fallback="previewTheme.opacity.btn || 1"/> + <ColorInput name="btnTextColor" v-model="btnTextColorLocal" :fallback="previewTheme.colors.btnText" :label="$t('settings.text')"/> + <ContrastRatio :contrast="previewContrast.btnText"/> + </div> + <div class="color-item"> + <h4>Borders</h4> + <ColorInput name="borderColor" v-model="borderColorLocal" :fallback="previewTheme.colors.border" label="Color"/> + <OpacityInput name="borderOpacity" v-model="borderOpacityLocal" :fallback="previewTheme.opacity.border || 1"/> + </div> + <div class="color-item"> + <h4>Faint text</h4> + <ColorInput name="faintColor" v-model="faintColorLocal" :fallback="previewTheme.colors.faint || 1" :label="$t('settings.text')"/> + <ColorInput name="faintLinkColor" v-model="faintLinkColorLocal" :fallback="previewTheme.colors.faintLink" :label="$t('settings.links')"/> + <ColorInput name="panelFaintColor" v-model="panelFaintColorLocal" :fallback="previewTheme.colors.panelFaint" :label="$t('settings.panel')"/> + <OpacityInput name="faintOpacity" v-model="faintOpacityLocal" :fallback="previewTheme.opacity.faint || 0.5"/> + </div> </div> - <div class="color-item"> - <h4>Alerts</h4> - <ColorInput name="badgeNotification" v-model="badgeNotificationColorLocal" :label="$t('settings.notification')" :fallback="previewTheme.colors.badgeNotification"/> - <ContrastRatio :contrast="previewContrast.badgeNotification"/> - <OpacityInput name="badgeOpacity" v-model="badgeOpacityLocal" :fallback="previewTheme.opacity.badge || 1"/> + <div label="Roundness" class="radius-container"> + <p>{{$t('settings.radii_help')}}</p> + <div class="radius-item"> + <label for="btnradius" class="theme-radius-lb">{{$t('settings.btnRadius')}}</label> + <input id="btnradius" class="theme-radius-rn" type="range" v-model="btnRadiusLocal" max="16"> + <input id="btnradius-t" class="theme-radius-in" type="text" v-model="btnRadiusLocal"> + </div> + <div class="radius-item"> + <label for="inputradius" class="theme-radius-lb">{{$t('settings.inputRadius')}}</label> + <input id="inputradius" class="theme-radius-rn" type="range" v-model="inputRadiusLocal" max="16"> + <input id="inputradius-t" class="theme-radius-in" type="text" v-model="inputRadiusLocal"> + </div> + <div class="radius-item"> + <label for="panelradius" class="theme-radius-lb">{{$t('settings.panelRadius')}}</label> + <input id="panelradius" class="theme-radius-rn" type="range" v-model="panelRadiusLocal" max="50"> + <input id="panelradius-t" class="theme-radius-in" type="text" v-model="panelRadiusLocal"> + </div> + <div class="radius-item"> + <label for="avatarradius" class="theme-radius-lb">{{$t('settings.avatarRadius')}}</label> + <input id="avatarradius" class="theme-radius-rn" type="range" v-model="avatarRadiusLocal" max="28"> + <input id="avatarradius-t" class="theme-radius-in" type="green" v-model="avatarRadiusLocal"> + </div> + <div class="radius-item"> + <label for="avataraltradius" class="theme-radius-lb">{{$t('settings.avatarAltRadius')}}</label> + <input id="avataraltradius" class="theme-radius-rn" type="range" v-model="avatarAltRadiusLocal" max="28"> + <input id="avataraltradius-t" class="theme-radius-in" type="text" v-model="avatarAltRadiusLocal"> + </div> + <div class="radius-item"> + <label for="attachmentradius" class="theme-radius-lb">{{$t('settings.attachmentRadius')}}</label> + <input id="attachmentrradius" class="theme-radius-rn" type="range" v-model="attachmentRadiusLocal" max="50"> + <input id="attachmentradius-t" class="theme-radius-in" type="text" v-model="attachmentRadiusLocal"> + </div> + <div class="radius-item"> + <label for="tooltipradius" class="theme-radius-lb">{{$t('settings.tooltipRadius')}}</label> + <input id="tooltipradius" class="theme-radius-rn" type="range" v-model="tooltipRadiusLocal" max="20"> + <input id="tooltipradius-t" class="theme-radius-in" type="text" v-model="tooltipRadiusLocal"> + </div> </div> - <div class="color-item"> - <h4>Panel header</h4> - <ColorInput name="panelColor" v-model="panelColorLocal" :fallback="fgColorLocal" :label="$t('settings.background')"/> - <OpacityInput name="panelOpacity" v-model="panelOpacityLocal" :fallback="previewTheme.opacity.panel || 1"/> - <ColorInput name="panelTextColor" v-model="panelTextColorLocal" :fallback="previewTheme.colors.panelText" :label="$t('settings.links')"/> - <ContrastRatio :contrast="previewContrast.panelText" large="1"/> - </div> - <div class="color-item"> - <h4>Top bar</h4> - <ColorInput name="topBarColor" v-model="topBarColorLocal" :fallback="fgColorLocal" :label="$t('settings.background')"/> - <ColorInput name="topBarTextColor" v-model="topBarTextColorLocal" :fallback="previewTheme.colors.topBarText" :label="$t('settings.text')"/> - <ContrastRatio :contrast="previewContrast.topBarText"/> - <ColorInput name="topBarLinkColor" v-model="topBarLinkColorLocal" :fallback="previewTheme.colors.topBarLink" :label="$t('settings.links')"/> - <ContrastRatio :contrast="previewContrast.topBarLink"/> - </div> - <div class="color-item"> - <h4>Text fields</h4> - <ColorInput name="inputColor" v-model="inputColorLocal" :fallback="fgColorLocal" :label="$t('settings.background')"/> - <OpacityInput name="inputOpacity" v-model="inputOpacityLocal" :fallback="previewTheme.opacity.input || 1"/> - <ColorInput name="inputTextColor" v-model="inputTextColorLocal" :fallback="previewTheme.colors.inputText" :label="$t('settings.text')"/> - <ContrastRatio :contrast="previewContrast.inputText"/> - </div> - <div class="color-item"> - <h4>Buttons</h4> - <ColorInput name="btnColor" v-model="btnColorLocal" :fallback="fgColorLocal" :label="$t('settings.background')"/> - <OpacityInput name="btnOpacity" v-model="btnOpacityLocal" :fallback="previewTheme.opacity.btn || 1"/> - <ColorInput name="btnTextColor" v-model="btnTextColorLocal" :fallback="previewTheme.colors.btnText" :label="$t('settings.text')"/> - <ContrastRatio :contrast="previewContrast.btnText"/> - </div> - <div class="color-item"> - <h4>Borders</h4> - <ColorInput name="borderColor" v-model="borderColorLocal" :fallback="previewTheme.colors.border" label="Color"/> - <OpacityInput name="borderOpacity" v-model="borderOpacityLocal" :fallback="previewTheme.opacity.border || 1"/> - </div> - <div class="color-item"> - <h4>Faint text</h4> - <ColorInput name="faintColor" v-model="faintColorLocal" :fallback="previewTheme.colors.faint || 1" :label="$t('settings.text')"/> - <ColorInput name="faintLinkColor" v-model="faintLinkColorLocal" :fallback="previewTheme.colors.faintLink" :label="$t('settings.links')"/> - <ColorInput name="panelFaintColor" v-model="panelFaintColorLocal" :fallback="previewTheme.colors.panelFaint" :label="$t('settings.panel')"/> - <OpacityInput name="faintOpacity" v-model="faintOpacityLocal" :fallback="previewTheme.opacity.faint || 0.5"/> - </div> - </div> - </div> - - <div class="radius-container"> - <p>{{$t('settings.radii_help')}}</p> - <div class="radius-item"> - <label for="btnradius" class="theme-radius-lb">{{$t('settings.btnRadius')}}</label> - <input id="btnradius" class="theme-radius-rn" type="range" v-model="btnRadiusLocal" max="16"> - <input id="btnradius-t" class="theme-radius-in" type="text" v-model="btnRadiusLocal"> - </div> - <div class="radius-item"> - <label for="inputradius" class="theme-radius-lb">{{$t('settings.inputRadius')}}</label> - <input id="inputradius" class="theme-radius-rn" type="range" v-model="inputRadiusLocal" max="16"> - <input id="inputradius-t" class="theme-radius-in" type="text" v-model="inputRadiusLocal"> - </div> - <div class="radius-item"> - <label for="panelradius" class="theme-radius-lb">{{$t('settings.panelRadius')}}</label> - <input id="panelradius" class="theme-radius-rn" type="range" v-model="panelRadiusLocal" max="50"> - <input id="panelradius-t" class="theme-radius-in" type="text" v-model="panelRadiusLocal"> - </div> - <div class="radius-item"> - <label for="avatarradius" class="theme-radius-lb">{{$t('settings.avatarRadius')}}</label> - <input id="avatarradius" class="theme-radius-rn" type="range" v-model="avatarRadiusLocal" max="28"> - <input id="avatarradius-t" class="theme-radius-in" type="green" v-model="avatarRadiusLocal"> - </div> - <div class="radius-item"> - <label for="avataraltradius" class="theme-radius-lb">{{$t('settings.avatarAltRadius')}}</label> - <input id="avataraltradius" class="theme-radius-rn" type="range" v-model="avatarAltRadiusLocal" max="28"> - <input id="avataraltradius-t" class="theme-radius-in" type="text" v-model="avatarAltRadiusLocal"> - </div> - <div class="radius-item"> - <label for="attachmentradius" class="theme-radius-lb">{{$t('settings.attachmentRadius')}}</label> - <input id="attachmentrradius" class="theme-radius-rn" type="range" v-model="attachmentRadiusLocal" max="50"> - <input id="attachmentradius-t" class="theme-radius-in" type="text" v-model="attachmentRadiusLocal"> - </div> - <div class="radius-item"> - <label for="tooltipradius" class="theme-radius-lb">{{$t('settings.tooltipRadius')}}</label> - <input id="tooltipradius" class="theme-radius-rn" type="range" v-model="tooltipRadiusLocal" max="20"> - <input id="tooltipradius-t" class="theme-radius-in" type="text" v-model="tooltipRadiusLocal"> - </div> - </div> + </tab-switcher> <div class="apply-container"> <button class="btn submit" @click="setCustomTheme">{{$t('general.apply')}}</button> @@ -193,14 +193,12 @@ .apply-container, .radius-container, -.color-container > div, +.color-container, .presets-container { display: flex; p { - flex: 2 0 100%; - margin-top: 2em; - margin-bottom: .5em; + margin-left: 1em } } @@ -208,7 +206,7 @@ flex-direction: column; } -.color-container > div{ +.color-container{ flex-wrap: wrap; justify-content: space-between; } @@ -231,6 +229,9 @@ border-color: var(--border, $fallback--border); margin: 1em -1em 0; padding: 1em; + background: var(--body-background-image); + background-size: cover; + background-position: 50% 50%; .btn { margin-top: 1em; diff --git a/src/components/timeline/timeline.vue b/src/components/timeline/timeline.vue index 77a9a2af..c2d5b9e6 100644 --- a/src/components/timeline/timeline.vue +++ b/src/components/timeline/timeline.vue @@ -63,11 +63,6 @@ color: $fallback--faint; color: var(--panelFaint, $fallback--faint); } - - .loadmore-error { - color: $fallback--text; - color: var(--text, $fallback--text); - } } .new-status-notification { diff --git a/src/services/color_convert/color_convert.js b/src/services/color_convert/color_convert.js index 7a5254b9..6f046a1d 100644 --- a/src/services/color_convert/color_convert.js +++ b/src/services/color_convert/color_convert.js @@ -80,30 +80,23 @@ const getContrastRatio = (a, b) => { } /** - * This generates what "worst case" color would look like for transparent - * segments. I.e. transparent black with yellow text over yellow background. + * This performs alpha blending between solid background and semi-transparent foreground * - * @param {Object} srgb - transparent color - * @param {Number} alpha - color's opacity/alpha channel - * @param {Object} textSrgb - text color (considered as worst case scenario for transparent background) + * @param {Object} fg - top layer color + * @param {Number} fga - top layer's alpha + * @param {Object} bg - bottom layer color * @returns {Object} sRGB of resulting color */ -const transparentWorstCase = (srgb, alpha, textSrgb) => { +const alphaBlend = (fg, fga, bg) => { + if (fga === 1 || typeof fga === 'undefined') return fg return 'rgb'.split('').reduce((acc, c) => { // Simplified https://en.wikipedia.org/wiki/Alpha_compositing#Alpha_blending // for opaque bg and transparent fg - acc[c] = (srgb[c] * alpha + textSrgb[c] * (1 - alpha)) + acc[c] = (fg[c] * fga + bg[c] * (1 - fga)) return acc }, {}) } -const worstCase = (bg, bga, text) => { - console.log(bg) - console.log(text) - if (bga === 1 || typeof bga === 'undefined') return bg - return transparentWorstCase(bg, bga, text) -} - const hex2rgb = (hex) => { const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex) return result ? { @@ -134,5 +127,5 @@ export { mixrgb, rgbstr2hex, getContrastRatio, - worstCase + alphaBlend } diff --git a/src/services/style_setter/style_setter.js b/src/services/style_setter/style_setter.js index 666e74c1..9388e6f9 100644 --- a/src/services/style_setter/style_setter.js +++ b/src/services/style_setter/style_setter.js @@ -1,6 +1,6 @@ import { times } from 'lodash' -import { brightness, invertLightness, convert } from 'chromatism' -import { rgb2hex, hex2rgb, mixrgb } from '../color_convert/color_convert.js' +import { brightness, invertLightness, convert, contrastRatio } from 'chromatism' +import { rgb2hex, hex2rgb, mixrgb, getContrastRatio, alphaBlend } from '../color_convert/color_convert.js' // While this is not used anymore right now, I left it in if we want to do custom // styles that aren't just colors, so user can pick from a few different distinct @@ -58,13 +58,17 @@ const rgb2rgba = function (rgba) { return `rgba(${rgba.r}, ${rgba.g}, ${rgba.b}, ${rgba.a})` } -const getTextColor = function (bg, text) { +const getTextColor = function (bg, text, preserve) { const bgIsLight = convert(bg).hsl.l > 50 const textIsLight = convert(text).hsl.l > 50 if ((bgIsLight && textIsLight) || (!bgIsLight && !textIsLight)) { const base = typeof text.a !== 'undefined' ? { a: text.a } : {} - return Object.assign(base, invertLightness(text).rgb) + const result = Object.assign(base, invertLightness(text).rgb) + if (!preserve && getContrastRatio(bg, result) < 4.5) { + return contrastRatio(bg, text).rgb + } + return result } return text } @@ -104,7 +108,12 @@ const generatePreset = (input) => { alert: 0.5, input: 0.5, faint: 0.5 - }, input.opacity) + }, Object.entries(input.opacity || {}).reduce((acc, [k, v]) => { + if (typeof v !== 'undefined') { + acc[k] = v + } + return acc + }, {})) const col = Object.entries(input.colors || input).reduce((acc, [k, v]) => { if (typeof v === 'object') { @@ -128,9 +137,9 @@ const generatePreset = (input) => { colors.fg = col.fg colors.fgText = col.fgText || getTextColor(colors.fg, colors.text) - colors.fgLink = col.fgLink || getTextColor(colors.fg, colors.link) + colors.fgLink = col.fgLink || getTextColor(colors.fg, colors.link, true) - colors.border = col.border || brightness(20 * mod, colors.fg).rgb + colors.border = col.border || brightness(2 * mod, colors.fg).rgb colors.btn = col.btn || Object.assign({}, col.fg) colors.btnText = col.btnText || getTextColor(colors.btn, colors.fgText) @@ -156,8 +165,11 @@ const generatePreset = (input) => { colors.cOrange = col.cOrange colors.alertError = col.alertError || Object.assign({}, col.cRed) + colors.alertErrorText = getTextColor(alphaBlend(colors.alertError, opacity.alert, colors.bg), colors.text) + colors.alertErrorPanelText = getTextColor(alphaBlend(colors.alertError, opacity.alert, colors.panel), colors.panelText) + colors.badgeNotification = col.badgeNotification || Object.assign({}, col.cRed) - colors.badgeNotificationText = col.badgeNotification || Object.assign({}, col.cRed) + colors.badgeNotificationText = contrastRatio(colors.badgeNotification).rgb Object.entries(opacity).forEach(([ k, v ]) => { if (typeof v === 'undefined') return diff --git a/src/services/user_highlighter/user_highlighter.js b/src/services/user_highlighter/user_highlighter.js index ebb25eca..f6ddfb9c 100644 --- a/src/services/user_highlighter/user_highlighter.js +++ b/src/services/user_highlighter/user_highlighter.js @@ -11,7 +11,7 @@ const highlightStyle = (prefs) => { if (type === 'striped') { return { backgroundImage: [ - 'repeating-linear-gradient(-45deg,', + 'repeating-linear-gradient(135deg,', `${tintColor} ,`, `${tintColor} 20px,`, `${tintColor2} 20px,`, From 75f0c191dd8ad029561901ce0dab8e10226b95f7 Mon Sep 17 00:00:00 2001 From: Henry Jameson <me@hjkos.com> Date: Wed, 14 Nov 2018 21:53:51 +0300 Subject: [PATCH 12/96] some initial work for user highlight v2 --- src/components/user_card_content/user_card_content.js | 2 +- src/services/color_convert/color_convert.js | 10 ++++++++++ src/services/style_setter/style_setter.js | 5 ++--- 3 files changed, 13 insertions(+), 4 deletions(-) diff --git a/src/components/user_card_content/user_card_content.js b/src/components/user_card_content/user_card_content.js index 76a5577e..eae436a9 100644 --- a/src/components/user_card_content/user_card_content.js +++ b/src/components/user_card_content/user_card_content.js @@ -5,7 +5,7 @@ export default { props: [ 'user', 'switcher', 'selected', 'hideBio' ], computed: { headingStyle () { - const color = this.$store.state.config.colors.bg + const color = this.$store.state.config.customTheme.colors.bg if (color) { const rgb = hex2rgb(color) const tintColor = `rgba(${Math.floor(rgb.r)}, ${Math.floor(rgb.g)}, ${Math.floor(rgb.b)}, .5)` diff --git a/src/services/color_convert/color_convert.js b/src/services/color_convert/color_convert.js index 6f046a1d..00b6c552 100644 --- a/src/services/color_convert/color_convert.js +++ b/src/services/color_convert/color_convert.js @@ -97,6 +97,15 @@ const alphaBlend = (fg, fga, bg) => { }, {}) } +const invert = (rgb) => { + return 'rgb'.split('').reduce((acc, c) => { + console.log(rgb[c]) + acc[c] = 255 - rgb[c] + console.log(acc[c]) + return acc + }, {}) +} + const hex2rgb = (hex) => { const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex) return result ? { @@ -125,6 +134,7 @@ export { rgb2hex, hex2rgb, mixrgb, + invert, rgbstr2hex, getContrastRatio, alphaBlend diff --git a/src/services/style_setter/style_setter.js b/src/services/style_setter/style_setter.js index 9388e6f9..907de586 100644 --- a/src/services/style_setter/style_setter.js +++ b/src/services/style_setter/style_setter.js @@ -40,8 +40,6 @@ const setStyle = (href, commit) => { colors[name] = color }) - commit('setOption', { name: 'colors', value: colors }) - body.removeChild(baseEl) const styleEl = document.createElement('style') @@ -74,7 +72,7 @@ const getTextColor = function (bg, text, preserve) { } const setColors = (input, commit) => { - const { colorRules, radiiRules } = generatePreset(input) + const { colorRules, radiiRules, theme } = generatePreset(input) const head = document.head const body = document.body body.style.display = 'none' @@ -91,6 +89,7 @@ const setColors = (input, commit) => { // commit('setOption', { name: 'colors', value: htmlColors }) // commit('setOption', { name: 'radii', value: radii }) commit('setOption', { name: 'customTheme', value: input }) + commit('setOption', { name: 'colors', value: theme.colors }) } const generatePreset = (input) => { From 2369f2e4cba2df8bb1dda431f193130bbf290d2c Mon Sep 17 00:00:00 2001 From: Henry Jameson <me@hjkos.com> Date: Wed, 14 Nov 2018 22:20:42 +0300 Subject: [PATCH 13/96] fixed webkit appearance of the UI --- src/components/color_input/color_input.vue | 5 +++-- src/components/contrast_ratio/contrast_ratio.vue | 2 +- src/components/opacity_input/opacity_input.vue | 5 ++--- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/components/color_input/color_input.vue b/src/components/color_input/color_input.vue index 49d9bed7..60a62fa8 100644 --- a/src/components/color_input/color_input.vue +++ b/src/components/color_input/color_input.vue @@ -47,6 +47,7 @@ export default { <style lang="scss"> .color-control { display: flex; + align-items: baseline; &.disabled *:not(.opt-l){ opacity: .5 @@ -75,10 +76,10 @@ export default { flex: 0; padding: 1px; cursor: pointer; - height: 100%; - max-height: 29px; + height: 29px; min-width: 2em; border: none; + align-self: stretch; } } </style> diff --git a/src/components/contrast_ratio/contrast_ratio.vue b/src/components/contrast_ratio/contrast_ratio.vue index cb65c371..9a93dcd0 100644 --- a/src/components/contrast_ratio/contrast_ratio.vue +++ b/src/components/contrast_ratio/contrast_ratio.vue @@ -36,7 +36,7 @@ export default { <style lang="scss"> .contrast-ratio { display: flex; - justify-content: end; + justify-content: flex-end; .label { margin-right: 1em; diff --git a/src/components/opacity_input/opacity_input.vue b/src/components/opacity_input/opacity_input.vue index efa6c449..4057dcca 100644 --- a/src/components/opacity_input/opacity_input.vue +++ b/src/components/opacity_input/opacity_input.vue @@ -41,6 +41,7 @@ export default { <style lang="scss"> .opacity-control { display: flex; + align-items: baseline; &.disabled *:not(.opt-l) { opacity: .5 @@ -48,7 +49,6 @@ export default { .opt-l { align-self: center; - line-height: 0; &::before { width: 14px; height: 14px; @@ -61,7 +61,6 @@ export default { } .input-range { - align-self: center; background: none; border: none; margin: 0; @@ -70,8 +69,8 @@ export default { min-width: 7em; flex: 1; } + .input-number { - align-self: center; margin: 0; min-width: 4em; flex: 0; From 75cdcc40db45463c1612950c1263b3e3dc2a2725 Mon Sep 17 00:00:00 2001 From: Henry Jameson <me@hjkos.com> Date: Thu, 15 Nov 2018 17:09:25 +0300 Subject: [PATCH 14/96] fix accidentally removed icon --- static/font/config.json | 6 ++++++ static/font/css/fontello-codes.css | 1 + static/font/css/fontello-embedded.css | 13 +++++++------ static/font/css/fontello-ie7-codes.css | 1 + static/font/css/fontello-ie7.css | 1 + static/font/css/fontello.css | 15 ++++++++------- static/font/demo.html | 13 +++++++------ static/font/font/fontello.eot | Bin 16124 -> 16384 bytes static/font/font/fontello.svg | 2 ++ static/font/font/fontello.ttf | Bin 15956 -> 16216 bytes static/font/font/fontello.woff | Bin 9848 -> 9972 bytes static/font/font/fontello.woff2 | Bin 8372 -> 8456 bytes 12 files changed, 33 insertions(+), 19 deletions(-) diff --git a/static/font/config.json b/static/font/config.json index be809269..1d1317d7 100644 --- a/static/font/config.json +++ b/static/font/config.json @@ -203,6 +203,12 @@ "css": "attention", "code": 59412, "src": "fontawesome" + }, + { + "uid": "1a5cfa186647e8c929c2b17b9fc4dac1", + "css": "plus-squared", + "code": 61694, + "src": "fontawesome" } ] } \ No newline at end of file diff --git a/static/font/css/fontello-codes.css b/static/font/css/fontello-codes.css index 21cf6f06..66a240cd 100644 --- a/static/font/css/fontello-codes.css +++ b/static/font/css/fontello-codes.css @@ -27,6 +27,7 @@ .icon-menu:before { content: '\f0c9'; } /* '' */ .icon-mail-alt:before { content: '\f0e0'; } /* '' */ .icon-comment-empty:before { content: '\f0e5'; } /* '' */ +.icon-plus-squared:before { content: '\f0fe'; } /* '' */ .icon-reply:before { content: '\f112'; } /* '' */ .icon-lock-open-alt:before { content: '\f13e'; } /* '' */ .icon-thumbs-up-alt:before { content: '\f164'; } /* '' */ diff --git a/static/font/css/fontello-embedded.css b/static/font/css/fontello-embedded.css index a1b3e3d6..65875bde 100644 --- a/static/font/css/fontello-embedded.css +++ b/static/font/css/fontello-embedded.css @@ -1,15 +1,15 @@ @font-face { font-family: 'fontello'; - src: url('../font/fontello.eot?61520746'); - src: url('../font/fontello.eot?61520746#iefix') format('embedded-opentype'), - url('../font/fontello.svg?61520746#fontello') format('svg'); + src: url('../font/fontello.eot?92539127'); + src: url('../font/fontello.eot?92539127#iefix') format('embedded-opentype'), + url('../font/fontello.svg?92539127#fontello') format('svg'); font-weight: normal; font-style: normal; } @font-face { font-family: 'fontello'; - src: url('data:application/octet-stream;base64,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') format('woff'), - url('data:application/octet-stream;base64,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RCywABUgR7AAI0KyAAEBFRQTLrAxKi2wRSywABUgR7AAI0KyAAEBFRQTLrAxKi2wRiyxAAEUE7AyKi2wRyywNCotsEgssAAWRSMgLiBGiiNhOLErARQrLbBJLLAII0KwSCstsEossgAAQSstsEsssgABQSstsEwssgEAQSstsE0ssgEBQSstsE4ssgAAQistsE8ssgABQistsFAssgEAQistsFEssgEBQistsFIssgAAPistsFMssgABPistsFQssgEAPistsFUssgEBPistsFYssgAAQCstsFcssgABQCstsFgssgEAQCstsFkssgEBQCstsFossgAAQystsFsssgABQystsFwssgEAQystsF0ssgEBQystsF4ssgAAPystsF8ssgABPystsGAssgEAPystsGEssgEBPystsGIssDcrLrErARQrLbBjLLA3K7A7Ky2wZCywNyuwPCstsGUssAAWsDcrsD0rLbBmLLA4Ky6xKwEUKy2wZyywOCuwOystsGgssDgrsDwrLbBpLLA4K7A9Ky2waiywOSsusSsBFCstsGsssDkrsDsrLbBsLLA5K7A8Ky2wbSywOSuwPSstsG4ssDorLrErARQrLbBvLLA6K7A7Ky2wcCywOiuwPCstsHEssDorsD0rLbByLLMJBAIDRVghGyMhWUIrsAhlsAMkUHiwARUwLQBLuADIUlixAQGOWbABuQgACABjcLEABUKyAAEAKrEABUKzCgIBCCqxAAVCsw4AAQgqsQAGQroCwAABAAkqsQAHQroAQAABAAkqsQMARLEkAYhRWLBAiFixA2REsSYBiFFYugiAAAEEQIhjVFixAwBEWVlZWbMMAgEMKrgB/4WwBI2xAgBEAAA=') format('truetype'); + src: url('data:application/octet-stream;base64,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') format('woff'), + url('data:application/octet-stream;base64,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') format('truetype'); } /* Chrome hack: SVG is rendered more smooth in Windozze. 100% magic, uncomment if you need it. */ /* Note, that will break hinting! In other OS-es font will be not as sharp as it could be */ @@ -17,7 +17,7 @@ @media screen and (-webkit-min-device-pixel-ratio:0) { @font-face { font-family: 'fontello'; - src: url('../font/fontello.svg?61520746#fontello') format('svg'); + src: url('../font/fontello.svg?92539127#fontello') format('svg'); } } */ @@ -80,6 +80,7 @@ .icon-menu:before { content: '\f0c9'; } /* '' */ .icon-mail-alt:before { content: '\f0e0'; } /* '' */ .icon-comment-empty:before { content: '\f0e5'; } /* '' */ +.icon-plus-squared:before { content: '\f0fe'; } /* '' */ .icon-reply:before { content: '\f112'; } /* '' */ .icon-lock-open-alt:before { content: '\f13e'; } /* '' */ .icon-thumbs-up-alt:before { content: '\f164'; } /* '' */ diff --git a/static/font/css/fontello-ie7-codes.css b/static/font/css/fontello-ie7-codes.css index 5e732ec9..689c8e43 100644 --- a/static/font/css/fontello-ie7-codes.css +++ b/static/font/css/fontello-ie7-codes.css @@ -27,6 +27,7 @@ .icon-menu { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } .icon-mail-alt { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } .icon-comment-empty { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-plus-squared { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } .icon-reply { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } .icon-lock-open-alt { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } .icon-thumbs-up-alt { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } diff --git a/static/font/css/fontello-ie7.css b/static/font/css/fontello-ie7.css index b4be6e9b..23ecfa3c 100644 --- a/static/font/css/fontello-ie7.css +++ b/static/font/css/fontello-ie7.css @@ -38,6 +38,7 @@ .icon-menu { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } .icon-mail-alt { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } .icon-comment-empty { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-plus-squared { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } .icon-reply { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } .icon-lock-open-alt { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } .icon-thumbs-up-alt { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } diff --git a/static/font/css/fontello.css b/static/font/css/fontello.css index d8419862..0181fa62 100644 --- a/static/font/css/fontello.css +++ b/static/font/css/fontello.css @@ -1,11 +1,11 @@ @font-face { font-family: 'fontello'; - src: url('../font/fontello.eot?91338570'); - src: url('../font/fontello.eot?91338570#iefix') format('embedded-opentype'), - url('../font/fontello.woff2?91338570') format('woff2'), - url('../font/fontello.woff?91338570') format('woff'), - url('../font/fontello.ttf?91338570') format('truetype'), - url('../font/fontello.svg?91338570#fontello') format('svg'); + src: url('../font/fontello.eot?13201279'); + src: url('../font/fontello.eot?13201279#iefix') format('embedded-opentype'), + url('../font/fontello.woff2?13201279') format('woff2'), + url('../font/fontello.woff?13201279') format('woff'), + url('../font/fontello.ttf?13201279') format('truetype'), + url('../font/fontello.svg?13201279#fontello') format('svg'); font-weight: normal; font-style: normal; } @@ -15,7 +15,7 @@ @media screen and (-webkit-min-device-pixel-ratio:0) { @font-face { font-family: 'fontello'; - src: url('../font/fontello.svg?91338570#fontello') format('svg'); + src: url('../font/fontello.svg?13201279#fontello') format('svg'); } } */ @@ -83,6 +83,7 @@ .icon-menu:before { content: '\f0c9'; } /* '' */ .icon-mail-alt:before { content: '\f0e0'; } /* '' */ .icon-comment-empty:before { content: '\f0e5'; } /* '' */ +.icon-plus-squared:before { content: '\f0fe'; } /* '' */ .icon-reply:before { content: '\f112'; } /* '' */ .icon-lock-open-alt:before { content: '\f13e'; } /* '' */ .icon-thumbs-up-alt:before { content: '\f164'; } /* '' */ diff --git a/static/font/demo.html b/static/font/demo.html index 22bc0e92..6e4d3eb0 100644 --- a/static/font/demo.html +++ b/static/font/demo.html @@ -229,11 +229,11 @@ body { } @font-face { font-family: 'fontello'; - src: url('./font/fontello.eot?93659852'); - src: url('./font/fontello.eot?93659852#iefix') format('embedded-opentype'), - url('./font/fontello.woff?93659852') format('woff'), - url('./font/fontello.ttf?93659852') format('truetype'), - url('./font/fontello.svg?93659852#fontello') format('svg'); + src: url('./font/fontello.eot?97354950'); + src: url('./font/fontello.eot?97354950#iefix') format('embedded-opentype'), + url('./font/fontello.woff?97354950') format('woff'), + url('./font/fontello.ttf?97354950') format('truetype'), + url('./font/fontello.svg?97354950#fontello') format('svg'); font-weight: normal; font-style: normal; } @@ -340,12 +340,13 @@ body { <div class="the-icons span3" title="Code: 0xf0e5"><i class="demo-icon icon-comment-empty"></i> <span class="i-name">icon-comment-empty</span><span class="i-code">0xf0e5</span></div> </div> <div class="row"> + <div class="the-icons span3" title="Code: 0xf0fe"><i class="demo-icon icon-plus-squared"></i> <span class="i-name">icon-plus-squared</span><span class="i-code">0xf0fe</span></div> <div class="the-icons span3" title="Code: 0xf112"><i class="demo-icon icon-reply"></i> <span class="i-name">icon-reply</span><span class="i-code">0xf112</span></div> <div class="the-icons span3" title="Code: 0xf13e"><i class="demo-icon icon-lock-open-alt"></i> <span class="i-name">icon-lock-open-alt</span><span class="i-code">0xf13e</span></div> <div class="the-icons span3" title="Code: 0xf164"><i class="demo-icon icon-thumbs-up-alt"></i> <span class="i-name">icon-thumbs-up-alt</span><span class="i-code">0xf164</span></div> - <div class="the-icons span3" title="Code: 0xf1e5"><i class="demo-icon icon-binoculars"></i> <span class="i-name">icon-binoculars</span><span class="i-code">0xf1e5</span></div> </div> <div class="row"> + <div class="the-icons span3" title="Code: 0xf1e5"><i class="demo-icon icon-binoculars"></i> <span class="i-name">icon-binoculars</span><span class="i-code">0xf1e5</span></div> <div class="the-icons span3" title="Code: 0xf234"><i class="demo-icon icon-user-plus"></i> <span class="i-name">icon-user-plus</span><span class="i-code">0xf234</span></div> </div> </div> diff --git a/static/font/font/fontello.eot b/static/font/font/fontello.eot index 993381629cbd44410df7caee5dfa646e5ed19525..a7b690a7d7864f9799f11e78da248e8dea1a1fbc 100644 GIT binary patch delta 797 zcmZ9KPfXKL9LImJ?K&c(b6crKCTuGK6WkK^*F-WHL)n3YQG*bZNyEmLNTsqt@SqTH zDiKK&<3%z&7-NVg^a6>-i!s4Jks~SxFEcao&@IXdHh<gV#lC*u_kDi-y}tK;?f%V$ z1;Rofz)0^h8)WQqDYe{pZ+-!QoCAR2aamFxe(_|Gp8?P-<Fi=@TPgnr@ZltIOeiV& z?(&0C<S~FoB_(APj2}imh2D^wyfHDc_A&=xhyk4FPbZ~>Z9R7kz`cmigJ}#Hx9BqR zTjch%oSnN>9<KbrKmh}blT+i;la1$Z06babm@LgHWUlEc@+)M=HAzl>eBA0l-UXmO zDN~v3hhJYm19Y$9rtc`zNhKfY!rbt-nj@FVs=7~7p*<it8$buTHadxZ2=n)Vf$B!= zcmx_u1-{@d`1K`yOaHFt^|JbivU7U5Xf1||iDJIwuT<0s-O@sE!a%K^h5wpbqRtpx z_-Yn2_tkC6Xf{<>uGX`5xwIkXJc;R8gR0;-K+#$kw4#UWf;RL-UEn~^*9A_1lD{s% zb+jGBrgNSGsBF-qv=v0~L8LF-M1ZsrhNcE79MJ@5*+6Ip@2g<M5*W!CFf7Yl1cqTp zfMwXBKtS{fV!PO2uy>Jwzr!eE*D1PKV{@(Jg^;hA7fuszFck24gEkA#8lA3=fW?0) z7;<_A>OF7P1a8pGlXaB9t=B>0g!}R6!Ov(s9;J>%<0KN}1ftbaG~QeTP7pYaC%p%S z{x}(nP)Dl<+EV9hgFRJ}?N`h_f1r`B{v)ts^9297ns$eXsOH==WK^|!E?8og6>Ed_ wg>9C1*1oi;&3YPXOu5}XK#_L!UH7*plQKDzac8d2NYlxLy6WxGcD)YMU(p`QkN^Mx delta 514 zcmZo@VEj|d_Q#HaA;fMXn;FaU1xM~rbm*>cVqjnlU|?VfOU_L!a5%mH6_CGzfq}Ur zxvWHi?f<_o3=Bp#KzW6<g7n<3i4!7${1^rX?t=8h;sT(!0FZA2q&d=aD%0-Emu+TX zU^QT1P|wOpO-vD9wH7F)GY3eRWdH@(Pcr`n@~;5-DjB&Y71c|_{yzf>`~V7c<m4wO zE|#7KWa~Bn`EI$16$Omtyvu<66F|N~USe+QnYnTbK>iB`2BxzG`Nbt4=9aoLFc|Cr zI=ZT$D7E0JqxK#K2HP`}7cfRME}r~{G2QeH10zs31H&^0W-v<!NOJ+zzhPiu*aZ}q z0(t=`@<QZ=(F>Cg{U1(#c<|xr<XKEIlXozwN-_fFoxsqA0R>L}#pJr#k(q~)6XZJ( zP~05G62xfrQHX&7gc)RkG%&b9m_ZImr$8`+Jdl10!3+uv44;6Z2f{%8A6R!PZf?}r z%_?*M$b#kZ{5D@1xS4@|Wnj3$Z?Y0bPma(tXH=RzL9cW&r+$E-k>DI5Ct)t(tw4E@ ePn9-j>2os!6_gpdOx|O3d-4)vo6Qf6<#_<4Q;_fg diff --git a/static/font/font/fontello.svg b/static/font/font/fontello.svg index c706fcad..fed9129e 100644 --- a/static/font/font/fontello.svg +++ b/static/font/font/fontello.svg @@ -62,6 +62,8 @@ <glyph glyph-name="comment-empty" unicode="" d="M500 643q-114 0-213-39t-157-105-59-142q0-62 40-119t113-98l48-28-15-53q-13-51-39-97 85 36 154 96l24 21 32-3q38-5 72-5 114 0 213 39t157 105 59 142-59 142-157 105-213 39z m500-286q0-97-67-179t-182-130-251-48q-39 0-81 4-110-97-257-135-27-8-63-12h-3q-8 0-15 6t-9 15v1q-2 2 0 6t1 6 2 5l4 5t4 5 4 5q4 5 17 19t20 22 17 22 18 28 15 33 15 42q-88 50-138 123t-51 157q0 97 67 179t182 130 251 48 251-48 182-130 67-179z" horiz-adv-x="1000" /> +<glyph glyph-name="plus-squared" unicode="" d="M714 321v72q0 14-10 25t-25 10h-179v179q0 15-11 25t-25 11h-71q-15 0-25-11t-11-25v-179h-178q-15 0-25-10t-11-25v-72q0-14 11-25t25-10h178v-179q0-14 11-25t25-11h71q15 0 25 11t11 25v179h179q14 0 25 10t10 25z m143 304v-536q0-66-47-113t-114-48h-535q-67 0-114 48t-47 113v536q0 66 47 113t114 48h535q67 0 114-48t47-113z" horiz-adv-x="857.1" /> + <glyph glyph-name="reply" unicode="" d="M1000 232q0-93-71-252-1-4-6-13t-7-17-7-12q-7-10-16-10-8 0-13 6t-5 14q0 5 1 15t2 13q3 38 3 69 0 56-10 101t-27 77-45 56-59 39-74 24-86 12-98 3h-125v-143q0-14-10-25t-26-11-25 11l-285 286q-11 10-11 25t11 25l285 286q11 10 25 10t26-10 10-25v-143h125q398 0 488-225 30-75 30-186z" horiz-adv-x="1000" /> <glyph glyph-name="lock-open-alt" unicode="" d="M589 428q23 0 38-15t16-38v-322q0-22-16-37t-38-16h-535q-23 0-38 16t-16 37v322q0 22 16 38t38 15h17v179q0 103 74 177t176 73 177-73 73-177q0-14-10-25t-25-11h-36q-14 0-25 11t-11 25q0 59-42 101t-101 42-101-42-41-101v-179h410z" horiz-adv-x="642.9" /> diff --git a/static/font/font/fontello.ttf b/static/font/font/fontello.ttf index d2d2963e7db8232c1a10317db768f4c3fe1b0b33..5fb1466a2af114268066dcb77a0963f9449eaf3e 100644 GIT binary patch delta 756 zcmZ9KO-vI(9L3-4Zd;Uo*mhZssnB*2O`rt&X(B;NQ5xgHC_#xyrGZkSR90FDCI-A} z2tqdTCP8D2F-8;if{8{CUhw<i0nvjOv?d-_q67~V|83*NoqhA>_n)_$nc4ktd3j<+ zp1cCE;{dw*GD+pe$Hpx2J%FF<8_T(kTZLahppE9;14=q`ZFZ)IxEH8Y(#fns^&P|q z$ScxAmj(tNJemg#abSP@U@F<~T$tYt_@-z+JV*u85?>&GL0mhS$&F7Gy0^YkAy0*= zq2a#d?Zx}gfW{ngJd+$(*tq2`@e^YAg=8l6`ewD8_%p!0QHHa*S3g$X0nHC+(N~p` zl(HJFr)=x{*^M*omfFSAk##V77|_9&Mh)cKDZUN?#dWx72RzJqG4Ia@_3QeQ{zYHa z3#!gs9GRKhyNA*g>nOD&_^+u)xx>t>O>-8ujn1ufd9^$I2^QCBuSJ30@Y7Kis>xf+ zLJfI;S#XoDmIV*67Ay;1Kx;BQ_DpiX)*|1-J0K%~Xj`j=fz>d9=Q=qW@eF*%z<7b~ z^DxnpO{^D!(I|955R9i`6pY70Avqw)wQ_~QRnJ1fI+IM@KG|zDRh2qXiUg`e=^*om zBcXsl?6iwUlgC>ZvIkFvBObrRy%cSlWbL$xY=I<M7s{YnrRhX$^Ls3jh;ch(2^Ni8 zC8m`knrJJ5Rg$cl$PR7h+Y_ue%Izu|v~P7!4X*TB#&y=#@*9<W@n68QvoZY7)#nWn zCaW6_qpU|g=R0YS+vgk=j)%@M(Np@Qvi8<j$x~#dse@y+YIXA`i&+^O&HAzzN0TF| Oe)Ugtix%{|&3^%@w!7Q_ delta 531 zcmcancco^6a(xp617iRK14CGHZeoGM>HV*O{1prg%pJ*PB?@f+|9xR#FtP#4E2I^q z=XOn;5DDbRFfeczq$d^^0L2A>d=ntek)BhTc3-}1GXn#w0Rw}2Rz_-KitwtnKq;L$ zK*B5oD8PP_`7e-v1;|&)$StX;UK;lQ8BpK{P@p3xKRI!+^fVw_w*km^%T25(U@Yfd z2IQXr@)hzDb5qaEl~VxnUobE*oh`^OF8MIG)Rlq3U<c6ARRu+<1y3Ed_b@Qno|*U~ znsM>u5XSVtHw=tG%?u3B7?{B<9U#pGRQ`s6g<%&^Tngv|pvVi67e+5kKJ<S$`QgEb zryqqr+I>v<`1F&>|NjgOFBrgbeGs|H0!*rsj6i)(VCcet0w)JCxdJU>o4k<e-(&+O z*2(jk**HP|2LZ*+>zIQWtv(7dFn}<FEReQ?U<NrLodUrO@<9421T!cwFnj`rF9-wm zd$8_Q+<a4gH>=G3BMX+t^V@u7;ARGf1_Q$lev_3jdh!Ncb4I1fFLX;MC+Y<V8VSx3 nauVhe-U^fl1%}e*LwelIKn3RvT_%ed-Jbl%$Y!&pu{;j|Qh%O~ diff --git a/static/font/font/fontello.woff b/static/font/font/fontello.woff index c1aa83bb3aeb19fbeb0626a27133e218ad0d0444..ca463697bc2307d76991a4af2a2cb0f5ba86a0f6 100644 GIT binary patch delta 7744 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 G{3U$6^%k4} delta 7641 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 diff --git a/static/font/font/fontello.woff2 b/static/font/font/fontello.woff2 index cbd9b09f706d5320154cd4fb11140fac7e7c35ed..09fafb310e4891a3337176a229ae0fdb5fb56f55 100644 GIT binary patch literal 8456 zcmV+jA@|;QPew8T0RR9103iqf4*&oF06$m&03fgc0RR9100000000000000000000 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 literal 8372 zcmV;lAWPqOPew8T0RR9103fsg4*&oF06tUz03cid0RR9100000000000000000000 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 K;pHEOfV%+u3jlop From edb429e30796c9312de9a28c262008c7d44757e6 Mon Sep 17 00:00:00 2001 From: Henry Jameson <me@hjkos.com> Date: Thu, 15 Nov 2018 17:17:20 +0300 Subject: [PATCH 15/96] cleanup and fixes --- src/components/style_switcher/style_switcher.js | 16 ++-------------- src/services/color_convert/color_convert.js | 2 -- 2 files changed, 2 insertions(+), 16 deletions(-) diff --git a/src/components/style_switcher/style_switcher.js b/src/components/style_switcher/style_switcher.js index 9e236488..f4f9331f 100644 --- a/src/components/style_switcher/style_switcher.js +++ b/src/components/style_switcher/style_switcher.js @@ -288,19 +288,6 @@ export default { }, setCustomTheme () { - if (!this.bgColorLocal && !this.btnColorLocal && !this.linkColorLocal) { - // reset to picked themes - } - - const rgb = (hex) => { - const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex) - return result ? { - r: parseInt(result[1], 16), - g: parseInt(result[2], 16), - b: parseInt(result[3], 16) - } : null - } - this.$store.dispatch('setOption', { name: 'customTheme', value: this.currentTheme @@ -310,6 +297,8 @@ export default { clearV1 () { this.bgOpacityLocal = undefined this.fgOpacityLocal = undefined + + this.fgTextColorLocal = undefined this.fgLinkColorLocal = undefined this.btnColorLocal = undefined @@ -371,7 +360,6 @@ export default { } const keys = new Set(version !== 1 ? Object.keys(colors) : []) - console.log(keys) if (version === 1) { // V1 ignores the rest this.clearV1() diff --git a/src/services/color_convert/color_convert.js b/src/services/color_convert/color_convert.js index 00b6c552..58c434fa 100644 --- a/src/services/color_convert/color_convert.js +++ b/src/services/color_convert/color_convert.js @@ -99,9 +99,7 @@ const alphaBlend = (fg, fga, bg) => { const invert = (rgb) => { return 'rgb'.split('').reduce((acc, c) => { - console.log(rgb[c]) acc[c] = 255 - rgb[c] - console.log(acc[c]) return acc }, {}) } From a5b4f31c12706b7226915864c01e5508caf262f9 Mon Sep 17 00:00:00 2001 From: Henry Jameson <me@hjkos.com> Date: Mon, 19 Nov 2018 04:40:25 +0300 Subject: [PATCH 16/96] shadow control initial stuff. not done yet tho --- src/components/color_input/color_input.vue | 8 +- .../opacity_input/opacity_input.vue | 6 +- .../shadow_control/shadow_control.js | 76 ++++++ .../shadow_control/shadow_control.vue | 249 ++++++++++++++++++ .../style_switcher/style_switcher.js | 18 ++ .../style_switcher/style_switcher.vue | 11 + src/services/style_setter/style_setter.js | 29 +- static/font/config.json | 7 +- static/font/css/fontello-codes.css | 1 + static/font/css/fontello-embedded.css | 13 +- static/font/css/fontello-ie7-codes.css | 1 + static/font/css/fontello-ie7.css | 1 + static/font/css/fontello.css | 15 +- static/font/demo.html | 17 +- static/font/font/fontello.eot | Bin 16384 -> 16552 bytes static/font/font/fontello.svg | 2 + static/font/font/fontello.ttf | Bin 16216 -> 16384 bytes static/font/font/fontello.woff | Bin 9972 -> 10072 bytes static/font/font/fontello.woff2 | Bin 8456 -> 8564 bytes 19 files changed, 424 insertions(+), 30 deletions(-) create mode 100644 src/components/shadow_control/shadow_control.js create mode 100644 src/components/shadow_control/shadow_control.vue diff --git a/src/components/color_input/color_input.vue b/src/components/color_input/color_input.vue index 60a62fa8..ea9fb3c4 100644 --- a/src/components/color_input/color_input.vue +++ b/src/components/color_input/color_input.vue @@ -1,5 +1,5 @@ <template> -<div class="color-control" :class="{ disabled: !present }"> +<div class="color-control" :class="{ disabled: !present || disabled }"> <label :for="name" class="label"> {{label}} </label> @@ -17,7 +17,7 @@ class="color-input" type="color" :value="value || fallback" - :disabled="!present" + :disabled="!present || disabled" @input="$emit('input', $event.target.value)" > <input @@ -25,7 +25,7 @@ class="text-input" type="text" :value="value || fallback" - :disabled="!present" + :disabled="!present || disabled" @input="$emit('input', $event.target.value)" > </div> @@ -34,7 +34,7 @@ <script> export default { props: [ - 'name', 'label', 'value', 'fallback' + 'name', 'label', 'value', 'fallback', 'disabled' ], computed: { present () { diff --git a/src/components/opacity_input/opacity_input.vue b/src/components/opacity_input/opacity_input.vue index 4057dcca..91c4f5e9 100644 --- a/src/components/opacity_input/opacity_input.vue +++ b/src/components/opacity_input/opacity_input.vue @@ -1,5 +1,5 @@ <template> -<div class="opacity-control" :class="{ disabled: !present }"> +<div class="opacity-control" :class="{ disabled: !present || disabled }"> <label :for="name" class="label"> {{$t('settings.opacity')}} </label> @@ -17,7 +17,7 @@ class="input-number" type="number" :value="value || fallback" - :disabled="!present" + :disabled="!present || disabled" @input="$emit('input', $event.target.value)" max="1" min="0" @@ -28,7 +28,7 @@ <script> export default { props: [ - 'name', 'value', 'fallback' + 'name', 'value', 'fallback', 'disabled' ], computed: { present () { diff --git a/src/components/shadow_control/shadow_control.js b/src/components/shadow_control/shadow_control.js new file mode 100644 index 00000000..c357581d --- /dev/null +++ b/src/components/shadow_control/shadow_control.js @@ -0,0 +1,76 @@ +import ColorInput from '../color_input/color_input.vue' +import OpacityInput from '../opacity_input/opacity_input.vue' +import StyleSetter from '../../services/style_setter/style_setter.js' +import { hex2rgb } from '../../services/color_convert/color_convert.js' +import { set } from 'vue' + +export default { + props: [ + 'value', 'fallback' + ], + data () { + return { + selectedId: 0, + cValue: this.value || this.fallback + } + }, + components: { + ColorInput, + OpacityInput + }, + methods: { + add () { + this.cValue.push(Object.assign({}, this.selected)) + this.selectedId = this.cValue.length - 1 + }, + del () { + this.cValue.splice(this.selectedId, 1) + this.selectedId = this.cValue.length === 0 ? undefined : this.selectedId - 1 + }, + moveUp () { + const movable = this.cValue.splice(this.selectedId, 1)[0] + this.cValue.splice(this.selectedId - 1, 0, movable) + this.selectedId -= 1 + }, + moveDn () { + const movable = this.cValue.splice(this.selectedId, 1)[0] + this.cValue.splice(this.selectedId + 1, 0, movable) + this.selectedId += 1 + } + }, + computed: { + selected () { + return this.cValue[this.selectedId] || { + x: 0, + y: 0, + blur: 0, + spread: 0, + inset: false, + color: '#000000', + alpha: 1 + } + }, + moveUpValid () { + return this.selectedId > 0 + }, + moveDnValid () { + return this.selectedId < this.cValue.length - 1 + }, + present () { + return typeof this.cValue[this.selectedId] !== 'undefined' && + !this.usingFallback + }, + usingFallback () { + return typeof this.value === 'undefined' + }, + rgb () { + return hex2rgb(this.selected.color) + }, + style () { + console.log(StyleSetter.generateShadow(this.cValue)) + return { + boxShadow: StyleSetter.generateShadow(this.cValue) + } + } + } +} diff --git a/src/components/shadow_control/shadow_control.vue b/src/components/shadow_control/shadow_control.vue new file mode 100644 index 00000000..24439449 --- /dev/null +++ b/src/components/shadow_control/shadow_control.vue @@ -0,0 +1,249 @@ +<template> +<div class="shadow-control" :class="{ disabled: !present }"> + <div class="shadow-preview-container"> + <div :disabled="!present" class="y-shift-control"> + <input + v-model="selected.y" + :disabled="!present" + class="input-number" + type="number"> + <div class="wrap"> + <input + v-model="selected.y" + :disabled="!present" + class="input-range" + type="range" + max="20" + min="-20"> + </div> + </div> + <div class="preview-window"> + <div class="preview-block" :style="style"></div> + </div> + <div :disabled="!present" class="x-shift-control"> + <input + v-model="selected.x" + :disabled="!present" + class="input-number" + type="number"> + <div class="wrap"> + <input + v-model="selected.x" + :disabled="!present" + class="input-range" + type="range" + max="20" + min="-20"> + </div> + </div> + </div> + + <div class="shadow-tweak"> + <div :disabled="usingFallback" class="id-control"> + <label for="id" class="label"> + Shadow id + </label> + <input + v-model="selectedId" + :disabled="usingFallback" + class="input-number" + type="number" + min="0" + :max="cValue.length - 1"> + <button class="btn btn-default" :disabled="!present" @click="del"> + <i class="icon-cancel"/> + </button> + <button class="btn btn-default" :disabled="!moveUpValid" @click="moveUp"> + <i class="icon-up-open"/> + </button> + <button class="btn btn-default" :disabled="!moveDnValid" @click="moveDn"> + <i class="icon-down-open"/> + </button> + <button class="btn btn-default" @click="add"> + <i class="icon-plus"/> + </button> + </div> + <div :disabled="!present" class="inset-control"> + <label for="inset" class="label"> + Inset + </label> + <input + v-model="selected.inset" + :disabled="!present" + name="inset" + id="inset" + class="input-inset" + type="checkbox"> + <label class="checkbox-label" for="inset"></label> + </div> + <div :disabled="!present" class="blur-control"> + <label for="spread" class="label"> + Blur + </label> + <input + v-model="selected.blur" + :disabled="!present" + name="blur" + id="blur" + class="input-range" + type="range" + max="20" + min="0"> + <input + v-model="selected.blur" + :disabled="!present" + class="input-number" + type="number" + min="0"> + </div> + <div :disabled="!present" class="spread-control"> + <label for="spread" class="label"> + Spread + </label> + <input + v-model="selected.spread" + :disabled="!present" + name="spread" + id="spread" + class="input-range" + type="range" + max="20" + min="-20"> + <input + v-model="selected.spread" + :disabled="!present" + class="input-number" + type="number"> + </div> + <ColorInput + v-model="selected.color" + :disabled="!present" + label="Color" + name="shadow"/> + <OpacityInput + v-model="selected.alpha" + :disabled="!present"/> + </div> +</div> +</template> + +<script src="./shadow_control.js" ></script> + +<style lang="scss"> +@import '../../_variables.scss'; +.shadow-control { + display: flex; + flex-wrap: wrap; + justify-content: center; + + .shadow-preview-container { + flex: 0; + display: flex; + flex-wrap: wrap; + + $side: 15em; + + input[type=number] { + width: 5em; + min-width: 2em; + } + .x-shift-control, + .y-shift-control { + display: flex; + flex: 0; + + &[disabled=disabled] *{ + opacity: .5 + } + + } + + .x-shift-control { + align-items: flex-start; + } + + .x-shift-control .wrap, + input[type=range] { + margin: 0; + width: $side; + height: 2em; + } + .y-shift-control { + flex-direction: column; + align-items: flex-end; + .wrap { + width: 2em; + height: $side; + } + input[type=range] { + transform-origin: 1em 1em; + transform: rotate(90deg); + } + } + .preview-window { + flex: 1; + background-color: white; + display: flex; + align-items: center; + justify-content: center; + background-image: + linear-gradient(45deg, #808080 25%, transparent 25%), + linear-gradient(-45deg, #808080 25%, transparent 25%), + linear-gradient(45deg, transparent 75%, #808080 75%), + linear-gradient(-45deg, transparent 75%, #808080 75%); + background-size: 20px 20px; + background-position:0 0, 0 10px, 10px -10px, -10px 0; + + border-radius: $fallback--inputRadius; + border-radius: var(--inputRadius, $fallback--inputRadius); + + .preview-block { + width: 33%; + height: 33%; + background-color: $fallback--bg; + background-color: var(--bg, $fallback--bg); + border-radius: $fallback--panelRadius; + border-radius: var(--panelRadius, $fallback--panelRadius); + } + } + } + + .shadow-tweak { + .label { + flex: 1; + min-width: 3em; + } + + .inset-control { + justify-content: flex-end; + label { + flex: 0 + } + } + + .blur-control, + .id-control, + .inset-control, + .spread-control { + display: flex; + align-items: baseline; + max-width: 100%; + + &[disabled=disabled] *{ + opacity: .5 + } + + .input-range { + flex: 1; + align-self: center; + } + + .input-number { + width: 4em; + min-width: 4em; + flex: 0; + } + } + } +} +</style> diff --git a/src/components/style_switcher/style_switcher.js b/src/components/style_switcher/style_switcher.js index f4f9331f..8e344eb1 100644 --- a/src/components/style_switcher/style_switcher.js +++ b/src/components/style_switcher/style_switcher.js @@ -1,5 +1,6 @@ import { rgb2hex, hex2rgb, getContrastRatio, alphaBlend } from '../../services/color_convert/color_convert.js' import ColorInput from '../color_input/color_input.vue' +import ShadowControl from '../shadow_control/shadow_control.vue' import ContrastRatio from '../contrast_ratio/contrast_ratio.vue' import OpacityInput from '../opacity_input/opacity_input.vue' import StyleSetter from '../../services/style_setter/style_setter.js' @@ -51,6 +52,9 @@ export default { faintOpacityLocal: undefined, faintLinkColorLocal: undefined, + shadowSelected: undefined, + shadowsLocal: {}, + cRedColorLocal: '', cBlueColorLocal: '', cGreenColorLocal: '', @@ -225,12 +229,23 @@ export default { previewRules () { if (!this.preview.colorRules) return '' return [this.preview.colorRules, this.preview.radiiRules, 'color: var(--text)'].join(';') + }, + shadowsAvailable () { + return Object.keys(this.preview.theme.shadows) + }, + currentShadow () { + const fallback = this.preview.theme.shadows[this.shadowSelected]; + return fallback ? { + fallback, + value: this.shadowsLocal[this.shadowSelected] + } : undefined } }, components: { ColorInput, OpacityInput, ContrastRatio, + ShadowControl, TabSwitcher }, methods: { @@ -340,6 +355,7 @@ export default { const colors = input.colors || input const radii = input.radii || input const opacity = input.opacity || input + const shadows = input.shadows || {} if (version === 0) { if (input.version) version = input.version @@ -384,6 +400,8 @@ export default { this.tooltipRadiusLocal = radii.tooltipRadius || 2 this.attachmentRadiusLocal = radii.attachmentRadius || 5 + this.shadowsLocal = shadows + Object.entries(opacity).forEach(([k, v]) => { if (typeof v === 'undefined' || v === null || Number.isNaN(v)) return this[k + 'OpacityLocal'] = v diff --git a/src/components/style_switcher/style_switcher.vue b/src/components/style_switcher/style_switcher.vue index 5bc38318..0352f546 100644 --- a/src/components/style_switcher/style_switcher.vue +++ b/src/components/style_switcher/style_switcher.vue @@ -170,6 +170,17 @@ <input id="tooltipradius-t" class="theme-radius-in" type="text" v-model="tooltipRadiusLocal"> </div> </div> + <div label="Shadow Realm" class="shadow-container"> + <div class="shadow-selector"> + <select id="style-switcher" v-model="shadowSelected" class="style-switcher"> + <option v-for="shadow in shadowsAvailable" + :value="shadow"> + {{shadow}} + </option> + </select> + </div> + <shadow-control v-if="currentShadow" :value="currentShadow.value" :fallback="currentShadow.fallback"/> + </div> </tab-switcher> <div class="apply-container"> diff --git a/src/services/style_setter/style_setter.js b/src/services/style_setter/style_setter.js index 907de586..3840e215 100644 --- a/src/services/style_setter/style_setter.js +++ b/src/services/style_setter/style_setter.js @@ -92,6 +92,19 @@ const setColors = (input, commit) => { commit('setOption', { name: 'colors', value: theme.colors }) } +const generateShadow = (input) => { + // >shad + return input.map((shad) => [ + shad.x, + shad.y, + shad.blur, + shad.spread + ].map(_ => _ + 'px').concat([ + rgb2rgba({...(hex2rgb(shad.color)), a: shad.alpha}), + shad.inset ? 'inset' : '' + ]).join(' ')).join(', ') +} + const generatePreset = (input) => { const radii = input.radii || { btnRadius: input.btnRadius, @@ -102,6 +115,17 @@ const generatePreset = (input) => { tooltipRadius: input.tooltipRadius, attachmentRadius: input.attachmentRadius } + const shadows = { + panel: [{ + x: 1, + y: 1, + blur: 4, + spread: 0, + color: '#000000', + alpha: 0.6 + }], + ...(input.shadows || {}) + } const colors = {} const opacity = Object.assign({ alert: 0.5, @@ -194,8 +218,10 @@ const generatePreset = (input) => { return { colorRules: Object.entries(htmlColors.complete).filter(([k, v]) => v).map(([k, v]) => `--${k}: ${v}`).join(';'), radiiRules: Object.entries(radii).filter(([k, v]) => v).map(([k, v]) => `--${k}: ${v}px`).join(';'), + shadowRules: Object.entries(shadows).filter(([k, v]) => v).map(([k, v]) => `--${k}-shadow: ${generateShadow(v)}`).join(';'), theme: { colors: htmlColors.solid, + shadows, opacity, radii } @@ -245,7 +271,8 @@ const StyleSetter = { setPreset, setColors, getTextColor, - generatePreset + generatePreset, + generateShadow } export default StyleSetter diff --git a/static/font/config.json b/static/font/config.json index 1d1317d7..3abeffe9 100644 --- a/static/font/config.json +++ b/static/font/config.json @@ -1,5 +1,4 @@ { - "name": "", "css_prefix_text": "icon-", "css_use_suffix": false, "hinting": true, @@ -209,6 +208,12 @@ "css": "plus-squared", "code": 61694, "src": "fontawesome" + }, + { + "uid": "44e04715aecbca7f266a17d5a7863c68", + "css": "plus", + "code": 59413, + "src": "fontawesome" } ] } \ No newline at end of file diff --git a/static/font/css/fontello-codes.css b/static/font/css/fontello-codes.css index 66a240cd..5cfcbf6a 100644 --- a/static/font/css/fontello-codes.css +++ b/static/font/css/fontello-codes.css @@ -20,6 +20,7 @@ .icon-globe:before { content: '\e812'; } /* '' */ .icon-brush:before { content: '\e813'; } /* '' */ .icon-attention:before { content: '\e814'; } /* '' */ +.icon-plus:before { content: '\e815'; } /* '' */ .icon-spin3:before { content: '\e832'; } /* '' */ .icon-spin4:before { content: '\e834'; } /* '' */ .icon-link-ext:before { content: '\f08e'; } /* '' */ diff --git a/static/font/css/fontello-embedded.css b/static/font/css/fontello-embedded.css index 65875bde..58a57456 100644 --- a/static/font/css/fontello-embedded.css +++ b/static/font/css/fontello-embedded.css @@ -1,15 +1,15 @@ @font-face { font-family: 'fontello'; - src: url('../font/fontello.eot?92539127'); - src: url('../font/fontello.eot?92539127#iefix') format('embedded-opentype'), - url('../font/fontello.svg?92539127#fontello') format('svg'); + src: url('../font/fontello.eot?4112743'); + src: url('../font/fontello.eot?4112743#iefix') format('embedded-opentype'), + url('../font/fontello.svg?4112743#fontello') format('svg'); font-weight: normal; font-style: normal; } @font-face { font-family: 'fontello'; - src: url('data:application/octet-stream;base64,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') format('woff'), - url('data:application/octet-stream;base64,AAEAAAAPAIAAAwBwR1NVQiCLJXoAAAD8AAAAVE9TLzI+L1OAAAABUAAAAFZjbWFwltkucwAAAagAAAOeY3Z0IAb//vQAADNAAAAAIGZwZ22KkZBZAAAzYAAAC3BnYXNwAAAAEAAAMzgAAAAIZ2x5Zmasr6AAAAVIAAAoQmhlYWQTsqAaAAAtjAAAADZoaGVhB8kD/gAALcQAAAAkaG10eH7+/+YAAC3oAAAAjGxvY2GiuKq/AAAudAAAAEhtYXhwAXgNpgAALrwAAAAgbmFtZcydHiAAAC7cAAACzXBvc3TL7NrPAAAxrAAAAYlwcmVw5UErvAAAPtAAAACGAAEAAAAKADAAPgACREZMVAAObGF0bgAaAAQAAAAAAAAAAQAAAAQAAAAAAAAAAQAAAAFsaWdhAAgAAAABAAAAAQAEAAQAAAABAAgAAQAGAAAAAQAAAAEDoQGQAAUAAAJ6ArwAAACMAnoCvAAAAeAAMQECAAACAAUDAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFBmRWQAQOgA8jQDWf9xAFoDZwCeAAAAAQAAAAAAAAAAAAUAAAADAAAALAAAAAQAAAH2AAEAAAAAAPAAAwABAAAALAADAAoAAAH2AAQAxAAAABwAEAADAAzoFOgy6DTwj/DJ8ODw5fD+8RLxPvFk8eXyNP//AADoAOgy6DTwjvDJ8ODw5fD+8RLxPvFk8eXyNP//AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAcAEQARABEAEYARgBGAEYARgBGAEYARgBGAAAAAQACAAMABAAFAAYABwAIAAkACgALAAwADQAOAA8AEAARABIAEwAUABUAFgAXABgAGQAaABsAHAAdAB4AHwAgACEAIgAAAQYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADAAAAAABqAAAAAAAAAAiAADoAAAA6AAAAAABAADoAQAA6AEAAAACAADoAgAA6AIAAAADAADoAwAA6AMAAAAEAADoBAAA6AQAAAAFAADoBQAA6AUAAAAGAADoBgAA6AYAAAAHAADoBwAA6AcAAAAIAADoCAAA6AgAAAAJAADoCQAA6AkAAAAKAADoCgAA6AoAAAALAADoCwAA6AsAAAAMAADoDAAA6AwAAAANAADoDQAA6A0AAAAOAADoDgAA6A4AAAAPAADoDwAA6A8AAAAQAADoEAAA6BAAAAARAADoEQAA6BEAAAASAADoEgAA6BIAAAATAADoEwAA6BMAAAAUAADoFAAA6BQAAAAVAADoMgAA6DIAAAAWAADoNAAA6DQAAAAXAADwjgAA8I4AAAAYAADwjwAA8I8AAAAZAADwyQAA8MkAAAAaAADw4AAA8OAAAAAbAADw5QAA8OUAAAAcAADw/gAA8P4AAAAdAADxEgAA8RIAAAAeAADxPgAA8T4AAAAfAADxZAAA8WQAAAAgAADx5QAA8eUAAAAhAADyNAAA8jQAAAAiAAAAAQAA//YC1AKNACQAHkAbIhkQBwQAAgFHAwECAAJvAQEAAGYUHBQUBAUYKyUUDwEGIi8BBwYiLwEmND8BJyY0PwE2Mh8BNzYyHwEWFA8BFxYC1A9MECwQpKQQLBBMEBCkpBAQTBAsEKSkECwQTA8PpKQPdxYQTA8PpaUPD0wQLBCkpBAsEEwQEKSkEBBMDy4PpKQPAAQAAP+4A6EDNQAIABEAKQBAAEZAQzUBBwYJAAICAAJHAAkGCW8IAQYHBm8ABwMHbwAEAAIEVAUBAwEBAAIDAGAABAQCWAACBAJMPTwjMyMiMiU5GBIKBR0rJTQmDgIeATY3NCYOAh4BNjcVFAYjISImJzU0NhczHgE7ATI2NzMyFgMGKwEVFAYHIyImJzUjIiY/ATYyHwEWAsoUHhQCGBoYjRQgEgIWHBhGIBb8yxceASAW7gw2I48iNg3uFiC2CRiPFA+PDxQBjxcTEfoKHgr6EiQOFgISIBIEGgwOFgISIBIEGomzFiAgFrMWIAEfKCgfHgFSFvoPFAEWDvosEfoKCvoRAAAAAAEAAP/RA6EDRwAfAB1AGhIPCgQDBQACAUcAAgACbwEBAABmHRQXAwUXKwEUDwETFRQOAS8BBwYiJjU0NxMnJjU0NyU3NjIfAQUWA6EPyjAMFQz7+gwWDAEwyw4fARh+CyAMfQEYIAHwDA/F/ukMCxABB4SEBxIKBAgBF8UPDBUFKP4XF/4oBQACAAD/0QOhA0cACQApACdAJBwZFA4NCQgHBgUDAQwAAgFHAAIAAm8BAQAAZiUkFxYSEAMFFCsBNy8BDwEXBzcXExQPARMVFCMiLwEHBiImNTQ3EycmNTQ3JTc2Mh8BBRYCe6rramnsqynT0/4PyjAXCgz7+gwWDAEwyw4fARh+CyAMfQEYIAEppiLV1SKm629vAbIMD8X+6QwcB4SEBxIKBAgBF8UPDBUFKP4XF/4oBQAAAAAC//3/uANfAxIABwAUACtAKAADAAABAwBgBAEBAgIBVAQBAQECWAACAQJMAAASEQwLAAcABxEFBRUrJREiDgIeAQEUDgEiLgI+ATIeAQGtU4xQAlSIAgFyxujIbgZ6vPS6fjUCYFKMpIxSATB1xHR0xOrEdHTEAAAFAAD/ygPoArgACQAaAD4ARABXAFdAVDQbAgAEUwYCAgBSQwIBAlBCKScIAQYGAQRHAAUEBW8AAgABAAIBbQABBgABBmsABgMABgNrAAMDbgAEAAAEVAAEBABYAAAEAExMSxMuGSQUHQcFGislNy4BNzQ3BgcWATQmByIGFRQWMjY1NDYzMjY3FBUGAg8BBiMiJyY1NDcuAScmNDc+ATMyFzc2MzIWHwEWBxYTFAYHExYXFAcGBw4BIzc+ATcmJzceARcWATYrMDgBIoBVXgFqEAtGZBAWEEQwCxDKO+o7HAUKB0QJGVCGMgsLVvyXMjIfBQoDDgskCwEJFVhJnQT6CxYnVNx8KXfIRUFdIzViIAtwTyNqPUM6QYSQAWcLEAFkRQsQEAswRBB1BAFp/lppMgknBgoHKiR4TREqEoOYCjYJBgYUBgEF/v1OgBsBGBleExMkLWBqSgqEaWRAPyRiNhMAAAIAAP//BDACgwAhAEMAQkA/IgEEBgFHAwEBBwYHAQZtCQEGBAcGBGsIAQIABwECB2AABAAABFQABAQAWAUBAAQATEJAFiElGCEWFSgTCgUdKyUUBichIiYvAS4BMxEjIi4BPwE2Mh8BFhQGByMVITIfARYlFA8BBiIvASY0NjsBNSEiLwEmNDY3ITIWHwEeARURMzIWAsoKCP3pBQYCAwECAWsPFAEIswsgDLIJFg5rAUEJBVkEAWUIsgwgC7MIFg5r/r4JBVkECggCGAQGAgMBAmsOFhIHDAECAwQBDAFPFhsK1gwM1gocFAHWBmwF4g0K1g0N1gobFtYHawUNCgECAwUCCAP+shYAAAACAAD/uANaAxIACABqAEVAQmVZTEEEAAQ7CgIBADQoGxAEAwEDRwAFBAVvBgEEAARvAAABAG8AAQMBbwADAgNvAAICZlxbU1FJSCsqIiATEgcFFisBNCYiDgEWMjYlFRQGDwEGBxYXFhQHDgEnIi8BBgcGBwYrASImNScmJwcGIicmJyY0Nz4BNyYvAS4BJzU0Nj8BNjcmJyY0Nz4BMzIfATY3Njc2OwEyFh8BFhc3NjIXFhcWFAcOAQcWHwEeAQI7UnhSAlZ0VgEcCAdoCgsTKAYFD1ANBwdNGRoJBwQQfAgMEBsXTwYQBkYWBAUIKAoPCGYHCAEKBWgIDhclBgUPUA0HCE0YGgkIAxF8BwwBDxwXTwUPB0gUBAQJKAoPCGYHCgFlO1RUdlRUeHwHDAEQHhUbMgYOBhVQAQU8DQhMHBAKB2cJDDwFBkAeBQ4GDDIPHBsPAQwHfAcMARAZGiAtBwwHFFAFPA0ITBwQCgdnCQs7BQVDHAUOBgwyDxwaEAEMAAAAAgAAAAADawLKACcAQABCQD8UAQIBAUcABgIFAgYFbQAFAwIFA2sABAMAAwQAbQABAAIGAQJgAAMEAANUAAMDAFgAAAMATBYjGSUqJScHBRsrJRQWDwEOAQcjIiY1ETQ2OwEyFhUXFg8BDgEnIyIGBxEUFhczMh4CARQHAQYiJj0BIyImPQE0NjczNTQ2FhcBFgFlAgECAQgIskNeXkOyCAoBAQECAQgIsiU0ATYktAYCBgICBgv+0QscFvoOFhYO+hYcCwEvCzUCEgUOCQIDXkMBiENeCggLCQYNBwgBNCb+eCU0AQQCCAEsDgv+0AoUD6EWDtYPFAGhDhYCCf7QCgAAAAABAAD/7gO2AjAAFAAZQBYNAQABAUcCAQEAAW8AAABmFBcSAwUXKwkBBiInASY0PwE2MhcJATYyHwEWFAOr/mIKHgr+YgsLXQoeCgEoASgLHAxcCwGW/mMLCwGdCx4KXAsL/tgBKAsLXAscAAAB//7/ewO4A2cAMQAfQBwAAQAAAVQAAQEAWAIBAAEATAEAKikAMQExAwUUKxciJy4BNwE2Fx4BFxYHAQ4BJyY2NwE2FgcBBhcWNzY3ATYmJyYHAQYeAjcBNhYHAQb0ZkRIBFYB8FBeLEYMGlD+JihgIB4GLAFMGDQa/rQsGAwMGBYB2jIgPDY2/hJCBGSGSgHwGDQa/hBShUhGwF4B8FAaDEYsYFD+JigKIBhkKgFOGjQY/rQsGggCBBYB2jJ2EA4y/hJMhmIEQAHuGC4a/hBSAAAAAAT///+4BC8DEgAIAA8AHwAvAFVAUh0UAgEDDwEAAQ4NDAkEAgAcFQIEAgRHAAIABAACBG0ABgcBAwEGA2AAAQAAAgEAYAAEBQUEVAAEBAVYAAUEBUwREC4rJiMZFxAfER8TExIIBRcrARQOASY0Nh4BARUhNTcXASUhIgYHERQWNyEyNicRNCYXERQGByEiJjcRNDY3ITIWAWU+Wj4+Wj4CPPzusloBHQEe/IMHCgEMBgN9BwwBClE0JfyDJDYBNCUDfSU0AhgtPgJCVkIEOv76+muzWQEdoQoI/VoHDAEKCAKmCAoS/VolNAE2JAKmJTQBNgAL////cQQvAxIADwAfAC8APwBPAF8AbwB/AI8AnwCvAMRAGZBAAgkIiIBgIAQFBHg4AgMCUDAAAwEABEdLsCFQWEA3ABUSDAIICRUIYBMBCRABBAUJBGARDQIFDgYCAgMFAmAPAQMKAQABAwBgCwcCAQEUWAAUFA0USRtAPgAVEgwCCAkVCGATAQkQAQQFCQRgEQ0CBQ4GAgIDBQJgDwEDCgEAAQMAYAsHAgEUFAFUCwcCAQEUWAAUARRMWUAmrqumo56blpSOjIaEfnx2c25rZmReW1ZUTks1NTUmNSY1NTMWBR0rFzU0JgcjIgYdARQWOwEyNic1NCYrASIGHQEUFjczMjYnNTQmJyMiBh0BFBYXMzI2ARE0JiMhIgYXERQWMyEyNgE1NCYHIyIGHQEUFjsBMjYBNTQmByMiBgcVFBY7ATI2AxE0JgchIgYXERQWFyEyNhc1NCYrASIGBxUUFjczMjY3NTQmJyMiBgcVFBYXMzI2NzU0JgcjIgYHFRQWOwEyNjcRFAYjISImNxE0NjchMhbWFA9IDhYWDkgOFgEUD0gOFhYOSA4WARQPSA4WFg5IDhYCOxYO/lMOFgEUDwGtDxT9xRQPSA4WFg5IDhYDERYORw8UARYORw8U1RYO/lMOFgEUDwGtDxTXFg5HDxQBFg5HDxQBFg5HDxQBFg5HDxQBFg5HDxQBFg5HDxRINCX8gyQ2ATQlA30lNCRIDhYBFA9IDhYW5EgOFhYOSA4WARTmRw8UARYORw8UARb+YQEeDhYWDv7iDhYWApFHDxYBFBBHDhYW/YtIDhYBFA9IDhYWAbsBHQ8WARQQ/uMPFAEWyUgOFhYOSA4WARTmRw8UARYORw8UARbkRw8WARQQRw4WFmf9EiU0NCUC7iU0ATYAAQAA/8cCdANLABQAF0AUCQEAAQFHAAEAAW8AAABmHBICBRYrCQEGIi8BJjQ3CQEmND8BNjIXARYUAmr+YgscC10LCwEo/tgLC10KHgoBngoBcP5hCgpdCxwLASkBKAscC10LC/5iCxwAAAAAAQAA/8cCmANLABQAF0AUAQEAAQFHAAEAAW8AAABmFxcCBRYrCQIWFA8BBiInASY0NwE2Mh8BFhQCjv7XASkKCl0LHAv+YgsLAZ4KHgpdCgKx/tj+1woeCl0KCgGfCh4KAZ4LC10KHgABAAAAAAO2Ak0AFAAZQBYFAQACAUcAAgACbwEBAABmFxQSAwUXKyUHBiInCQEGIi8BJjQ3ATYyFwEWFAOrXAseCv7Y/tgLHAtdCwsBngscCwGeC3JcCgoBKf7XCgpcCx4KAZ4KCv5iCxwAAAADAAD/cQPEA1oADAAaAEIA6UAMAAECAAFHKBsCAwFGS7AOUFhAKwcBBQEAAQVlAAACAQBjAAMAAQUDAWAABAQIWAAICAxIAAICBlgABgYNBkkbS7AhUFhALAcBBQEAAQVlAAACAQACawADAAEFAwFgAAQECFgACAgMSAACAgZYAAYGDQZJG0uwJFBYQCkHAQUBAAEFZQAAAgEAAmsAAwABBQMBYAACAAYCBlwABAQIWAAICAwESRtALwcBBQEAAQVlAAACAQACawAIAAQDCARgAAMAAQUDAWAAAgYGAlQAAgIGWAAGAgZMWVlZQAwfIhIoFhEjExIJBR0rBTQjIiY3NCIVFBY3MiUhJhE0LgIiDgIVEAUUBisBFAYiJjUjIiY1PgQ3NDY3JjU0PgEWFRQHHgEXFB4DAf0JITABEjooCf6MAtaVGjRSbFI0GgKmKh36VHZU+h0qHC4wJBIChGkFICwgBWqCARYiMDBZCDAhCQkpOgGpqAEpHDw4IiI4PBz+16gdKjtUVDsqHRgyVF6ITVSSEAoLFx4CIhULChCSVE6GYFI0AAAAAgAAAAACgwMSAAcAHwAqQCcFAwIAAQIBAAJtAAICbgAEAQEEVAAEBAFYAAEEAUwjEyU2ExAGBRorEyE1NCYOARcFERQGByEiJicRNDYXMzU0NjIWBxUzMhazAR1UdlQBAdAgFv3pFx4BIBYRlMyWAhIXHgGsbDtUAlA9of6+Fh4BIBUBQhYgAWxmlJRmbB4AA//9/7gDWQMSAAwBvQH3AndLsAlQWEE8AL0AuwC4AJ8AlgCIAAYAAwAAAI8AAQACAAMA2gDTAG0AWQBRAEIAPgAzACAAGQAKAAcAAgGeAZgBlgGMAYsBegF1AWUBYwEDAOEA4AAMAAYABwFTAU0BKAADAAgABgH0AdsB0QHLAcABvgE4ATMACAABAAgABgBHG0uwClBYQUMAuwC4AJ8AiAAEAAUAAAC9AAEAAwAFAI8AAQACAAMA2gDTAG0AWQBRAEIAPgAzACAAGQAKAAcAAgGeAZgBlgGMAYsBegF1AWUBYwEDAOEA4AAMAAYABwFTAU0BKAADAAgABgH0AdsB0QHLAcABvgE4ATMACAABAAgABwBHAJYAAQAFAAEARhtBPAC9ALsAuACfAJYAiAAGAAMAAACPAAEAAgADANoA0wBtAFkAUQBCAD4AMwAgABkACgAHAAIBngGYAZYBjAGLAXoBdQFlAWMBAwDhAOAADAAGAAcBUwFNASgAAwAIAAYB9AHbAdEBywHAAb4BOAEzAAgAAQAIAAYAR1lZS7AJUFhANQACAwcDAgdtAAcGAwcGawAGCAMGCGsACAEDCAFrAAEBbgkBAAMDAFQJAQAAA1gFBAIDAANMG0uwClBYQDoEAQMFAgUDZQACBwUCB2sABwYFBwZrAAYIBQYIawAIAQUIAWsAAQFuCQEABQUAVAkBAAAFVgAFAAVKG0A1AAIDBwMCB20ABwYDBwZrAAYIAwYIawAIAQMIAWsAAQFuCQEAAwMAVAkBAAADWAUEAgMAA0xZWUEZAAEAAAHYAdYBuQG3AVcBVgDHAMUAtQC0ALEArgB5AHYABwAGAAAADAABAAwACgAFABQrATIeARQOASIuAj4BAQ4BBzI+ATU+ATc2FyY2PwE2PwEGJjUUBzQmBjUuBC8BJjQvAQcGFCoBFCIGIgc2JyYjNiYnMy4CJy4BBwYUHwEWBh4BBwYPAQYWFxYUBiIPAQYmJyYnJgcmJyYHMiYHPgEjNj8BNicWPwE2NzYyFjMWNCcyJyYnJgcGFyIPAQYvASYnIgc2JiM2JyYiDwEGHgEyFxYHIgYiBhYHLgEnFicjIgYiJyY3NBcnBgcyNj8BNhc3FyYHBgcWBycuASciBwYHHgIUNxYHMhcWFxYHJyYGFjMiDwEGHwEGFjcGHwMeAhcGFgciBjUeAhQWNzYnLgI1MzIfAQYeAjMeAQcyHgQfAxYyPwE2FhcWNyIfAR4BFR4BFzY1BhYzNjUGLwEmNCY2FzI2LgInBiYnFAYVIzY0PwE2LwEmByIHDgMmJy4BND8BNic2PwE2OwEyNDYmIxY2FxY3JyY3FjceAh8BFjY3FhceAT4BJjUnNS4BNjc0Nj8BNicyNycmIjc2Jz4BMxY2Jz4BNxY2Jj4BFTc2IxY3Nic2JiczMjU2JyYDNjcmIi8BNiYvASYvASYPASIPARUmJyIuAQ4BDwEmNiYGDwEGNgYVDgEVLgE3HgEXFgcGBwYXFAYWAa10xnJyxujIbgZ6vAETAggDAQIEAxEVEwoBDAIIBgMBBwYEBAoFBgQBCAECAQMDBAQEBAYBBgIICQUEBgIEAwEIDAEFHAQDAgIBCAEOAQIHCQMEBAEEAgMBBwoCBAUNAwMUDhMECAYBAgECBQkCARMJBgQCBQYKAwgEBwUCAwYJBAYBBQkEBQMDAgUEAQ4HCw8EEAMDAQgECAEIAwEIBAMCAgMEAgQSBQMMDAEDAwIMGRsDBgUFEwUDCwQNCwEEAgYECAQJBFEyBAUCBgUDARgKAQIHBQQDBAQEAQIBAQECCgcHEgQHCQQDCAQCDgEBAgIOAgQCAg8IAwQDAgMFAQQKCgEECAQFDAcCAwgDCQcWBgYFCAgQBBQKAQIEAgYDDgMEAQoFCBEKAgICAgEFAgQBCgIDDAMCCAECCAMBAwIHCwQBAgIIFAMICgECAQQCAwUCAQMCAQMBBBgDCQMBAQEDDQIOBAIDAQQDBQIGCAQCAgEIBAQHCAUHDAQEAgICBgEFBAMCAwUMBAISAQQCAgUOCQICCggFCQIGBgcFCQwKaXNQAQwBDQEEAxUBAwUCAwICAQUMCAMGBgYGAQEECAQKAQcGAgoCBAEMAQECAgQLDwECCQoBAxJ0xOrEdHTE6sR0/t0BCAIGBgEECAMFCwEMAQMCAgwBCgcCAwQCBAECBgwFBgMDAgQBAQMDBAIEAQMDAgIIBAIGBAEDBAEEBAYHAwgHCgcEBQYFDAMBAgQCAQMMCQ4DBAUHCAUDEQIDDggFDAMBAwkJBgQDBgEOBAoEAQIFAgIGCgQHBwcBCQUIBwgDAgcDAgQCBgIEBQoDAw4CBQICBQQHAgEKCA8CAwMHAwIOAwIDBAYEBgQEAQEtTwQBCAQDBAYPCgIGBAUEBQ4JFAsCAQYaAgEXBQQGAwUUAwMQBQIBBAgFCAQBCxgNBQwCAgQEDAgOBA4BCgsUBwgBBQMNAgECARIDCgQECQUGAgMKAwIDBQwCEAgSAwMEBAYCBAoHDgEFAgQBBAICEAUPBQIFAwILAggEBAICBBgOCQ4FCQEEBgECAwIBBAMGBwYFAg8KAQQBAgMBAgMIBRcEAggIAwUOAgoKBQECAwQLCQUCAgICBgIKBgoEBAQDAQQKBAYBBwIBBwYFBAIDAQUEAv4NFVUCAgUEBgIPAQECAQIBAQMCCgMGAgIFBgcDDgYCAQUEAggBAggCAgICBRwIEQkOCQwCBBAHAAIAAP+lA48DJAAMABcAIkAfFAEBAhEFAgABAkcAAgECbwABAAFvAAAAZhsWIgMFFyslFAYnIic+ASc0NjIWARYUBwEuAScBNjIB0K57UUREUgFYelgBniAh/sIUUjgBPiBe0XywASgnilI9WFgB9SBeIP7CN1QUAT4gAAAD//X/uAPzA1kADwAhADMAZEAMGxECAwIJAQIBAAJHS7AkUFhAHQACBQMFAgNtAAMAAAEDAGAAAQAEAQRcAAUFDAVJG0AiAAUCBW8AAgMCbwADAAABAwBgAAEEBAFUAAEBBFgABAEETFlACRc4JycmIwYFGislNTQmKwEiBh0BFBYXMzI2JxM0JyYrASIHBhUXFBY3MzI2AwEWBw4BByEiJicmNwE+ATIWAjsKB2wHCgoHbAcKAQoFBwd6BggFCQwHZwgMCAGsFBUJIhL8phIiCRUUAa0JIiYiWmoICgoIaggKAQzXAQEGBAYGBAj/BQgBBgIQ/O4jIxESARQQIyMDEhEUFAAAAAAC//3/cQPrA1kAJwBQALBADiQWBgMBAkxCNAMEAwJHS7AhUFhAJgABAgMCAQNtBwEDBAIDBGsAAgIAWAYBAAAMSAAEBAVYAAUFDQVJG0uwJFBYQCMAAQIDAgEDbQcBAwQCAwRrAAQABQQFXAACAgBYBgEAAAwCSRtAKQABAgMCAQNtBwEDBAIDBGsGAQAAAgEAAmAABAUFBFQABAQFWAAFBAVMWVlAFykoAQBHRTEvKFApUBQSDAoAJwEnCAUUKwEiBwYHBgcUFh8BMzI1Njc2NzYzMhYXBwYWHwEWPgEvAS4BDwEmJyYBIhUGBwYHBiMiJyYnNzYmLwEmDgEfAR4BPwEWFxYzMjc2NzY3NCYvAQHug3FtQ0UFBQQEVBMFNTNTV2NPjjQ6CQIM9wsUCgQ6AhIJQURaXAEzEwU1M1NWY1BIRTU7CAIL+AsUCgQ6AhIKQERaXWaCcW5CRQUFBAQDWUA+a26BCAkCARJiU1EvMT44OQkTAzIDCRYQ4wgLBjxGJij+BBJiU1EvMSAeODkJEwMyAwkWEOMICwY8RiYoQD5rboIICAIBAAAAAAL///9iA+oDWQAfAEEASUAKBAECAAFHMQEBREuwJFBYQBMAAgABAAIBbQABAW4DAQAADABJG0APAwEAAgBvAAIBAm8AAQFmWUANAQAhIBQTAB8BHwQFFCsBIgcGBzE2NzYXFhcWFxYGBwYXHgE3PgE3NiYnLgEnJgEiBwYHBgcGFhcWFxYXFjc2NzEGBwYnJicmJyY2NzYmJyYB8ldRVERWbGpnak9CISEGJQ4aEDMRAwoCIwElJpBeW/4FGA8EBAYBJAIkJkhbe3d5fWFWbGpna09CISAFJQgGDhIDWR0eOUUVFB4gT0JWU7NRKRsQAREDDwZaw1ldkCYl/u4QBAYIBlrDWV1IWyQiGBlRRRUUHiBPQlZTs1EVIQ4SAAAAAAIAAAAAA+gDWQAnAD8AfUATKAEBBhEBAgE3LgIEAiEBBQQER0uwJFBYQCQABAIFAgQFbQAFAwIFA2sAAQACBAECYAADAAADAFwABgYMBkkbQCwABgEGbwAEAgUCBAVtAAUDAgUDawABAAIEAQJgAAMAAANUAAMDAFgAAAMATFlACjobJTU2JTMHBRsrARUUBiMhIiY1ETQ2NyEyFh0BFAYjISIGBxEUFhchMjY9ATQ2OwEyFhMRFA4BLwEBBiIvASY0NwEnJjQ2MyEyFgMSXkP+MENeXkMBiQcKCgf+dyU0ATYkAdAlNAoIJAgK1hYcC2L+lAUQBEAGBgFsYgsWDgEdDxQBU7JDXl5DAdBCXgEKCCQICjQl/jAlNAE2JLIICgoB2v7jDxQCDGL+lAYGQAUOBgFsYgscFhYAAAACAAD/uANZAxIAGAAoADJALxIJAgIAAUcAAgABAAIBbQAEAAACBABgAAEDAwFUAAEBA1gAAwEDTDU3FBkzBQUZKwERNCYnISIGHwEBBhQfARYyNwEXFjMyNzYTERQGByEiJjURNDY3ITIWAsoUD/70GBMSUP7WCws5CxwLASpRCg8GCBWPXkP96UNeXkMCF0NeAVMBDA8UAS0QUP7WCx4KOQoKASpQCwMKATX96EJeAWBBAhhCXgFgAAAAAAMAAAAAA1oCywAPAB8ALwA3QDQoAQQFCAACAAECRwAFAAQDBQRgAAMAAgEDAmAAAQAAAVQAAQEAWAAAAQBMJjUmNSYzBgUaKyUVFAYHISImJzU0NjchMhYDFRQGJyEiJic1NDYXITIWAxUUBiMhIiYnNTQ2FyEyFgNZFBD87w8UARYOAxEPFgEUEPzvDxQBFg4DEQ8WARQQ/O8PFAEWDgMRDxZrRw8UARYORw8UARYBEEgOFgEUD0gOFgEUAQ5HDhYWDkcPFgEUAAAAAAL///+4A+kCygAZADgALUAqCQACAgMBRwADAgNvAAIBAm8AAQAAAVQAAQEAWAAAAQBMNzQmJDozBAUWKwERFAYHISImNxEWFxYXHgI3MzI+ATc2NzY3FAYHBg8BDgInIyImLwEuAS8BJicuASc0NjMhMhYD6DQl/MokNgEZH8pMICZEGwIcQigfX7cgGDYp0jQ1DCIeDQIMHhEeDSIGk2ASIzwBLisDNiQ2Ac3+RSU0ATYkAbsbFok3GBocARocF0R8Fr8sUB2SIycJEgwBCgoSCBwDZUIOF1IkKzo0AAAAAgAA/3ED6ALKABcAPQBiQAw0CAIBACYLAgMCAkdLsCFQWEAXAAQFAQABBABgAAEAAgMBAmAAAwMNA0kbQB4AAwIDcAAEBQEAAQQAYAABAgIBVAABAQJYAAIBAkxZQBEBADs6JCIdGxIQABcBFwYFFCsBIg4BBxQWHwEHBgc2PwEXFjMyPgIuAQEUDgEjIicGBwYHIyImJzUmNiY/ATY/AT4CPwEuASc0PgEgHgEB9HLGdAFQSTAPDRpVRRggJiJyxnQCeMIBgIbmiCcqbpMbJAMIDgICBAIDDAQNFAcUEAcPWGQBhuYBEOaGAoNOhEw+cikcNTMuJDwVAwVOhJiETv7iYaRgBGEmCAQMCQECCAQDDwUOFggcHBMqMpJUYaRgYKQAAAIAAP+4A1kDEgAjADMAQUA+DQEAAR8BBAMCRwIBAAEDAQADbQUBAwQBAwRrAAcAAQAHAWAABAYGBFQABAQGWAAGBAZMNTUjMxYjJCMIBRwrATU0JgcjNTQmJyMiBgcVIyIGBxUUFjczFRQWOwEyNjc1MzI2ExEUBgchIiY1ETQ2NyEyFgLKFA+zFg5HDxQBsg8UARYOshYORw8UAbMOFo5eQ/3pQ15eQwIXQ14BQUgOFgGzDxQBFg6zFA9IDhYBsw4WFg6zFAE//ehCXgFgQQIYQl4BYAAAAAEAAP+4A+gDNQArAClAJiYBBAMBRwADBANvAAQBBG8AAQIBbwACAAJvAAAAZiMXEz0XBQUZKyUUBw4CBwYiJjU0Njc2NTQuBSsBFRQGIicBJjQ3ATYyFgcVMyAXFgPoRwEKBAUHEQoCAQMUIjg+VlY3fRQgCf7jCwsBHQscGAJ9AY5aHuhdnwQSEAQKDAgFFAMmHzhaQDAeEgaPDhYLAR4KHgoBHgoUD4/hSwABAAAAAAKDA1oAIwBmS7AkUFhAIAAEBQAFBABtAgYCAAEFAAFrAAEBbgAFBQNYAAMDDAVJG0AlAAQFAAUEAG0CBgIAAQUAAWsAAQFuAAMFBQNUAAMDBVgABQMFTFlAEwEAIB8bGBQTEA4JBgAjASMHBRQrATIWFxEUBgchIiYnETQ2FzM1NDYeAQcUBisBIiY1NCYiBhcVAk0XHgEgFv3pFx4BIBYRlMyWAhQPJA4WVHZUAQGsHhf+vhYeASAVAUIWIAGzZ5QCkGkOFhYOO1RUO7MAAAMAAP+4A30DEgAIABgAVQBOQEtKAQgHHxsCAAMAAQEAMRECAgEERwAHCAdvAAgDCG8GAQMAA28AAAEAbwAEAgRwAAECAgFUAAEBAlgFAQIBAkwvLBUkPyY1ExIJBR0rNzQuAQ4BHgE2ExEUBgcjIiYnETQ2FzMyFgUUBxYVFgcWBwYHFgcGByMiLgEnJiciJicRND4CNzY3PgI3PgMzMh4EBhcUDgEHDgIHMzIWjxYdFAEWHRRaFBCgDxQBFg6gDxYClB8JARkJCQkWBSAkSkglVjIqRRMPFAEUGzocJhIKDgYFBAYQFQ8ZKhgUCAYCAgwIDAEIBAObK0BrDxQBFh0UARYBLP6bDxQBFg4BZQ4WARQPMCMZEioiHyMfFT4nKwESDg8YARYOAWUOFgFAIzESCiIUGBYYIhYMEhoYIBINFSwWFAQMDgZAAAAABQAA/3ED6ANZABAAFAAlAC8AOQDbQBczKQIHCCEBBQIdFQ0MBAAFA0cEAQUBRkuwIVBYQC0GDAMLBAEHAgcBAm0AAgUHAgVrAAUABwUAawkBBwcIWAoBCAgMSAQBAAANAEkbS7AkUFhALAYMAwsEAQcCBwECbQACBQcCBWsABQAHBQBrBAEAAG4JAQcHCFgKAQgIDAdJG0AyBgwDCwQBBwIHAQJtAAIFBwIFawAFAAcFAGsEAQAAbgoBCAcHCFQKAQgIB1YJAQcIB0pZWUAgEREAADc1MjEtKygnJCIfHhsZERQRFBMSABAADzcNBRUrAREUBgcRFAYHISImJxETNjMhESMRAREUBgchIiYnESImJxEzMhclFSM1NDY7ATIWBRUjNTQ2OwEyFgGJFg4UEP7jDxQBiwQNAZ+OAjsWDv7jDxQBDxQB7Q0E/j7FCgihCAoBd8UKCKEICgKm/lQPFAH+vw8UARYOAR0B6Az+eAGI/gz+4w8UARYOAUEWDgGsDK19fQgKCgh9fQgKCgAAAAMAAP+4BHgDEwAIACwATwB3QHQsJQIKByAfDgMDAjITAgQIA0cAAQcBbwAHCgdvDgEACg0KAA1tAAsNAg0LAm0MAQoADQsKDWAGAQIFAQMIAgNgAAgEBAhUAAgIBFgJAQQIBEwBAE1LSkhFREE/NjMxLykoJCIcGxcVEhAKCQUEAAgBCA8FFCsBIiY+AR4CBgUzMhYHFRQGKwEVFAYHIyImPQEjIiYnNTQ2NzM1NDYXMzIWFwEUFjczFQYjISImNTQ+BRcyFx4BMjY3NjMyFyMiBhUBiVl+Anq2eAaEAcPEBwwBCgjEDAZrCArFBwoBDAbFCghrBwoB/mUqHY8mOf4YQ1IEDBIeJjohCwssVGRULAsLSTB9HSoBZX6wgAJ8tHpJDAZrCArFBwoBDAbFCghrBwoBxAcMAQoI/r8dLAGFHE5DHjhCNjgiGgIKIiIiIgo2Kh0AAAAAAQAAAAEAAGfyjqxfDzz1AAsD6AAAAADYEy42AAAAANgTLjb/9f9iBHgDZwAAAAgAAgAAAAAAAAABAAADWf9xAAAEdv/1//MEeAABAAAAAAAAAAAAAAAAAAAAIwPoAAADEQAAA6AAAAOgAAADoAAAA1n//QPoAAAELwAAA1kAAAOgAAAD6AAAA6v//gQv//8EL///AsoAAALKAAAD6AAAA+gAAAKCAAADWf/9A6AAAAPo//UD6P/9A+n//wPoAAADWQAAA1kAAAPo//8D6AAAA1kAAAPoAAACggAAA6AAAAPoAAAEdgAAAAAAAABKAM4BEgFsAagCWgLgA6IEJARaBMQFPgaQBsYG+gcwCAQITAxQDI4NEg3oDngPFg90D9oQShDcEUgRnhIIEq4TdhQhAAEAAAAjAfgACwAAAAAAAgAsADwAcwAAAKoLcAAAAAAAAAASAN4AAQAAAAAAAAA1AAAAAQAAAAAAAQAIADUAAQAAAAAAAgAHAD0AAQAAAAAAAwAIAEQAAQAAAAAABAAIAEwAAQAAAAAABQALAFQAAQAAAAAABgAIAF8AAQAAAAAACgArAGcAAQAAAAAACwATAJIAAwABBAkAAABqAKUAAwABBAkAAQAQAQ8AAwABBAkAAgAOAR8AAwABBAkAAwAQAS0AAwABBAkABAAQAT0AAwABBAkABQAWAU0AAwABBAkABgAQAWMAAwABBAkACgBWAXMAAwABBAkACwAmAclDb3B5cmlnaHQgKEMpIDIwMTggYnkgb3JpZ2luYWwgYXV0aG9ycyBAIGZvbnRlbGxvLmNvbWZvbnRlbGxvUmVndWxhcmZvbnRlbGxvZm9udGVsbG9WZXJzaW9uIDEuMGZvbnRlbGxvR2VuZXJhdGVkIGJ5IHN2ZzJ0dGYgZnJvbSBGb250ZWxsbyBwcm9qZWN0Lmh0dHA6Ly9mb250ZWxsby5jb20AQwBvAHAAeQByAGkAZwBoAHQAIAAoAEMAKQAgADIAMAAxADgAIABiAHkAIABvAHIAaQBnAGkAbgBhAGwAIABhAHUAdABoAG8AcgBzACAAQAAgAGYAbwBuAHQAZQBsAGwAbwAuAGMAbwBtAGYAbwBuAHQAZQBsAGwAbwBSAGUAZwB1AGwAYQByAGYAbwBuAHQAZQBsAGwAbwBmAG8AbgB0AGUAbABsAG8AVgBlAHIAcwBpAG8AbgAgADEALgAwAGYAbwBuAHQAZQBsAGwAbwBHAGUAbgBlAHIAYQB0AGUAZAAgAGIAeQAgAHMAdgBnADIAdAB0AGYAIABmAHIAbwBtACAARgBvAG4AdABlAGwAbABvACAAcAByAG8AagBlAGMAdAAuAGgAdAB0AHAAOgAvAC8AZgBvAG4AdABlAGwAbABvAC4AYwBvAG0AAAAAAgAAAAAAAAAKAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAjAQIBAwEEAQUBBgEHAQgBCQEKAQsBDAENAQ4BDwEQAREBEgETARQBFQEWARcBGAEZARoBGwEcAR0BHgEfASABIQEiASMBJAAGY2FuY2VsBnVwbG9hZARzdGFyCnN0YXItZW1wdHkGYWRqdXN0B2V5ZS1vZmYHcmV0d2VldANjb2cGbG9nb3V0CWRvd24tb3BlbgZhdHRhY2gHcGljdHVyZQV2aWRlbwpyaWdodC1vcGVuCWxlZnQtb3Blbgd1cC1vcGVuBGJlbGwEbG9jawVnbG9iZQVicnVzaAlhdHRlbnRpb24Fc3BpbjMFc3BpbjQIbGluay1leHQMbGluay1leHQtYWx0BG1lbnUIbWFpbC1hbHQNY29tbWVudC1lbXB0eQxwbHVzLXNxdWFyZWQFcmVwbHkNbG9jay1vcGVuLWFsdA10aHVtYnMtdXAtYWx0CmJpbm9jdWxhcnMJdXNlci1wbHVzAAAAAAAAAQAB//8ADwAAAAAAAAAAAAAAAAAAAAAAGAAYABgAGANn/2IDZ/9isAAsILAAVVhFWSAgS7gADlFLsAZTWliwNBuwKFlgZiCKVViwAiVhuQgACABjYyNiGyEhsABZsABDI0SyAAEAQ2BCLbABLLAgYGYtsAIsIGQgsMBQsAQmWrIoAQpDRWNFUltYISMhG4pYILBQUFghsEBZGyCwOFBYIbA4WVkgsQEKQ0VjRWFksChQWCGxAQpDRWNFILAwUFghsDBZGyCwwFBYIGYgiophILAKUFhgGyCwIFBYIbAKYBsgsDZQWCGwNmAbYFlZWRuwAStZWSOwAFBYZVlZLbADLCBFILAEJWFkILAFQ1BYsAUjQrAGI0IbISFZsAFgLbAELCMhIyEgZLEFYkIgsAYjQrEBCkNFY7EBCkOwAWBFY7ADKiEgsAZDIIogirABK7EwBSWwBCZRWGBQG2FSWVgjWSEgsEBTWLABKxshsEBZI7AAUFhlWS2wBSywB0MrsgACAENgQi2wBiywByNCIyCwACNCYbACYmawAWOwAWCwBSotsAcsICBFILALQ2O4BABiILAAUFiwQGBZZrABY2BEsAFgLbAILLIHCwBDRUIqIbIAAQBDYEItsAkssABDI0SyAAEAQ2BCLbAKLCAgRSCwASsjsABDsAQlYCBFiiNhIGQgsCBQWCGwABuwMFBYsCAbsEBZWSOwAFBYZVmwAyUjYUREsAFgLbALLCAgRSCwASsjsABDsAQlYCBFiiNhIGSwJFBYsAAbsEBZI7AAUFhlWbADJSNhRESwAWAtsAwsILAAI0KyCwoDRVghGyMhWSohLbANLLECAkWwZGFELbAOLLABYCAgsAxDSrAAUFggsAwjQlmwDUNKsABSWCCwDSNCWS2wDywgsBBiZrABYyC4BABjiiNhsA5DYCCKYCCwDiNCIy2wECxLVFixBGREWSSwDWUjeC2wESxLUVhLU1ixBGREWRshWSSwE2UjeC2wEiyxAA9DVVixDw9DsAFhQrAPK1mwAEOwAiVCsQwCJUKxDQIlQrABFiMgsAMlUFixAQBDYLAEJUKKiiCKI2GwDiohI7ABYSCKI2GwDiohG7EBAENgsAIlQrACJWGwDiohWbAMQ0ewDUNHYLACYiCwAFBYsEBgWWawAWMgsAtDY7gEAGIgsABQWLBAYFlmsAFjYLEAABMjRLABQ7AAPrIBAQFDYEItsBMsALEAAkVUWLAPI0IgRbALI0KwCiOwAWBCIGCwAWG1EBABAA4AQkKKYLESBiuwcisbIlktsBQssQATKy2wFSyxARMrLbAWLLECEystsBcssQMTKy2wGCyxBBMrLbAZLLEFEystsBossQYTKy2wGyyxBxMrLbAcLLEIEystsB0ssQkTKy2wHiwAsA0rsQACRVRYsA8jQiBFsAsjQrAKI7ABYEIgYLABYbUQEAEADgBCQopgsRIGK7ByKxsiWS2wHyyxAB4rLbAgLLEBHistsCEssQIeKy2wIiyxAx4rLbAjLLEEHistsCQssQUeKy2wJSyxBh4rLbAmLLEHHistsCcssQgeKy2wKCyxCR4rLbApLCA8sAFgLbAqLCBgsBBgIEMjsAFgQ7ACJWGwAWCwKSohLbArLLAqK7AqKi2wLCwgIEcgILALQ2O4BABiILAAUFiwQGBZZrABY2AjYTgjIIpVWCBHICCwC0NjuAQAYiCwAFBYsEBgWWawAWNgI2E4GyFZLbAtLACxAAJFVFiwARawLCqwARUwGyJZLbAuLACwDSuxAAJFVFiwARawLCqwARUwGyJZLbAvLCA1sAFgLbAwLACwAUVjuAQAYiCwAFBYsEBgWWawAWOwASuwC0NjuAQAYiCwAFBYsEBgWWawAWOwASuwABa0AAAAAABEPiM4sS8BFSotsDEsIDwgRyCwC0NjuAQAYiCwAFBYsEBgWWawAWNgsABDYTgtsDIsLhc8LbAzLCA8IEcgsAtDY7gEAGIgsABQWLBAYFlmsAFjYLAAQ2GwAUNjOC2wNCyxAgAWJSAuIEewACNCsAIlSYqKRyNHI2EgWGIbIVmwASNCsjMBARUUKi2wNSywABawBCWwBCVHI0cjYbAJQytlii4jICA8ijgtsDYssAAWsAQlsAQlIC5HI0cjYSCwBCNCsAlDKyCwYFBYILBAUVizAiADIBuzAiYDGllCQiMgsAhDIIojRyNHI2EjRmCwBEOwAmIgsABQWLBAYFlmsAFjYCCwASsgiophILACQ2BkI7ADQ2FkUFiwAkNhG7ADQ2BZsAMlsAJiILAAUFiwQGBZZrABY2EjICCwBCYjRmE4GyOwCENGsAIlsAhDRyNHI2FgILAEQ7ACYiCwAFBYsEBgWWawAWNgIyCwASsjsARDYLABK7AFJWGwBSWwAmIgsABQWLBAYFlmsAFjsAQmYSCwBCVgZCOwAyVgZFBYIRsjIVkjICCwBCYjRmE4WS2wNyywABYgICCwBSYgLkcjRyNhIzw4LbA4LLAAFiCwCCNCICAgRiNHsAErI2E4LbA5LLAAFrADJbACJUcjRyNhsABUWC4gPCMhG7ACJbACJUcjRyNhILAFJbAEJUcjRyNhsAYlsAUlSbACJWG5CAAIAGNjIyBYYhshWWO4BABiILAAUFiwQGBZZrABY2AjLiMgIDyKOCMhWS2wOiywABYgsAhDIC5HI0cjYSBgsCBgZrACYiCwAFBYsEBgWWawAWMjICA8ijgtsDssIyAuRrACJUZSWCA8WS6xKwEUKy2wPCwjIC5GsAIlRlBYIDxZLrErARQrLbA9LCMgLkawAiVGUlggPFkjIC5GsAIlRlBYIDxZLrErARQrLbA+LLA1KyMgLkawAiVGUlggPFkusSsBFCstsD8ssDYriiAgPLAEI0KKOCMgLkawAiVGUlggPFkusSsBFCuwBEMusCsrLbBALLAAFrAEJbAEJiAuRyNHI2GwCUMrIyA8IC4jOLErARQrLbBBLLEIBCVCsAAWsAQlsAQlIC5HI0cjYSCwBCNCsAlDKyCwYFBYILBAUVizAiADIBuzAiYDGllCQiMgR7AEQ7ACYiCwAFBYsEBgWWawAWNgILABKyCKimEgsAJDYGQjsANDYWRQWLACQ2EbsANDYFmwAyWwAmIgsABQWLBAYFlmsAFjYbACJUZhOCMgPCM4GyEgIEYjR7ABKyNhOCFZsSsBFCstsEIssDUrLrErARQrLbBDLLA2KyEjICA8sAQjQiM4sSsBFCuwBEMusCsrLbBELLAAFSBHsAAjQrIAAQEVFBMusDEqLbBFLLAAFSBHsAAjQrIAAQEVFBMusDEqLbBGLLEAARQTsDIqLbBHLLA0Ki2wSCywABZFIyAuIEaKI2E4sSsBFCstsEkssAgjQrBIKy2wSiyyAABBKy2wSyyyAAFBKy2wTCyyAQBBKy2wTSyyAQFBKy2wTiyyAABCKy2wTyyyAAFCKy2wUCyyAQBCKy2wUSyyAQFCKy2wUiyyAAA+Ky2wUyyyAAE+Ky2wVCyyAQA+Ky2wVSyyAQE+Ky2wViyyAABAKy2wVyyyAAFAKy2wWCyyAQBAKy2wWSyyAQFAKy2wWiyyAABDKy2wWyyyAAFDKy2wXCyyAQBDKy2wXSyyAQFDKy2wXiyyAAA/Ky2wXyyyAAE/Ky2wYCyyAQA/Ky2wYSyyAQE/Ky2wYiywNysusSsBFCstsGMssDcrsDsrLbBkLLA3K7A8Ky2wZSywABawNyuwPSstsGYssDgrLrErARQrLbBnLLA4K7A7Ky2waCywOCuwPCstsGkssDgrsD0rLbBqLLA5Ky6xKwEUKy2wayywOSuwOystsGwssDkrsDwrLbBtLLA5K7A9Ky2wbiywOisusSsBFCstsG8ssDorsDsrLbBwLLA6K7A8Ky2wcSywOiuwPSstsHIsswkEAgNFWCEbIyFZQiuwCGWwAyRQeLABFTAtAEu4AMhSWLEBAY5ZsAG5CAAIAGNwsQAFQrIAAQAqsQAFQrMKAgEIKrEABUKzDgABCCqxAAZCugLAAAEACSqxAAdCugBAAAEACSqxAwBEsSQBiFFYsECIWLEDZESxJgGIUVi6CIAAAQRAiGNUWLEDAERZWVlZswwCAQwquAH/hbAEjbECAEQAAA==') format('truetype'); + src: url('data:application/octet-stream;base64,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') format('woff'), + url('data:application/octet-stream;base64,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') format('truetype'); } /* Chrome hack: SVG is rendered more smooth in Windozze. 100% magic, uncomment if you need it. */ /* Note, that will break hinting! In other OS-es font will be not as sharp as it could be */ @@ -17,7 +17,7 @@ @media screen and (-webkit-min-device-pixel-ratio:0) { @font-face { font-family: 'fontello'; - src: url('../font/fontello.svg?92539127#fontello') format('svg'); + src: url('../font/fontello.svg?4112743#fontello') format('svg'); } } */ @@ -73,6 +73,7 @@ .icon-globe:before { content: '\e812'; } /* '' */ .icon-brush:before { content: '\e813'; } /* '' */ .icon-attention:before { content: '\e814'; } /* '' */ +.icon-plus:before { content: '\e815'; } /* '' */ .icon-spin3:before { content: '\e832'; } /* '' */ .icon-spin4:before { content: '\e834'; } /* '' */ .icon-link-ext:before { content: '\f08e'; } /* '' */ diff --git a/static/font/css/fontello-ie7-codes.css b/static/font/css/fontello-ie7-codes.css index 689c8e43..fa7c1002 100644 --- a/static/font/css/fontello-ie7-codes.css +++ b/static/font/css/fontello-ie7-codes.css @@ -20,6 +20,7 @@ .icon-globe { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } .icon-brush { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } .icon-attention { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-plus { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } .icon-spin3 { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } .icon-spin4 { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } .icon-link-ext { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } diff --git a/static/font/css/fontello-ie7.css b/static/font/css/fontello-ie7.css index 23ecfa3c..b37a63cd 100644 --- a/static/font/css/fontello-ie7.css +++ b/static/font/css/fontello-ie7.css @@ -31,6 +31,7 @@ .icon-globe { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } .icon-brush { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } .icon-attention { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-plus { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } .icon-spin3 { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } .icon-spin4 { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } .icon-link-ext { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } diff --git a/static/font/css/fontello.css b/static/font/css/fontello.css index 0181fa62..38caec46 100644 --- a/static/font/css/fontello.css +++ b/static/font/css/fontello.css @@ -1,11 +1,11 @@ @font-face { font-family: 'fontello'; - src: url('../font/fontello.eot?13201279'); - src: url('../font/fontello.eot?13201279#iefix') format('embedded-opentype'), - url('../font/fontello.woff2?13201279') format('woff2'), - url('../font/fontello.woff?13201279') format('woff'), - url('../font/fontello.ttf?13201279') format('truetype'), - url('../font/fontello.svg?13201279#fontello') format('svg'); + src: url('../font/fontello.eot?3996201'); + src: url('../font/fontello.eot?3996201#iefix') format('embedded-opentype'), + url('../font/fontello.woff2?3996201') format('woff2'), + url('../font/fontello.woff?3996201') format('woff'), + url('../font/fontello.ttf?3996201') format('truetype'), + url('../font/fontello.svg?3996201#fontello') format('svg'); font-weight: normal; font-style: normal; } @@ -15,7 +15,7 @@ @media screen and (-webkit-min-device-pixel-ratio:0) { @font-face { font-family: 'fontello'; - src: url('../font/fontello.svg?13201279#fontello') format('svg'); + src: url('../font/fontello.svg?3996201#fontello') format('svg'); } } */ @@ -76,6 +76,7 @@ .icon-globe:before { content: '\e812'; } /* '' */ .icon-brush:before { content: '\e813'; } /* '' */ .icon-attention:before { content: '\e814'; } /* '' */ +.icon-plus:before { content: '\e815'; } /* '' */ .icon-spin3:before { content: '\e832'; } /* '' */ .icon-spin4:before { content: '\e834'; } /* '' */ .icon-link-ext:before { content: '\f08e'; } /* '' */ diff --git a/static/font/demo.html b/static/font/demo.html index 6e4d3eb0..cb1aa970 100644 --- a/static/font/demo.html +++ b/static/font/demo.html @@ -229,11 +229,11 @@ body { } @font-face { font-family: 'fontello'; - src: url('./font/fontello.eot?97354950'); - src: url('./font/fontello.eot?97354950#iefix') format('embedded-opentype'), - url('./font/fontello.woff?97354950') format('woff'), - url('./font/fontello.ttf?97354950') format('truetype'), - url('./font/fontello.svg?97354950#fontello') format('svg'); + src: url('./font/fontello.eot?15755415'); + src: url('./font/fontello.eot?15755415#iefix') format('embedded-opentype'), + url('./font/fontello.woff?15755415') format('woff'), + url('./font/fontello.ttf?15755415') format('truetype'), + url('./font/fontello.svg?15755415#fontello') format('svg'); font-weight: normal; font-style: normal; } @@ -329,23 +329,24 @@ body { </div> <div class="row"> <div class="the-icons span3" title="Code: 0xe814"><i class="demo-icon icon-attention"></i> <span class="i-name">icon-attention</span><span class="i-code">0xe814</span></div> + <div class="the-icons span3" title="Code: 0xe815"><i class="demo-icon icon-plus"></i> <span class="i-name">icon-plus</span><span class="i-code">0xe815</span></div> <div class="the-icons span3" title="Code: 0xe832"><i class="demo-icon icon-spin3 animate-spin"></i> <span class="i-name">icon-spin3</span><span class="i-code">0xe832</span></div> <div class="the-icons span3" title="Code: 0xe834"><i class="demo-icon icon-spin4 animate-spin"></i> <span class="i-name">icon-spin4</span><span class="i-code">0xe834</span></div> - <div class="the-icons span3" title="Code: 0xf08e"><i class="demo-icon icon-link-ext"></i> <span class="i-name">icon-link-ext</span><span class="i-code">0xf08e</span></div> </div> <div class="row"> + <div class="the-icons span3" title="Code: 0xf08e"><i class="demo-icon icon-link-ext"></i> <span class="i-name">icon-link-ext</span><span class="i-code">0xf08e</span></div> <div class="the-icons span3" title="Code: 0xf08f"><i class="demo-icon icon-link-ext-alt"></i> <span class="i-name">icon-link-ext-alt</span><span class="i-code">0xf08f</span></div> <div class="the-icons span3" title="Code: 0xf0c9"><i class="demo-icon icon-menu"></i> <span class="i-name">icon-menu</span><span class="i-code">0xf0c9</span></div> <div class="the-icons span3" title="Code: 0xf0e0"><i class="demo-icon icon-mail-alt"></i> <span class="i-name">icon-mail-alt</span><span class="i-code">0xf0e0</span></div> - <div class="the-icons span3" title="Code: 0xf0e5"><i class="demo-icon icon-comment-empty"></i> <span class="i-name">icon-comment-empty</span><span class="i-code">0xf0e5</span></div> </div> <div class="row"> + <div class="the-icons span3" title="Code: 0xf0e5"><i class="demo-icon icon-comment-empty"></i> <span class="i-name">icon-comment-empty</span><span class="i-code">0xf0e5</span></div> <div class="the-icons span3" title="Code: 0xf0fe"><i class="demo-icon icon-plus-squared"></i> <span class="i-name">icon-plus-squared</span><span class="i-code">0xf0fe</span></div> <div class="the-icons span3" title="Code: 0xf112"><i class="demo-icon icon-reply"></i> <span class="i-name">icon-reply</span><span class="i-code">0xf112</span></div> <div class="the-icons span3" title="Code: 0xf13e"><i class="demo-icon icon-lock-open-alt"></i> <span class="i-name">icon-lock-open-alt</span><span class="i-code">0xf13e</span></div> - <div class="the-icons span3" title="Code: 0xf164"><i class="demo-icon icon-thumbs-up-alt"></i> <span class="i-name">icon-thumbs-up-alt</span><span class="i-code">0xf164</span></div> </div> <div class="row"> + <div class="the-icons span3" title="Code: 0xf164"><i class="demo-icon icon-thumbs-up-alt"></i> <span class="i-name">icon-thumbs-up-alt</span><span class="i-code">0xf164</span></div> <div class="the-icons span3" title="Code: 0xf1e5"><i class="demo-icon icon-binoculars"></i> <span class="i-name">icon-binoculars</span><span class="i-code">0xf1e5</span></div> <div class="the-icons span3" title="Code: 0xf234"><i class="demo-icon icon-user-plus"></i> <span class="i-name">icon-user-plus</span><span class="i-code">0xf234</span></div> </div> diff --git a/static/font/font/fontello.eot b/static/font/font/fontello.eot index a7b690a7d7864f9799f11e78da248e8dea1a1fbc..7dfef262db9c104276310ad6c8aeeb45ad3f33a5 100644 GIT binary patch delta 782 zcmZ9KPfQa*7{%Xo+JYA7AKI-Vwkz9~5==2&C<l$!3nF;nK#*#pZb~T%wHu41BqpHI ziyAF%IFe{$Ob^CO^#mcFG$e8{F?fUnq<AoB#1Lbs-?Dl!o8P>7Z|2*~e7n=rtazJQ zi*W$)8Eee!?_HUli9eXS1+YE}2a*{@4X<U+k}m@MWpZL%v{rV10CJhu#bGs_xj$1# z0PWR)O-(B~m2wBIuaZ`$$0mn+)GzCRr5^}?97!od_4CiVfk>FvV<Qw)E%6HZE93(s zneqG``#@!bg1ZzH#<EG}&8OmXpv_IbC!^$5Hd*_Eyj~z)S2C#&b3T#$G+<g*v$^r& z_Sg47lrF#u>W!4Te!6u5IF;9)vWsj^`_0lVJ781|d<Wl~9VOjQ`*&c*JIWjB3LG_M zkM_`X-rEKJ>Hh!GewofNsTAk6Y~Sm>*8!3?V6IGgfHDDrG-?1ZX}bXq09%g?aFFz| z0UAh`3~-2a)qswJE$X2T4wLR0zz2MD8Ng3^(jWwpCPM}|LP~`?Xre3AA#@;>wk*Mh zsl7&U7k5EIOFRTKH)p9QOtmmhpS+)hW6;E?ALk@FD9Mu5($LC0ZfmndLzUDN>g=GQ zxuZSSDbsMAMFp=%jtbl*3T06k6+~9{`WP9Xca$c&S=7@_!N(k;XY+3?i2DALTMXAV z&aU+OFWXOS!^ZU=4WGTQmj9_X80lmItq@6YC!m!hN$En}vAVbROZEjvljDgq;4C;d tTz#&e^^5MSRHa`rP`cJ;<5V^g?J}{Twh*1OnANeHIV~aIE3L`?nm@V`zIOls delta 621 zcmZ9IF=!J}7{~vYOJgxHNt4oQJlkuks8~}qAv#E{A~iVGrM5WOYnoh!o;^$`aj|hw zs0KAWaBGPUQlu!F#X)d!DJnW@>EKey;1EMCA|>&EIb6KQkN^9B@B7~S?pwYk6$+9I z!@&5Nk}K|1jJjSLzBgY0q)`B4NkcOqeT`)4mjQVxIhRvi&Bi7WWf9c0sT+4o4=3m+ z0k^4ZS(9-;{V8g@K64|TUU^Xl+7iH_bD5Mj9eh;|0Y{5Gk7pRzu_`y{-_h^S7`gn- z##r+^162l!GqXwU$?Iotfk=)%VQ6_%%I|tg|At<@rWvUZi#;m+I^g(d&SrD(f2@51 zj<2xk1@n5!+&FuLxkH~tS-K)U7Tc13cncD%fg0r2Zh*Rz`CD+pYDD2L$naD<MbUA& z`#h{Q^8XTl92Z#b5|`xPVp4YdJl2)gpjuy8j60k2Cx9wJ7d2*sZa#e92D_;bY_NyA zY6Fg__RR)+sT(%z3^k6+0{eiU0UPvChiuSGowiY+a>HT^^Z|7?VSzAE*={=!s=QPW zdKJBHC|7-_f59zVbH|rpB>qpuX5^&QF9rrD#KPcIZ`GUkb@_6>HUCBbdPkyTDbOBx g9-Qm!YfZAh;yvb;nI4Lch^gqJaK>&|?!>~Lzh+jVrT_o{ diff --git a/static/font/font/fontello.svg b/static/font/font/fontello.svg index fed9129e..6e5616a1 100644 --- a/static/font/font/fontello.svg +++ b/static/font/font/fontello.svg @@ -48,6 +48,8 @@ <glyph glyph-name="attention" unicode="" d="M571 90v106q0 8-5 13t-12 5h-108q-7 0-12-5t-5-13v-106q0-8 5-13t12-6h108q7 0 12 6t5 13z m-1 208l10 257q0 6-5 10-7 6-14 6h-122q-6 0-14-6-5-4-5-12l9-255q0-5 6-9t13-3h103q8 0 14 3t5 9z m-7 522l428-786q20-35-1-70-9-17-26-26t-35-10h-858q-18 0-35 10t-26 26q-21 35-1 70l429 786q9 17 26 27t36 10 36-10 27-27z" horiz-adv-x="1000" /> +<glyph glyph-name="plus" unicode="" d="M786 446v-107q0-22-16-38t-38-15h-232v-233q0-22-16-37t-38-16h-107q-22 0-38 16t-15 37v233h-232q-23 0-38 15t-16 38v107q0 23 16 38t38 16h232v232q0 22 15 38t38 16h107q23 0 38-16t16-38v-232h232q23 0 38-16t16-38z" horiz-adv-x="785.7" /> + <glyph glyph-name="spin3" unicode="" d="M494 857c-266 0-483-210-494-472-1-19 13-20 13-20l84 0c16 0 19 10 19 18 10 199 176 358 378 358 107 0 205-45 273-118l-58-57c-11-12-11-27 5-31l247-50c21-5 46 11 37 44l-58 227c-2 9-16 22-29 13l-65-60c-89 91-214 148-352 148z m409-508c-16 0-19-10-19-18-10-199-176-358-377-358-108 0-205 45-274 118l59 57c10 12 10 27-5 31l-248 50c-21 5-46-11-37-44l58-227c2-9 16-22 30-13l64 60c89-91 214-148 353-148 265 0 482 210 493 473 1 18-13 19-13 19l-84 0z" horiz-adv-x="1000" /> <glyph glyph-name="spin4" unicode="" d="M498 857c-114 0-228-39-320-116l0 0c173 140 428 130 588-31 134-134 164-332 89-495-10-29-5-50 12-68 21-20 61-23 84 0 3 3 12 15 15 24 71 180 33 393-112 539-99 98-228 147-356 147z m-409-274c-14 0-29-5-39-16-3-3-13-15-15-24-71-180-34-393 112-539 185-185 479-195 676-31l0 0c-173-140-428-130-589 31-134 134-163 333-89 495 11 29 6 50-12 68-11 11-27 17-44 16z" horiz-adv-x="1001" /> diff --git a/static/font/font/fontello.ttf b/static/font/font/fontello.ttf index 5fb1466a2af114268066dcb77a0963f9449eaf3e..1fe7c631348c3a8f9d12ba8b15e70de95907627a 100644 GIT binary patch delta 775 zcmZ9KUr19?9LK+BXLnkf`{Q<3X4`Fdn-g={HXTuvz1XrIB!*={HRjyHtzFVWA#y~$ z^iZ=)j}k(J4<W_sDUs-*5VD6L^3fn=_7D~sKDmB(w_f7#Ip6dBo%6e#-|wEKn}q@^ z$`dyL)(4<JnM){vFS!%sX8`^*d3{teRknTs;WF(@LrOY#cd|GDL~4OLC7l>iC~l$s zWzyPoc6_K;`L+TGaUk$9lS&LWzM75!!2s=dWhhXyz$@f0k@seDqhq(s{gqV;+@?S= zn@=WQewuj<?6;He%_YVZHooHtc`ZS@lE|grPq`%W1;DVV<VQwlHom_DLR0`NDpym= z%CWXN;ON-o=6N=y##y>`6O599pWxNiKGN-Ud=o~zrFb({z)@dzs9)I`XAiUy`+umX z4aWtUvP|l<VN2~W2<jtl+V-OQWk6PEd1b-@lnIEWAsskLBRbd#tl!hYF4FrtXd+$E z!EVxJ9l9CTshb+uL%O8{7x2TT12^dr9X}*m4C-JnDJ5#aL)FtIG$5B^g0E?!+7oW$ zHppo0_QS}Hd1?<+1jgy-$4NNO8W^?alpOZSVc8@!wK0d?)FRVVBYXVOPMTUeBVExj zO@~-WbUMNzk=rD(EQ!OS#L7+=Bja)o(?T=zT69fxF^l9_`x_P|?R+Wg4m31B{Cv`V z(R_FVbzFOLSaQxT{!{f?dz5+Aa{B<c0%~_KDW7ZTXn1YDV4kyhEDx++Ytg!D>$ClC poVQ=5EUiRu=}oYXQ`(0EJqG4e?V%~bsAR8=sLw-pN(aO4`ah*^yn+A# delta 602 zcmZ9IL1+^}6o&uF+FDI)(xkLnW4lch6?Mf#ss|}5QiFI9DV3t2Ynp5hUAr_(iI+A9 zJt#$62E4Vj3L;We%tb`-;-#qQv6dnVR&wwVixy9b|77vv4j=#gZ{C}E`-(-eG(Hud zC<5XffPu7aI`=*qdHP3yHk%%E^~PHD7mz&8b3N-=_U-AZLHa9z-?7ZR!+41PD7DEN zzMjo4Je>m?Qox>*xr{j!c|O+x9GK*JPmY0&i&~Zb4gJoX?H0zX1GVoAlo^;D9!Z<` zUp#&d7%qLvHVcj@Y<WokieA5F+L?DVojUyr;Ct_k<lVPFmOlW87TEMHXEfuip4i9S z-jCCNE{b{CFRZ=m5G)2N(CWMW)Xhv^#|EfAqWl95fpV+-CN8v}f?9q5FL~Z~n&piT z=6xc}qJX+ljj8^{<TI@{kpRjBZPddaXy?0kJg}8|*8|(Aiyq*BDqlRXox1A5#!%s~ z6xad$<hu%VQhPnnMIG{pLg$9X3UmW&Y(jw;Q0i~k)lu4{?{1D-Yj*T<@YrwoH8piC zMU0+*D$g26L|oo52IW`dYFD|r5Nr#&!R64I&`L|HWj5Rtei9jL?XC|pUdkTyYfK+a S^vRcr8F?vrv-CL`3;YFN#+@Gk diff --git a/static/font/font/fontello.woff b/static/font/font/fontello.woff index ca463697bc2307d76991a4af2a2cb0f5ba86a0f6..fc65e32512172f75ddafe16b924f90a2f98c3d26 100644 GIT binary patch delta 7822 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 delta 7738 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 AZU6uP diff --git a/static/font/font/fontello.woff2 b/static/font/font/fontello.woff2 index 09fafb310e4891a3337176a229ae0fdb5fb56f55..8513d894effdd5926975a2c9987741e1a01fcf3a 100644 GIT binary patch literal 8564 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 literal 8456 zcmV+jA@|;QPew8T0RR9103iqf4*&oF06$m&03fgc0RR9100000000000000000000 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 From a8180d03be3488aeb7cfc811a1b49f6519836ab5 Mon Sep 17 00:00:00 2001 From: Henry Jameson <me@hjkos.com> Date: Mon, 19 Nov 2018 18:15:27 +0300 Subject: [PATCH 17/96] it works, now to clean it up --- src/App.scss | 1 + .../shadow_control/shadow_control.js | 6 +- .../shadow_control/shadow_control.vue | 11 +- .../style_switcher/style_switcher.js | 38 ++++-- .../style_switcher/style_switcher.vue | 116 ++++++++++-------- src/services/style_setter/style_setter.js | 3 +- 6 files changed, 108 insertions(+), 67 deletions(-) diff --git a/src/App.scss b/src/App.scss index 8fb3c488..19c069ed 100644 --- a/src/App.scss +++ b/src/App.scss @@ -313,6 +313,7 @@ main-router { border-radius: $fallback--panelRadius; border-radius: var(--panelRadius, $fallback--panelRadius); box-shadow: 1px 1px 4px rgba(0,0,0,.6); + box-shadow: var(--panel-shadow); } .panel-body:empty::before { diff --git a/src/components/shadow_control/shadow_control.js b/src/components/shadow_control/shadow_control.js index c357581d..a6992999 100644 --- a/src/components/shadow_control/shadow_control.js +++ b/src/components/shadow_control/shadow_control.js @@ -9,6 +9,8 @@ export default { 'value', 'fallback' ], data () { + console.log('sdsa') + console.log(this.value, this.fallback) return { selectedId: 0, cValue: this.value || this.fallback @@ -36,6 +38,9 @@ export default { const movable = this.cValue.splice(this.selectedId, 1)[0] this.cValue.splice(this.selectedId + 1, 0, movable) this.selectedId += 1 + }, + update () { + this.$emit('input', this.cValue) } }, computed: { @@ -67,7 +72,6 @@ export default { return hex2rgb(this.selected.color) }, style () { - console.log(StyleSetter.generateShadow(this.cValue)) return { boxShadow: StyleSetter.generateShadow(this.cValue) } diff --git a/src/components/shadow_control/shadow_control.vue b/src/components/shadow_control/shadow_control.vue index 24439449..614de76a 100644 --- a/src/components/shadow_control/shadow_control.vue +++ b/src/components/shadow_control/shadow_control.vue @@ -5,6 +5,7 @@ <input v-model="selected.y" :disabled="!present" + @input="update" class="input-number" type="number"> <div class="wrap"> @@ -182,15 +183,15 @@ } .preview-window { flex: 1; - background-color: white; + background-color: #999999; display: flex; align-items: center; justify-content: center; background-image: - linear-gradient(45deg, #808080 25%, transparent 25%), - linear-gradient(-45deg, #808080 25%, transparent 25%), - linear-gradient(45deg, transparent 75%, #808080 75%), - linear-gradient(-45deg, transparent 75%, #808080 75%); + linear-gradient(45deg, #666666 25%, transparent 25%), + linear-gradient(-45deg, #666666 25%, transparent 25%), + linear-gradient(45deg, transparent 75%, #666666 75%), + linear-gradient(-45deg, transparent 75%, #666666 75%); background-size: 20px 20px; background-position:0 0, 0 10px, 10px -10px, -10px 0; diff --git a/src/components/style_switcher/style_switcher.js b/src/components/style_switcher/style_switcher.js index 8e344eb1..b40511df 100644 --- a/src/components/style_switcher/style_switcher.js +++ b/src/components/style_switcher/style_switcher.js @@ -1,4 +1,5 @@ import { rgb2hex, hex2rgb, getContrastRatio, alphaBlend } from '../../services/color_convert/color_convert.js' +import { set, delete as del } from 'vue' import ColorInput from '../color_input/color_input.vue' import ShadowControl from '../shadow_control/shadow_control.vue' import ContrastRatio from '../contrast_ratio/contrast_ratio.vue' @@ -155,7 +156,7 @@ export default { } }, previewTheme () { - if (!this.preview.theme) return { colors: {}, opacity: {}, radii: {} } + if (!this.preview.theme) return { colors: {}, opacity: {}, radii: {}, shadows: {} } return this.preview.theme }, previewContrast () { @@ -231,14 +232,30 @@ export default { return [this.preview.colorRules, this.preview.radiiRules, 'color: var(--text)'].join(';') }, shadowsAvailable () { - return Object.keys(this.preview.theme.shadows) + return Object.keys(this.previewTheme.shadows) }, - currentShadow () { - const fallback = this.preview.theme.shadows[this.shadowSelected]; - return fallback ? { - fallback, - value: this.shadowsLocal[this.shadowSelected] - } : undefined + currentShadowOverriden: { + get () { + return this.currentShadow + }, + set (val) { + if (val) { + set(this.shadowsLocal, this.shadowSelected, Object.assign({}, this.currentShadowFallback)) + } else { + del(this.shadowsLocal, this.shadowSelected) + } + } + }, + currentShadowFallback () { + return this.previewTheme.shadows[this.shadowSelected] + }, + currentShadow: { + get () { + return this.shadowsLocal[this.shadowSelected] + }, + set (v) { + set(this.shadowsLocal, this.shadowSelected, v) + } } }, components: { @@ -305,7 +322,10 @@ export default { setCustomTheme () { this.$store.dispatch('setOption', { name: 'customTheme', - value: this.currentTheme + value: { + ...this.currentTheme, + shadows: this.shadowsLocal + } }) }, diff --git a/src/components/style_switcher/style_switcher.vue b/src/components/style_switcher/style_switcher.vue index 0352f546..af816a23 100644 --- a/src/components/style_switcher/style_switcher.vue +++ b/src/components/style_switcher/style_switcher.vue @@ -1,49 +1,49 @@ <template> -<div> - <div class="presets-container"> - <div> - {{$t('settings.presets')}} - <label for="style-switcher" class='select'> - <select id="style-switcher" v-model="selected" class="style-switcher"> - <option v-for="style in availableStyles" - :value="style" - :style="{ - backgroundColor: style[1], - color: style[3] - }"> - {{style[0]}} - </option> - </select> - <i class="icon-down-open"/> - </label> - </div> - <div class="import-export"> - <button class="btn" @click="exportCurrentTheme">{{ $t('settings.export_theme') }}</button> - <button class="btn" @click="importTheme">{{ $t('settings.import_theme') }}</button> - <p v-if="invalidThemeImported" class="import-warning">{{ $t('settings.invalid_theme_imported') }}</p> - </div> - </div> - - <div class="preview-container"> - <div class="panel dummy" :style="previewRules"> - <div class="panel-heading">Preview</div> - <div class="panel-body theme-preview-content"> - <div class="avatar"> - ( ͡° ͜ʖ ͡°) - </div> - <h4>Content</h4> - <br> - A bunch of more content and - <a style="color: var(--link)">a nice lil' link</a> - <i style="color: var(--cBlue)" class="icon-reply"/> - <i style="color: var(--cGreen)" class="icon-retweet"/> - <i style="color: var(--cRed)" class="icon-cancel"/> - <i style="color: var(--cOrange)" class="icon-star"/> - <br> - <button class="btn">Button</button> + <div> + <div class="presets-container"> + <div> + {{$t('settings.presets')}} + <label for="style-switcher" class='select'> + <select id="style-switcher" v-model="selected" class="style-switcher"> + <option v-for="style in availableStyles" + :value="style" + :style="{ + backgroundColor: style[1], + color: style[3] + }"> + {{style[0]}} + </option> + </select> + <i class="icon-down-open"/> + </label> + </div> + <div class="import-export"> + <button class="btn" @click="exportCurrentTheme">{{ $t('settings.export_theme') }}</button> + <button class="btn" @click="importTheme">{{ $t('settings.import_theme') }}</button> + <p v-if="invalidThemeImported" class="import-warning">{{ $t('settings.invalid_theme_imported') }}</p> + </div> + </div> + + <div class="preview-container"> + <div class="panel dummy" :style="previewRules"> + <div class="panel-heading">Preview</div> + <div class="panel-body theme-preview-content"> + <div class="avatar"> + ( ͡° ͜ʖ ͡°) + </div> + <h4>Content</h4> + <br> + A bunch of more content and + <a style="color: var(--link)">a nice lil' link</a> + <i style="color: var(--cBlue)" class="icon-reply"/> + <i style="color: var(--cGreen)" class="icon-retweet"/> + <i style="color: var(--cRed)" class="icon-cancel"/> + <i style="color: var(--cOrange)" class="icon-star"/> + <br> + <button class="btn">Button</button> + </div> </div> </div> - </div> <p>{{$t('settings.theme_help')}}</p> <tab-switcher> @@ -171,15 +171,29 @@ </div> </div> <div label="Shadow Realm" class="shadow-container"> - <div class="shadow-selector"> - <select id="style-switcher" v-model="shadowSelected" class="style-switcher"> - <option v-for="shadow in shadowsAvailable" - :value="shadow"> - {{shadow}} - </option> - </select> + <div> + Shadow + <label for="shadow-switcher" class="shadow-selector select"> + <select id="shadow-switcher" v-model="shadowSelected" class="shadow-switcher"> + <option v-for="shadow in shadowsAvailable" + :value="shadow"> + {{shadow}} + </option> + </select> + <i class="icon-down-open"/> + </label> + <label for="override" class="label"> + Override + </label> + <input + v-model="currentShadowOverriden" + name="override" + id="override" + class="input-override" + type="checkbox"> + <label class="checkbox-label" for="override"></label> </div> - <shadow-control v-if="currentShadow" :value="currentShadow.value" :fallback="currentShadow.fallback"/> + <shadow-control v-if="currentShadowFallback" :fallback="currentShadowFallback" v-model="currentShadow"/> </div> </tab-switcher> diff --git a/src/services/style_setter/style_setter.js b/src/services/style_setter/style_setter.js index 3840e215..aac04055 100644 --- a/src/services/style_setter/style_setter.js +++ b/src/services/style_setter/style_setter.js @@ -72,7 +72,7 @@ const getTextColor = function (bg, text, preserve) { } const setColors = (input, commit) => { - const { colorRules, radiiRules, theme } = generatePreset(input) + const { colorRules, radiiRules, shadowRules, theme } = generatePreset(input) const head = document.head const body = document.body body.style.display = 'none' @@ -84,6 +84,7 @@ const setColors = (input, commit) => { styleSheet.toString() styleSheet.insertRule(`body { ${colorRules} }`, 'index-max') styleSheet.insertRule(`body { ${radiiRules} }`, 'index-max') + styleSheet.insertRule(`body { ${shadowRules} }`, 'index-max') body.style.display = 'initial' // commit('setOption', { name: 'colors', value: htmlColors }) From 56fec664a9bb5c1539423e396c127c4a45e8f4e9 Mon Sep 17 00:00:00 2001 From: Henry Jameson <me@hjkos.com> Date: Mon, 19 Nov 2018 20:22:46 +0300 Subject: [PATCH 18/96] cleanup and optimization --- src/App.scss | 2 +- .../shadow_control/shadow_control.js | 10 +- .../shadow_control/shadow_control.vue | 1 - .../style_switcher/style_switcher.js | 204 +++++++++--------- src/modules/config.js | 6 +- src/modules/instance.js | 4 +- src/services/style_setter/style_setter.js | 144 +++++++++---- 7 files changed, 216 insertions(+), 155 deletions(-) diff --git a/src/App.scss b/src/App.scss index 19c069ed..1021b64b 100644 --- a/src/App.scss +++ b/src/App.scss @@ -313,7 +313,7 @@ main-router { border-radius: $fallback--panelRadius; border-radius: var(--panelRadius, $fallback--panelRadius); box-shadow: 1px 1px 4px rgba(0,0,0,.6); - box-shadow: var(--panel-shadow); + box-shadow: var(--panelShadow); } .panel-body:empty::before { diff --git a/src/components/shadow_control/shadow_control.js b/src/components/shadow_control/shadow_control.js index a6992999..54813685 100644 --- a/src/components/shadow_control/shadow_control.js +++ b/src/components/shadow_control/shadow_control.js @@ -1,16 +1,13 @@ import ColorInput from '../color_input/color_input.vue' import OpacityInput from '../opacity_input/opacity_input.vue' -import StyleSetter from '../../services/style_setter/style_setter.js' +import { getCssShadow } from '../../services/style_setter/style_setter.js' import { hex2rgb } from '../../services/color_convert/color_convert.js' -import { set } from 'vue' export default { props: [ 'value', 'fallback' ], data () { - console.log('sdsa') - console.log(this.value, this.fallback) return { selectedId: 0, cValue: this.value || this.fallback @@ -38,9 +35,6 @@ export default { const movable = this.cValue.splice(this.selectedId, 1)[0] this.cValue.splice(this.selectedId + 1, 0, movable) this.selectedId += 1 - }, - update () { - this.$emit('input', this.cValue) } }, computed: { @@ -73,7 +67,7 @@ export default { }, style () { return { - boxShadow: StyleSetter.generateShadow(this.cValue) + boxShadow: getCssShadow(this.cValue) } } } diff --git a/src/components/shadow_control/shadow_control.vue b/src/components/shadow_control/shadow_control.vue index 614de76a..6847278c 100644 --- a/src/components/shadow_control/shadow_control.vue +++ b/src/components/shadow_control/shadow_control.vue @@ -5,7 +5,6 @@ <input v-model="selected.y" :disabled="!present" - @input="update" class="input-number" type="number"> <div class="wrap"> diff --git a/src/components/style_switcher/style_switcher.js b/src/components/style_switcher/style_switcher.js index b40511df..e523cd7b 100644 --- a/src/components/style_switcher/style_switcher.js +++ b/src/components/style_switcher/style_switcher.js @@ -1,12 +1,23 @@ import { rgb2hex, hex2rgb, getContrastRatio, alphaBlend } from '../../services/color_convert/color_convert.js' import { set, delete as del } from 'vue' +import { generateColors, generateShadows, generateRadii, composePreset } from '../../services/style_setter/style_setter.js' import ColorInput from '../color_input/color_input.vue' import ShadowControl from '../shadow_control/shadow_control.vue' import ContrastRatio from '../contrast_ratio/contrast_ratio.vue' import OpacityInput from '../opacity_input/opacity_input.vue' -import StyleSetter from '../../services/style_setter/style_setter.js' import TabSwitcher from '../tab_switcher/tab_switcher.jsx' +const v1OnlyNames = [ + 'bg', + 'fg', + 'text', + 'link', + 'cRed', + 'cGreen', + 'cBlue', + 'cOrange' +].map(_ => _ + 'ColorLocal') + export default { data () { return { @@ -53,14 +64,14 @@ export default { faintOpacityLocal: undefined, faintLinkColorLocal: undefined, - shadowSelected: undefined, - shadowsLocal: {}, - cRedColorLocal: '', cBlueColorLocal: '', cGreenColorLocal: '', cOrangeColorLocal: '', + shadowSelected: undefined, + shadowsLocal: {}, + btnRadiusLocal: '', inputRadiusLocal: '', panelRadiusLocal: '', @@ -86,81 +97,90 @@ export default { selectedVersion () { return Array.isArray(this.selected) ? 1 : 2 }, - currentTheme () { + currentColors () { return { - colors: { - bg: this.bgColorLocal, - text: this.textColorLocal, - link: this.linkColorLocal, + bg: this.bgColorLocal, + text: this.textColorLocal, + link: this.linkColorLocal, - fg: this.fgColorLocal, - fgText: this.fgTextColorLocal, - fgLink: this.fgLinkColorLocal, + fg: this.fgColorLocal, + fgText: this.fgTextColorLocal, + fgLink: this.fgLinkColorLocal, - panel: this.panelColorLocal, - panelText: this.panelTextColorLocal, - panelFaint: this.panelFaintColorLocal, + panel: this.panelColorLocal, + panelText: this.panelTextColorLocal, + panelFaint: this.panelFaintColorLocal, - input: this.inputColorLocal, - inputText: this.inputTextColorLocal, + input: this.inputColorLocal, + inputText: this.inputTextColorLocal, - topBar: this.topBarColorLocal, - topBarText: this.topBarTextColorLocal, - topBarLink: this.topBarLinkColorLocal, + topBar: this.topBarColorLocal, + topBarText: this.topBarTextColorLocal, + topBarLink: this.topBarLinkColorLocal, - btn: this.btnColorLocal, - btnText: this.btnTextColorLocal, + btn: this.btnColorLocal, + btnText: this.btnTextColorLocal, - alertError: this.alertErrorColorLocal, - badgeNotification: this.badgeNotificationColorLocal, + alertError: this.alertErrorColorLocal, + badgeNotification: this.badgeNotificationColorLocal, - faint: this.faintColorLocal, - faintLink: this.faintLinkColorLocal, - border: this.borderColorLocal, + faint: this.faintColorLocal, + faintLink: this.faintLinkColorLocal, + border: this.borderColorLocal, - cRed: this.cRedColorLocal, - cBlue: this.cBlueColorLocal, - cGreen: this.cGreenColorLocal, - cOrange: this.cOrangeColorLocal - }, - opacity: { - bg: this.bgOpacityLocal, - btn: this.btnOpacityLocal, - input: this.inputOpacityLocal, - panel: this.panelOpacityLocal, - topBar: this.topBarOpacityLocal, - border: this.borderOpacityLocal, - faint: this.faintOpacityLocal - }, - radii: { - btnRadius: this.btnRadiusLocal, - inputRadius: this.inputRadiusLocal, - panelRadius: this.panelRadiusLocal, - avatarRadius: this.avatarRadiusLocal, - avatarAltRadius: this.avatarAltRadiusLocal, - tooltipRadius: this.tooltipRadiusLocal, - attachmentRadius: this.attachmentRadiusLocal - } + cRed: this.cRedColorLocal, + cBlue: this.cBlueColorLocal, + cGreen: this.cGreenColorLocal, + cOrange: this.cOrangeColorLocal } }, - preview () { - try { - if (!this.currentTheme.colors.bg) { - return {} - } - return StyleSetter.generatePreset(this.currentTheme) - } catch (e) { - console.error('CATCH') - console.error(e) + currentOpacity () { + return { + bg: this.bgOpacityLocal, + btn: this.btnOpacityLocal, + input: this.inputOpacityLocal, + panel: this.panelOpacityLocal, + topBar: this.topBarOpacityLocal, + border: this.borderOpacityLocal, + faint: this.faintOpacityLocal + } + }, + currentRadii () { + return { + btn: this.btnRadiusLocal, + input: this.inputRadiusLocal, + panel: this.panelRadiusLocal, + avatar: this.avatarRadiusLocal, + avatarAlt: this.avatarAltRadiusLocal, + tooltip: this.tooltipRadiusLocal, + attachment: this.attachmentRadiusLocal + } + }, + previewColors () { + if (this.currentColors.bg) { + return generateColors({ + opacity: this.currentOpacity, + colors: this.currentColors + }) + } else { return {} } }, + previewRadii () { + return generateRadii(this.currentRadii) + }, + previewShadows () { + return generateShadows({ shadows: this.shadowsLocal }) + }, + preview () { + return composePreset(this.previewColors, this.previewRadii, this.previewShadows) + }, previewTheme () { - if (!this.preview.theme) return { colors: {}, opacity: {}, radii: {}, shadows: {} } + if (!this.preview.theme.colors) return { colors: {}, opacity: {}, radii: {}, shadows: {} } return this.preview.theme }, previewContrast () { - if (!this.previewTheme.colors) return {} + if (!this.previewTheme.colors.bg) return {} const colors = this.previewTheme.colors const opacity = this.previewTheme.opacity if (!colors.bg) return {} @@ -228,19 +248,19 @@ export default { return Object.entries(ratios).reduce((acc, [k, v]) => { acc[k] = hints(v); return acc }, {}) }, previewRules () { - if (!this.preview.colorRules) return '' - return [this.preview.colorRules, this.preview.radiiRules, 'color: var(--text)'].join(';') + if (!this.preview.rules) return '' + return [...Object.values(this.preview.rules), 'color: var(--text)'].join(';') }, shadowsAvailable () { return Object.keys(this.previewTheme.shadows) }, currentShadowOverriden: { get () { - return this.currentShadow + return !!this.currentShadow }, set (val) { if (val) { - set(this.shadowsLocal, this.shadowSelected, Object.assign({}, this.currentShadowFallback)) + set(this.shadowsLocal, this.shadowSelected, this.currentShadowFallback.map(_ => Object.assign({}, _))) } else { del(this.shadowsLocal, this.shadowSelected) } @@ -270,7 +290,12 @@ export default { const stringified = JSON.stringify({ // To separate from other random JSON files and possible future theme formats _pleroma_theme_version: 2, - theme: this.currentTheme + theme: { + shadows: this.shadowsLocal, + opacity: this.currentOpacity, + colors: this.currentColors, + radii: this.currentRadii + } }, null, 2) // Pretty-print and indent with 2 spaces // Create an invisible link with a data url and simulate a click @@ -323,47 +348,22 @@ export default { this.$store.dispatch('setOption', { name: 'customTheme', value: { - ...this.currentTheme, - shadows: this.shadowsLocal + shadows: this.shadowsLocal, + opacity: this.currentOpacity, + colors: this.currentColors, + radii: this.currentRadii } }) }, + // Clears all the extra stuff when loading V1 theme clearV1 () { - this.bgOpacityLocal = undefined - this.fgOpacityLocal = undefined - - this.fgTextColorLocal = undefined - this.fgLinkColorLocal = undefined - - this.btnColorLocal = undefined - this.btnTextColorLocal = undefined - this.btnOpacityLocal = undefined - - this.inputColorLocal = undefined - this.inputTextColorLocal = undefined - this.inputOpacityLocal = undefined - - this.panelColorLocal = undefined - this.panelTextColorLocal = undefined - this.panelFaintColorLocal = undefined - this.panelOpacityLocal = undefined - - this.topBarColorLocal = undefined - this.topBarTextColorLocal = undefined - this.topBarLinkColorLocal = undefined - this.topBarOpacityLocal = undefined - - this.borderColorLocal = undefined - this.borderOpacityLocal = undefined - - this.faintColorLocal = undefined - this.faintOpacityLocal = undefined - this.faintLinkColorLocal = undefined - - this.alertErrorColorLocal = undefined - - this.badgeNotificationColorLocal = undefined + Object.keys(this.$data) + .filter(_ => _.endsWith('ColorLocal') || _.endsWith('OpacityLocal')) + .filter(_ => !v1OnlyNames.includes(_)) + .forEach(key => { + set(this.$data, key, undefined) + }) }, /** diff --git a/src/modules/config.js b/src/modules/config.js index 375d0167..96f2fd5e 100644 --- a/src/modules/config.js +++ b/src/modules/config.js @@ -1,5 +1,5 @@ import { set, delete as del } from 'vue' -import StyleSetter from '../services/style_setter/style_setter.js' +import { setPreset, setColors } from '../services/style_setter/style_setter.js' const browserLocale = (window.navigator.language || 'en').split('-')[0] @@ -51,10 +51,10 @@ const config = { commit('setOption', {name, value}) switch (name) { case 'theme': - StyleSetter.setPreset(value, commit) + setPreset(value, commit) break case 'customTheme': - StyleSetter.setColors(value, commit) + setColors(value, commit) } } } diff --git a/src/modules/instance.js b/src/modules/instance.js index cb724821..611212c3 100644 --- a/src/modules/instance.js +++ b/src/modules/instance.js @@ -1,5 +1,5 @@ import { set } from 'vue' -import StyleSetter from '../services/style_setter/style_setter.js' +import { setPreset } from '../services/style_setter/style_setter.js' const defaultState = { // Stuff from static/config.json and apiConfig @@ -54,7 +54,7 @@ const instance = { dispatch('setPageTitle') break case 'theme': - StyleSetter.setPreset(value, commit) + setPreset(value, commit) } } } diff --git a/src/services/style_setter/style_setter.js b/src/services/style_setter/style_setter.js index aac04055..cfff51ea 100644 --- a/src/services/style_setter/style_setter.js +++ b/src/services/style_setter/style_setter.js @@ -72,7 +72,7 @@ const getTextColor = function (bg, text, preserve) { } const setColors = (input, commit) => { - const { colorRules, radiiRules, shadowRules, theme } = generatePreset(input) + const { rules, theme } = generatePreset(input) const head = document.head const body = document.body body.style.display = 'none' @@ -81,10 +81,11 @@ const setColors = (input, commit) => { head.appendChild(styleEl) const styleSheet = styleEl.sheet + console.log(rules) styleSheet.toString() - styleSheet.insertRule(`body { ${colorRules} }`, 'index-max') - styleSheet.insertRule(`body { ${radiiRules} }`, 'index-max') - styleSheet.insertRule(`body { ${shadowRules} }`, 'index-max') + styleSheet.insertRule(`body { ${rules.radii} }`, 'index-max') + styleSheet.insertRule(`body { ${rules.colors} }`, 'index-max') + styleSheet.insertRule(`body { ${rules.shadows} }`, 'index-max') body.style.display = 'initial' // commit('setOption', { name: 'colors', value: htmlColors }) @@ -93,7 +94,8 @@ const setColors = (input, commit) => { commit('setOption', { name: 'colors', value: theme.colors }) } -const generateShadow = (input) => { +const getCssShadow = (input) => { + console.log(input) // >shad return input.map((shad) => [ shad.x, @@ -106,27 +108,8 @@ const generateShadow = (input) => { ]).join(' ')).join(', ') } -const generatePreset = (input) => { - const radii = input.radii || { - btnRadius: input.btnRadius, - inputRadius: input.inputRadius, - panelRadius: input.panelRadius, - avatarRadius: input.avatarRadius, - avatarAltRadius: input.avatarAltRadius, - tooltipRadius: input.tooltipRadius, - attachmentRadius: input.attachmentRadius - } - const shadows = { - panel: [{ - x: 1, - y: 1, - blur: 4, - spread: 0, - color: '#000000', - alpha: 0.6 - }], - ...(input.shadows || {}) - } +const generateColors = (input) => { + console.log(input.opacity) const colors = {} const opacity = Object.assign({ alert: 0.5, @@ -138,7 +121,7 @@ const generatePreset = (input) => { } return acc }, {})) - + console.log(colors, opacity) const col = Object.entries(input.colors || input).reduce((acc, [k, v]) => { if (typeof v === 'object') { acc[k] = v @@ -205,7 +188,11 @@ const generatePreset = (input) => { colors[k + 'Link'].a = v colors['panelFaint'].a = v } - colors[k].a = v + if (colors[k]) { + colors[k].a = v + } else { + console.error('Wrong key ' + k) + } }) const htmlColors = Object.entries(colors) @@ -215,20 +202,99 @@ const generatePreset = (input) => { acc.complete[k] = typeof v.a === 'undefined' ? rgb2hex(v) : rgb2rgba(v) return acc }, { complete: {}, solid: {} }) - return { - colorRules: Object.entries(htmlColors.complete).filter(([k, v]) => v).map(([k, v]) => `--${k}: ${v}`).join(';'), - radiiRules: Object.entries(radii).filter(([k, v]) => v).map(([k, v]) => `--${k}: ${v}px`).join(';'), - shadowRules: Object.entries(shadows).filter(([k, v]) => v).map(([k, v]) => `--${k}-shadow: ${generateShadow(v)}`).join(';'), + rules: { + colors: Object.entries(htmlColors.complete) + .filter(([k, v]) => v) + .map(([k, v]) => `--${k}: ${v}`) + .join(';') + }, theme: { colors: htmlColors.solid, - shadows, - opacity, + opacity + } + } +} + +const generateRadii = (input) => { + const inputRadii = input.radii || { + btn: input.btnRadius, + input: input.inputRadius, + panel: input.panelRadius, + avatar: input.avatarRadius, + avatarAlt: input.avatarAltRadius, + tooltip: input.tooltipRadius, + attachment: input.attachmentRadius + } + + const radii = { + btn: 4, + input: 4, + panel: 10, + avatar: 5, + avatarAlt: 50, + tooltip: 2, + attachment: 5, + ...inputRadii + } + + return { + rules: { + radii: Object.entries(radii).filter(([k, v]) => v).map(([k, v]) => `--${k}Radius: ${v}px`).join(';') + }, + theme: { radii } } } +const generateShadows = (input) => { + const shadows = { + panel: [{ + x: 1, + y: 1, + blur: 4, + spread: 0, + color: '#000000', + alpha: 0.6 + }], + ...(input.shadows || {}) + } + console.log('benis') + + return { + rules: { + shadows: Object.entries(shadows).filter(([k, v]) => v).map(([k, v]) => `--${k}Shadow: ${getCssShadow(v)}`).join(';') + }, + theme: { + shadows + } + } +} + +const composePreset = (colors, radii, shadows) => { + return { + rules: { + ...shadows.rules, + ...colors.rules, + ...radii.rules + }, + theme: { + ...shadows.theme, + ...colors.theme, + ...radii.theme + } + } +} + +const generatePreset = (input) => { + const shadows = generateShadows(input) + const colors = generateColors(input) + const radii = generateRadii(input) + + return composePreset(colors, radii, shadows) +} + const setPreset = (val, commit) => { window.fetch('/static/styles.json') .then((data) => data.json()) @@ -267,13 +333,15 @@ const setPreset = (val, commit) => { }) } -const StyleSetter = { +export { setStyle, setPreset, setColors, getTextColor, + generateColors, + generateRadii, + generateShadows, generatePreset, - generateShadow + composePreset, + getCssShadow } - -export default StyleSetter From cb8218c3c1b567d6e2eac570a5a12cf37951239f Mon Sep 17 00:00:00 2001 From: Henry Jameson <me@hjkos.com> Date: Mon, 19 Nov 2018 21:01:46 +0300 Subject: [PATCH 19/96] consolelog --- src/services/style_setter/style_setter.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/services/style_setter/style_setter.js b/src/services/style_setter/style_setter.js index cfff51ea..e7d8252c 100644 --- a/src/services/style_setter/style_setter.js +++ b/src/services/style_setter/style_setter.js @@ -95,7 +95,6 @@ const setColors = (input, commit) => { } const getCssShadow = (input) => { - console.log(input) // >shad return input.map((shad) => [ shad.x, From 32132e225c749e506285370a2a065bb71920ce59 Mon Sep 17 00:00:00 2001 From: Henry Jameson <me@hjkos.com> Date: Tue, 20 Nov 2018 20:58:20 +0300 Subject: [PATCH 20/96] localization and small fixes --- src/components/color_input/color_input.vue | 17 ++++--- .../opacity_input/opacity_input.vue | 17 ++++--- .../shadow_control/shadow_control.vue | 27 ++++++----- .../style_switcher/style_switcher.js | 1 + .../style_switcher/style_switcher.vue | 44 ++++++++--------- src/i18n/en.json | 47 ++++++++++++++++++- 6 files changed, 99 insertions(+), 54 deletions(-) diff --git a/src/components/color_input/color_input.vue b/src/components/color_input/color_input.vue index ea9fb3c4..b756d265 100644 --- a/src/components/color_input/color_input.vue +++ b/src/components/color_input/color_input.vue @@ -1,17 +1,16 @@ <template> -<div class="color-control" :class="{ disabled: !present || disabled }"> +<div class="color-control style-control" :class="{ disabled: !present || disabled }"> <label :for="name" class="label"> {{label}} </label> <input - v-if="typeof fallback !== 'undefined'" - class="opt" - :id="name + '-o'" - type="checkbox" - :checked="present" - @input="$emit('input', typeof value === 'undefined' ? fallback : undefined)" - > - <label v-if="typeof fallback !== 'undefined'" class="opt-l" :for="name + '-o'"></label> + v-if="typeof fallback !== 'undefined'" + class="opt" + :id="name + '-o'" + type="checkbox" + :checked="present" + @input="$emit('input', typeof value === 'undefined' ? fallback : undefined)"> + <label v-if="typeof fallback !== 'undefined'" class="opt-l" :for="name + '-o'"></label> <input :id="name" class="color-input" diff --git a/src/components/opacity_input/opacity_input.vue b/src/components/opacity_input/opacity_input.vue index 91c4f5e9..e0567ec7 100644 --- a/src/components/opacity_input/opacity_input.vue +++ b/src/components/opacity_input/opacity_input.vue @@ -1,16 +1,15 @@ <template> -<div class="opacity-control" :class="{ disabled: !present || disabled }"> +<div class="opacity-control style-control" :class="{ disabled: !present || disabled }"> <label :for="name" class="label"> - {{$t('settings.opacity')}} + {{$t('settings.style.common.opacity')}} </label> <input - v-if="typeof fallback !== 'undefined'" - class="opt" - :id="name + '-o'" - type="checkbox" - :checked="present" - @input="$emit('input', !present ? fallback : undefined)" - > + v-if="typeof fallback !== 'undefined'" + class="opt" + :id="name + '-o'" + type="checkbox" + :checked="present" + @input="$emit('input', !present ? fallback : undefined)"> <label v-if="typeof fallback !== 'undefined'" class="opt-l" :for="name + '-o'"></label> <input :id="name" diff --git a/src/components/shadow_control/shadow_control.vue b/src/components/shadow_control/shadow_control.vue index 6847278c..b99df35a 100644 --- a/src/components/shadow_control/shadow_control.vue +++ b/src/components/shadow_control/shadow_control.vue @@ -40,16 +40,17 @@ <div class="shadow-tweak"> <div :disabled="usingFallback" class="id-control"> - <label for="id" class="label"> - Shadow id + <label for="shadow-switcher" class="select"> + <select + v-model="selectedId" class="shadow-switcher" + :disabled="usingFallback" + id="shadow-switcher"> + <option v-for="(shadow, index) in cValue" :value="index"> + {{$t('settings.style.shadows.shadow_id', { value: index })}} + </option> + </select> + <i class="icon-down-open"/> </label> - <input - v-model="selectedId" - :disabled="usingFallback" - class="input-number" - type="number" - min="0" - :max="cValue.length - 1"> <button class="btn btn-default" :disabled="!present" @click="del"> <i class="icon-cancel"/> </button> @@ -65,7 +66,7 @@ </div> <div :disabled="!present" class="inset-control"> <label for="inset" class="label"> - Inset + {{$t('settings.style.shadows.inset')}} </label> <input v-model="selected.inset" @@ -78,7 +79,7 @@ </div> <div :disabled="!present" class="blur-control"> <label for="spread" class="label"> - Blur + {{$t('settings.style.shadows.blur')}} </label> <input v-model="selected.blur" @@ -98,7 +99,7 @@ </div> <div :disabled="!present" class="spread-control"> <label for="spread" class="label"> - Spread + {{$t('settings.style.shadows.spread')}} </label> <input v-model="selected.spread" @@ -118,7 +119,7 @@ <ColorInput v-model="selected.color" :disabled="!present" - label="Color" + :label="$t('settings.style.common.color')" name="shadow"/> <OpacityInput v-model="selected.alpha" diff --git a/src/components/style_switcher/style_switcher.js b/src/components/style_switcher/style_switcher.js index e523cd7b..7cb6197c 100644 --- a/src/components/style_switcher/style_switcher.js +++ b/src/components/style_switcher/style_switcher.js @@ -7,6 +7,7 @@ import ContrastRatio from '../contrast_ratio/contrast_ratio.vue' import OpacityInput from '../opacity_input/opacity_input.vue' import TabSwitcher from '../tab_switcher/tab_switcher.jsx' +// List of color values used in v1 const v1OnlyNames = [ 'bg', 'fg', diff --git a/src/components/style_switcher/style_switcher.vue b/src/components/style_switcher/style_switcher.vue index af816a23..fd5d830a 100644 --- a/src/components/style_switcher/style_switcher.vue +++ b/src/components/style_switcher/style_switcher.vue @@ -47,9 +47,9 @@ <p>{{$t('settings.theme_help')}}</p> <tab-switcher> - <div label="Basic" class="color-container"> + <div :label="$t('settings.style.basic_colors._tab_label')" class="color-container"> <div class="color-item"> - <h4>Main colors</h4> + <h4>{{ $t('settings.style.basic_colors.main') }}</h4> <ColorInput name="bgColor" v-model="bgColorLocal" :label="$t('settings.background')"/> <OpacityInput name="bgOpacity" v-model="bgOpacityLocal" :fallback="previewTheme.opacity.bg || 1"/> <ColorInput name="textColor" v-model="textColorLocal" :label="$t('settings.text')"/> @@ -58,14 +58,14 @@ <ContrastRatio :contrast="previewContrast.bgLink"/> </div> <div class="color-item"> - <h4>Panel header, top bar, buttons, text fields</h4> + <h4>{{ $t('settings.style.basic_colors.foreground') }}</h4> <ColorInput name="fgColor" v-model="fgColorLocal" :label="$t('settings.foreground')"/> <ColorInput name="fgTextColor" v-model="fgTextColorLocal" :label="$t('settings.text')" :fallback="previewTheme.colors.fgText"/> <ColorInput name="fgLinkColor" v-model="fgLinkColorLocal" :label="$t('settings.links')" :fallback="previewTheme.colors.fgLink"/> - <p>See "Advanced" tab for more detailed control</p> + <p>{{ $t('settings.style.basic_colors.foreground_hint') }}</p> </div> <div class="color-item"> - <h4>Icons, alerts, etc.</h4> + <h4>{{ $t('settings.style.basic_colors.rgbo') }}</h4> <ColorInput name="cRedColor" v-model="cRedColorLocal" :label="$t('settings.cRed')"/> <ContrastRatio :contrast="previewContrast.bgRed"/> <ColorInput name="cBlueColor" v-model="cBlueColorLocal" :label="$t('settings.cBlue')"/> @@ -80,25 +80,25 @@ </div> </div> - <div label="Advanced" class="color-container"> + <div :label="$t('settings.style.advanced_colors._tab_label')" class="color-container"> <div class="color-item"> - <h4>Alerts</h4> - <ColorInput name="alertError" v-model="alertErrorColorLocal" :label="$t('settings.error')" :fallback="previewTheme.colors.alertError"/> + <h4>{{ $t('settings.style.advanced_colors.alert') }}</h4> + <ColorInput name="alertError" v-model="alertErrorColorLocal" :label="$t('settings.style.advanced_colors.alert_error')" :fallback="previewTheme.colors.alertError"/> <ContrastRatio :contrast="previewContrast.alertError"/> </div> <div class="color-item"> - <h4>Badges</h4> - <ColorInput name="badgeNotification" v-model="badgeNotificationColorLocal" :label="$t('settings.notification')" :fallback="previewTheme.colors.badgeNotification"/> + <h4>{{ $t('settings.style.advanced_colors.badge') }}</h4> + <ColorInput name="badgeNotification" v-model="badgeNotificationColorLocal" :label="$t('settings.style.advanced_colors.badge_notification')" :fallback="previewTheme.colors.badgeNotification"/> </div> <div class="color-item"> - <h4>Panel header</h4> + <h4>{{ $t('settings.style.advanced_colors.panel_header') }}</h4> <ColorInput name="panelColor" v-model="panelColorLocal" :fallback="fgColorLocal" :label="$t('settings.background')"/> <OpacityInput name="panelOpacity" v-model="panelOpacityLocal" :fallback="previewTheme.opacity.panel || 1"/> <ColorInput name="panelTextColor" v-model="panelTextColorLocal" :fallback="previewTheme.colors.panelText" :label="$t('settings.links')"/> <ContrastRatio :contrast="previewContrast.panelText" large="1"/> </div> <div class="color-item"> - <h4>Top bar</h4> + <h4>{{ $t('settings.style.advanced_colors.top_bar') }}</h4> <ColorInput name="topBarColor" v-model="topBarColorLocal" :fallback="fgColorLocal" :label="$t('settings.background')"/> <ColorInput name="topBarTextColor" v-model="topBarTextColorLocal" :fallback="previewTheme.colors.topBarText" :label="$t('settings.text')"/> <ContrastRatio :contrast="previewContrast.topBarText"/> @@ -106,33 +106,33 @@ <ContrastRatio :contrast="previewContrast.topBarLink"/> </div> <div class="color-item"> - <h4>Text fields</h4> + <h4>{{ $t('settings.style.advanced_colors.inputs') }}</h4> <ColorInput name="inputColor" v-model="inputColorLocal" :fallback="fgColorLocal" :label="$t('settings.background')"/> <OpacityInput name="inputOpacity" v-model="inputOpacityLocal" :fallback="previewTheme.opacity.input || 1"/> <ColorInput name="inputTextColor" v-model="inputTextColorLocal" :fallback="previewTheme.colors.inputText" :label="$t('settings.text')"/> <ContrastRatio :contrast="previewContrast.inputText"/> </div> <div class="color-item"> - <h4>Buttons</h4> + <h4>{{ $t('settings.style.advanced_colors.buttons') }}</h4> <ColorInput name="btnColor" v-model="btnColorLocal" :fallback="fgColorLocal" :label="$t('settings.background')"/> <OpacityInput name="btnOpacity" v-model="btnOpacityLocal" :fallback="previewTheme.opacity.btn || 1"/> <ColorInput name="btnTextColor" v-model="btnTextColorLocal" :fallback="previewTheme.colors.btnText" :label="$t('settings.text')"/> <ContrastRatio :contrast="previewContrast.btnText"/> </div> <div class="color-item"> - <h4>Borders</h4> + <h4>{{ $t('settings.style.advanced_colors.borders') }}</h4> <ColorInput name="borderColor" v-model="borderColorLocal" :fallback="previewTheme.colors.border" label="Color"/> <OpacityInput name="borderOpacity" v-model="borderOpacityLocal" :fallback="previewTheme.opacity.border || 1"/> </div> <div class="color-item"> - <h4>Faint text</h4> + <h4>{{ $t('settings.style.advanced_colors.faint_text') }}</h4> <ColorInput name="faintColor" v-model="faintColorLocal" :fallback="previewTheme.colors.faint || 1" :label="$t('settings.text')"/> <ColorInput name="faintLinkColor" v-model="faintLinkColorLocal" :fallback="previewTheme.colors.faintLink" :label="$t('settings.links')"/> - <ColorInput name="panelFaintColor" v-model="panelFaintColorLocal" :fallback="previewTheme.colors.panelFaint" :label="$t('settings.panel')"/> + <ColorInput name="panelFaintColor" v-model="panelFaintColorLocal" :fallback="previewTheme.colors.panelFaint" :label="$t('settings.style.advanced_colors.panel_header')"/> <OpacityInput name="faintOpacity" v-model="faintOpacityLocal" :fallback="previewTheme.opacity.faint || 0.5"/> </div> </div> - <div label="Roundness" class="radius-container"> + <div :label="$t('settings.style.radii._tab_label')" class="radius-container"> <p>{{$t('settings.radii_help')}}</p> <div class="radius-item"> <label for="btnradius" class="theme-radius-lb">{{$t('settings.btnRadius')}}</label> @@ -170,20 +170,20 @@ <input id="tooltipradius-t" class="theme-radius-in" type="text" v-model="tooltipRadiusLocal"> </div> </div> - <div label="Shadow Realm" class="shadow-container"> + <div :label="$t('settings.style.shadows._tab_label')" class="shadow-container"> <div> - Shadow + {{$t('settings.style.shadows.component')}} <label for="shadow-switcher" class="shadow-selector select"> <select id="shadow-switcher" v-model="shadowSelected" class="shadow-switcher"> <option v-for="shadow in shadowsAvailable" :value="shadow"> - {{shadow}} + {{$t('settings.style.shadows.components.' + shadow)}} </option> </select> <i class="icon-down-open"/> </label> <label for="override" class="label"> - Override + {{$t('settings.style.shadows.override')}} </label> <input v-model="currentShadowOverriden" diff --git a/src/i18n/en.json b/src/i18n/en.json index d825dcc1..04e9977b 100644 --- a/src/i18n/en.json +++ b/src/i18n/en.json @@ -108,7 +108,6 @@ "follow_import_error": "Error importing followers", "follows_imported": "Follows imported! Processing them will take a while.", "foreground": "Foreground", - "opacity": "Opacity", "general": "General", "hide_attachments_in_convo": "Hide attachments in conversations", "hide_attachments_in_tl": "Hide attachments in timeline", @@ -162,6 +161,52 @@ "values": { "false": "no", "true": "yes" + }, + "style": { + "common": { + "color": "Color", + "opacity": "Opacity" + }, + "basic_colors": { + "_tab_label": "Basic", + "main": "Basic colors", + "foreground": "Panel header, top bar, buttons, text fields", + "foreground_hint": "See \"Advanced\" tab for more detailed control", + "rgbo": "Icons, accents, badges" + }, + "advanced_colors": { + "_tab_label": "Advanced", + "alert": "Alert background", + "alert_error": "Error", + "badge": "Badge background", + "badge_notification": "Notification", + "panel_header": "Panel header", + "top_bar": "Top bar", + "borders": "Borders", + "buttons": "Buttons", + "inputs": "Input fields", + "faint_text": "Faded text" + }, + "radii": { + "_tab_label": "Roundness" + }, + "shadows": { + "_tab_label": "Shadow and lighting", + "component": "Component", + "override": "Override", + "shadow_id": "Shadow #{value}", + "blur": "Blur", + "spread": "Spread", + "inset": "Inset", + "components": { + "panel": "Panel", + "button": "Button", + "button_hover": "Button (hover)", + "button_pressed": "Button (pressed)", + "button_pressed_hover": "Button (pressed+hover)", + "input_box": "Input field" + } + } } }, "timeline": { From 2609c0d0d279031cba579d60bf94cca81544ec4f Mon Sep 17 00:00:00 2001 From: Henry Jameson <me@hjkos.com> Date: Tue, 20 Nov 2018 22:14:49 +0300 Subject: [PATCH 21/96] unification of stylings --- src/App.scss | 13 + src/components/color_input/color_input.vue | 35 +-- .../opacity_input/opacity_input.vue | 42 +-- .../shadow_control/shadow_control.vue | 52 ++-- .../style_switcher/style_switcher.vue | 272 +++++------------- 5 files changed, 104 insertions(+), 310 deletions(-) diff --git a/src/App.scss b/src/App.scss index 1021b64b..970a5f74 100644 --- a/src/App.scss +++ b/src/App.scss @@ -64,6 +64,11 @@ button { font-size: 14px; font-family: sans-serif; + i[class*=icon-] { + color: $fallback--text; + color: var(--btnText, $fallback--text); + } + &::-moz-focus-inner { border: none; } @@ -145,6 +150,14 @@ input, textarea, .select { line-height: 16px; } + &[type=range] { + background: none; + border: none; + margin: 0; + box-shadow: none; + flex: 1; + } + &[type=radio], &[type=checkbox] { display: none; diff --git a/src/components/color_input/color_input.vue b/src/components/color_input/color_input.vue index b756d265..34eec248 100644 --- a/src/components/color_input/color_input.vue +++ b/src/components/color_input/color_input.vue @@ -5,7 +5,7 @@ </label> <input v-if="typeof fallback !== 'undefined'" - class="opt" + class="opt exlcude-disabled" :id="name + '-o'" type="checkbox" :checked="present" @@ -45,40 +45,9 @@ export default { <style lang="scss"> .color-control { - display: flex; - align-items: baseline; - - &.disabled *:not(.opt-l){ - opacity: .5 - } - - .label { - flex: 2; - min-width: 7em; - } - - .opt-l { - align-self: center; - flex: 0; - &::before { - width: 14px; - height: 14px; - } - } - - .text-input { + input.text-input { max-width: 7em; flex: 1; } - - .color-input { - flex: 0; - padding: 1px; - cursor: pointer; - height: 29px; - min-width: 2em; - border: none; - align-self: stretch; - } } </style> diff --git a/src/components/opacity_input/opacity_input.vue b/src/components/opacity_input/opacity_input.vue index e0567ec7..3926915b 100644 --- a/src/components/opacity_input/opacity_input.vue +++ b/src/components/opacity_input/opacity_input.vue @@ -5,7 +5,7 @@ </label> <input v-if="typeof fallback !== 'undefined'" - class="opt" + class="opt exclude-disabled" :id="name + '-o'" type="checkbox" :checked="present" @@ -36,43 +36,3 @@ export default { } } </script> - -<style lang="scss"> -.opacity-control { - display: flex; - align-items: baseline; - - &.disabled *:not(.opt-l) { - opacity: .5 - } - - .opt-l { - align-self: center; - &::before { - width: 14px; - height: 14px; - } - } - - .label { - flex: 2; - min-width: 7em; - } - - .input-range { - background: none; - border: none; - margin: 0; - height: auto; - box-shadow: none; - min-width: 7em; - flex: 1; - } - - .input-number { - margin: 0; - min-width: 4em; - flex: 0; - } -} -</style> diff --git a/src/components/shadow_control/shadow_control.vue b/src/components/shadow_control/shadow_control.vue index b99df35a..cd774d32 100644 --- a/src/components/shadow_control/shadow_control.vue +++ b/src/components/shadow_control/shadow_control.vue @@ -39,7 +39,7 @@ </div> <div class="shadow-tweak"> - <div :disabled="usingFallback" class="id-control"> + <div :disabled="usingFallback" class="id-control style-control"> <label for="shadow-switcher" class="select"> <select v-model="selectedId" class="shadow-switcher" @@ -64,7 +64,7 @@ <i class="icon-plus"/> </button> </div> - <div :disabled="!present" class="inset-control"> + <div :disabled="!present" class="inset-control style-control"> <label for="inset" class="label"> {{$t('settings.style.shadows.inset')}} </label> @@ -77,7 +77,7 @@ type="checkbox"> <label class="checkbox-label" for="inset"></label> </div> - <div :disabled="!present" class="blur-control"> + <div :disabled="!present" class="blur-control style-control"> <label for="spread" class="label"> {{$t('settings.style.shadows.blur')}} </label> @@ -97,7 +97,7 @@ type="number" min="0"> </div> - <div :disabled="!present" class="spread-control"> + <div :disabled="!present" class="spread-control style-control"> <label for="spread" class="label"> {{$t('settings.style.shadows.spread')}} </label> @@ -137,6 +137,11 @@ flex-wrap: wrap; justify-content: center; + + .shadow-preview-container, + .shadow-tweak { + margin: 5px 6px 0 0; + } .shadow-preview-container { flex: 0; display: flex; @@ -210,39 +215,18 @@ } .shadow-tweak { - .label { - flex: 1; - min-width: 3em; - } + flex: 1; - .inset-control { - justify-content: flex-end; - label { - flex: 0 + .id-control { + align-items: stretch; + .select, .btn { + margin-right: 5px; } - } - - .blur-control, - .id-control, - .inset-control, - .spread-control { - display: flex; - align-items: baseline; - max-width: 100%; - - &[disabled=disabled] *{ - opacity: .5 - } - - .input-range { + .select { flex: 1; - align-self: center; - } - - .input-number { - width: 4em; - min-width: 4em; - flex: 0; + select { + align-self: initial; + } } } } diff --git a/src/components/style_switcher/style_switcher.vue b/src/components/style_switcher/style_switcher.vue index fd5d830a..5f0c2566 100644 --- a/src/components/style_switcher/style_switcher.vue +++ b/src/components/style_switcher/style_switcher.vue @@ -1,52 +1,53 @@ <template> - <div> - <div class="presets-container"> - <div> - {{$t('settings.presets')}} - <label for="style-switcher" class='select'> - <select id="style-switcher" v-model="selected" class="style-switcher"> - <option v-for="style in availableStyles" - :value="style" - :style="{ - backgroundColor: style[1], - color: style[3] - }"> - {{style[0]}} - </option> - </select> - <i class="icon-down-open"/> - </label> - </div> - <div class="import-export"> - <button class="btn" @click="exportCurrentTheme">{{ $t('settings.export_theme') }}</button> - <button class="btn" @click="importTheme">{{ $t('settings.import_theme') }}</button> - <p v-if="invalidThemeImported" class="import-warning">{{ $t('settings.invalid_theme_imported') }}</p> - </div> +<div class="style-switcher"> + <div class="presets-container"> + <div> + {{$t('settings.presets')}} + <label for="preset-switcher" class='select'> + <select id="preset-switcher" v-model="selected" class="preset-switcher"> + <option v-for="style in availableStyles" + :value="style" + :style="{ + backgroundColor: style[1], + color: style[3] + }"> + {{style[0]}} + </option> + </select> + <i class="icon-down-open"/> + </label> </div> + <div class="import-export"> + <button class="btn" @click="exportCurrentTheme">{{ $t('settings.export_theme') }}</button> + <button class="btn" @click="importTheme">{{ $t('settings.import_theme') }}</button> + <p v-if="invalidThemeImported" class="import-warning">{{ $t('settings.invalid_theme_imported') }}</p> + </div> + </div> - <div class="preview-container"> - <div class="panel dummy" :style="previewRules"> - <div class="panel-heading">Preview</div> - <div class="panel-body theme-preview-content"> - <div class="avatar"> - ( ͡° ͜ʖ ͡°) - </div> - <h4>Content</h4> - <br> - A bunch of more content and - <a style="color: var(--link)">a nice lil' link</a> - <i style="color: var(--cBlue)" class="icon-reply"/> - <i style="color: var(--cGreen)" class="icon-retweet"/> - <i style="color: var(--cRed)" class="icon-cancel"/> - <i style="color: var(--cOrange)" class="icon-star"/> - <br> - <button class="btn">Button</button> + <div class="preview-container"> + <div class="panel dummy" :style="previewRules"> + <div class="panel-heading">Preview</div> + <div class="panel-body theme-preview-content"> + <div class="avatar"> + ( ͡° ͜ʖ ͡°) </div> + <h4>Content</h4> + <br> + A bunch of more content and + <a style="color: var(--link)">a nice lil' link</a> + <i style="color: var(--cBlue)" class="icon-reply"/> + <i style="color: var(--cGreen)" class="icon-retweet"/> + <i style="color: var(--cRed)" class="icon-cancel"/> + <i style="color: var(--cOrange)" class="icon-star"/> + <br> + <button class="btn">Button</button> </div> </div> + </div> - <p>{{$t('settings.theme_help')}}</p> - <tab-switcher> + <p>{{$t('settings.theme_help')}}</p> + <keep-alive> + <tab-switcher key="style-tweak"> <div :label="$t('settings.style.basic_colors._tab_label')" class="color-container"> <div class="color-item"> <h4>{{ $t('settings.style.basic_colors.main') }}</h4> @@ -171,31 +172,36 @@ </div> </div> <div :label="$t('settings.style.shadows._tab_label')" class="shadow-container"> - <div> - {{$t('settings.style.shadows.component')}} - <label for="shadow-switcher" class="shadow-selector select"> - <select id="shadow-switcher" v-model="shadowSelected" class="shadow-switcher"> - <option v-for="shadow in shadowsAvailable" - :value="shadow"> - {{$t('settings.style.shadows.components.' + shadow)}} - </option> - </select> - <i class="icon-down-open"/> - </label> - <label for="override" class="label"> - {{$t('settings.style.shadows.override')}} - </label> - <input - v-model="currentShadowOverriden" - name="override" - id="override" - class="input-override" - type="checkbox"> - <label class="checkbox-label" for="override"></label> + <div class="shadow-selector"> + <div> + {{$t('settings.style.shadows.component')}} + <label for="shadow-switcher" class="select"> + <select id="shadow-switcher" v-model="shadowSelected" class="shadow-switcher"> + <option v-for="shadow in shadowsAvailable" + :value="shadow"> + {{$t('settings.style.shadows.components.' + shadow)}} + </option> + </select> + <i class="icon-down-open"/> + </label> + </div> + <div class="override"> + <label for="override" class="label"> + {{$t('settings.style.shadows.override')}} + </label> + <input + v-model="currentShadowOverriden" + name="override" + id="override" + class="input-override" + type="checkbox"> + <label class="checkbox-label" for="override"></label> + </div> </div> <shadow-control v-if="currentShadowFallback" :fallback="currentShadowFallback" v-model="currentShadow"/> </div> </tab-switcher> + </keep-alive> <div class="apply-container"> <button class="btn submit" @click="setCustomTheme">{{$t('general.apply')}}</button> @@ -205,142 +211,4 @@ <script src="./style_switcher.js"></script> -<style lang="scss"> -@import '../../_variables.scss'; -.style-switcher { - margin-right: 1em; -} - -.import-warning { - color: $fallback--cRed; - color: var(--cRed, $fallback--cRed); -} - -.apply-container, -.radius-container, -.color-container, -.presets-container { - display: flex; - - p { - margin-left: 1em - } -} - -.radius-container { - flex-direction: column; -} - -.color-container{ - flex-wrap: wrap; - justify-content: space-between; -} - -.presets-container { - justify-content: center; - .import-export { - display: flex; - - .btn { - margin-left: .5em; - } - } -} - -.preview-container { - border-top: 1px dashed; - border-bottom: 1px dashed; - border-color: $fallback--border; - border-color: var(--border, $fallback--border); - margin: 1em -1em 0; - padding: 1em; - background: var(--body-background-image); - background-size: cover; - background-position: 50% 50%; - - .btn { - margin-top: 1em; - min-height: 30px; - width: 10em; - } -} - -.apply-container { - justify-content: center; -} - -.radius-item, -.color-item { - min-width: 20em; - margin: 5px 6px 0 0; - display:flex; - flex-direction: column; - flex: 1 1 0; - - &.wide { - min-width: 60% - } - &:not(.wide):nth-child(2n+1) { - margin-right: 7px; - - } - - .color, .opacity { - display:flex; - align-items: baseline; - } - - h4 { - margin-top: 1em; - } -} - -.radius-item { - flex-basis: auto; -} - -.theme-radius-rn, -.theme-color-cl { - border: 0; - box-shadow: none; - background: transparent; - color: var(--faint, $fallback--faint); - align-self: stretch; -} - -.theme-color-cl, -.theme-radius-in, -.theme-color-in { - margin-left: 4px; -} - -.theme-radius-in { - min-width: 1em; -} - -.theme-radius-in { - max-width: 7em; - flex: 1; -} - -.theme-radius-lb{ - max-width: 50em; -} - -.theme-preview-content { - padding: 20px; -} - -.dummy { - .avatar { - background: linear-gradient(135deg, #b8e1fc 0%,#a9d2f3 10%,#90bae4 25%,#90bcea 37%,#90bff0 50%,#6ba8e5 51%,#a2daf5 83%,#bdf3fd 100%); - color: black; - text-align: center; - height: 48px; - line-height: 48px; - width: 48px; - float: left; - margin-right: 1em; - } -} -</style> +<style src="./style_switcher.scss" lang="scss"></style> From d7af2c8419df59d8b897bc57e94f6cc67bd60eca Mon Sep 17 00:00:00 2001 From: Henry Jameson <me@hjkos.com> Date: Tue, 20 Nov 2018 23:25:38 +0300 Subject: [PATCH 22/96] mentioned bug in tab-switcher, made shadow-control work in zero-state --- .../shadow_control/shadow_control.js | 19 +++++++++++++------ .../shadow_control/shadow_control.vue | 7 +++---- .../style_switcher/style_switcher.js | 4 ++++ .../style_switcher/style_switcher.vue | 2 +- src/components/tab_switcher/tab_switcher.jsx | 2 ++ 5 files changed, 23 insertions(+), 11 deletions(-) diff --git a/src/components/shadow_control/shadow_control.js b/src/components/shadow_control/shadow_control.js index 54813685..b4f48668 100644 --- a/src/components/shadow_control/shadow_control.js +++ b/src/components/shadow_control/shadow_control.js @@ -37,9 +37,12 @@ export default { this.selectedId += 1 } }, + beforeUpdate () { + this.cValue = this.value || this.fallback + }, computed: { selected () { - return this.cValue[this.selectedId] || { + return this.isReady && this.cValue[this.selectedId] || { x: 0, y: 0, blur: 0, @@ -50,13 +53,17 @@ export default { } }, moveUpValid () { - return this.selectedId > 0 + return this.isReady && this.selectedId > 0 }, moveDnValid () { - return this.selectedId < this.cValue.length - 1 + return this.isReady && this.selectedId < this.cValue.length - 1 + }, + isReady () { + return typeof this.cValue !== 'undefined' }, present () { - return typeof this.cValue[this.selectedId] !== 'undefined' && + return this.isReady && + typeof this.cValue[this.selectedId] !== 'undefined' && !this.usingFallback }, usingFallback () { @@ -66,9 +73,9 @@ export default { return hex2rgb(this.selected.color) }, style () { - return { + return this.isReady ? { boxShadow: getCssShadow(this.cValue) - } + } : {} } } } diff --git a/src/components/shadow_control/shadow_control.vue b/src/components/shadow_control/shadow_control.vue index cd774d32..f8066947 100644 --- a/src/components/shadow_control/shadow_control.vue +++ b/src/components/shadow_control/shadow_control.vue @@ -43,7 +43,7 @@ <label for="shadow-switcher" class="select"> <select v-model="selectedId" class="shadow-switcher" - :disabled="usingFallback" + :disabled="!isReady || usingFallback" id="shadow-switcher"> <option v-for="(shadow, index) in cValue" :value="index"> {{$t('settings.style.shadows.shadow_id', { value: index })}} @@ -51,7 +51,7 @@ </select> <i class="icon-down-open"/> </label> - <button class="btn btn-default" :disabled="!present" @click="del"> + <button class="btn btn-default" :disabled="!isReady || !present" @click="del"> <i class="icon-cancel"/> </button> <button class="btn btn-default" :disabled="!moveUpValid" @click="moveUp"> @@ -60,7 +60,7 @@ <button class="btn btn-default" :disabled="!moveDnValid" @click="moveDn"> <i class="icon-down-open"/> </button> - <button class="btn btn-default" @click="add"> + <button class="btn btn-default" :disabled="!isReady" @click="add"> <i class="icon-plus"/> </button> </div> @@ -137,7 +137,6 @@ flex-wrap: wrap; justify-content: center; - .shadow-preview-container, .shadow-tweak { margin: 5px 6px 0 0; diff --git a/src/components/style_switcher/style_switcher.js b/src/components/style_switcher/style_switcher.js index 7cb6197c..acb1764d 100644 --- a/src/components/style_switcher/style_switcher.js +++ b/src/components/style_switcher/style_switcher.js @@ -93,6 +93,9 @@ export default { }, mounted () { this.normalizeLocalState(this.$store.state.config.customTheme) + if (typeof this.shadowSelected === 'undefined') { + this.shadowSelected = this.shadowsAvailable[0] + } }, computed: { selectedVersion () { @@ -180,6 +183,7 @@ export default { if (!this.preview.theme.colors) return { colors: {}, opacity: {}, radii: {}, shadows: {} } return this.preview.theme }, + // This needs optimization maybe previewContrast () { if (!this.previewTheme.colors.bg) return {} const colors = this.previewTheme.colors diff --git a/src/components/style_switcher/style_switcher.vue b/src/components/style_switcher/style_switcher.vue index 5f0c2566..5cf75636 100644 --- a/src/components/style_switcher/style_switcher.vue +++ b/src/components/style_switcher/style_switcher.vue @@ -198,7 +198,7 @@ <label class="checkbox-label" for="override"></label> </div> </div> - <shadow-control v-if="currentShadowFallback" :fallback="currentShadowFallback" v-model="currentShadow"/> + <shadow-control :fallback="currentShadowFallback" v-model="currentShadow"/> </div> </tab-switcher> </keep-alive> diff --git a/src/components/tab_switcher/tab_switcher.jsx b/src/components/tab_switcher/tab_switcher.jsx index 3fff38f6..ea582450 100644 --- a/src/components/tab_switcher/tab_switcher.jsx +++ b/src/components/tab_switcher/tab_switcher.jsx @@ -1,5 +1,7 @@ import Vue from 'vue' +// FIXME: This doesn't like v-if directly inside the tab's contents, breaks vue really bad + import './tab_switcher.scss' export default Vue.component('tab-switcher', { From 0184d5fff01f03d099de50773e59cb6363c5bede Mon Sep 17 00:00:00 2001 From: Henry Jameson <me@hjkos.com> Date: Tue, 20 Nov 2018 23:34:04 +0300 Subject: [PATCH 23/96] whoops --- .../style_switcher/style_switcher.scss | 192 ++++++++++++++++++ 1 file changed, 192 insertions(+) create mode 100644 src/components/style_switcher/style_switcher.scss diff --git a/src/components/style_switcher/style_switcher.scss b/src/components/style_switcher/style_switcher.scss new file mode 100644 index 00000000..6e34a0f7 --- /dev/null +++ b/src/components/style_switcher/style_switcher.scss @@ -0,0 +1,192 @@ +@import '../../_variables.scss'; +.style-switcher { + .preset-switcher { + margin-right: 1em; + } + + .style-control { + display: flex; + align-items: baseline; + margin-bottom: 5px; + + .label { + flex: 1; + } + + input, select { + min-width: 3em; + margin: 0; + flex: 0; + + &[type=color] { + padding: 1px; + cursor: pointer; + height: 29px; + min-width: 2em; + border: none; + align-self: stretch; + } + + &[type=number] { + min-width: 5em; + } + + &[type=range] { + flex: 1; + min-width: 3em; + } + + &[type=checkbox] + label { + margin: 6px 0; + } + + &:not([type=number]):not([type=text]) { + align-self: center; + } + + &.disabled *:not(.exlcude-disabled) { + opacity: .5 + } + } + } + + .import-warning { + color: $fallback--cRed; + color: var(--cRed, $fallback--cRed); + } + + .apply-container, + .radius-container, + .color-container, + .presets-container { + display: flex; + + p { + margin-left: 1em + } + } + + .radius-container { + flex-direction: column; + } + + .color-container{ + flex-wrap: wrap; + justify-content: space-between; + } + + .presets-container, + .shadow-selector { + display: flex; + justify-content: center; + align-items: baseline; + + .import-export { + display: flex; + + .btn { + margin-left: .5em; + } + } + .override { + margin-left: .5em; + } + } + + .preview-container { + border-top: 1px dashed; + border-bottom: 1px dashed; + border-color: $fallback--border; + border-color: var(--border, $fallback--border); + margin: 1em -1em 0; + padding: 1em; + background: var(--body-background-image); + background-size: cover; + background-position: 50% 50%; + + .btn { + margin-top: 1em; + min-height: 30px; + width: 10em; + } + } + + .apply-container { + justify-content: center; + } + + .radius-item, + .color-item { + min-width: 20em; + margin: 5px 6px 0 0; + display:flex; + flex-direction: column; + flex: 1 1 0; + + &.wide { + min-width: 60% + } + &:not(.wide):nth-child(2n+1) { + margin-right: 7px; + + } + + .color, .opacity { + display:flex; + align-items: baseline; + } + + h4 { + margin-top: 1em; + } + } + + .radius-item { + flex-basis: auto; + } + + .theme-radius-rn, + .theme-color-cl { + border: 0; + box-shadow: none; + background: transparent; + color: var(--faint, $fallback--faint); + align-self: stretch; + } + + .theme-color-cl, + .theme-radius-in, + .theme-color-in { + margin-left: 4px; + } + + .theme-radius-in { + min-width: 1em; + } + + .theme-radius-in { + max-width: 7em; + flex: 1; + } + + .theme-radius-lb{ + max-width: 50em; + } + + .theme-preview-content { + padding: 20px; + } + + .dummy { + .avatar { + background: linear-gradient(135deg, #b8e1fc 0%,#a9d2f3 10%,#90bae4 25%,#90bcea 37%,#90bff0 50%,#6ba8e5 51%,#a2daf5 83%,#bdf3fd 100%); + color: black; + text-align: center; + height: 48px; + line-height: 48px; + width: 48px; + float: left; + margin-right: 1em; + } + } +} From 73a9370710f46c0594eda01ac8fe016e87c7e18c Mon Sep 17 00:00:00 2001 From: Henry Jameson <me@hjkos.com> Date: Wed, 21 Nov 2018 03:14:59 +0300 Subject: [PATCH 24/96] fixed and updated roundness tab --- src/components/range_input/range_input.vue | 48 +++++++++++++++++++ .../style_switcher/style_switcher.js | 23 +++++---- .../style_switcher/style_switcher.scss | 12 +++-- .../style_switcher/style_switcher.vue | 42 +++------------- src/services/style_setter/style_setter.js | 25 +++------- 5 files changed, 84 insertions(+), 66 deletions(-) create mode 100644 src/components/range_input/range_input.vue diff --git a/src/components/range_input/range_input.vue b/src/components/range_input/range_input.vue new file mode 100644 index 00000000..3e50664b --- /dev/null +++ b/src/components/range_input/range_input.vue @@ -0,0 +1,48 @@ +<template> +<div class="range-control style-control" :class="{ disabled: !present || disabled }"> + <label :for="name" class="label"> + {{label}} + </label> + <input + v-if="typeof fallback !== 'undefined'" + class="opt exclude-disabled" + :id="name + '-o'" + type="checkbox" + :checked="present" + @input="$emit('input', !present ? fallback : undefined)"> + <label v-if="typeof fallback !== 'undefined'" class="opt-l" :for="name + '-o'"></label> + <input + :id="name" + class="input-number" + type="range" + :value="value || fallback" + :disabled="!present || disabled" + @input="$emit('input', $event.target.value)" + :max="max || hardMax || 100" + :min="min || hardMin || 0" + :step="step || 1"> + <input + :id="name" + class="input-number" + type="number" + :value="value || fallback" + :disabled="!present || disabled" + @input="$emit('input', $event.target.value)" + :max="hardMax" + :min="hardMin" + :step="step || 1"> +</div> +</template> + +<script> +export default { + props: [ + 'name', 'value', 'fallback', 'disabled', 'label', 'max', 'min', 'step', 'hardMin', 'hardMax' + ], + computed: { + present () { + return typeof this.value !== 'undefined' + } + } +} +</script> diff --git a/src/components/style_switcher/style_switcher.js b/src/components/style_switcher/style_switcher.js index acb1764d..d0f72427 100644 --- a/src/components/style_switcher/style_switcher.js +++ b/src/components/style_switcher/style_switcher.js @@ -2,9 +2,10 @@ import { rgb2hex, hex2rgb, getContrastRatio, alphaBlend } from '../../services/c import { set, delete as del } from 'vue' import { generateColors, generateShadows, generateRadii, composePreset } from '../../services/style_setter/style_setter.js' import ColorInput from '../color_input/color_input.vue' +import RangeInput from '../range_input/range_input.vue' +import OpacityInput from '../opacity_input/opacity_input.vue' import ShadowControl from '../shadow_control/shadow_control.vue' import ContrastRatio from '../contrast_ratio/contrast_ratio.vue' -import OpacityInput from '../opacity_input/opacity_input.vue' import TabSwitcher from '../tab_switcher/tab_switcher.jsx' // List of color values used in v1 @@ -171,7 +172,7 @@ export default { } }, previewRadii () { - return generateRadii(this.currentRadii) + return generateRadii({ radii: this.currentRadii }) }, previewShadows () { return generateShadows({ shadows: this.shadowsLocal }) @@ -181,6 +182,7 @@ export default { }, previewTheme () { if (!this.preview.theme.colors) return { colors: {}, opacity: {}, radii: {}, shadows: {} } + console.log(this.preview.theme.radii) return this.preview.theme }, // This needs optimization maybe @@ -286,6 +288,7 @@ export default { components: { ColorInput, OpacityInput, + RangeInput, ContrastRatio, ShadowControl, TabSwitcher @@ -379,6 +382,8 @@ export default { normalizeLocalState (input, version = 0) { const colors = input.colors || input const radii = input.radii || input + console.log('Benis') + console.log(JSON.stringify(radii, null, 2)) const opacity = input.opacity || input const shadows = input.shadows || {} @@ -417,13 +422,13 @@ export default { }) // TODO optimize this - this.btnRadiusLocal = radii.btnRadius || 4 - this.inputRadiusLocal = radii.inputRadius || 4 - this.panelRadiusLocal = radii.panelRadius || 10 - this.avatarRadiusLocal = radii.avatarRadius || 5 - this.avatarAltRadiusLocal = radii.avatarAltRadius || 50 - this.tooltipRadiusLocal = radii.tooltipRadius || 2 - this.attachmentRadiusLocal = radii.attachmentRadius || 5 + this.btnRadiusLocal = radii.btn + this.inputRadiusLocal = radii.input + this.panelRadiusLocal = radii.panel + this.avatarRadiusLocal = radii.avatar + this.avatarAltRadiusLocal = radii.avatarAlt + this.tooltipRadiusLocal = radii.tooltip + this.attachmentRadiusLocal = radii.attachment this.shadowsLocal = shadows diff --git a/src/components/style_switcher/style_switcher.scss b/src/components/style_switcher/style_switcher.scss index 6e34a0f7..6c6e913a 100644 --- a/src/components/style_switcher/style_switcher.scss +++ b/src/components/style_switcher/style_switcher.scss @@ -13,6 +13,14 @@ flex: 1; } + &.disabled { + input, select { + &:not(.exclude-disabled) { + opacity: .5 + } + } + } + input, select { min-width: 3em; margin: 0; @@ -43,10 +51,6 @@ &:not([type=number]):not([type=text]) { align-self: center; } - - &.disabled *:not(.exlcude-disabled) { - opacity: .5 - } } } diff --git a/src/components/style_switcher/style_switcher.vue b/src/components/style_switcher/style_switcher.vue index 5cf75636..54ea072f 100644 --- a/src/components/style_switcher/style_switcher.vue +++ b/src/components/style_switcher/style_switcher.vue @@ -135,41 +135,13 @@ </div> <div :label="$t('settings.style.radii._tab_label')" class="radius-container"> <p>{{$t('settings.radii_help')}}</p> - <div class="radius-item"> - <label for="btnradius" class="theme-radius-lb">{{$t('settings.btnRadius')}}</label> - <input id="btnradius" class="theme-radius-rn" type="range" v-model="btnRadiusLocal" max="16"> - <input id="btnradius-t" class="theme-radius-in" type="text" v-model="btnRadiusLocal"> - </div> - <div class="radius-item"> - <label for="inputradius" class="theme-radius-lb">{{$t('settings.inputRadius')}}</label> - <input id="inputradius" class="theme-radius-rn" type="range" v-model="inputRadiusLocal" max="16"> - <input id="inputradius-t" class="theme-radius-in" type="text" v-model="inputRadiusLocal"> - </div> - <div class="radius-item"> - <label for="panelradius" class="theme-radius-lb">{{$t('settings.panelRadius')}}</label> - <input id="panelradius" class="theme-radius-rn" type="range" v-model="panelRadiusLocal" max="50"> - <input id="panelradius-t" class="theme-radius-in" type="text" v-model="panelRadiusLocal"> - </div> - <div class="radius-item"> - <label for="avatarradius" class="theme-radius-lb">{{$t('settings.avatarRadius')}}</label> - <input id="avatarradius" class="theme-radius-rn" type="range" v-model="avatarRadiusLocal" max="28"> - <input id="avatarradius-t" class="theme-radius-in" type="green" v-model="avatarRadiusLocal"> - </div> - <div class="radius-item"> - <label for="avataraltradius" class="theme-radius-lb">{{$t('settings.avatarAltRadius')}}</label> - <input id="avataraltradius" class="theme-radius-rn" type="range" v-model="avatarAltRadiusLocal" max="28"> - <input id="avataraltradius-t" class="theme-radius-in" type="text" v-model="avatarAltRadiusLocal"> - </div> - <div class="radius-item"> - <label for="attachmentradius" class="theme-radius-lb">{{$t('settings.attachmentRadius')}}</label> - <input id="attachmentrradius" class="theme-radius-rn" type="range" v-model="attachmentRadiusLocal" max="50"> - <input id="attachmentradius-t" class="theme-radius-in" type="text" v-model="attachmentRadiusLocal"> - </div> - <div class="radius-item"> - <label for="tooltipradius" class="theme-radius-lb">{{$t('settings.tooltipRadius')}}</label> - <input id="tooltipradius" class="theme-radius-rn" type="range" v-model="tooltipRadiusLocal" max="20"> - <input id="tooltipradius-t" class="theme-radius-in" type="text" v-model="tooltipRadiusLocal"> - </div> + <RangeInput name="btnRadius" :label="$t('settings.btnRadius')" v-model="btnRadiusLocal" :fallback="previewTheme.radii.btn" max="16" hardMin="0"/> + <RangeInput name="inputRadius" :label="$t('settings.inputRadius')" v-model="inputRadiusLocal" :fallback="previewTheme.radii.input" max="16" hardMin="0"/> + <RangeInput name="panelRadius" :label="$t('settings.panelRadius')" v-model="panelRadiusLocal" :fallback="previewTheme.radii.panel" max="50" hardMin="0"/> + <RangeInput name="avatarRadius" :label="$t('settings.avatarRadius')" v-model="avatarRadiusLocal" :fallback="previewTheme.radii.avatar" max="28" hardMin="0"/> + <RangeInput name="avatarAltRadius" :label="$t('settings.avatarAltRadius')" v-model="avatarAltRadiusLocal" :fallback="previewTheme.radii.avatarAlt" max="28" hardMin="0"/> + <RangeInput name="attachmentRadius" :label="$t('settings.attachmentRadius')" v-model="attachmentRadiusLocal" :fallback="previewTheme.radii.attachment" max="50" hardMin="0"/> + <RangeInput name="tooltipRadius" :label="$t('settings.tooltipRadius')" v-model="tooltipRadiusLocal" :fallback="previewTheme.radii.tooltip" max="50" hardMin="0"/> </div> <div :label="$t('settings.style.shadows._tab_label')" class="shadow-container"> <div class="shadow-selector"> diff --git a/src/services/style_setter/style_setter.js b/src/services/style_setter/style_setter.js index e7d8252c..69a1b899 100644 --- a/src/services/style_setter/style_setter.js +++ b/src/services/style_setter/style_setter.js @@ -81,7 +81,6 @@ const setColors = (input, commit) => { head.appendChild(styleEl) const styleSheet = styleEl.sheet - console.log(rules) styleSheet.toString() styleSheet.insertRule(`body { ${rules.radii} }`, 'index-max') styleSheet.insertRule(`body { ${rules.colors} }`, 'index-max') @@ -108,7 +107,6 @@ const getCssShadow = (input) => { } const generateColors = (input) => { - console.log(input.opacity) const colors = {} const opacity = Object.assign({ alert: 0.5, @@ -120,7 +118,6 @@ const generateColors = (input) => { } return acc }, {})) - console.log(colors, opacity) const col = Object.entries(input.colors || input).reduce((acc, [k, v]) => { if (typeof v === 'object') { acc[k] = v @@ -216,26 +213,19 @@ const generateColors = (input) => { } const generateRadii = (input) => { - const inputRadii = input.radii || { - btn: input.btnRadius, - input: input.inputRadius, - panel: input.panelRadius, - avatar: input.avatarRadius, - avatarAlt: input.avatarAltRadius, - tooltip: input.tooltipRadius, - attachment: input.attachmentRadius - } - - const radii = { + console.log(input) + const radii = Object.entries(input.radii).filter(([k, v]) => v).reduce((acc, [k, v]) => { + acc[k] = v + return acc + }, { btn: 4, input: 4, panel: 10, avatar: 5, avatarAlt: 50, tooltip: 2, - attachment: 5, - ...inputRadii - } + attachment: 5 + }) return { rules: { @@ -259,7 +249,6 @@ const generateShadows = (input) => { }], ...(input.shadows || {}) } - console.log('benis') return { rules: { From b7fb720c190385b887ee7d7c3c46936e9a603862 Mon Sep 17 00:00:00 2001 From: Henry Jameson <me@hjkos.com> Date: Wed, 21 Nov 2018 03:23:02 +0300 Subject: [PATCH 25/96] cleanup, cold-boot issue fixed --- src/services/style_setter/style_setter.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/services/style_setter/style_setter.js b/src/services/style_setter/style_setter.js index 69a1b899..45e950f0 100644 --- a/src/services/style_setter/style_setter.js +++ b/src/services/style_setter/style_setter.js @@ -213,8 +213,7 @@ const generateColors = (input) => { } const generateRadii = (input) => { - console.log(input) - const radii = Object.entries(input.radii).filter(([k, v]) => v).reduce((acc, [k, v]) => { + const radii = Object.entries(input.radii || {}).filter(([k, v]) => v).reduce((acc, [k, v]) => { acc[k] = v return acc }, { From aa93664fd68e3fc27821fc1b03af25b6a6e6ae87 Mon Sep 17 00:00:00 2001 From: Henry Jameson <me@hjkos.com> Date: Wed, 21 Nov 2018 03:51:57 +0300 Subject: [PATCH 26/96] fix coldboot --- src/components/user_card_content/user_card_content.js | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/components/user_card_content/user_card_content.js b/src/components/user_card_content/user_card_content.js index eae436a9..4c7b6a68 100644 --- a/src/components/user_card_content/user_card_content.js +++ b/src/components/user_card_content/user_card_content.js @@ -5,9 +5,8 @@ export default { props: [ 'user', 'switcher', 'selected', 'hideBio' ], computed: { headingStyle () { - const color = this.$store.state.config.customTheme.colors.bg - if (color) { - const rgb = hex2rgb(color) + const rgb = this.$store.state.config.customTheme.colors.bg + if (rgb) { const tintColor = `rgba(${Math.floor(rgb.r)}, ${Math.floor(rgb.g)}, ${Math.floor(rgb.b)}, .5)` return { backgroundColor: `rgb(${Math.floor(rgb.r * 0.53)}, ${Math.floor(rgb.g * 0.56)}, ${Math.floor(rgb.b * 0.59)})`, From c3d8ff65bdcf02a014aafab93628e0508509d25a Mon Sep 17 00:00:00 2001 From: Henry Jameson <me@hjkos.com> Date: Wed, 21 Nov 2018 03:52:12 +0300 Subject: [PATCH 27/96] fix notification unseen display rendering underneath the highlight --- src/components/notifications/notifications.scss | 17 +++++++++++++++-- src/components/notifications/notifications.vue | 1 + 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/src/components/notifications/notifications.scss b/src/components/notifications/notifications.scss index fcc0c3d4..26455ffb 100644 --- a/src/components/notifications/notifications.scss +++ b/src/components/notifications/notifications.scss @@ -26,9 +26,22 @@ color: var(--text, $fallback--text); } - .unseen { + .notification { + position: relative; - background-image: linear-gradient(135deg, var(--badgeNotification, $fallback--cRed) 4px, transparent 10px) + .notification-overlay { + position: absolute; + top: 0; + right: 0; + left: 0; + bottom: 0; + } + + &.unseen { + .notification-overlay { + background-image: linear-gradient(135deg, var(--badgeNotification, $fallback--cRed) 4px, transparent 10px) + } + } } } diff --git a/src/components/notifications/notifications.vue b/src/components/notifications/notifications.vue index 7a4322f9..eb6c4dd0 100644 --- a/src/components/notifications/notifications.vue +++ b/src/components/notifications/notifications.vue @@ -13,6 +13,7 @@ </div> <div class="panel-body"> <div v-for="notification in visibleNotifications" :key="notification.action.id" class="notification" :class='{"unseen": !notification.seen}'> + <div class="notification-overlay"></div> <notification :notification="notification"></notification> </div> </div> From a79d9d9774a5b8f9f86d1190371df8af42248a1e Mon Sep 17 00:00:00 2001 From: Henry Jameson <me@hjkos.com> Date: Wed, 21 Nov 2018 04:14:10 +0300 Subject: [PATCH 28/96] attempted fix^W workaround for tab-switcher bug --- .../style_switcher/style_switcher.vue | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/src/components/style_switcher/style_switcher.vue b/src/components/style_switcher/style_switcher.vue index 54ea072f..fbf9a20a 100644 --- a/src/components/style_switcher/style_switcher.vue +++ b/src/components/style_switcher/style_switcher.vue @@ -134,14 +134,16 @@ </div> </div> <div :label="$t('settings.style.radii._tab_label')" class="radius-container"> - <p>{{$t('settings.radii_help')}}</p> - <RangeInput name="btnRadius" :label="$t('settings.btnRadius')" v-model="btnRadiusLocal" :fallback="previewTheme.radii.btn" max="16" hardMin="0"/> - <RangeInput name="inputRadius" :label="$t('settings.inputRadius')" v-model="inputRadiusLocal" :fallback="previewTheme.radii.input" max="16" hardMin="0"/> - <RangeInput name="panelRadius" :label="$t('settings.panelRadius')" v-model="panelRadiusLocal" :fallback="previewTheme.radii.panel" max="50" hardMin="0"/> - <RangeInput name="avatarRadius" :label="$t('settings.avatarRadius')" v-model="avatarRadiusLocal" :fallback="previewTheme.radii.avatar" max="28" hardMin="0"/> - <RangeInput name="avatarAltRadius" :label="$t('settings.avatarAltRadius')" v-model="avatarAltRadiusLocal" :fallback="previewTheme.radii.avatarAlt" max="28" hardMin="0"/> - <RangeInput name="attachmentRadius" :label="$t('settings.attachmentRadius')" v-model="attachmentRadiusLocal" :fallback="previewTheme.radii.attachment" max="50" hardMin="0"/> - <RangeInput name="tooltipRadius" :label="$t('settings.tooltipRadius')" v-model="tooltipRadiusLocal" :fallback="previewTheme.radii.tooltip" max="50" hardMin="0"/> + <div> + <p>{{$t('settings.radii_help')}}</p> + <RangeInput name="btnRadius" :label="$t('settings.btnRadius')" v-model="btnRadiusLocal" :fallback="previewTheme.radii.btn" max="16" hardMin="0"/> + <RangeInput name="inputRadius" :label="$t('settings.inputRadius')" v-model="inputRadiusLocal" :fallback="previewTheme.radii.input" max="16" hardMin="0"/> + <RangeInput name="panelRadius" :label="$t('settings.panelRadius')" v-model="panelRadiusLocal" :fallback="previewTheme.radii.panel" max="50" hardMin="0"/> + <RangeInput name="avatarRadius" :label="$t('settings.avatarRadius')" v-model="avatarRadiusLocal" :fallback="previewTheme.radii.avatar" max="28" hardMin="0"/> + <RangeInput name="avatarAltRadius" :label="$t('settings.avatarAltRadius')" v-model="avatarAltRadiusLocal" :fallback="previewTheme.radii.avatarAlt" max="28" hardMin="0"/> + <RangeInput name="attachmentRadius" :label="$t('settings.attachmentRadius')" v-model="attachmentRadiusLocal" :fallback="previewTheme.radii.attachment" max="50" hardMin="0"/> + <RangeInput name="tooltipRadius" :label="$t('settings.tooltipRadius')" v-model="tooltipRadiusLocal" :fallback="previewTheme.radii.tooltip" max="50" hardMin="0"/> + </div> </div> <div :label="$t('settings.style.shadows._tab_label')" class="shadow-container"> <div class="shadow-selector"> From 3d6547001ecf9ae5e923d399dfe2c80b920beecf Mon Sep 17 00:00:00 2001 From: Henry Jameson <me@hjkos.com> Date: Wed, 21 Nov 2018 04:14:32 +0300 Subject: [PATCH 29/96] panels now have shadow-overlay so that it's possible to have inset shadow all over the panel, without header overlapping it --- src/App.scss | 25 +++++++++++++++++++++---- 1 file changed, 21 insertions(+), 4 deletions(-) diff --git a/src/App.scss b/src/App.scss index 970a5f74..30020722 100644 --- a/src/App.scss +++ b/src/App.scss @@ -317,16 +317,33 @@ main-router { .panel { display: flex; + position: relative; + flex-direction: column; margin: 0.5em; background-color: $fallback--bg; background-color: var(--bg, $fallback--bg); - border-radius: $fallback--panelRadius; - border-radius: var(--panelRadius, $fallback--panelRadius); - box-shadow: 1px 1px 4px rgba(0,0,0,.6); - box-shadow: var(--panelShadow); + &::after, & { + border-radius: $fallback--panelRadius; + border-radius: var(--panelRadius, $fallback--panelRadius); + } + + &::after { + content: ''; + position: absolute; + + top: 0; + bottom: 0; + left: 0; + right: 0; + + pointer-events: none; + + box-shadow: 1px 1px 4px rgba(0,0,0,.6); + box-shadow: var(--panelShadow); + } } .panel-body:empty::before { From 50562eb6b700cee84f70210fd1adc8df3b18b92b Mon Sep 17 00:00:00 2001 From: Henry Jameson <me@hjkos.com> Date: Wed, 21 Nov 2018 04:26:45 +0300 Subject: [PATCH 30/96] fix lint, for shadows, it's now possible to refer css variables as colors --- .../user_card_content/user_card_content.js | 1 - src/services/style_setter/style_setter.js | 20 ++++++++++++++++++- 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/src/components/user_card_content/user_card_content.js b/src/components/user_card_content/user_card_content.js index 4c7b6a68..e8073021 100644 --- a/src/components/user_card_content/user_card_content.js +++ b/src/components/user_card_content/user_card_content.js @@ -1,5 +1,4 @@ import StillImage from '../still-image/still-image.vue' -import { hex2rgb } from '../../services/color_convert/color_convert.js' export default { props: [ 'user', 'switcher', 'selected', 'hideBio' ], diff --git a/src/services/style_setter/style_setter.js b/src/services/style_setter/style_setter.js index 45e950f0..1b7ec7f2 100644 --- a/src/services/style_setter/style_setter.js +++ b/src/services/style_setter/style_setter.js @@ -101,11 +101,29 @@ const getCssShadow = (input) => { shad.blur, shad.spread ].map(_ => _ + 'px').concat([ - rgb2rgba({...(hex2rgb(shad.color)), a: shad.alpha}), + getCssColor(shad.color, shad.alpha), shad.inset ? 'inset' : '' ]).join(' ')).join(', ') } +const getCssColor = (input, a) => { + console.log(input) + let rgb = {} + if (typeof input === 'object') { + rgb = input + } else if (typeof input === 'string') { + if (input.startsWith('#')) { + rgb = hex2rgb(input) + } else if (input.startsWith('--')) { + return `var(${input})` + } else { + return input + } + } + console.log(rgb2rgba({ ...rgb, a })) + return rgb2rgba({ ...rgb, a }) +} + const generateColors = (input) => { const colors = {} const opacity = Object.assign({ From acf414e4517434d8847e842930c697b168d94cde Mon Sep 17 00:00:00 2001 From: Henry Jameson <me@hjkos.com> Date: Wed, 21 Nov 2018 07:38:00 +0300 Subject: [PATCH 31/96] changed the way tab-switcher works to avoid removing/adding nodes since that seems to cause issues, instead hiding nodes with css. --- src/components/tab_switcher/tab_switcher.jsx | 19 ++++++++++--------- src/components/tab_switcher/tab_switcher.scss | 5 +++++ 2 files changed, 15 insertions(+), 9 deletions(-) diff --git a/src/components/tab_switcher/tab_switcher.jsx b/src/components/tab_switcher/tab_switcher.jsx index ea582450..a044b740 100644 --- a/src/components/tab_switcher/tab_switcher.jsx +++ b/src/components/tab_switcher/tab_switcher.jsx @@ -1,7 +1,5 @@ import Vue from 'vue' -// FIXME: This doesn't like v-if directly inside the tab's contents, breaks vue really bad - import './tab_switcher.scss' export default Vue.component('tab-switcher', { @@ -27,11 +25,14 @@ export default Vue.component('tab-switcher', { } return (<button onClick={this.activateTab(index)} class={ classes.join(' ') }>{slot.data.attrs.label}</button>) }); - const contents = ( - <div> - {this.$slots.default.filter(slot => slot.data)[this.active]} - </div> - ); + const contents = this.$slots.default.filter(_=>_.data).map(( slot, index ) => { + const active = index === this.active + return ( + <div class={active ? 'active' : 'hidden'}> + {slot} + </div> + ) + }); return ( <div class="tab-switcher"> <div class="tabs"> @@ -40,7 +41,7 @@ export default Vue.component('tab-switcher', { <div class="contents"> {contents} </div> - </div> - ) + </div> + ) } }) diff --git a/src/components/tab_switcher/tab_switcher.scss b/src/components/tab_switcher/tab_switcher.scss index 6f3f9f27..4740fbde 100644 --- a/src/components/tab_switcher/tab_switcher.scss +++ b/src/components/tab_switcher/tab_switcher.scss @@ -1,6 +1,11 @@ @import '../../_variables.scss'; .tab-switcher { + .contents { + .hidden { + display: none; + } + } .tabs { display: flex; position: relative; From dc3df7bc4e91abf4a911e4c5ecc0c627d00dcfb1 Mon Sep 17 00:00:00 2001 From: Henry Jameson <me@hjkos.com> Date: Wed, 21 Nov 2018 18:22:05 +0300 Subject: [PATCH 32/96] fixes --- src/components/notifications/notifications.scss | 1 + src/components/post_status_form/post_status_form.vue | 4 ++-- src/components/timeline/timeline.vue | 2 +- src/components/user_card_content/user_card_content.js | 6 ++++-- src/services/style_setter/style_setter.js | 3 +++ 5 files changed, 11 insertions(+), 5 deletions(-) diff --git a/src/components/notifications/notifications.scss b/src/components/notifications/notifications.scss index 26455ffb..3f22b690 100644 --- a/src/components/notifications/notifications.scss +++ b/src/components/notifications/notifications.scss @@ -35,6 +35,7 @@ right: 0; left: 0; bottom: 0; + pointer-events: none; } &.unseen { diff --git a/src/components/post_status_form/post_status_form.vue b/src/components/post_status_form/post_status_form.vue index 4514e79f..751b048a 100644 --- a/src/components/post_status_form/post_status_form.vue +++ b/src/components/post_status_form/post_status_form.vue @@ -154,7 +154,7 @@ margin-left: $fallback--attachmentRadius; margin-left: var(--attachmentRadius, $fallback--attachmentRadius); background-color: $fallback--fg; - background-color: var(--fg, $fallback--fg); + background-color: var(--btn, $fallback--fg); border-bottom-left-radius: 0; border-bottom-right-radius: 0; } @@ -292,7 +292,7 @@ &.highlighted { background-color: $fallback--fg; - background-color: var(--fg, $fallback--fg); + background-color: var(--lightBg, $fallback--fg); } } } diff --git a/src/components/timeline/timeline.vue b/src/components/timeline/timeline.vue index c2d5b9e6..39f1b5bc 100644 --- a/src/components/timeline/timeline.vue +++ b/src/components/timeline/timeline.vue @@ -75,6 +75,6 @@ padding: 10px; z-index: 1; background-color: $fallback--fg; - background-color: var(--fg, $fallback--fg); + background-color: var(--panel, $fallback--fg); } </style> diff --git a/src/components/user_card_content/user_card_content.js b/src/components/user_card_content/user_card_content.js index e8073021..6f9ed9fe 100644 --- a/src/components/user_card_content/user_card_content.js +++ b/src/components/user_card_content/user_card_content.js @@ -1,11 +1,13 @@ import StillImage from '../still-image/still-image.vue' +import { hex2rgb } from '../../services/color_convert/color_convert.js' export default { props: [ 'user', 'switcher', 'selected', 'hideBio' ], computed: { headingStyle () { - const rgb = this.$store.state.config.customTheme.colors.bg - if (rgb) { + const color = this.$store.state.config.customTheme.colors.bg + if (color) { + const rgb = (typeof color === 'string') ? hex2rgb(color) : color const tintColor = `rgba(${Math.floor(rgb.r)}, ${Math.floor(rgb.g)}, ${Math.floor(rgb.b)}, .5)` return { backgroundColor: `rgb(${Math.floor(rgb.r * 0.53)}, ${Math.floor(rgb.g * 0.56)}, ${Math.floor(rgb.b * 0.59)})`, diff --git a/src/services/style_setter/style_setter.js b/src/services/style_setter/style_setter.js index 1b7ec7f2..57bd2841 100644 --- a/src/services/style_setter/style_setter.js +++ b/src/services/style_setter/style_setter.js @@ -202,6 +202,9 @@ const generateColors = (input) => { colors[k + 'Link'].a = v colors['panelFaint'].a = v } + if (k === 'bg') { + colors['lightBg'].a = v + } if (colors[k]) { colors[k].a = v } else { From 3bdcdefc9bd697413c5ef4b3e0b606e045b8e612 Mon Sep 17 00:00:00 2001 From: Henry Jameson <me@hjkos.com> Date: Wed, 21 Nov 2018 20:18:49 +0300 Subject: [PATCH 33/96] better tooltips, localized, too --- .../contrast_ratio/contrast_ratio.vue | 28 +++++++++++++++---- src/i18n/en.json | 14 +++++++++- 2 files changed, 35 insertions(+), 7 deletions(-) diff --git a/src/components/contrast_ratio/contrast_ratio.vue b/src/components/contrast_ratio/contrast_ratio.vue index 9a93dcd0..f4f9ea86 100644 --- a/src/components/contrast_ratio/contrast_ratio.vue +++ b/src/components/contrast_ratio/contrast_ratio.vue @@ -1,6 +1,6 @@ <template> <span v-if="contrast" class="contrast-ratio"> - <span :title="`Contrast is ${contrast.text}`" class="rating"> + <span :title="hint" class="rating"> <span v-if="contrast.aaa"> <i class="icon-thumbs-up-alt"/> </span> @@ -11,14 +11,14 @@ <i class="icon-attention"/> </span> </span> - <span class="rating" v-if="contrast && large" :title="`Contrast is ${contrast.text} (18pt+)`"> - <span v-if="contrast.aaa"> + <span class="rating" v-if="contrast && large" :title="hint_18pt"> + <span v-if="contrast.laaa"> <i class="icon-thumbs-up-alt"/> </span> - <span v-if="!contrast.aaa && contrast.aa"> + <span v-if="!contrast.laaa && contrast.laa"> <i class="icon-adjust"/> </span> - <span v-if="!contrast.aaa && !contrast.aa"> + <span v-if="!contrast.laaa && !contrast.laa"> <i class="icon-attention"/> </span> </span> @@ -29,7 +29,23 @@ export default { props: [ 'large', 'contrast' - ] + ], + computed: { + hint () { + const levelVal = this.contrast.aaa ? 'aaa' : (this.contrast.aa ? 'aa' : 'bad') + const level = this.$t(`settings.style.common.contrast.level.${levelVal}`) + const context = this.$t('settings.style.common.contrast.context.text') + const ratio = this.contrast.text + return this.$t('settings.style.common.contrast.hint', { level, context, ratio }) + }, + hint_18pt () { + const levelVal = this.contrast.laaa ? 'aaa' : (this.contrast.laa ? 'aa' : 'bad') + const level = this.$t(`settings.style.common.contrast.level.${levelVal}`) + const context = this.$t('settings.style.common.contrast.context.18pt') + const ratio = this.contrast.text + return this.$t('settings.style.common.contrast.hint', { level, context, ratio }) + } + } } </script> diff --git a/src/i18n/en.json b/src/i18n/en.json index 04e9977b..18b47ba7 100644 --- a/src/i18n/en.json +++ b/src/i18n/en.json @@ -165,7 +165,19 @@ "style": { "common": { "color": "Color", - "opacity": "Opacity" + "opacity": "Opacity", + "contrast": { + "hint": "Contrast ratio is {ratio}, it {level} {context}", + "level": { + "aa": "meets Level AA guideline (minimal)", + "aaa": "meets Level AAA guideline (recommended)", + "bad": "doesn't meet any accessibility guidelines" + }, + "context": { + "18pt": "for large (18pt+) text", + "text": "for text" + } + } }, "basic_colors": { "_tab_label": "Basic", From 621ab806e63e42e0495fa174fd64f8eaeeb46b91 Mon Sep 17 00:00:00 2001 From: Henry Jameson <me@hjkos.com> Date: Wed, 21 Nov 2018 21:23:07 +0300 Subject: [PATCH 34/96] more default shadows, replaced original shadows with generated ones. maybe gotta update fallbacks... --- src/App.scss | 17 +++-- src/components/status/status.vue | 1 + .../user_card_content/user_card_content.vue | 1 + src/i18n/en.json | 13 +++- src/services/style_setter/style_setter.js | 76 +++++++++++++++++++ 5 files changed, 98 insertions(+), 10 deletions(-) diff --git a/src/App.scss b/src/App.scss index 30020722..a41140ef 100644 --- a/src/App.scss +++ b/src/App.scss @@ -58,9 +58,10 @@ button { border-radius: $fallback--btnRadius; border-radius: var(--btnRadius, $fallback--btnRadius); cursor: pointer; - border-top: 1px solid rgba(255, 255, 255, 0.2); - border-bottom: 1px solid rgba(0, 0, 0, 0.2); + /* border-top: 1px solid rgba(255, 255, 255, 0.2); */ + /* border-bottom: 1px solid rgba(0, 0, 0, 0.2); */ box-shadow: 0px 0px 2px black; + box-shadow: var(--buttonShadow); font-size: 14px; font-family: sans-serif; @@ -75,11 +76,12 @@ button { &:hover { box-shadow: 0px 0px 4px rgba(255, 255, 255, 0.3); + box-shadow: var(--buttonHoverShadow); } &:active { - border-bottom: 1px solid rgba(255, 255, 255, 0.2); - border-top: 1px solid rgba(0, 0, 0, 0.2); + /* border-bottom: 1px solid rgba(255, 255, 255, 0.2); */ + /* border-top: 1px solid rgba(0, 0, 0, 0.2); */ } &:disabled { @@ -104,9 +106,10 @@ input, textarea, .select { border: none; border-radius: $fallback--inputRadius; border-radius: var(--inputRadius, $fallback--inputRadius); - border-bottom: 1px solid rgba(255, 255, 255, 0.2); - border-top: 1px solid rgba(0, 0, 0, 0.2); + /* border-bottom: 1px solid rgba(255, 255, 255, 0.2); */ + /* border-top: 1px solid rgba(0, 0, 0, 0.2); */ box-shadow: 0px 0px 2px black inset; + box-shadow: var(--inputShadow); background-color: $fallback--fg; background-color: var(--input, $fallback--fg); color: $fallback--lightText; @@ -184,6 +187,7 @@ input, textarea, .select { border-bottom: 1px solid rgba(255, 255, 255, 0.2); border-top: 1px solid rgba(0, 0, 0, 0.2); box-shadow: 0px 0px 2px black inset; + box-shadow: var(--inputShadow); margin-right: .5em; background-color: $fallback--fg; background-color: var(--input, $fallback--fg); @@ -365,6 +369,7 @@ main-router { background-color: $fallback--fg; background-color: var(--panel, $fallback--fg); align-items: baseline; + box-shadow: var(--panelHeaderShadow); .title { flex: 1 0 auto; diff --git a/src/components/status/status.vue b/src/components/status/status.vue index 57a007d9..c4a268d0 100644 --- a/src/components/status/status.vue +++ b/src/components/status/status.vue @@ -146,6 +146,7 @@ border-radius: $fallback--tooltipRadius; border-radius: var(--tooltipRadius, $fallback--tooltipRadius); box-shadow: 2px 2px 3px rgba(0, 0, 0, 0.5); + box-shadow: var(--popupShadow); margin-top: 0.25em; margin-left: 0.5em; z-index: 50; diff --git a/src/components/user_card_content/user_card_content.vue b/src/components/user_card_content/user_card_content.vue index f1b54fad..a2bb99a1 100644 --- a/src/components/user_card_content/user_card_content.vue +++ b/src/components/user_card_content/user_card_content.vue @@ -155,6 +155,7 @@ width: 56px; height: 56px; box-shadow: 0px 1px 8px rgba(0,0,0,0.75); + box-shadow: var(--avatarShadow); object-fit: cover; &.animated::before { diff --git a/src/i18n/en.json b/src/i18n/en.json index 18b47ba7..3fcc6da2 100644 --- a/src/i18n/en.json +++ b/src/i18n/en.json @@ -212,11 +212,16 @@ "inset": "Inset", "components": { "panel": "Panel", + "panelHeader": "Panel header", + "topBar": "Top bar", + "avatar": "User avatar (in post display)", + "avatarStatus": "User avatar (in profile view)", + "popup": "Popups and tooltips", "button": "Button", - "button_hover": "Button (hover)", - "button_pressed": "Button (pressed)", - "button_pressed_hover": "Button (pressed+hover)", - "input_box": "Input field" + "buttonHover": "Button (hover)", + "buttonPressed": "Button (pressed)", + "buttonPressedHover": "Button (pressed+hover)", + "input": "Input field" } } } diff --git a/src/services/style_setter/style_setter.js b/src/services/style_setter/style_setter.js index 57bd2841..8142da8b 100644 --- a/src/services/style_setter/style_setter.js +++ b/src/services/style_setter/style_setter.js @@ -258,6 +258,40 @@ const generateRadii = (input) => { } const generateShadows = (input) => { + const buttonInsetFakeBorders = [{ + x: 0, + y: 101, + blur: 0, + spread: -100, + color: '#FFFFFF', + alpha: 0.2, + inset: true + }, { + x: 0, + y: -101, + blur: 0, + spread: -100, + color: '#000000', + alpha: 0.2, + inset: true + }] + const inputInsetFakeBorders = [{ + x: 0, + y: 101, + blur: 0, + spread: -100, + color: '#000000', + alpha: 0.2, + inset: true + }, { + x: 0, + y: -101, + blur: 0, + spread: -100, + color: '#FFFFFF', + alpha: 0.3, + inset: true + }] const shadows = { panel: [{ x: 1, @@ -267,6 +301,48 @@ const generateShadows = (input) => { color: '#000000', alpha: 0.6 }], + popup: [{ + x: 2, + y: 2, + blur: 3, + spread: 0, + color: '#000000', + alpha: 0.5 + }], + avatar: [{ + x: 0, + y: 1, + blur: 8, + spread: 0, + color: '#000000', + alpha: 0.7 + }], + panelHeader: [], + button: [{ + x: 0, + y: 0, + blur: 2, + spread: 0, + color: '#000000', + alpha: 1 + }, ...buttonInsetFakeBorders], + buttonHover: [{ + x: 0, + y: 0, + blur: 4, + spread: 0, + color: '--faint', + alpha: 1 + }, ...buttonInsetFakeBorders], + input: [{ + x: 0, + y: 0, + blur: 2, + inset: true, + spread: 0, + color: '#000000', + alpha: 1 + }, ...inputInsetFakeBorders], ...(input.shadows || {}) } From de88cfb94d9df08ad8afa7df66d499a2fa249a98 Mon Sep 17 00:00:00 2001 From: Henry Jameson <me@hjkos.com> Date: Wed, 21 Nov 2018 21:28:22 +0300 Subject: [PATCH 35/96] compensate tab-switcher for fake borders --- src/components/tab_switcher/tab_switcher.scss | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/components/tab_switcher/tab_switcher.scss b/src/components/tab_switcher/tab_switcher.scss index 4740fbde..fee30800 100644 --- a/src/components/tab_switcher/tab_switcher.scss +++ b/src/components/tab_switcher/tab_switcher.scss @@ -13,6 +13,8 @@ width: 100%; overflow: hidden; padding-top: 5px; + height: 32px; + box-sizing: border-box; &::after, &::before { display: block; @@ -29,7 +31,7 @@ .tab { border-bottom-left-radius: 0; border-bottom-right-radius: 0; - padding: .3em 1em; + padding: .3em 1em 99px; &:not(.active) { border-bottom: 1px solid; From 017fa60a82b4a7183dcbac0debf44de3b324ba07 Mon Sep 17 00:00:00 2001 From: Henry Jameson <me@hjkos.com> Date: Wed, 21 Nov 2018 21:32:51 +0300 Subject: [PATCH 36/96] better default pleroma shadows, matches original borders more closely --- src/services/style_setter/style_setter.js | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/services/style_setter/style_setter.js b/src/services/style_setter/style_setter.js index 8142da8b..b77b2d01 100644 --- a/src/services/style_setter/style_setter.js +++ b/src/services/style_setter/style_setter.js @@ -260,34 +260,34 @@ const generateRadii = (input) => { const generateShadows = (input) => { const buttonInsetFakeBorders = [{ x: 0, - y: 101, + y: 1, blur: 0, - spread: -100, + spread: 0, color: '#FFFFFF', alpha: 0.2, inset: true }, { x: 0, - y: -101, + y: -1, blur: 0, - spread: -100, + spread: 0, color: '#000000', alpha: 0.2, inset: true }] const inputInsetFakeBorders = [{ x: 0, - y: 101, + y: 1, blur: 0, - spread: -100, + spread: 0, color: '#000000', alpha: 0.2, inset: true }, { x: 0, - y: -101, + y: -1, blur: 0, - spread: -100, + spread: 0, color: '#FFFFFF', alpha: 0.3, inset: true @@ -334,7 +334,7 @@ const generateShadows = (input) => { color: '--faint', alpha: 1 }, ...buttonInsetFakeBorders], - input: [{ + input: [...inputInsetFakeBorders, { x: 0, y: 0, blur: 2, @@ -342,7 +342,7 @@ const generateShadows = (input) => { spread: 0, color: '#000000', alpha: 1 - }, ...inputInsetFakeBorders], + }], ...(input.shadows || {}) } From b3ec3d450c8daf100ae7654c8db69551aa5b1a75 Mon Sep 17 00:00:00 2001 From: Henry Jameson <me@hjkos.com> Date: Wed, 21 Nov 2018 21:35:18 +0300 Subject: [PATCH 37/96] fixup! better default pleroma shadows, matches original borders more closely --- src/services/style_setter/style_setter.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/services/style_setter/style_setter.js b/src/services/style_setter/style_setter.js index b77b2d01..60eec7ea 100644 --- a/src/services/style_setter/style_setter.js +++ b/src/services/style_setter/style_setter.js @@ -289,7 +289,7 @@ const generateShadows = (input) => { blur: 0, spread: 0, color: '#FFFFFF', - alpha: 0.3, + alpha: 0.2, inset: true }] const shadows = { From 18e0828ee7bf4d90267db62bc079e1d8b3cf45ee Mon Sep 17 00:00:00 2001 From: Henry Jameson <me@hjkos.com> Date: Wed, 21 Nov 2018 21:40:45 +0300 Subject: [PATCH 38/96] last shadow override i wanted to make for now. also small tweak --- src/App.scss | 1 + src/components/post_status_form/post_status_form.vue | 2 ++ src/components/style_switcher/style_switcher.js | 2 +- src/services/style_setter/style_setter.js | 8 ++++++++ 4 files changed, 12 insertions(+), 1 deletion(-) diff --git a/src/App.scss b/src/App.scss index a41140ef..e8aa5846 100644 --- a/src/App.scss +++ b/src/App.scss @@ -430,6 +430,7 @@ nav { color: $fallback--faint; color: var(--faint, $fallback--faint); box-shadow: 0px 0px 4px rgba(0,0,0,.6); + box-shadow: var(--topBarShadow); } .fade-enter-active, .fade-leave-active { diff --git a/src/components/post_status_form/post_status_form.vue b/src/components/post_status_form/post_status_form.vue index 751b048a..1c79cab3 100644 --- a/src/components/post_status_form/post_status_form.vue +++ b/src/components/post_status_form/post_status_form.vue @@ -258,6 +258,8 @@ position: absolute; z-index: 1; box-shadow: 1px 2px 4px rgba(0, 0, 0, 0.5); + // this doesn't match original but i don't care, making it uniform. + box-shadow: var(--popupShadow); min-width: 75%; background: $fallback--bg; background: var(--bg, $fallback--bg); diff --git a/src/components/style_switcher/style_switcher.js b/src/components/style_switcher/style_switcher.js index d0f72427..c39aa95b 100644 --- a/src/components/style_switcher/style_switcher.js +++ b/src/components/style_switcher/style_switcher.js @@ -259,7 +259,7 @@ export default { return [...Object.values(this.preview.rules), 'color: var(--text)'].join(';') }, shadowsAvailable () { - return Object.keys(this.previewTheme.shadows) + return Object.keys(this.previewTheme.shadows).sort() }, currentShadowOverriden: { get () { diff --git a/src/services/style_setter/style_setter.js b/src/services/style_setter/style_setter.js index 60eec7ea..20f54a84 100644 --- a/src/services/style_setter/style_setter.js +++ b/src/services/style_setter/style_setter.js @@ -301,6 +301,14 @@ const generateShadows = (input) => { color: '#000000', alpha: 0.6 }], + topBar: [{ + x: 0, + y: 0, + blur: 4, + spread: 0, + color: '#000000', + alpha: 0.6 + }], popup: [{ x: 2, y: 2, From 92afd6af12abc0a11f721a8f4c181c55522743ac Mon Sep 17 00:00:00 2001 From: Henry Jameson <me@hjkos.com> Date: Wed, 21 Nov 2018 22:01:34 +0300 Subject: [PATCH 39/96] layout fixes --- .../contrast_ratio/contrast_ratio.vue | 3 +++ .../style_switcher/style_switcher.scss | 17 +++++++++++++---- .../style_switcher/style_switcher.vue | 10 ++++------ src/i18n/en.json | 6 +++--- 4 files changed, 23 insertions(+), 13 deletions(-) diff --git a/src/components/contrast_ratio/contrast_ratio.vue b/src/components/contrast_ratio/contrast_ratio.vue index f4f9ea86..bd971d00 100644 --- a/src/components/contrast_ratio/contrast_ratio.vue +++ b/src/components/contrast_ratio/contrast_ratio.vue @@ -54,6 +54,9 @@ export default { display: flex; justify-content: flex-end; + margin-top: -4px; + margin-bottom: 5px; + .label { margin-right: 1em; } diff --git a/src/components/style_switcher/style_switcher.scss b/src/components/style_switcher/style_switcher.scss index 6c6e913a..f8529b4f 100644 --- a/src/components/style_switcher/style_switcher.scss +++ b/src/components/style_switcher/style_switcher.scss @@ -59,6 +59,10 @@ color: var(--cRed, $fallback--cRed); } + .tab-switcher { + margin: 0 -1em; + } + .apply-container, .radius-container, .color-container, @@ -75,10 +79,19 @@ } .color-container{ + > h4 { + width: 99%; + } flex-wrap: wrap; justify-content: space-between; } + .color-container, + .shadow-container, + .radius-container { + margin: 1em 1em 0; + } + .presets-container, .shadow-selector { display: flex; @@ -139,10 +152,6 @@ display:flex; align-items: baseline; } - - h4 { - margin-top: 1em; - } } .radius-item { diff --git a/src/components/style_switcher/style_switcher.vue b/src/components/style_switcher/style_switcher.vue index fbf9a20a..4ed62242 100644 --- a/src/components/style_switcher/style_switcher.vue +++ b/src/components/style_switcher/style_switcher.vue @@ -48,9 +48,9 @@ <p>{{$t('settings.theme_help')}}</p> <keep-alive> <tab-switcher key="style-tweak"> - <div :label="$t('settings.style.basic_colors._tab_label')" class="color-container"> + <div :label="$t('settings.style.common_colors._tab_label')" class="color-container"> + <h4>{{ $t('settings.style.common_colors.main') }}</h4> <div class="color-item"> - <h4>{{ $t('settings.style.basic_colors.main') }}</h4> <ColorInput name="bgColor" v-model="bgColorLocal" :label="$t('settings.background')"/> <OpacityInput name="bgOpacity" v-model="bgOpacityLocal" :fallback="previewTheme.opacity.bg || 1"/> <ColorInput name="textColor" v-model="textColorLocal" :label="$t('settings.text')"/> @@ -59,21 +59,19 @@ <ContrastRatio :contrast="previewContrast.bgLink"/> </div> <div class="color-item"> - <h4>{{ $t('settings.style.basic_colors.foreground') }}</h4> <ColorInput name="fgColor" v-model="fgColorLocal" :label="$t('settings.foreground')"/> <ColorInput name="fgTextColor" v-model="fgTextColorLocal" :label="$t('settings.text')" :fallback="previewTheme.colors.fgText"/> <ColorInput name="fgLinkColor" v-model="fgLinkColorLocal" :label="$t('settings.links')" :fallback="previewTheme.colors.fgLink"/> - <p>{{ $t('settings.style.basic_colors.foreground_hint') }}</p> + <p>{{ $t('settings.style.common_colors.foreground_hint') }}</p> </div> + <h4>{{ $t('settings.style.common_colors.rgbo') }}</h4> <div class="color-item"> - <h4>{{ $t('settings.style.basic_colors.rgbo') }}</h4> <ColorInput name="cRedColor" v-model="cRedColorLocal" :label="$t('settings.cRed')"/> <ContrastRatio :contrast="previewContrast.bgRed"/> <ColorInput name="cBlueColor" v-model="cBlueColorLocal" :label="$t('settings.cBlue')"/> <ContrastRatio :contrast="previewContrast.bgBlue"/> </div> <div class="color-item"> - <h4>.</h4> <ColorInput name="cGreenColor" v-model="cGreenColorLocal" :label="$t('settings.cGreen')"/> <ContrastRatio :contrast="previewContrast.bgGreen"/> <ColorInput name="cOrangeColor" v-model="cOrangeColorLocal" :label="$t('settings.cOrange')"/> diff --git a/src/i18n/en.json b/src/i18n/en.json index 3fcc6da2..b039757e 100644 --- a/src/i18n/en.json +++ b/src/i18n/en.json @@ -179,9 +179,9 @@ } } }, - "basic_colors": { - "_tab_label": "Basic", - "main": "Basic colors", + "common_colors": { + "_tab_label": "Common", + "main": "Common colors", "foreground": "Panel header, top bar, buttons, text fields", "foreground_hint": "See \"Advanced\" tab for more detailed control", "rgbo": "Icons, accents, badges" From b8b5dbf63e73e9ca7f5683cb6cca8d47e9cee49c Mon Sep 17 00:00:00 2001 From: Henry Jameson <me@hjkos.com> Date: Wed, 21 Nov 2018 22:08:27 +0300 Subject: [PATCH 40/96] fix --- src/components/tab_switcher/tab_switcher.jsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/tab_switcher/tab_switcher.jsx b/src/components/tab_switcher/tab_switcher.jsx index a044b740..9e3dee04 100644 --- a/src/components/tab_switcher/tab_switcher.jsx +++ b/src/components/tab_switcher/tab_switcher.jsx @@ -41,7 +41,7 @@ export default Vue.component('tab-switcher', { <div class="contents"> {contents} </div> - </div> - ) + </div> + ) } }) From cd6c5b3e33fc71d68892f5d6985a3d27125468bc Mon Sep 17 00:00:00 2001 From: Henry Jameson <me@hjkos.com> Date: Wed, 21 Nov 2018 23:30:47 +0300 Subject: [PATCH 41/96] fix for tab-switcher --- src/components/tab_switcher/tab_switcher.scss | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/src/components/tab_switcher/tab_switcher.scss b/src/components/tab_switcher/tab_switcher.scss index fee30800..bd93e988 100644 --- a/src/components/tab_switcher/tab_switcher.scss +++ b/src/components/tab_switcher/tab_switcher.scss @@ -29,15 +29,24 @@ } .tab { + position: relative; border-bottom-left-radius: 0; border-bottom-right-radius: 0; - padding: .3em 1em 99px; + padding: 5px 1em 99px; &:not(.active) { - border-bottom: 1px solid; - border-bottom-color: $fallback--border; - border-bottom-color: var(--border, $fallback--border); z-index: 4; + + &::after { + content: ''; + position: absolute; + left: 0; + right: 0; + top: 26px; + border-bottom: 1px solid; + border-bottom-color: $fallback--border; + border-bottom-color: var(--border, $fallback--border); + } } &.active { From 379144f4abe7075d4a86b44d09d712fde5ebaae6 Mon Sep 17 00:00:00 2001 From: Henry Jameson <me@hjkos.com> Date: Thu, 22 Nov 2018 03:55:45 +0300 Subject: [PATCH 42/96] fix for zero-state for shadow-control --- .../shadow_control/shadow_control.js | 39 +++++++++++-------- .../style_switcher/style_switcher.js | 3 -- .../style_switcher/style_switcher.vue | 2 +- src/services/style_setter/style_setter.js | 2 - 4 files changed, 23 insertions(+), 23 deletions(-) diff --git a/src/components/shadow_control/shadow_control.js b/src/components/shadow_control/shadow_control.js index b4f48668..a1484d09 100644 --- a/src/components/shadow_control/shadow_control.js +++ b/src/components/shadow_control/shadow_control.js @@ -4,13 +4,17 @@ import { getCssShadow } from '../../services/style_setter/style_setter.js' import { hex2rgb } from '../../services/color_convert/color_convert.js' export default { + // 'Value' and 'Fallback' can be undefined, but if they are + // initially vue won't detect it when they become something else + // therefore i'm using "ready" which should be passed as true when + // data becomes available props: [ - 'value', 'fallback' + 'value', 'fallback', 'ready' ], data () { return { selectedId: 0, - cValue: this.value || this.fallback + cValue: this.value || this.fallback || [] } }, components: { @@ -42,27 +46,28 @@ export default { }, computed: { selected () { - return this.isReady && this.cValue[this.selectedId] || { - x: 0, - y: 0, - blur: 0, - spread: 0, - inset: false, - color: '#000000', - alpha: 1 + if (this.ready && this.cValue.length > 0) { + return this.cValue[this.selectedId] + } else { + return { + x: 0, + y: 0, + blur: 0, + spread: 0, + inset: false, + color: '#000000', + alpha: 1 + } } }, moveUpValid () { - return this.isReady && this.selectedId > 0 + return this.ready && this.selectedId > 0 }, moveDnValid () { - return this.isReady && this.selectedId < this.cValue.length - 1 - }, - isReady () { - return typeof this.cValue !== 'undefined' + return this.ready && this.selectedId < this.cValue.length - 1 }, present () { - return this.isReady && + return this.ready && typeof this.cValue[this.selectedId] !== 'undefined' && !this.usingFallback }, @@ -73,7 +78,7 @@ export default { return hex2rgb(this.selected.color) }, style () { - return this.isReady ? { + return this.ready ? { boxShadow: getCssShadow(this.cValue) } : {} } diff --git a/src/components/style_switcher/style_switcher.js b/src/components/style_switcher/style_switcher.js index c39aa95b..2af77a85 100644 --- a/src/components/style_switcher/style_switcher.js +++ b/src/components/style_switcher/style_switcher.js @@ -182,7 +182,6 @@ export default { }, previewTheme () { if (!this.preview.theme.colors) return { colors: {}, opacity: {}, radii: {}, shadows: {} } - console.log(this.preview.theme.radii) return this.preview.theme }, // This needs optimization maybe @@ -382,8 +381,6 @@ export default { normalizeLocalState (input, version = 0) { const colors = input.colors || input const radii = input.radii || input - console.log('Benis') - console.log(JSON.stringify(radii, null, 2)) const opacity = input.opacity || input const shadows = input.shadows || {} diff --git a/src/components/style_switcher/style_switcher.vue b/src/components/style_switcher/style_switcher.vue index 4ed62242..658bb288 100644 --- a/src/components/style_switcher/style_switcher.vue +++ b/src/components/style_switcher/style_switcher.vue @@ -170,7 +170,7 @@ <label class="checkbox-label" for="override"></label> </div> </div> - <shadow-control :fallback="currentShadowFallback" v-model="currentShadow"/> + <shadow-control :ready="!!currentShadowFallback" :fallback="currentShadowFallback" v-model="currentShadow"/> </div> </tab-switcher> </keep-alive> diff --git a/src/services/style_setter/style_setter.js b/src/services/style_setter/style_setter.js index 20f54a84..4a48f419 100644 --- a/src/services/style_setter/style_setter.js +++ b/src/services/style_setter/style_setter.js @@ -107,7 +107,6 @@ const getCssShadow = (input) => { } const getCssColor = (input, a) => { - console.log(input) let rgb = {} if (typeof input === 'object') { rgb = input @@ -120,7 +119,6 @@ const getCssColor = (input, a) => { return input } } - console.log(rgb2rgba({ ...rgb, a })) return rgb2rgba({ ...rgb, a }) } From 324aadb7c180baf51e77d08058683c7e256bfcfb Mon Sep 17 00:00:00 2001 From: Henry Jameson <me@hjkos.com> Date: Thu, 22 Nov 2018 04:25:15 +0300 Subject: [PATCH 43/96] fix --- src/components/shadow_control/shadow_control.vue | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/components/shadow_control/shadow_control.vue b/src/components/shadow_control/shadow_control.vue index f8066947..2bc05cb7 100644 --- a/src/components/shadow_control/shadow_control.vue +++ b/src/components/shadow_control/shadow_control.vue @@ -43,7 +43,7 @@ <label for="shadow-switcher" class="select"> <select v-model="selectedId" class="shadow-switcher" - :disabled="!isReady || usingFallback" + :disabled="!ready || usingFallback" id="shadow-switcher"> <option v-for="(shadow, index) in cValue" :value="index"> {{$t('settings.style.shadows.shadow_id', { value: index })}} @@ -51,7 +51,7 @@ </select> <i class="icon-down-open"/> </label> - <button class="btn btn-default" :disabled="!isReady || !present" @click="del"> + <button class="btn btn-default" :disabled="!ready || !present" @click="del"> <i class="icon-cancel"/> </button> <button class="btn btn-default" :disabled="!moveUpValid" @click="moveUp"> @@ -60,7 +60,7 @@ <button class="btn btn-default" :disabled="!moveDnValid" @click="moveDn"> <i class="icon-down-open"/> </button> - <button class="btn btn-default" :disabled="!isReady" @click="add"> + <button class="btn btn-default" :disabled="!ready" @click="add"> <i class="icon-plus"/> </button> </div> From 631b8433c04799a04da5369273f5d1861a346739 Mon Sep 17 00:00:00 2001 From: Henry Jameson <me@hjkos.com> Date: Thu, 22 Nov 2018 04:37:49 +0300 Subject: [PATCH 44/96] bundling v2 themes works --- .../style_switcher/style_switcher.js | 2 + .../style_switcher/style_switcher.vue | 6 +- static/styles.json | 228 +++++++++++++++++- 3 files changed, 232 insertions(+), 4 deletions(-) diff --git a/src/components/style_switcher/style_switcher.js b/src/components/style_switcher/style_switcher.js index 2af77a85..710694d4 100644 --- a/src/components/style_switcher/style_switcher.js +++ b/src/components/style_switcher/style_switcher.js @@ -447,6 +447,8 @@ export default { this.cGreenColorLocal = this.selected[6] this.cBlueColorLocal = this.selected[7] this.cOrangeColorLocal = this.selected[8] + } else if (this.selectedVersion >= 2) { + this.normalizeLocalState(this.selected.theme, 2) } } } diff --git a/src/components/style_switcher/style_switcher.vue b/src/components/style_switcher/style_switcher.vue index 658bb288..37709363 100644 --- a/src/components/style_switcher/style_switcher.vue +++ b/src/components/style_switcher/style_switcher.vue @@ -8,10 +8,10 @@ <option v-for="style in availableStyles" :value="style" :style="{ - backgroundColor: style[1], - color: style[3] + backgroundColor: style[1] || style.theme.colors.bg, + color: style[3] || style.theme.colors.text }"> - {{style[0]}} + {{style[0] || style.name}} </option> </select> <i class="icon-down-open"/> diff --git a/static/styles.json b/static/styles.json index 7116ef20..1155e73e 100644 --- a/static/styles.json +++ b/static/styles.json @@ -5,5 +5,231 @@ "bird": [ "Bird", "#f8fafd", "#e6ecf0", "#14171a", "#0084b8", "#e0245e", "#17bf63", "#1b95e0", "#fab81e"], "ir-black": [ "Ir Black", "#000000", "#242422", "#b5b3aa", "#ff6c60", "#FF6C60", "#A8FF60", "#96CBFE", "#FFFFB6" ], "monokai": [ "Monokai", "#272822", "#383830", "#f8f8f2", "#f92672", "#F92672", "#a6e22e", "#66d9ef", "#f4bf75" ], - "mammal": [ "Mammal", "#272c37", "#444b5d", "#f8f8f8", "#9bacc8", "#7f3142", "#2bd850", "#2b90d9", "#ca8f04" ] + "mammal": [ "Mammal", "#272c37", "#444b5d", "#f8f8f8", "#9bacc8", "#7f3142", "#2bd850", "#2b90d9", "#ca8f04" ], + "redmond-xx": { + "_pleroma_theme_version": 2, + "name": "Redmond XX", + "theme": { + "shadows": { + "panel": [ + { + "x": "-1", + "y": "-1", + "blur": "0", + "spread": 0, + "color": "#000000", + "alpha": "1", + "inset": true + }, + { + "x": "1", + "y": "1", + "blur": "0", + "spread": 0, + "color": "--bg", + "alpha": "1", + "inset": true + }, + { + "x": "2", + "y": "2", + "blur": "0", + "spread": 0, + "color": "#FFFFFF", + "alpha": "1", + "inset": true + }, + { + "x": "-2", + "y": "-2", + "blur": "0", + "spread": 0, + "color": "#848484", + "alpha": "1", + "inset": true + }, + { + "x": "0", + "y": "0", + "blur": "0", + "spread": "3", + "color": "--bg", + "alpha": "1", + "inset": true + } + ], + "button": [ + { + "x": "-1", + "y": "-1", + "blur": "0", + "spread": 0, + "color": "#000000", + "alpha": "1", + "inset": true + }, + { + "x": "1", + "y": "1", + "blur": "0", + "spread": 0, + "color": "--bg", + "alpha": "1", + "inset": true + }, + { + "x": "2", + "y": "2", + "blur": "0", + "spread": 0, + "color": "#FFFFFF", + "alpha": "1", + "inset": true + }, + { + "x": "-2", + "y": "-2", + "blur": "0", + "spread": 0, + "color": "#848484", + "alpha": "1", + "inset": true + }, + { + "x": "0", + "y": "0", + "blur": "0", + "spread": "3", + "color": "--bg", + "alpha": "1", + "inset": true + } + ], + "buttonHover": [ + { + "x": "-1", + "y": "-1", + "blur": "0", + "spread": 0, + "color": "#000000", + "alpha": "1", + "inset": true + }, + { + "x": "1", + "y": "1", + "blur": "0", + "spread": 0, + "color": "--bg", + "alpha": "1", + "inset": true + }, + { + "x": "2", + "y": "2", + "blur": "0", + "spread": 0, + "color": "#FFFFFF", + "alpha": "1", + "inset": true + }, + { + "x": "-2", + "y": "-2", + "blur": "0", + "spread": 0, + "color": "#848484", + "alpha": "1", + "inset": true + }, + { + "x": "0", + "y": "0", + "blur": "0", + "spread": "3", + "color": "--bg", + "alpha": "1", + "inset": true + } + ], + "input": [ + { + "x": "1", + "y": "1", + "blur": "0", + "spread": 0, + "color": "#000000", + "alpha": "1", + "inset": true + }, + { + "x": "-1", + "y": "-1", + "blur": "0", + "spread": 0, + "color": "--bg", + "alpha": "1", + "inset": true + }, + { + "x": "-2", + "y": "-2", + "blur": "0", + "spread": 0, + "color": "#FFFFFF", + "alpha": "1", + "inset": true + }, + { + "x": "2", + "y": "2", + "blur": "0", + "spread": 0, + "color": "#848484", + "alpha": "1", + "inset": true + }, + { + "x": "0", + "y": "0", + "blur": "0", + "spread": "3", + "color": "--input", + "alpha": "1", + "inset": true + } + ] + }, + "opacity": { + "input": "1" + }, + "colors": { + "bg": "#c0c0c0", + "text": "#000000", + "link": "#0000ff", + "fg": "#c0c0c0", + "panel": "#000080", + "input": "#ffffff", + "topBar": "#000080", + "topBarLink": "#ffffff", + "btn": "#c0c0c0", + "faint": "#3f3f3f", + "faintLink": "#404080", + "border": "#808080", + "cRed": "#FF0000", + "cBlue": "#008080", + "cGreen": "#00FF00", + "cOrange": "#808000" + }, + "radii": { + "btn": "0", + "input": "0", + "panel": "0", + "avatar": "0", + "avatarAlt": "0", + "tooltip": "0", + "attachment": "0" + } + } + } } From d6f7cb469c95af95791984eabeb301028962e22b Mon Sep 17 00:00:00 2001 From: Henry Jameson <me@hjkos.com> Date: Thu, 22 Nov 2018 05:13:09 +0300 Subject: [PATCH 45/96] small tab-switcher tweak --- src/components/tab_switcher/tab_switcher.scss | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/components/tab_switcher/tab_switcher.scss b/src/components/tab_switcher/tab_switcher.scss index bd93e988..9fd9d905 100644 --- a/src/components/tab_switcher/tab_switcher.scss +++ b/src/components/tab_switcher/tab_switcher.scss @@ -34,6 +34,10 @@ border-bottom-right-radius: 0; padding: 5px 1em 99px; + &:hover { + z-index: 6; + } + &:not(.active) { z-index: 4; From 29082e9aee3dc50acfd5f1635f1a09017b83a893 Mon Sep 17 00:00:00 2001 From: Henry Jameson <me@hjkos.com> Date: Fri, 23 Nov 2018 00:24:16 +0300 Subject: [PATCH 46/96] fixed checkbox styles, optimized default shadows --- src/App.scss | 9 +--- src/components/tab_switcher/tab_switcher.scss | 8 +-- src/services/style_setter/style_setter.js | 50 ++++++------------- 3 files changed, 20 insertions(+), 47 deletions(-) diff --git a/src/App.scss b/src/App.scss index e8aa5846..3059d753 100644 --- a/src/App.scss +++ b/src/App.scss @@ -58,8 +58,6 @@ button { border-radius: $fallback--btnRadius; border-radius: var(--btnRadius, $fallback--btnRadius); cursor: pointer; - /* border-top: 1px solid rgba(255, 255, 255, 0.2); */ - /* border-bottom: 1px solid rgba(0, 0, 0, 0.2); */ box-shadow: 0px 0px 2px black; box-shadow: var(--buttonShadow); font-size: 14px; @@ -80,8 +78,7 @@ button { } &:active { - /* border-bottom: 1px solid rgba(255, 255, 255, 0.2); */ - /* border-top: 1px solid rgba(0, 0, 0, 0.2); */ + box-shadow: var(--buttonPressedShadow); } &:disabled { @@ -106,8 +103,6 @@ input, textarea, .select { border: none; border-radius: $fallback--inputRadius; border-radius: var(--inputRadius, $fallback--inputRadius); - /* border-bottom: 1px solid rgba(255, 255, 255, 0.2); */ - /* border-top: 1px solid rgba(0, 0, 0, 0.2); */ box-shadow: 0px 0px 2px black inset; box-shadow: var(--inputShadow); background-color: $fallback--fg; @@ -184,8 +179,6 @@ input, textarea, .select { height: 1.1em; border-radius: $fallback--checkBoxRadius; border-radius: var(--checkBoxRadius, $fallback--checkBoxRadius); - border-bottom: 1px solid rgba(255, 255, 255, 0.2); - border-top: 1px solid rgba(0, 0, 0, 0.2); box-shadow: 0px 0px 2px black inset; box-shadow: var(--inputShadow); margin-right: .5em; diff --git a/src/components/tab_switcher/tab_switcher.scss b/src/components/tab_switcher/tab_switcher.scss index 9fd9d905..d0e5ea87 100644 --- a/src/components/tab_switcher/tab_switcher.scss +++ b/src/components/tab_switcher/tab_switcher.scss @@ -34,13 +34,13 @@ border-bottom-right-radius: 0; padding: 5px 1em 99px; - &:hover { - z-index: 6; - } - &:not(.active) { z-index: 4; + &:hover { + z-index: 6; + } + &::after { content: ''; position: absolute; diff --git a/src/services/style_setter/style_setter.js b/src/services/style_setter/style_setter.js index 4a48f419..10583722 100644 --- a/src/services/style_setter/style_setter.js +++ b/src/services/style_setter/style_setter.js @@ -256,40 +256,26 @@ const generateRadii = (input) => { } const generateShadows = (input) => { - const buttonInsetFakeBorders = [{ + const border = (top, shadow) => ({ x: 0, - y: 1, + y: top ? 1 : -1, blur: 0, spread: 0, - color: '#FFFFFF', + color: shadow ? '#000000' : '#FFFFFF', alpha: 0.2, inset: true - }, { + }) + const buttonInsetFakeBorders = [border(true, false), border(false, true)] + const inputInsetFakeBorders = [border(true, true), border(false, false)] + const hoverGlow = { x: 0, - y: -1, - blur: 0, + y: 0, + blur: 4, spread: 0, - color: '#000000', - alpha: 0.2, - inset: true - }] - const inputInsetFakeBorders = [{ - x: 0, - y: 1, - blur: 0, - spread: 0, - color: '#000000', - alpha: 0.2, - inset: true - }, { - x: 0, - y: -1, - blur: 0, - spread: 0, - color: '#FFFFFF', - alpha: 0.2, - inset: true - }] + color: '--faint', + alpha: 1 + } + const shadows = { panel: [{ x: 1, @@ -332,14 +318,8 @@ const generateShadows = (input) => { color: '#000000', alpha: 1 }, ...buttonInsetFakeBorders], - buttonHover: [{ - x: 0, - y: 0, - blur: 4, - spread: 0, - color: '--faint', - alpha: 1 - }, ...buttonInsetFakeBorders], + buttonHover: [hoverGlow, ...buttonInsetFakeBorders], + buttonPressed: [hoverGlow, ...inputInsetFakeBorders], input: [...inputInsetFakeBorders, { x: 0, y: 0, From 7af6be84bb8d019510b14dfe2e614156c92ff201 Mon Sep 17 00:00:00 2001 From: Henry Jameson <me@hjkos.com> Date: Fri, 23 Nov 2018 00:30:28 +0300 Subject: [PATCH 47/96] fake borders fallback --- src/App.scss | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/App.scss b/src/App.scss index 3059d753..8732d23c 100644 --- a/src/App.scss +++ b/src/App.scss @@ -58,7 +58,7 @@ button { border-radius: $fallback--btnRadius; border-radius: var(--btnRadius, $fallback--btnRadius); cursor: pointer; - box-shadow: 0px 0px 2px black; + box-shadow: 0px 0px 2px 0px rgba(0, 0, 0, 1), 0px 1px 0px 0px rgba(255, 255, 255, 0.2) inset, 0px -1px 0px 0px rgba(0, 0, 0, 0.2) inset; box-shadow: var(--buttonShadow); font-size: 14px; font-family: sans-serif; @@ -78,6 +78,7 @@ button { } &:active { + box-shadow: 0px 0px 4px 0px rgba(255, 255, 255, 0.3), 0px 1px 0px 0px rgba(0, 0, 0, 0.2) inset, 0px -1px 0px 0px rgba(255, 255, 255, 0.2) inset; box-shadow: var(--buttonPressedShadow); } @@ -103,7 +104,7 @@ input, textarea, .select { border: none; border-radius: $fallback--inputRadius; border-radius: var(--inputRadius, $fallback--inputRadius); - box-shadow: 0px 0px 2px black inset; + box-shadow: 0px 1px 0px 0px rgba(0, 0, 0, 0.2) inset, 0px -1px 0px 0px rgba(255, 255, 255, 0.2) inset, 0px 0px 2px 0px rgba(0, 0, 0, 1) inset; box-shadow: var(--inputShadow); background-color: $fallback--fg; background-color: var(--input, $fallback--fg); From 8fd1b87e87e53c33de3976c3a87266a25a3905f7 Mon Sep 17 00:00:00 2001 From: Henry Jameson <me@hjkos.com> Date: Fri, 23 Nov 2018 01:13:57 +0300 Subject: [PATCH 48/96] more authentic redmond theme --- static/styles.json | 131 ++++++++++++++++++++++++++++++--------------- 1 file changed, 89 insertions(+), 42 deletions(-) diff --git a/static/styles.json b/static/styles.json index 1155e73e..4585dff0 100644 --- a/static/styles.json +++ b/static/styles.json @@ -26,16 +26,7 @@ "y": "1", "blur": "0", "spread": 0, - "color": "--bg", - "alpha": "1", - "inset": true - }, - { - "x": "2", - "y": "2", - "blur": "0", - "spread": 0, - "color": "#FFFFFF", + "color": "#dfdfdf", "alpha": "1", "inset": true }, @@ -48,6 +39,15 @@ "alpha": "1", "inset": true }, + { + "x": "2", + "y": "2", + "blur": "0", + "spread": 0, + "color": "#FFFFFF", + "alpha": "1", + "inset": true + }, { "x": "0", "y": "0", @@ -73,15 +73,6 @@ "y": "1", "blur": "0", "spread": 0, - "color": "--bg", - "alpha": "1", - "inset": true - }, - { - "x": "2", - "y": "2", - "blur": "0", - "spread": 0, "color": "#FFFFFF", "alpha": "1", "inset": true @@ -95,6 +86,15 @@ "alpha": "1", "inset": true }, + { + "x": "2", + "y": "2", + "blur": "0", + "spread": 0, + "color": "#dfdfdf", + "alpha": "1", + "inset": true + }, { "x": "0", "y": "0", @@ -120,15 +120,6 @@ "y": "1", "blur": "0", "spread": 0, - "color": "--bg", - "alpha": "1", - "inset": true - }, - { - "x": "2", - "y": "2", - "blur": "0", - "spread": 0, "color": "#FFFFFF", "alpha": "1", "inset": true @@ -142,6 +133,62 @@ "alpha": "1", "inset": true }, + { + "x": "2", + "y": "2", + "blur": "0", + "spread": 0, + "color": "#dfdfdf", + "alpha": "1", + "inset": true + }, + { + "x": "0", + "y": "0", + "blur": "0", + "spread": "3", + "color": "--bg", + "alpha": "1", + "inset": true + } + ], + "buttonPressed": [ + { + "x": "1", + "y": "1", + "blur": "0", + "spread": 0, + "color": "#000000", + "alpha": "1", + "inset": true + }, + { + "x": "-1", + "y": "-1", + "blur": "0", + "spread": 0, + "color": "#FFFFFF", + "alpha": "1", + "inset": true + }, + { + "x": "2", + "y": "2", + "blur": "0", + "spread": 0, + "color": "#848484", + "alpha": "1", + "inset": true + }, + { + "x": "-2", + "y": "-2", + "blur": "0", + "spread": 0, + "color": "#dfdfdf", + "alpha": "1", + "inset": true + }, { "x": "0", "y": "0", @@ -153,21 +200,21 @@ } ], "input": [ - { - "x": "1", - "y": "1", - "blur": "0", - "spread": 0, - "color": "#000000", - "alpha": "1", - "inset": true - }, { "x": "-1", "y": "-1", "blur": "0", "spread": 0, - "color": "--bg", + "color": "#FFFFFF", + "alpha": "1", + "inset": true + }, + { + "x": "1", + "y": "1", + "blur": "0", + "spread": 0, + "color": "#848484", "alpha": "1", "inset": true }, @@ -176,7 +223,7 @@ "y": "-2", "blur": "0", "spread": 0, - "color": "#FFFFFF", + "color": "#dfdfdf", "alpha": "1", "inset": true }, @@ -185,7 +232,7 @@ "y": "2", "blur": "0", "spread": 0, - "color": "#848484", + "color": "#000000", "alpha": "1", "inset": true }, @@ -197,8 +244,8 @@ "color": "--input", "alpha": "1", "inset": true - } - ] + } + ] }, "opacity": { "input": "1" From d2f3b6d2442a04e6bcb244d0644c8c0dd6caa8fe Mon Sep 17 00:00:00 2001 From: Henry Jameson <me@hjkos.com> Date: Fri, 23 Nov 2018 01:45:08 +0300 Subject: [PATCH 49/96] more styles, temporarily in one file, fix for panel header box-shadow affecting the user-card one --- .../user_card_content/user_card_content.vue | 1 + static/styles.json | 572 +++++++++++++++++- 2 files changed, 571 insertions(+), 2 deletions(-) diff --git a/src/components/user_card_content/user_card_content.vue b/src/components/user_card_content/user_card_content.vue index a2bb99a1..14eb4023 100644 --- a/src/components/user_card_content/user_card_content.vue +++ b/src/components/user_card_content/user_card_content.vue @@ -124,6 +124,7 @@ .panel-heading { padding: 0.6em 0em; text-align: center; + box-shadow: none; } } diff --git a/static/styles.json b/static/styles.json index 4585dff0..d9e1ea8c 100644 --- a/static/styles.json +++ b/static/styles.json @@ -244,8 +244,8 @@ "color": "--input", "alpha": "1", "inset": true - } - ] + } + ] }, "opacity": { "input": "1" @@ -278,5 +278,573 @@ "attachment": "0" } } + }, + "redmond-xx-se": { + "_pleroma_theme_version": 2, + "name": "Redmond XX SE", + "theme": { + "shadows": { + "panel": [ + { + "x": "-1", + "y": "-1", + "blur": "0", + "spread": 0, + "color": "#000000", + "alpha": "1", + "inset": true + }, + { + "x": "1", + "y": "1", + "blur": "0", + "spread": 0, + "color": "#dfdfdf", + "alpha": "1", + "inset": true + }, + { + "x": "-2", + "y": "-2", + "blur": "0", + "spread": 0, + "color": "#848484", + "alpha": "1", + "inset": true + }, + { + "x": "2", + "y": "2", + "blur": "0", + "spread": 0, + "color": "#FFFFFF", + "alpha": "1", + "inset": true + }, + { + "x": "0", + "y": "0", + "blur": "0", + "spread": "3", + "color": "--bg", + "alpha": "1", + "inset": true + } + ], + "button": [ + { + "x": "-1", + "y": "-1", + "blur": "0", + "spread": 0, + "color": "#000000", + "alpha": "1", + "inset": true + }, + { + "x": "1", + "y": "1", + "blur": "0", + "spread": 0, + "color": "#FFFFFF", + "alpha": "1", + "inset": true + }, + { + "x": "-2", + "y": "-2", + "blur": "0", + "spread": 0, + "color": "#848484", + "alpha": "1", + "inset": true + }, + { + "x": "2", + "y": "2", + "blur": "0", + "spread": 0, + "color": "#dfdfdf", + "alpha": "1", + "inset": true + }, + { + "x": "0", + "y": "0", + "blur": "0", + "spread": "3", + "color": "--bg", + "alpha": "1", + "inset": true + } + ], + "buttonHover": [ + { + "x": "-1", + "y": "-1", + "blur": "0", + "spread": 0, + "color": "#000000", + "alpha": "1", + "inset": true + }, + { + "x": "1", + "y": "1", + "blur": "0", + "spread": 0, + "color": "#FFFFFF", + "alpha": "1", + "inset": true + }, + { + "x": "-2", + "y": "-2", + "blur": "0", + "spread": 0, + "color": "#848484", + "alpha": "1", + "inset": true + }, + { + "x": "2", + "y": "2", + "blur": "0", + "spread": 0, + "color": "#dfdfdf", + "alpha": "1", + "inset": true + }, + { + "x": "0", + "y": "0", + "blur": "0", + "spread": "3", + "color": "--bg", + "alpha": "1", + "inset": true + } + ], + "buttonPressed": [ + { + "x": "1", + "y": "1", + "blur": "0", + "spread": 0, + "color": "#000000", + "alpha": "1", + "inset": true + }, + { + "x": "-1", + "y": "-1", + "blur": "0", + "spread": 0, + "color": "#FFFFFF", + "alpha": "1", + "inset": true + }, + { + "x": "2", + "y": "2", + "blur": "0", + "spread": 0, + "color": "#848484", + "alpha": "1", + "inset": true + }, + { + "x": "-2", + "y": "-2", + "blur": "0", + "spread": 0, + "color": "#dfdfdf", + "alpha": "1", + "inset": true + }, + { + "x": "0", + "y": "0", + "blur": "0", + "spread": "3", + "color": "--bg", + "alpha": "1", + "inset": true + } + ], + "input": [ + { + "x": "-1", + "y": "-1", + "blur": "0", + "spread": 0, + "color": "#FFFFFF", + "alpha": "1", + "inset": true + }, + { + "x": "1", + "y": "1", + "blur": "0", + "spread": 0, + "color": "#848484", + "alpha": "1", + "inset": true + }, + { + "x": "-2", + "y": "-2", + "blur": "0", + "spread": 0, + "color": "#dfdfdf", + "alpha": "1", + "inset": true + }, + { + "x": "2", + "y": "2", + "blur": "0", + "spread": 0, + "color": "#000000", + "alpha": "1", + "inset": true + }, + { + "x": "0", + "y": "0", + "blur": "0", + "spread": "3", + "color": "--input", + "alpha": "1", + "inset": true + } + ], + "panelHeader": [ + { + "x": "-2200", + "y": 0, + "blur": "200", + "spread": "-2000", + "inset": true, + "color": "#1084d0", + "alpha": 1 + } + ] + }, + "opacity": { + "input": "1" + }, + "colors": { + "bg": "#c0c0c0", + "text": "#000000", + "link": "#0000ff", + "fg": "#c0c0c0", + "panel": "#000080", + "input": "#ffffff", + "topBar": "#000080", + "topBarLink": "#ffffff", + "btn": "#c0c0c0", + "faint": "#3f3f3f", + "faintLink": "#404080", + "border": "#808080", + "cRed": "#FF0000", + "cBlue": "#008080", + "cGreen": "#00FF00", + "cOrange": "#808000" + }, + "radii": { + "btn": "0", + "input": "0", + "panel": "0", + "avatar": "0", + "avatarAlt": "0", + "tooltip": "0", + "attachment": "0" + } + } + }, + "redmond-xxi": { + "_pleroma_theme_version": 2, + "name": "Redmond XXI", + "theme": { + "shadows": { + "panel": [ + { + "x": "-1", + "y": "-1", + "blur": "0", + "spread": 0, + "color": "#000000", + "alpha": "1", + "inset": true + }, + { + "x": "1", + "y": "1", + "blur": "0", + "spread": 0, + "color": "#dfdfdf", + "alpha": "1", + "inset": true + }, + { + "x": "-2", + "y": "-2", + "blur": "0", + "spread": 0, + "color": "#848484", + "alpha": "1", + "inset": true + }, + { + "x": "2", + "y": "2", + "blur": "0", + "spread": 0, + "color": "#FFFFFF", + "alpha": "1", + "inset": true + }, + { + "x": "0", + "y": "0", + "blur": "0", + "spread": "3", + "color": "--bg", + "alpha": "1", + "inset": true + } + ], + "button": [ + { + "x": "-1", + "y": "-1", + "blur": "0", + "spread": 0, + "color": "#000000", + "alpha": "1", + "inset": true + }, + { + "x": "1", + "y": "1", + "blur": "0", + "spread": 0, + "color": "#FFFFFF", + "alpha": "1", + "inset": true + }, + { + "x": "-2", + "y": "-2", + "blur": "0", + "spread": 0, + "color": "#848484", + "alpha": "1", + "inset": true + }, + { + "x": "2", + "y": "2", + "blur": "0", + "spread": 0, + "color": "#dfdfdf", + "alpha": "1", + "inset": true + }, + { + "x": "0", + "y": "0", + "blur": "0", + "spread": "3", + "color": "--bg", + "alpha": "1", + "inset": true + } + ], + "buttonHover": [ + { + "x": "-1", + "y": "-1", + "blur": "0", + "spread": 0, + "color": "#000000", + "alpha": "1", + "inset": true + }, + { + "x": "1", + "y": "1", + "blur": "0", + "spread": 0, + "color": "#FFFFFF", + "alpha": "1", + "inset": true + }, + { + "x": "-2", + "y": "-2", + "blur": "0", + "spread": 0, + "color": "#848484", + "alpha": "1", + "inset": true + }, + { + "x": "2", + "y": "2", + "blur": "0", + "spread": 0, + "color": "#dfdfdf", + "alpha": "1", + "inset": true + }, + { + "x": "0", + "y": "0", + "blur": "0", + "spread": "3", + "color": "--bg", + "alpha": "1", + "inset": true + } + ], + "buttonPressed": [ + { + "x": "1", + "y": "1", + "blur": "0", + "spread": 0, + "color": "#000000", + "alpha": "1", + "inset": true + }, + { + "x": "-1", + "y": "-1", + "blur": "0", + "spread": 0, + "color": "#FFFFFF", + "alpha": "1", + "inset": true + }, + { + "x": "2", + "y": "2", + "blur": "0", + "spread": 0, + "color": "#848484", + "alpha": "1", + "inset": true + }, + { + "x": "-2", + "y": "-2", + "blur": "0", + "spread": 0, + "color": "#dfdfdf", + "alpha": "1", + "inset": true + }, + { + "x": "0", + "y": "0", + "blur": "0", + "spread": "3", + "color": "--bg", + "alpha": "1", + "inset": true + } + ], + "input": [ + { + "x": "-1", + "y": "-1", + "blur": "0", + "spread": 0, + "color": "#FFFFFF", + "alpha": "1", + "inset": true + }, + { + "x": "1", + "y": "1", + "blur": "0", + "spread": 0, + "color": "#848484", + "alpha": "1", + "inset": true + }, + { + "x": "-2", + "y": "-2", + "blur": "0", + "spread": 0, + "color": "#dfdfdf", + "alpha": "1", + "inset": true + }, + { + "x": "2", + "y": "2", + "blur": "0", + "spread": 0, + "color": "#000000", + "alpha": "1", + "inset": true + }, + { + "x": "0", + "y": "0", + "blur": "0", + "spread": "3", + "color": "--input", + "alpha": "1", + "inset": true + } + ], + "panelHeader": [ + { + "x": "-2200", + "y": 0, + "blur": "200", + "spread": "-2000", + "inset": true, + "color": "#a5cef7", + "alpha": 1 + } + ] + }, + "opacity": { + "input": "1" + }, + "colors": { + "bg": "#d6d6ce", + "text": "#000000", + "link": "#0000ff", + "fg": "#d6d6ce", + "panel": "#042967", + "input": "#ffffff", + "topBar": "#042967", + "topBarLink": "#ffffff", + "btn": "#d6d6ce", + "faint": "#3f3f3f", + "faintLink": "#404080", + "border": "#808080", + "cRed": "#FF0000", + "cBlue": "#008080", + "cGreen": "#00FF00", + "cOrange": "#808000" + }, + "radii": { + "btn": "0", + "input": "0", + "panel": "0", + "avatar": "0", + "avatarAlt": "0", + "tooltip": "0", + "attachment": "0" + } + } } } From 91ea9b7b0e7b2ad818c0ac026951cd5a9c68bfdf Mon Sep 17 00:00:00 2001 From: Henry Jameson <me@hjkos.com> Date: Fri, 23 Nov 2018 07:28:53 +0300 Subject: [PATCH 50/96] checkbox radius --- src/App.scss | 4 ++-- src/_variables.scss | 2 +- src/components/style_switcher/style_switcher.js | 3 +++ src/components/style_switcher/style_switcher.vue | 3 ++- src/i18n/en.json | 1 + static/styles.json | 3 +++ 6 files changed, 12 insertions(+), 4 deletions(-) diff --git a/src/App.scss b/src/App.scss index 8732d23c..004c6fc3 100644 --- a/src/App.scss +++ b/src/App.scss @@ -178,8 +178,8 @@ input, textarea, .select { transition: color 200ms; width: 1.1em; height: 1.1em; - border-radius: $fallback--checkBoxRadius; - border-radius: var(--checkBoxRadius, $fallback--checkBoxRadius); + border-radius: $fallback--checkboxRadius; + border-radius: var(--checkboxRadius, $fallback--checkboxRadius); box-shadow: 0px 0px 2px black inset; box-shadow: var(--inputShadow); margin-right: .5em; diff --git a/src/_variables.scss b/src/_variables.scss index d0d91efe..150e4fb5 100644 --- a/src/_variables.scss +++ b/src/_variables.scss @@ -19,7 +19,7 @@ $fallback--cOrange: orange; $fallback--alertError: rgba(211,16,20,.5); $fallback--panelRadius: 10px; -$fallback--checkBoxRadius: 2px; +$fallback--checkboxRadius: 2px; $fallback--btnRadius: 4px; $fallback--inputRadius: 4px; $fallback--tooltipRadius: 5px; diff --git a/src/components/style_switcher/style_switcher.js b/src/components/style_switcher/style_switcher.js index 710694d4..edc614c3 100644 --- a/src/components/style_switcher/style_switcher.js +++ b/src/components/style_switcher/style_switcher.js @@ -76,6 +76,7 @@ export default { btnRadiusLocal: '', inputRadiusLocal: '', + checkboxRadiusLocal: '', panelRadiusLocal: '', avatarRadiusLocal: '', avatarAltRadiusLocal: '', @@ -154,6 +155,7 @@ export default { return { btn: this.btnRadiusLocal, input: this.inputRadiusLocal, + checkbox: this.checkboxRadiusLocal, panel: this.panelRadiusLocal, avatar: this.avatarRadiusLocal, avatarAlt: this.avatarAltRadiusLocal, @@ -421,6 +423,7 @@ export default { // TODO optimize this this.btnRadiusLocal = radii.btn this.inputRadiusLocal = radii.input + this.checkboxRadiusLocal = radii.checkbox this.panelRadiusLocal = radii.panel this.avatarRadiusLocal = radii.avatar this.avatarAltRadiusLocal = radii.avatarAlt diff --git a/src/components/style_switcher/style_switcher.vue b/src/components/style_switcher/style_switcher.vue index 37709363..6463a4ca 100644 --- a/src/components/style_switcher/style_switcher.vue +++ b/src/components/style_switcher/style_switcher.vue @@ -135,7 +135,8 @@ <div> <p>{{$t('settings.radii_help')}}</p> <RangeInput name="btnRadius" :label="$t('settings.btnRadius')" v-model="btnRadiusLocal" :fallback="previewTheme.radii.btn" max="16" hardMin="0"/> - <RangeInput name="inputRadius" :label="$t('settings.inputRadius')" v-model="inputRadiusLocal" :fallback="previewTheme.radii.input" max="16" hardMin="0"/> + <RangeInput name="inputRadius" :label="$t('settings.inputRadius')" v-model="inputRadiusLocal" :fallback="previewTheme.radii.input" max="9" hardMin="0"/> + <RangeInput name="checkboxRadius" :label="$t('settings.checkboxRadius')" v-model="checkboxRadiusLocal" :fallback="previewTheme.radii.checkbox" max="16" hardMin="0"/> <RangeInput name="panelRadius" :label="$t('settings.panelRadius')" v-model="panelRadiusLocal" :fallback="previewTheme.radii.panel" max="50" hardMin="0"/> <RangeInput name="avatarRadius" :label="$t('settings.avatarRadius')" v-model="avatarRadiusLocal" :fallback="previewTheme.radii.avatar" max="28" hardMin="0"/> <RangeInput name="avatarAltRadius" :label="$t('settings.avatarAltRadius')" v-model="avatarAltRadiusLocal" :fallback="previewTheme.radii.avatarAlt" max="28" hardMin="0"/> diff --git a/src/i18n/en.json b/src/i18n/en.json index b039757e..f990dd04 100644 --- a/src/i18n/en.json +++ b/src/i18n/en.json @@ -114,6 +114,7 @@ "import_followers_from_a_csv_file": "Import follows from a csv file", "import_theme": "Load preset", "inputRadius": "Input fields", + "checkboxRadius": "Checkboxes", "instance_default": "(default: {value})", "interfaceLanguage": "Interface language", "invalid_theme_imported": "The selected file is not a supported Pleroma theme. No changes to your theme were made.", diff --git a/static/styles.json b/static/styles.json index d9e1ea8c..a53eeaa1 100644 --- a/static/styles.json +++ b/static/styles.json @@ -271,6 +271,7 @@ "radii": { "btn": "0", "input": "0", + "checkbox": "0", "panel": "0", "avatar": "0", "avatarAlt": "0", @@ -555,6 +556,7 @@ "radii": { "btn": "0", "input": "0", + "checkbox": "0", "panel": "0", "avatar": "0", "avatarAlt": "0", @@ -839,6 +841,7 @@ "radii": { "btn": "0", "input": "0", + "checkbox": "0", "panel": "0", "avatar": "0", "avatarAlt": "0", From 754d71ec1933d897f9206ddca080bb258256edbb Mon Sep 17 00:00:00 2001 From: Henry Jameson <me@hjkos.com> Date: Fri, 23 Nov 2018 08:24:55 +0300 Subject: [PATCH 51/96] added checkboxes to keep current roundness and shadows, also cleaned up how shadows/roundness are reset when switching themes --- .../style_switcher/style_switcher.js | 52 +++++++++++++---- .../style_switcher/style_switcher.scss | 18 ++++-- .../style_switcher/style_switcher.vue | 57 ++++++++++++------- src/i18n/en.json | 5 ++ 4 files changed, 99 insertions(+), 33 deletions(-) diff --git a/src/components/style_switcher/style_switcher.js b/src/components/style_switcher/style_switcher.js index edc614c3..0c302e93 100644 --- a/src/components/style_switcher/style_switcher.js +++ b/src/components/style_switcher/style_switcher.js @@ -27,6 +27,9 @@ export default { selected: this.$store.state.config.theme, invalidThemeImported: false, + keepShadows: false, + keepRoundness: false, + textColorLocal: '', linkColorLocal: '', @@ -375,6 +378,19 @@ export default { }) }, + clearRoundness () { + Object.keys(this.$data) + .filter(_ => _.endsWith('RadiusLocal')) + .forEach(key => { + set(this.$data, key, undefined) + }) + }, + + clearShadows () { + this.shadowsLocal = {} + console.log(this.shadowsLocal) + }, + /** * This applies stored theme data onto form. * @param {Object} input - input data @@ -420,17 +436,24 @@ export default { this[key + 'ColorLocal'] = rgb2hex(colors[key]) }) - // TODO optimize this - this.btnRadiusLocal = radii.btn - this.inputRadiusLocal = radii.input - this.checkboxRadiusLocal = radii.checkbox - this.panelRadiusLocal = radii.panel - this.avatarRadiusLocal = radii.avatar - this.avatarAltRadiusLocal = radii.avatarAlt - this.tooltipRadiusLocal = radii.tooltip - this.attachmentRadiusLocal = radii.attachment + if (!this.keepRoundness) { + this.clearRoundness() + // TODO optimize this + this.btnRadiusLocal = radii.btn + this.inputRadiusLocal = radii.input + this.checkboxRadiusLocal = radii.checkbox + this.panelRadiusLocal = radii.panel + this.avatarRadiusLocal = radii.avatar + this.avatarAltRadiusLocal = radii.avatarAlt + this.tooltipRadiusLocal = radii.tooltip + this.attachmentRadiusLocal = radii.attachment + } - this.shadowsLocal = shadows + if (!this.keepShadows) { + this.clearShadows() + this.shadowsLocal = shadows + this.shadowSelected = this.shadowsAvailable[0] + } Object.entries(opacity).forEach(([k, v]) => { if (typeof v === 'undefined' || v === null || Number.isNaN(v)) return @@ -441,7 +464,16 @@ export default { watch: { selected () { if (this.selectedVersion === 1) { + if (!this.keepRoundness) { + this.clearRoundness() + } + + if (!this.keepShadows) { + this.clearShadows() + } + this.clearV1() + this.bgColorLocal = this.selected[1] this.fgColorLocal = this.selected[2] this.textColorLocal = this.selected[3] diff --git a/src/components/style_switcher/style_switcher.scss b/src/components/style_switcher/style_switcher.scss index f8529b4f..9cdc9f33 100644 --- a/src/components/style_switcher/style_switcher.scss +++ b/src/components/style_switcher/style_switcher.scss @@ -66,7 +66,7 @@ .apply-container, .radius-container, .color-container, - .presets-container { + { display: flex; p { @@ -88,12 +88,14 @@ .color-container, .shadow-container, - .radius-container { + .radius-container, + .presets-container { margin: 1em 1em 0; } - .presets-container, - .shadow-selector { + .shadow-selector, + .save-load, + .save-load-options { display: flex; justify-content: center; align-items: baseline; @@ -110,6 +112,14 @@ } } + .save-load-options { + flex-wrap: wrap; + margin-top: .5em; + span { + margin: 0 .5em; + } + } + .preview-container { border-top: 1px dashed; border-bottom: 1px dashed; diff --git a/src/components/style_switcher/style_switcher.vue b/src/components/style_switcher/style_switcher.vue index 6463a4ca..0c6b811d 100644 --- a/src/components/style_switcher/style_switcher.vue +++ b/src/components/style_switcher/style_switcher.vue @@ -1,26 +1,45 @@ <template> <div class="style-switcher"> <div class="presets-container"> - <div> - {{$t('settings.presets')}} - <label for="preset-switcher" class='select'> - <select id="preset-switcher" v-model="selected" class="preset-switcher"> - <option v-for="style in availableStyles" - :value="style" - :style="{ - backgroundColor: style[1] || style.theme.colors.bg, - color: style[3] || style.theme.colors.text - }"> - {{style[0] || style.name}} - </option> - </select> - <i class="icon-down-open"/> - </label> + <div class="save-load"> + <div> + {{$t('settings.presets')}} + <label for="preset-switcher" class='select'> + <select id="preset-switcher" v-model="selected" class="preset-switcher"> + <option v-for="style in availableStyles" + :value="style" + :style="{ + backgroundColor: style[1] || style.theme.colors.bg, + color: style[3] || style.theme.colors.text + }"> + {{style[0] || style.name}} + </option> + </select> + <i class="icon-down-open"/> + </label> + </div> + <div class="import-export"> + <button class="btn" @click="exportCurrentTheme">{{ $t('settings.export_theme') }}</button> + <button class="btn" @click="importTheme">{{ $t('settings.import_theme') }}</button> + <p v-if="invalidThemeImported" class="import-warning">{{ $t('settings.invalid_theme_imported') }}</p> + </div> </div> - <div class="import-export"> - <button class="btn" @click="exportCurrentTheme">{{ $t('settings.export_theme') }}</button> - <button class="btn" @click="importTheme">{{ $t('settings.import_theme') }}</button> - <p v-if="invalidThemeImported" class="import-warning">{{ $t('settings.invalid_theme_imported') }}</p> + <div class="save-load-options"> + <span> + <input + id="keep-shadows" + type="checkbox" + v-model="keepShadows"> + <label for="keep-shadows">{{$t('settings.style.switcher.keep_shadows')}}</label> + </span> + <span> + <input + id="keep-roundness" + type="checkbox" + v-model="keepRoundness"> + <label for="keep-roundness">{{$t('settings.style.switcher.keep_roundness')}}</label> + </span> + <p>{{$t('settings.style.switcher.save_load_hint')}}</p> </div> </div> diff --git a/src/i18n/en.json b/src/i18n/en.json index f990dd04..a9d50dbe 100644 --- a/src/i18n/en.json +++ b/src/i18n/en.json @@ -164,6 +164,11 @@ "true": "yes" }, "style": { + "switcher": { + "keep_shadows": "Keep shadows", + "keep_roundness": "Keep roundness", + "save_load_hint": "\"Keep\" options preserve currently set options when selecting or loading themes, it also stores said options when exporting a theme." + }, "common": { "color": "Color", "opacity": "Opacity", From 1059d9b602656355780f3bcd1ed3d7885de590c1 Mon Sep 17 00:00:00 2001 From: Henry Jameson <me@hjkos.com> Date: Fri, 23 Nov 2018 09:02:10 +0300 Subject: [PATCH 52/96] radii v1 fixes --- src/components/style_switcher/style_switcher.js | 14 +++++--------- src/services/style_setter/style_setter.js | 3 ++- 2 files changed, 7 insertions(+), 10 deletions(-) diff --git a/src/components/style_switcher/style_switcher.js b/src/components/style_switcher/style_switcher.js index 0c302e93..33be522b 100644 --- a/src/components/style_switcher/style_switcher.js +++ b/src/components/style_switcher/style_switcher.js @@ -438,15 +438,11 @@ export default { if (!this.keepRoundness) { this.clearRoundness() - // TODO optimize this - this.btnRadiusLocal = radii.btn - this.inputRadiusLocal = radii.input - this.checkboxRadiusLocal = radii.checkbox - this.panelRadiusLocal = radii.panel - this.avatarRadiusLocal = radii.avatar - this.avatarAltRadiusLocal = radii.avatarAlt - this.tooltipRadiusLocal = radii.tooltip - this.attachmentRadiusLocal = radii.attachment + Object.entries(radii).forEach(([k, v]) => { + // 'Radius' is kept mostly for v1->v2 localstorage transition + const key = k.endsWith('Radius') ? k.split('Radius')[0] : k + this[key + 'RadiusLocal'] = v + }) } if (!this.keepShadows) { diff --git a/src/services/style_setter/style_setter.js b/src/services/style_setter/style_setter.js index 10583722..2662fc42 100644 --- a/src/services/style_setter/style_setter.js +++ b/src/services/style_setter/style_setter.js @@ -233,7 +233,8 @@ const generateColors = (input) => { const generateRadii = (input) => { const radii = Object.entries(input.radii || {}).filter(([k, v]) => v).reduce((acc, [k, v]) => { - acc[k] = v + const key = k.endsWith('Radius') ? k.split('Radius')[0] : k + acc[key] = v return acc }, { btn: 4, From 652b98b13c573f4c8d84f2e55251b2244b243afc Mon Sep 17 00:00:00 2001 From: Henry Jameson <me@hjkos.com> Date: Fri, 23 Nov 2018 09:14:52 +0300 Subject: [PATCH 53/96] fix v1->v2 transition for localstorage --- src/services/style_setter/style_setter.js | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/src/services/style_setter/style_setter.js b/src/services/style_setter/style_setter.js index 2662fc42..d7487eed 100644 --- a/src/services/style_setter/style_setter.js +++ b/src/services/style_setter/style_setter.js @@ -232,9 +232,16 @@ const generateColors = (input) => { } const generateRadii = (input) => { - const radii = Object.entries(input.radii || {}).filter(([k, v]) => v).reduce((acc, [k, v]) => { - const key = k.endsWith('Radius') ? k.split('Radius')[0] : k - acc[key] = v + let inputRadii = input.radii || {} + // v1 -> v2 + if (typeof input.btnRadius !== 'undefined') { + inputRadii = Object + .entries(input) + .filter(([k, v]) => k.endsWith('Radius')) + .reduce((acc, e) => { acc[e[0].split('Radius')[0]] = e[1]; return acc }, {}) + } + const radii = Object.entries(inputRadii).filter(([k, v]) => v).reduce((acc, [k, v]) => { + acc[k] = v return acc }, { btn: 4, From 26b9f787bb01551f991f9e79aa8c0fc59313b95e Mon Sep 17 00:00:00 2001 From: Henry Jameson <me@hjkos.com> Date: Fri, 23 Nov 2018 10:17:01 +0300 Subject: [PATCH 54/96] added "keep opacity" option, fixed opacity loading, fixed missing shadows not affecting the preview (i.e. previewing pleroma-dark when redmond is applied) --- .../style_switcher/style_switcher.js | 27 +++++++++++++++---- .../style_switcher/style_switcher.vue | 7 +++++ src/i18n/en.json | 1 + src/services/style_setter/style_setter.js | 8 ++++-- 4 files changed, 36 insertions(+), 7 deletions(-) diff --git a/src/components/style_switcher/style_switcher.js b/src/components/style_switcher/style_switcher.js index 33be522b..e1a17837 100644 --- a/src/components/style_switcher/style_switcher.js +++ b/src/components/style_switcher/style_switcher.js @@ -28,6 +28,7 @@ export default { invalidThemeImported: false, keepShadows: false, + keepOpacity: false, keepRoundness: false, textColorLocal: '', @@ -386,6 +387,14 @@ export default { }) }, + clearOpacity () { + Object.keys(this.$data) + .filter(_ => _.endsWith('OpacityLocal')) + .forEach(key => { + set(this.$data, key, undefined) + }) + }, + clearShadows () { this.shadowsLocal = {} console.log(this.shadowsLocal) @@ -397,9 +406,10 @@ export default { * @param {Number} version - version of data. 0 means try to guess based on data. */ normalizeLocalState (input, version = 0) { + console.log(input.opacity) const colors = input.colors || input const radii = input.radii || input - const opacity = input.opacity || input + const opacity = input.opacity const shadows = input.shadows || {} if (version === 0) { @@ -451,10 +461,13 @@ export default { this.shadowSelected = this.shadowsAvailable[0] } - Object.entries(opacity).forEach(([k, v]) => { - if (typeof v === 'undefined' || v === null || Number.isNaN(v)) return - this[k + 'OpacityLocal'] = v - }) + if (opacity && !this.keepOpacity) { + this.clearOpacity() + Object.entries(opacity).forEach(([k, v]) => { + if (typeof v === 'undefined' || v === null || Number.isNaN(v)) return + this[k + 'OpacityLocal'] = v + }) + } } }, watch: { @@ -468,6 +481,10 @@ export default { this.clearShadows() } + if (!this.keepOpacity) { + this.clearOpacity() + } + this.clearV1() this.bgColorLocal = this.selected[1] diff --git a/src/components/style_switcher/style_switcher.vue b/src/components/style_switcher/style_switcher.vue index 0c6b811d..ed0dd733 100644 --- a/src/components/style_switcher/style_switcher.vue +++ b/src/components/style_switcher/style_switcher.vue @@ -32,6 +32,13 @@ v-model="keepShadows"> <label for="keep-shadows">{{$t('settings.style.switcher.keep_shadows')}}</label> </span> + <span> + <input + id="keep-opacity" + type="checkbox" + v-model="keepOpacity"> + <label for="keep-opacity">{{$t('settings.style.switcher.keep_opacity')}}</label> + </span> <span> <input id="keep-roundness" diff --git a/src/i18n/en.json b/src/i18n/en.json index a9d50dbe..98a91013 100644 --- a/src/i18n/en.json +++ b/src/i18n/en.json @@ -166,6 +166,7 @@ "style": { "switcher": { "keep_shadows": "Keep shadows", + "keep_opacity": "Keep opacity", "keep_roundness": "Keep roundness", "save_load_hint": "\"Keep\" options preserve currently set options when selecting or loading themes, it also stores said options when exporting a theme." }, diff --git a/src/services/style_setter/style_setter.js b/src/services/style_setter/style_setter.js index d7487eed..60531f28 100644 --- a/src/services/style_setter/style_setter.js +++ b/src/services/style_setter/style_setter.js @@ -94,7 +94,10 @@ const setColors = (input, commit) => { } const getCssShadow = (input) => { - // >shad + if (input.length === 0) { + return 'none' + } + return input.map((shad) => [ shad.x, shad.y, @@ -340,9 +343,10 @@ const generateShadows = (input) => { ...(input.shadows || {}) } + console.log(Object.entries(shadows).map(([k, v]) => `--${k}Shadow: ${getCssShadow(v)}`).join(';')) return { rules: { - shadows: Object.entries(shadows).filter(([k, v]) => v).map(([k, v]) => `--${k}Shadow: ${getCssShadow(v)}`).join(';') + shadows: Object.entries(shadows).map(([k, v]) => `--${k}Shadow: ${getCssShadow(v)}`).join(';') }, theme: { shadows From b07d7d7229c376a0588d86ee8a28735bdcf99b7f Mon Sep 17 00:00:00 2001 From: Henry Jameson <me@hjkos.com> Date: Fri, 23 Nov 2018 11:36:36 +0300 Subject: [PATCH 55/96] reset buttons, better disabled for shadows --- src/App.scss | 5 ++ src/components/settings/settings.vue | 8 +-- .../shadow_control/shadow_control.vue | 9 +++- .../style_switcher/style_switcher.js | 4 ++ .../style_switcher/style_switcher.scss | 54 +++++++++++++++---- .../style_switcher/style_switcher.vue | 21 ++++++-- src/i18n/en.json | 5 +- src/services/style_setter/style_setter.js | 1 + 8 files changed, 85 insertions(+), 22 deletions(-) diff --git a/src/App.scss b/src/App.scss index 004c6fc3..ceb0bb87 100644 --- a/src/App.scss +++ b/src/App.scss @@ -120,6 +120,11 @@ input, textarea, .select { line-height: 16px; hyphens: none; + &:disabled, &[disabled=disabled] { + cursor: not-allowed; + opacity: 0.5; + } + .icon-down-open { position: absolute; top: 0; diff --git a/src/components/settings/settings.vue b/src/components/settings/settings.vue index 990d1f0d..2097fd22 100644 --- a/src/components/settings/settings.vue +++ b/src/components/settings/settings.vue @@ -208,12 +208,8 @@ .btn { min-height: 28px; - } - - .submit { - margin-top: 1em; - min-height: 30px; - width: 10em; + min-width: 10em; + padding: 0 2em; } } .select-multiple { diff --git a/src/components/shadow_control/shadow_control.vue b/src/components/shadow_control/shadow_control.vue index 2bc05cb7..b608d0ff 100644 --- a/src/components/shadow_control/shadow_control.vue +++ b/src/components/shadow_control/shadow_control.vue @@ -40,7 +40,7 @@ <div class="shadow-tweak"> <div :disabled="usingFallback" class="id-control style-control"> - <label for="shadow-switcher" class="select"> + <label for="shadow-switcher" class="select" :disabled="!ready || usingFallback"> <select v-model="selectedId" class="shadow-switcher" :disabled="!ready || usingFallback" @@ -60,7 +60,7 @@ <button class="btn btn-default" :disabled="!moveDnValid" @click="moveDn"> <i class="icon-down-open"/> </button> - <button class="btn btn-default" :disabled="!ready" @click="add"> + <button class="btn btn-default" :disabled="usingFallback" @click="add"> <i class="icon-plus"/> </button> </div> @@ -219,8 +219,13 @@ .id-control { align-items: stretch; .select, .btn { + min-width: 1px; margin-right: 5px; } + .btn { + padding: 0 .4em; + margin: 0 .1em; + } .select { flex: 1; select { diff --git a/src/components/style_switcher/style_switcher.js b/src/components/style_switcher/style_switcher.js index e1a17837..d45ce455 100644 --- a/src/components/style_switcher/style_switcher.js +++ b/src/components/style_switcher/style_switcher.js @@ -369,6 +369,10 @@ export default { }) }, + clearAll () { + this.normalizeLocalState(this.$store.state.config.customTheme) + }, + // Clears all the extra stuff when loading V1 theme clearV1 () { Object.keys(this.$data) diff --git a/src/components/style_switcher/style_switcher.scss b/src/components/style_switcher/style_switcher.scss index 9cdc9f33..ad203856 100644 --- a/src/components/style_switcher/style_switcher.scss +++ b/src/components/style_switcher/style_switcher.scss @@ -63,15 +63,16 @@ margin: 0 -1em; } + .reset-container { + flex-wrap: wrap; + } + + .reset-container, .apply-container, .radius-container, .color-container, { display: flex; - - p { - margin-left: 1em - } } .radius-container { @@ -93,7 +94,38 @@ margin: 1em 1em 0; } - .shadow-selector, + .tab-header { + display: flex; + justify-content: space-between; + align-items: baseline; + width: 100%; + min-height: 30px; + + .btn { + min-width: 1px; + flex: 0 auto; + padding: 0 1em; + } + + p { + flex: 1; + margin: 0; + } + + margin-bottom: 1em; + } + + .shadow-selector { + .override { + flex: 1; + margin-left: .5em; + } + .select-container { + margin-top: -4px; + margin-bottom: -3px; + } + } + .save-load, .save-load-options { display: flex; @@ -102,11 +134,8 @@ .import-export { display: flex; - - .btn { - margin-left: .5em; - } } + .override { margin-left: .5em; } @@ -132,6 +161,7 @@ background-position: 50% 50%; .btn { + margin-left: 0; margin-top: 1em; min-height: 30px; width: 10em; @@ -153,6 +183,7 @@ &.wide { min-width: 60% } + &:not(.wide):nth-child(2n+1) { margin-right: 7px; @@ -200,6 +231,11 @@ padding: 20px; } + .btn { + margin-left: .25em; + margin-right: .25em; + } + .dummy { .avatar { background: linear-gradient(135deg, #b8e1fc 0%,#a9d2f3 10%,#90bae4 25%,#90bcea 37%,#90bff0 50%,#6ba8e5 51%,#a2daf5 83%,#bdf3fd 100%); diff --git a/src/components/style_switcher/style_switcher.vue b/src/components/style_switcher/style_switcher.vue index ed0dd733..9e5cffbe 100644 --- a/src/components/style_switcher/style_switcher.vue +++ b/src/components/style_switcher/style_switcher.vue @@ -71,10 +71,13 @@ </div> </div> - <p>{{$t('settings.theme_help')}}</p> <keep-alive> <tab-switcher key="style-tweak"> <div :label="$t('settings.style.common_colors._tab_label')" class="color-container"> + <div class="tab-header"> + <p>{{$t('settings.theme_help')}}</p> + <button class="btn" @click="clearOpacity">{{$t('settings.style.switcher.clear_opacity')}}</button> + </div> <h4>{{ $t('settings.style.common_colors.main') }}</h4> <div class="color-item"> <ColorInput name="bgColor" v-model="bgColorLocal" :label="$t('settings.background')"/> @@ -106,6 +109,11 @@ </div> <div :label="$t('settings.style.advanced_colors._tab_label')" class="color-container"> + <div class="tab-header"> + <p>{{$t('settings.theme_help')}}</p> + <button class="btn" @click="clearV1">{{$t('settings.style.switcher.clear_all')}}</button> + <button class="btn" @click="clearOpacity">{{$t('settings.style.switcher.clear_opacity')}}</button> + </div> <div class="color-item"> <h4>{{ $t('settings.style.advanced_colors.alert') }}</h4> <ColorInput name="alertError" v-model="alertErrorColorLocal" :label="$t('settings.style.advanced_colors.alert_error')" :fallback="previewTheme.colors.alertError"/> @@ -159,7 +167,10 @@ </div> <div :label="$t('settings.style.radii._tab_label')" class="radius-container"> <div> - <p>{{$t('settings.radii_help')}}</p> + <div class="tab-header"> + <p>{{$t('settings.radii_help')}}</p> + <button class="btn" @click="clearRoundness">{{$t('settings.style.switcher.clear_all')}}</button> + </div> <RangeInput name="btnRadius" :label="$t('settings.btnRadius')" v-model="btnRadiusLocal" :fallback="previewTheme.radii.btn" max="16" hardMin="0"/> <RangeInput name="inputRadius" :label="$t('settings.inputRadius')" v-model="inputRadiusLocal" :fallback="previewTheme.radii.input" max="9" hardMin="0"/> <RangeInput name="checkboxRadius" :label="$t('settings.checkboxRadius')" v-model="checkboxRadiusLocal" :fallback="previewTheme.radii.checkbox" max="16" hardMin="0"/> @@ -171,8 +182,8 @@ </div> </div> <div :label="$t('settings.style.shadows._tab_label')" class="shadow-container"> - <div class="shadow-selector"> - <div> + <div class="tab-header shadow-selector"> + <div class="select-container"> {{$t('settings.style.shadows.component')}} <label for="shadow-switcher" class="select"> <select id="shadow-switcher" v-model="shadowSelected" class="shadow-switcher"> @@ -196,6 +207,7 @@ type="checkbox"> <label class="checkbox-label" for="override"></label> </div> + <button class="btn" @click="clearShadows">{{$t('settings.style.switcher.clear_all')}}</button> </div> <shadow-control :ready="!!currentShadowFallback" :fallback="currentShadowFallback" v-model="currentShadow"/> </div> @@ -204,6 +216,7 @@ <div class="apply-container"> <button class="btn submit" @click="setCustomTheme">{{$t('general.apply')}}</button> + <button class="btn" @click="clearAll">{{$t('settings.style.switcher.reset')}}</button> </div> </div> </template> diff --git a/src/i18n/en.json b/src/i18n/en.json index 98a91013..15d57765 100644 --- a/src/i18n/en.json +++ b/src/i18n/en.json @@ -168,7 +168,10 @@ "keep_shadows": "Keep shadows", "keep_opacity": "Keep opacity", "keep_roundness": "Keep roundness", - "save_load_hint": "\"Keep\" options preserve currently set options when selecting or loading themes, it also stores said options when exporting a theme." + "save_load_hint": "\"Keep\" options preserve currently set options when selecting or loading themes, it also stores said options when exporting a theme.", + "reset": "Reset", + "clear_all": "Clear all", + "clear_opacity": "Clear opacity" }, "common": { "color": "Color", diff --git a/src/services/style_setter/style_setter.js b/src/services/style_setter/style_setter.js index 60531f28..680aadb4 100644 --- a/src/services/style_setter/style_setter.js +++ b/src/services/style_setter/style_setter.js @@ -249,6 +249,7 @@ const generateRadii = (input) => { }, { btn: 4, input: 4, + checkbox: 2, panel: 10, avatar: 5, avatarAlt: 50, From 1a8d24d649228a245e7f5b6ebf6883d8f1c6797b Mon Sep 17 00:00:00 2001 From: Henry Jameson <me@hjkos.com> Date: Sun, 25 Nov 2018 17:21:53 +0300 Subject: [PATCH 56/96] some help strings --- src/components/shadow_control/shadow_control.vue | 4 ++++ src/components/style_switcher/style_switcher.vue | 7 +++++-- src/i18n/en.json | 7 +++++-- src/services/style_setter/style_setter.js | 1 + 4 files changed, 15 insertions(+), 4 deletions(-) diff --git a/src/components/shadow_control/shadow_control.vue b/src/components/shadow_control/shadow_control.vue index b608d0ff..85346e17 100644 --- a/src/components/shadow_control/shadow_control.vue +++ b/src/components/shadow_control/shadow_control.vue @@ -124,6 +124,9 @@ <OpacityInput v-model="selected.alpha" :disabled="!present"/> + <div> + {{$t('settings.style.shadows.hint')}} + </div> </div> </div> </template> @@ -215,6 +218,7 @@ .shadow-tweak { flex: 1; + min-width: 280px; .id-control { align-items: stretch; diff --git a/src/components/style_switcher/style_switcher.vue b/src/components/style_switcher/style_switcher.vue index 9e5cffbe..f5090321 100644 --- a/src/components/style_switcher/style_switcher.vue +++ b/src/components/style_switcher/style_switcher.vue @@ -75,9 +75,11 @@ <tab-switcher key="style-tweak"> <div :label="$t('settings.style.common_colors._tab_label')" class="color-container"> <div class="tab-header"> - <p>{{$t('settings.theme_help')}}</p> + <p>{{$t('settings.theme_help')}}</p> <button class="btn" @click="clearOpacity">{{$t('settings.style.switcher.clear_opacity')}}</button> + <button class="btn" @click="clearV1">{{$t('settings.style.switcher.clear_all')}}</button> </div> + <p>{{$t('settings.theme_help_v2_1')}}</p> <h4>{{ $t('settings.style.common_colors.main') }}</h4> <div class="color-item"> <ColorInput name="bgColor" v-model="bgColorLocal" :label="$t('settings.background')"/> @@ -106,13 +108,14 @@ <ColorInput name="cOrangeColor" v-model="cOrangeColorLocal" :label="$t('settings.cOrange')"/> <ContrastRatio :contrast="previewContrast.bgOrange"/> </div> + <p>{{$t('settings.theme_help_v2_2')}}</p> </div> <div :label="$t('settings.style.advanced_colors._tab_label')" class="color-container"> <div class="tab-header"> <p>{{$t('settings.theme_help')}}</p> - <button class="btn" @click="clearV1">{{$t('settings.style.switcher.clear_all')}}</button> <button class="btn" @click="clearOpacity">{{$t('settings.style.switcher.clear_opacity')}}</button> + <button class="btn" @click="clearV1">{{$t('settings.style.switcher.clear_all')}}</button> </div> <div class="color-item"> <h4>{{ $t('settings.style.advanced_colors.alert') }}</h4> diff --git a/src/i18n/en.json b/src/i18n/en.json index 15d57765..5bd1ddbb 100644 --- a/src/i18n/en.json +++ b/src/i18n/en.json @@ -157,6 +157,8 @@ "text": "Text", "theme": "Theme", "theme_help": "Use hex color codes (#rrggbb) to customize your color theme.", + "theme_help_v2_1": "You can also override certain component's colors and opacity by toggling the checkbox, use \"Clear all\" button to clear all overrides.", + "theme_help_v2_2": "Icons underneath some entries are background/text contrast indicators, hover over for detailed info. Please keep in mind that when using transparency contrast indicators show the worst possible case.", "tooltipRadius": "Tooltips/alerts", "user_settings": "User Settings", "values": { @@ -220,12 +222,13 @@ "blur": "Blur", "spread": "Spread", "inset": "Inset", + "hint": "For shadows you can also use --variable as a color value to use CSS3 variables. Please note that setting opacity won't work in this case.", "components": { "panel": "Panel", "panelHeader": "Panel header", "topBar": "Top bar", - "avatar": "User avatar (in post display)", - "avatarStatus": "User avatar (in profile view)", + "avatar": "User avatar (in profile view)", + "avatarStatus": "User avatar (in post display)", "popup": "Popups and tooltips", "button": "Button", "buttonHover": "Button (hover)", diff --git a/src/services/style_setter/style_setter.js b/src/services/style_setter/style_setter.js index 680aadb4..5726cd61 100644 --- a/src/services/style_setter/style_setter.js +++ b/src/services/style_setter/style_setter.js @@ -321,6 +321,7 @@ const generateShadows = (input) => { color: '#000000', alpha: 0.7 }], + avatarStatus: [], panelHeader: [], button: [{ x: 0, From 698ebf70037c3231b96f417ad1afeaad1da47d55 Mon Sep 17 00:00:00 2001 From: Henry Jameson <me@hjkos.com> Date: Sun, 25 Nov 2018 17:24:58 +0300 Subject: [PATCH 57/96] fixed indentation --- .../user_card_content/user_card_content.vue | 128 +++++++++--------- 1 file changed, 64 insertions(+), 64 deletions(-) diff --git a/src/components/user_card_content/user_card_content.vue b/src/components/user_card_content/user_card_content.vue index 14eb4023..22f77ffc 100644 --- a/src/components/user_card_content/user_card_content.vue +++ b/src/components/user_card_content/user_card_content.vue @@ -41,74 +41,74 @@ </div> </div> <div v-if="isOtherUser" class="user-interactions"> - <div class="follow" v-if="loggedIn"> - <span v-if="user.following"> - <!--Following them!--> - <button @click="unfollowUser" class="pressed"> - {{ $t('user_card.following') }} - </button> - </span> - <span v-if="!user.following"> - <button @click="followUser"> - {{ $t('user_card.follow') }} - </button> - </span> - </div> - <div class='mute' v-if='isOtherUser'> - <span v-if='user.muted'> - <button @click="toggleMute" class="pressed"> - {{ $t('user_card.muted') }} - </button> - </span> - <span v-if='!user.muted'> - <button @click="toggleMute"> - {{ $t('user_card.mute') }} - </button> - </span> - </div> - <div class="remote-follow" v-if='!loggedIn && user.is_local'> - <form method="POST" :action='subscribeUrl'> - <input type="hidden" name="nickname" :value="user.screen_name"> - <input type="hidden" name="profile" value=""> - <button click="submit" class="remote-button"> - {{ $t('user_card.remote_follow') }} - </button> - </form> - </div> - <div class='block' v-if='isOtherUser && loggedIn'> - <span v-if='user.statusnet_blocking'> - <button @click="unblockUser" class="pressed"> - {{ $t('user_card.blocked') }} - </button> - </span> - <span v-if='!user.statusnet_blocking'> - <button @click="blockUser"> - {{ $t('user_card.block') }} - </button> - </span> - </div> + <div class="follow" v-if="loggedIn"> + <span v-if="user.following"> + <!--Following them!--> + <button @click="unfollowUser" class="pressed"> + {{ $t('user_card.following') }} + </button> + </span> + <span v-if="!user.following"> + <button @click="followUser"> + {{ $t('user_card.follow') }} + </button> + </span> + </div> + <div class='mute' v-if='isOtherUser'> + <span v-if='user.muted'> + <button @click="toggleMute" class="pressed"> + {{ $t('user_card.muted') }} + </button> + </span> + <span v-if='!user.muted'> + <button @click="toggleMute"> + {{ $t('user_card.mute') }} + </button> + </span> + </div> + <div class="remote-follow" v-if='!loggedIn && user.is_local'> + <form method="POST" :action='subscribeUrl'> + <input type="hidden" name="nickname" :value="user.screen_name"> + <input type="hidden" name="profile" value=""> + <button click="submit" class="remote-button"> + {{ $t('user_card.remote_follow') }} + </button> + </form> + </div> + <div class='block' v-if='isOtherUser && loggedIn'> + <span v-if='user.statusnet_blocking'> + <button @click="unblockUser" class="pressed"> + {{ $t('user_card.blocked') }} + </button> + </span> + <span v-if='!user.statusnet_blocking'> + <button @click="blockUser"> + {{ $t('user_card.block') }} + </button> + </span> </div> </div> </div> - <div class="panel-body profile-panel-body"> - <div class="user-counts" :class="{clickable: switcher}"> - <div class="user-count" v-on:click.prevent="setProfileView('statuses')" :class="{selected: selected === 'statuses'}"> - <h5>{{ $t('user_card.statuses') }}</h5> - <span>{{user.statuses_count}} <br></span> - </div> - <div class="user-count" v-on:click.prevent="setProfileView('friends')" :class="{selected: selected === 'friends'}"> - <h5>{{ $t('user_card.followees') }}</h5> - <span>{{user.friends_count}}</span> - </div> - <div class="user-count" v-on:click.prevent="setProfileView('followers')" :class="{selected: selected === 'followers'}"> - <h5>{{ $t('user_card.followers') }}</h5> - <span>{{user.followers_count}}</span> - </div> - </div> - <p v-if="!hideBio && user.description_html" class="profile-bio" v-html="user.description_html"></p> - <p v-else-if="!hideBio" class="profile-bio">{{ user.description }}</p> - </div> </div> + <div class="panel-body profile-panel-body"> + <div class="user-counts" :class="{clickable: switcher}"> + <div class="user-count" v-on:click.prevent="setProfileView('statuses')" :class="{selected: selected === 'statuses'}"> + <h5>{{ $t('user_card.statuses') }}</h5> + <span>{{user.statuses_count}} <br></span> + </div> + <div class="user-count" v-on:click.prevent="setProfileView('friends')" :class="{selected: selected === 'friends'}"> + <h5>{{ $t('user_card.followees') }}</h5> + <span>{{user.friends_count}}</span> + </div> + <div class="user-count" v-on:click.prevent="setProfileView('followers')" :class="{selected: selected === 'followers'}"> + <h5>{{ $t('user_card.followers') }}</h5> + <span>{{user.followers_count}}</span> + </div> + </div> + <p v-if="!hideBio && user.description_html" class="profile-bio" v-html="user.description_html"></p> + <p v-else-if="!hideBio" class="profile-bio">{{ user.description }}</p> + </div> +</div> </template> <script src="./user_card_content.js"></script> From 2f1070deb63af7f351fb3e33e44394160c689be4 Mon Sep 17 00:00:00 2001 From: Henry Jameson <me@hjkos.com> Date: Sun, 25 Nov 2018 17:42:41 +0300 Subject: [PATCH 58/96] collateral fixes --- src/components/status/status.vue | 1 + .../user_card_content/user_card_content.vue | 12 +++++++++++- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/src/components/status/status.vue b/src/components/status/status.vue index c4a268d0..710f2027 100644 --- a/src/components/status/status.vue +++ b/src/components/status/status.vue @@ -533,6 +533,7 @@ a.unmute { .status-el:last-child { border-bottom-radius: 0 0 $fallback--panelRadius $fallback--panelRadius;; border-radius: 0 0 var(--panelRadius, $fallback--panelRadius) var(--panelRadius, $fallback--panelRadius); + border-bottom: none; } } diff --git a/src/components/user_card_content/user_card_content.vue b/src/components/user_card_content/user_card_content.vue index 22f77ffc..9830f283 100644 --- a/src/components/user_card_content/user_card_content.vue +++ b/src/components/user_card_content/user_card_content.vue @@ -120,6 +120,7 @@ background-size: cover; border-radius: $fallback--panelRadius; border-radius: var(--panelRadius, $fallback--panelRadius); + overflow: hidden; .panel-heading { padding: 0.6em 0em; @@ -147,7 +148,6 @@ padding: 16px 10px 6px 10px; display: flex; max-height: 56px; - overflow: hidden; .avatar { border-radius: $fallback--avatarRadius; @@ -187,6 +187,16 @@ text-overflow: ellipsis; white-space: nowrap; flex: 1 1 0; + // This is so that text doesn't get overlapped by avatar's shadow if it has + // big one + z-index: 1; + + img { + width: 26px; + height: 26px; + vertical-align: middle; + object-fit: contain + } } .user-name{ From 883a76147af6f9d60c6ac10e23b88e3148f076bb Mon Sep 17 00:00:00 2001 From: Henry Jameson <me@hjkos.com> Date: Sun, 25 Nov 2018 19:12:38 +0300 Subject: [PATCH 59/96] validity checks, no longer exploding when something is invalid --- .../style_switcher/style_switcher.js | 67 ++++++++++++++----- .../style_switcher/style_switcher.vue | 2 +- 2 files changed, 52 insertions(+), 17 deletions(-) diff --git a/src/components/style_switcher/style_switcher.js b/src/components/style_switcher/style_switcher.js index d45ce455..286210b5 100644 --- a/src/components/style_switcher/style_switcher.js +++ b/src/components/style_switcher/style_switcher.js @@ -27,6 +27,14 @@ export default { selected: this.$store.state.config.theme, invalidThemeImported: false, + previewShadows: {}, + previewColors: {}, + previewRadii: {}, + + shadowsInvalid: true, + colorsInvalid: true, + radiiInvalid: true, + keepShadows: false, keepOpacity: false, keepRoundness: false, @@ -167,22 +175,6 @@ export default { attachment: this.attachmentRadiusLocal } }, - previewColors () { - if (this.currentColors.bg) { - return generateColors({ - opacity: this.currentOpacity, - colors: this.currentColors - }) - } else { - return {} - } - }, - previewRadii () { - return generateRadii({ radii: this.currentRadii }) - }, - previewShadows () { - return generateShadows({ shadows: this.shadowsLocal }) - }, preview () { return composePreset(this.previewColors, this.previewRadii, this.previewShadows) }, @@ -288,6 +280,9 @@ export default { set (v) { set(this.shadowsLocal, this.shadowSelected, v) } + }, + themeValid () { + return !this.shadowsInvalid && !this.colorsInvalid && !this.radiiInvalid } }, components: { @@ -475,6 +470,46 @@ export default { } }, watch: { + currentRadii () { + try { + this.previewRadii = generateRadii({ radii: this.currentRadii }) + this.radiiInvalid = false + } catch (e) { + this.radiiInvalid = true + console.warn(e) + } + }, + shadowsLocal () { + try { + this.previewShadows = generateShadows({ shadows: this.shadowsLocal }) + this.shadowsInvalid = false + } catch (e) { + this.shadowsInvalid = true + console.warn(e) + } + }, + currentColors () { + try { + this.previewColors = generateColors({ + opacity: this.currentOpacity, + colors: this.currentColors + }) + this.colorsInvalid = false + } catch (e) { + this.colorsInvalid = true + console.warn(e) + } + }, + currentOpacity () { + try { + this.previewColors = generateColors({ + opacity: this.currentOpacity, + colors: this.currentColors + }) + } catch (e) { + console.warn(e) + } + }, selected () { if (this.selectedVersion === 1) { if (!this.keepRoundness) { diff --git a/src/components/style_switcher/style_switcher.vue b/src/components/style_switcher/style_switcher.vue index f5090321..5e9af19e 100644 --- a/src/components/style_switcher/style_switcher.vue +++ b/src/components/style_switcher/style_switcher.vue @@ -218,7 +218,7 @@ </keep-alive> <div class="apply-container"> - <button class="btn submit" @click="setCustomTheme">{{$t('general.apply')}}</button> + <button class="btn submit" :disabled="!themeValid" @click="setCustomTheme">{{$t('general.apply')}}</button> <button class="btn" @click="clearAll">{{$t('settings.style.switcher.reset')}}</button> </div> </div> From e8536f3d95144945dcbd6bd96542f8401de3f1ed Mon Sep 17 00:00:00 2001 From: Henry Jameson <me@hjkos.com> Date: Sun, 25 Nov 2018 19:15:54 +0300 Subject: [PATCH 60/96] clean up --- src/components/style_switcher/style_switcher.js | 2 -- src/services/style_setter/style_setter.js | 1 - 2 files changed, 3 deletions(-) diff --git a/src/components/style_switcher/style_switcher.js b/src/components/style_switcher/style_switcher.js index 286210b5..57faa61b 100644 --- a/src/components/style_switcher/style_switcher.js +++ b/src/components/style_switcher/style_switcher.js @@ -396,7 +396,6 @@ export default { clearShadows () { this.shadowsLocal = {} - console.log(this.shadowsLocal) }, /** @@ -405,7 +404,6 @@ export default { * @param {Number} version - version of data. 0 means try to guess based on data. */ normalizeLocalState (input, version = 0) { - console.log(input.opacity) const colors = input.colors || input const radii = input.radii || input const opacity = input.opacity diff --git a/src/services/style_setter/style_setter.js b/src/services/style_setter/style_setter.js index 5726cd61..e11ee90c 100644 --- a/src/services/style_setter/style_setter.js +++ b/src/services/style_setter/style_setter.js @@ -345,7 +345,6 @@ const generateShadows = (input) => { ...(input.shadows || {}) } - console.log(Object.entries(shadows).map(([k, v]) => `--${k}Shadow: ${getCssShadow(v)}`).join(';')) return { rules: { shadows: Object.entries(shadows).map(([k, v]) => `--${k}Shadow: ${getCssShadow(v)}`).join(';') From 1a65895bfd441ea57163ee3e185785c82a81b736 Mon Sep 17 00:00:00 2001 From: Henry Jameson <me@hjkos.com> Date: Sun, 25 Nov 2018 21:48:16 +0300 Subject: [PATCH 61/96] initial font support --- src/App.scss | 3 + src/components/font_control/font_control.vue | 93 +++++++++++++++++++ .../shadow_control/shadow_control.vue | 5 +- .../style_switcher/style_switcher.js | 63 ++++++++++--- .../style_switcher/style_switcher.scss | 3 + .../style_switcher/style_switcher.vue | 55 ++++++++--- src/i18n/en.json | 13 +++ src/services/style_setter/style_setter.js | 48 +++++++++- 8 files changed, 251 insertions(+), 32 deletions(-) create mode 100644 src/components/font_control/font_control.vue diff --git a/src/App.scss b/src/App.scss index ceb0bb87..45491fd2 100644 --- a/src/App.scss +++ b/src/App.scss @@ -34,6 +34,7 @@ h4 { body { font-family: sans-serif; + font-family: var(--interfaceFont, sans-serif); font-size: 14px; margin: 0; color: $fallback--text; @@ -62,6 +63,7 @@ button { box-shadow: var(--buttonShadow); font-size: 14px; font-family: sans-serif; + font-family: var(--interfaceFont, sans-serif); i[class*=icon-] { color: $fallback--text; @@ -111,6 +113,7 @@ input, textarea, .select { color: $fallback--lightText; color: var(--inputText, $fallback--lightText); font-family: sans-serif; + font-family: var(--inputFont, sans-serif); font-size: 14px; padding: 8px 7px; box-sizing: border-box; diff --git a/src/components/font_control/font_control.vue b/src/components/font_control/font_control.vue new file mode 100644 index 00000000..424f6259 --- /dev/null +++ b/src/components/font_control/font_control.vue @@ -0,0 +1,93 @@ +<template> +<div class="font-control style-control"> + <label :for="preset === 'custom' ? name : name + '-font-switcher'" class="label"> + {{label}} + </label> + <input + v-if="typeof fallback !== 'undefined'" + class="opt exlcude-disabled" + type="checkbox" + :id="name + '-o'" + :checked="present" + @input="$emit('input', typeof value === 'undefined' ? fallback : undefined)"> + <label v-if="typeof fallback !== 'undefined'" class="opt-l" :for="name + '-o'"></label> + <label :for="name + '-font-switcher'" class="select" :disabled="!present"> + <select + :disabled="!present" + v-model="preset" + class="font-switcher" + id="name + '-font-switcher'"> + <option v-for="option in options" :value="option"> + {{ option }} + </option> + </select> + <i class="icon-down-open"/> + </label> + <input + v-if="preset === 'custom'" + class="custom-font" + type="text" + id="name" + v-model="family"> +</div> +</template> + +<script> +import { set } from 'vue' + +export default { + props: [ + 'name', 'label', 'value', 'fallback', 'options' + ], + data () { + return { + lValue: this.value + } + }, + beforeUpdate () { + this.lValue = this.value + }, + computed: { + present () { + return typeof this.lValue !== 'undefined' + }, + dValue () { + return this.lValue || this.fallback || {} + }, + family: { + get () { + return this.dValue.family + }, + set (v) { + set(this.lValue, 'family', v) + this.$emit('input', this.lValue) + } + }, + preset: { + get () { + console.log(this.family) + if (this.family === 'serif' || + this.family === 'sans-serif' || + this.family === 'monospace' || + this.family === 'inherit') { + return this.family + } else { + return 'custom' + } + }, + set (v) { + this.family = v === 'custom' ? '' : v + } + } + } +} +</script> + +<style lang="scss"> +@import '../../_variables.scss'; +.font-control { + input.custom-font { + min-width: 10em; + } +} +</style> diff --git a/src/components/shadow_control/shadow_control.vue b/src/components/shadow_control/shadow_control.vue index 85346e17..744925d4 100644 --- a/src/components/shadow_control/shadow_control.vue +++ b/src/components/shadow_control/shadow_control.vue @@ -124,9 +124,9 @@ <OpacityInput v-model="selected.alpha" :disabled="!present"/> - <div> + <p> {{$t('settings.style.shadows.hint')}} - </div> + </p> </div> </div> </template> @@ -139,6 +139,7 @@ display: flex; flex-wrap: wrap; justify-content: center; + margin-bottom: 1em; .shadow-preview-container, .shadow-tweak { diff --git a/src/components/style_switcher/style_switcher.js b/src/components/style_switcher/style_switcher.js index 57faa61b..bbd28bdc 100644 --- a/src/components/style_switcher/style_switcher.js +++ b/src/components/style_switcher/style_switcher.js @@ -1,10 +1,11 @@ import { rgb2hex, hex2rgb, getContrastRatio, alphaBlend } from '../../services/color_convert/color_convert.js' import { set, delete as del } from 'vue' -import { generateColors, generateShadows, generateRadii, composePreset } from '../../services/style_setter/style_setter.js' +import { generateColors, generateShadows, generateRadii, generateFonts, composePreset } from '../../services/style_setter/style_setter.js' import ColorInput from '../color_input/color_input.vue' import RangeInput from '../range_input/range_input.vue' import OpacityInput from '../opacity_input/opacity_input.vue' import ShadowControl from '../shadow_control/shadow_control.vue' +import FontControl from '../font_control/font_control.vue' import ContrastRatio from '../contrast_ratio/contrast_ratio.vue' import TabSwitcher from '../tab_switcher/tab_switcher.jsx' @@ -30,6 +31,7 @@ export default { previewShadows: {}, previewColors: {}, previewRadii: {}, + previewFonts: {}, shadowsInvalid: true, colorsInvalid: true, @@ -38,6 +40,7 @@ export default { keepShadows: false, keepOpacity: false, keepRoundness: false, + keepFonts: false, textColorLocal: '', linkColorLocal: '', @@ -85,6 +88,7 @@ export default { shadowSelected: undefined, shadowsLocal: {}, + fontsLocal: {}, btnRadiusLocal: '', inputRadiusLocal: '', @@ -176,10 +180,11 @@ export default { } }, preview () { - return composePreset(this.previewColors, this.previewRadii, this.previewShadows) + return composePreset(this.previewColors, this.previewRadii, this.previewShadows, this.previewFonts) }, previewTheme () { - if (!this.preview.theme.colors) return { colors: {}, opacity: {}, radii: {}, shadows: {} } + if (!this.preview.theme.colors) return { colors: {}, opacity: {}, radii: {}, shadows: {}, fonts: {} } + console.log(this.preview.theme) return this.preview.theme }, // This needs optimization maybe @@ -253,7 +258,11 @@ export default { }, previewRules () { if (!this.preview.rules) return '' - return [...Object.values(this.preview.rules), 'color: var(--text)'].join(';') + return [ + ...Object.values(this.preview.rules), + 'color: var(--text)', + 'font-family: var(--interfaceFont, sans-serif)' + ].join(';') }, shadowsAvailable () { return Object.keys(this.previewTheme.shadows).sort() @@ -291,6 +300,7 @@ export default { RangeInput, ContrastRatio, ShadowControl, + FontControl, TabSwitcher }, methods: { @@ -300,6 +310,7 @@ export default { _pleroma_theme_version: 2, theme: { shadows: this.shadowsLocal, + fonts: this.fontsLocal, opacity: this.currentOpacity, colors: this.currentColors, radii: this.currentRadii @@ -357,6 +368,7 @@ export default { name: 'customTheme', value: { shadows: this.shadowsLocal, + fonts: this.fontsLocal, opacity: this.currentOpacity, colors: this.currentColors, radii: this.currentRadii @@ -398,6 +410,10 @@ export default { this.shadowsLocal = {} }, + clearFonts () { + this.fontsLocal = {} + }, + /** * This applies stored theme data onto form. * @param {Object} input - input data @@ -408,6 +424,7 @@ export default { const radii = input.radii || input const opacity = input.opacity const shadows = input.shadows || {} + const fonts = input.fonts || {} if (version === 0) { if (input.version) version = input.version @@ -458,6 +475,11 @@ export default { this.shadowSelected = this.shadowsAvailable[0] } + if (!this.keepFonts) { + this.clearFonts() + this.fontsLocal = fonts + } + if (opacity && !this.keepOpacity) { this.clearOpacity() Object.entries(opacity).forEach(([k, v]) => { @@ -477,14 +499,31 @@ export default { console.warn(e) } }, - shadowsLocal () { - try { - this.previewShadows = generateShadows({ shadows: this.shadowsLocal }) - this.shadowsInvalid = false - } catch (e) { - this.shadowsInvalid = true - console.warn(e) - } + shadowsLocal: { + handler () { + try { + this.previewShadows = generateShadows({ shadows: this.shadowsLocal }) + this.shadowsInvalid = false + } catch (e) { + this.shadowsInvalid = true + console.warn(e) + } + }, + deep: true + }, + fontsLocal: { + handler () { + try { + this.previewFonts = generateFonts({ fonts: this.fontsLocal }) + console.log('BENIS') + console.log(this.previewFonts) + this.fontsInvalid = false + } catch (e) { + this.fontsInvalid = true + console.warn(e) + } + }, + deep: true }, currentColors () { try { diff --git a/src/components/style_switcher/style_switcher.scss b/src/components/style_switcher/style_switcher.scss index ad203856..9c4f4ecd 100644 --- a/src/components/style_switcher/style_switcher.scss +++ b/src/components/style_switcher/style_switcher.scss @@ -67,6 +67,7 @@ flex-wrap: wrap; } + .fonts-container, .reset-container, .apply-container, .radius-container, @@ -75,6 +76,7 @@ display: flex; } + .fonts-container, .radius-container { flex-direction: column; } @@ -87,6 +89,7 @@ justify-content: space-between; } + .fonts-container, .color-container, .shadow-container, .radius-container, diff --git a/src/components/style_switcher/style_switcher.vue b/src/components/style_switcher/style_switcher.vue index 5e9af19e..f64bda3f 100644 --- a/src/components/style_switcher/style_switcher.vue +++ b/src/components/style_switcher/style_switcher.vue @@ -75,7 +75,7 @@ <tab-switcher key="style-tweak"> <div :label="$t('settings.style.common_colors._tab_label')" class="color-container"> <div class="tab-header"> - <p>{{$t('settings.theme_help')}}</p> + <p>{{$t('settings.theme_help')}}</p> <button class="btn" @click="clearOpacity">{{$t('settings.style.switcher.clear_opacity')}}</button> <button class="btn" @click="clearV1">{{$t('settings.style.switcher.clear_all')}}</button> </div> @@ -169,20 +169,18 @@ </div> </div> <div :label="$t('settings.style.radii._tab_label')" class="radius-container"> - <div> - <div class="tab-header"> - <p>{{$t('settings.radii_help')}}</p> - <button class="btn" @click="clearRoundness">{{$t('settings.style.switcher.clear_all')}}</button> - </div> - <RangeInput name="btnRadius" :label="$t('settings.btnRadius')" v-model="btnRadiusLocal" :fallback="previewTheme.radii.btn" max="16" hardMin="0"/> - <RangeInput name="inputRadius" :label="$t('settings.inputRadius')" v-model="inputRadiusLocal" :fallback="previewTheme.radii.input" max="9" hardMin="0"/> - <RangeInput name="checkboxRadius" :label="$t('settings.checkboxRadius')" v-model="checkboxRadiusLocal" :fallback="previewTheme.radii.checkbox" max="16" hardMin="0"/> - <RangeInput name="panelRadius" :label="$t('settings.panelRadius')" v-model="panelRadiusLocal" :fallback="previewTheme.radii.panel" max="50" hardMin="0"/> - <RangeInput name="avatarRadius" :label="$t('settings.avatarRadius')" v-model="avatarRadiusLocal" :fallback="previewTheme.radii.avatar" max="28" hardMin="0"/> - <RangeInput name="avatarAltRadius" :label="$t('settings.avatarAltRadius')" v-model="avatarAltRadiusLocal" :fallback="previewTheme.radii.avatarAlt" max="28" hardMin="0"/> - <RangeInput name="attachmentRadius" :label="$t('settings.attachmentRadius')" v-model="attachmentRadiusLocal" :fallback="previewTheme.radii.attachment" max="50" hardMin="0"/> - <RangeInput name="tooltipRadius" :label="$t('settings.tooltipRadius')" v-model="tooltipRadiusLocal" :fallback="previewTheme.radii.tooltip" max="50" hardMin="0"/> + <div class="tab-header"> + <p>{{$t('settings.radii_help')}}</p> + <button class="btn" @click="clearRoundness">{{$t('settings.style.switcher.clear_all')}}</button> </div> + <RangeInput name="btnRadius" :label="$t('settings.btnRadius')" v-model="btnRadiusLocal" :fallback="previewTheme.radii.btn" max="16" hardMin="0"/> + <RangeInput name="inputRadius" :label="$t('settings.inputRadius')" v-model="inputRadiusLocal" :fallback="previewTheme.radii.input" max="9" hardMin="0"/> + <RangeInput name="checkboxRadius" :label="$t('settings.checkboxRadius')" v-model="checkboxRadiusLocal" :fallback="previewTheme.radii.checkbox" max="16" hardMin="0"/> + <RangeInput name="panelRadius" :label="$t('settings.panelRadius')" v-model="panelRadiusLocal" :fallback="previewTheme.radii.panel" max="50" hardMin="0"/> + <RangeInput name="avatarRadius" :label="$t('settings.avatarRadius')" v-model="avatarRadiusLocal" :fallback="previewTheme.radii.avatar" max="28" hardMin="0"/> + <RangeInput name="avatarAltRadius" :label="$t('settings.avatarAltRadius')" v-model="avatarAltRadiusLocal" :fallback="previewTheme.radii.avatarAlt" max="28" hardMin="0"/> + <RangeInput name="attachmentRadius" :label="$t('settings.attachmentRadius')" v-model="attachmentRadiusLocal" :fallback="previewTheme.radii.attachment" max="50" hardMin="0"/> + <RangeInput name="tooltipRadius" :label="$t('settings.tooltipRadius')" v-model="tooltipRadiusLocal" :fallback="previewTheme.radii.tooltip" max="50" hardMin="0"/> </div> <div :label="$t('settings.style.shadows._tab_label')" class="shadow-container"> <div class="tab-header shadow-selector"> @@ -214,6 +212,35 @@ </div> <shadow-control :ready="!!currentShadowFallback" :fallback="currentShadowFallback" v-model="currentShadow"/> </div> + <div :label="$t('settings.style.fonts._tab_label')" class="fonts-container"> + <div class="tab-header"> + <p>{{$t('settings.style.fonts.help')}}</p> + </div> + <FontControl + name="ui" + v-model="fontsLocal.interface" + :label="$t('settings.style.fonts.components.interface')" + :fallback="previewTheme.fonts.interface" + :options="['serif', 'sans-serif', 'monospace', 'custom']" /> + <FontControl + name="input" + v-model="fontsLocal.input" + :label="$t('settings.style.fonts.components.input')" + :fallback="previewTheme.fonts.input" + :options="['serif', 'sans-serif', 'monospace', 'inherit', 'custom']" /> + <FontControl + name="post" + v-model="fontsLocal.post" + :label="$t('settings.style.fonts.components.post')" + :fallback="previewTheme.fonts.post" + :options="['serif', 'sans-serif', 'monospace', 'inherit', 'custom']" /> + <FontControl + name="postCode" + v-model="fontsLocal.postCode" + :label="$t('settings.style.fonts.components.postCode')" + :fallback="previewTheme.fonts.postCode" + :options="['serif', 'sans-serif', 'monospace', 'inherit', 'custom']" /> + </div> </tab-switcher> </keep-alive> diff --git a/src/i18n/en.json b/src/i18n/en.json index 5bd1ddbb..6f439f65 100644 --- a/src/i18n/en.json +++ b/src/i18n/en.json @@ -236,6 +236,19 @@ "buttonPressedHover": "Button (pressed+hover)", "input": "Input field" } + }, + "fonts": { + "_tab_label": "Fonts", + "help": "Select font to use for elements of UI. For \"custom\" you have to enter exact font name as it appears in system.", + "components": { + "interface": "Interface", + "input": "Input fields", + "post": "Post text", + "postCode": "Monospaced text in a post (rich text)" + }, + "family": "Font name", + "size": "Size (in px)", + "weight": "Weight (boldness)" } } }, diff --git a/src/services/style_setter/style_setter.js b/src/services/style_setter/style_setter.js index e11ee90c..f2c9c13e 100644 --- a/src/services/style_setter/style_setter.js +++ b/src/services/style_setter/style_setter.js @@ -85,6 +85,7 @@ const setColors = (input, commit) => { styleSheet.insertRule(`body { ${rules.radii} }`, 'index-max') styleSheet.insertRule(`body { ${rules.colors} }`, 'index-max') styleSheet.insertRule(`body { ${rules.shadows} }`, 'index-max') + styleSheet.insertRule(`body { ${rules.fonts} }`, 'index-max') body.style.display = 'initial' // commit('setOption', { name: 'colors', value: htmlColors }) @@ -267,6 +268,41 @@ const generateRadii = (input) => { } } +const generateFonts = (input) => { + const fonts = Object.entries(input.fonts || {}).filter(([k, v]) => v).reduce((acc, [k, v]) => { + acc[k] = Object.entries(v).filter(([k, v]) => v).reduce((acc, [k, v]) => { + acc[k] = v + return acc + }, acc[k]) + return acc + }, { + interface: { + family: 'sans-serif' + }, + input: { + family: 'inherit' + }, + post: { + family: 'inherit' + }, + postCode: { + family: 'monospace' + } + }) + + return { + rules: { + fonts: Object + .entries(fonts) + .filter(([k, v]) => v) + .map(([k, v]) => `--${k}Font: ${v.family}`).join(';') + }, + theme: { + fonts + } + } +} + const generateShadows = (input) => { const border = (top, shadow) => ({ x: 0, @@ -355,17 +391,19 @@ const generateShadows = (input) => { } } -const composePreset = (colors, radii, shadows) => { +const composePreset = (colors, radii, shadows, fonts) => { return { rules: { ...shadows.rules, ...colors.rules, - ...radii.rules + ...radii.rules, + ...fonts.rules }, theme: { ...shadows.theme, ...colors.theme, - ...radii.theme + ...radii.theme, + ...fonts.theme } } } @@ -374,8 +412,9 @@ const generatePreset = (input) => { const shadows = generateShadows(input) const colors = generateColors(input) const radii = generateRadii(input) + const fonts = generateFonts(input) - return composePreset(colors, radii, shadows) + return composePreset(colors, radii, shadows, fonts) } const setPreset = (val, commit) => { @@ -424,6 +463,7 @@ export { generateColors, generateRadii, generateShadows, + generateFonts, generatePreset, composePreset, getCssShadow From 707441ffe684f662a9b99c261d61fe2da5b5140f Mon Sep 17 00:00:00 2001 From: Henry Jameson <me@hjkos.com> Date: Sun, 25 Nov 2018 22:06:49 +0300 Subject: [PATCH 62/96] more fonts --- src/components/status/status.vue | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/components/status/status.vue b/src/components/status/status.vue index 710f2027..7d4ecbc7 100644 --- a/src/components/status/status.vue +++ b/src/components/status/status.vue @@ -324,6 +324,8 @@ .status-content { margin-right: 0.5em; + font-family: var(--postFont, sans-serif); + img, video { max-width: 100%; max-height: 400px; @@ -338,6 +340,7 @@ pre { overflow: auto; + font-family: var(--postCodeFont, sans-serif); } p { From 1087741b0df00d42576c55f5869d331469bcbb6e Mon Sep 17 00:00:00 2001 From: Henry Jameson <me@hjkos.com> Date: Sun, 25 Nov 2018 22:39:06 +0300 Subject: [PATCH 63/96] font control args to allow passing an option list of fonts, for future use --- src/components/font_control/font_control.vue | 14 +++++++++++--- src/components/style_switcher/style_switcher.vue | 12 +++++------- 2 files changed, 16 insertions(+), 10 deletions(-) diff --git a/src/components/font_control/font_control.vue b/src/components/font_control/font_control.vue index 424f6259..004b5546 100644 --- a/src/components/font_control/font_control.vue +++ b/src/components/font_control/font_control.vue @@ -17,7 +17,7 @@ v-model="preset" class="font-switcher" id="name + '-font-switcher'"> - <option v-for="option in options" :value="option"> + <option v-for="option in availableOptions" :value="option"> {{ option }} </option> </select> @@ -37,11 +37,19 @@ import { set } from 'vue' export default { props: [ - 'name', 'label', 'value', 'fallback', 'options' + 'name', 'label', 'value', 'fallback', 'options', 'no-inherit' ], data () { return { - lValue: this.value + lValue: this.value, + availableOptions: [ + this.noInherit ? '' : 'inherit', + 'custom', + ...(this.options || []), + 'serif', + 'monospace', + 'sans-serif' + ].filter(_ => _) } }, beforeUpdate () { diff --git a/src/components/style_switcher/style_switcher.vue b/src/components/style_switcher/style_switcher.vue index f64bda3f..a444c6a7 100644 --- a/src/components/style_switcher/style_switcher.vue +++ b/src/components/style_switcher/style_switcher.vue @@ -215,31 +215,29 @@ <div :label="$t('settings.style.fonts._tab_label')" class="fonts-container"> <div class="tab-header"> <p>{{$t('settings.style.fonts.help')}}</p> + <button class="btn" @click="clearFonts">{{$t('settings.style.switcher.clear_all')}}</button> </div> <FontControl name="ui" v-model="fontsLocal.interface" :label="$t('settings.style.fonts.components.interface')" :fallback="previewTheme.fonts.interface" - :options="['serif', 'sans-serif', 'monospace', 'custom']" /> + no-inherit="1"/> <FontControl name="input" v-model="fontsLocal.input" :label="$t('settings.style.fonts.components.input')" - :fallback="previewTheme.fonts.input" - :options="['serif', 'sans-serif', 'monospace', 'inherit', 'custom']" /> + :fallback="previewTheme.fonts.input"/> <FontControl name="post" v-model="fontsLocal.post" :label="$t('settings.style.fonts.components.post')" - :fallback="previewTheme.fonts.post" - :options="['serif', 'sans-serif', 'monospace', 'inherit', 'custom']" /> + :fallback="previewTheme.fonts.post"/> <FontControl name="postCode" v-model="fontsLocal.postCode" :label="$t('settings.style.fonts.components.postCode')" - :fallback="previewTheme.fonts.postCode" - :options="['serif', 'sans-serif', 'monospace', 'inherit', 'custom']" /> + :fallback="previewTheme.fonts.postCode"/> </div> </tab-switcher> </keep-alive> From 94b481fa9c2bd5a9e135b589dcb78aa5fe802c87 Mon Sep 17 00:00:00 2001 From: Henry Jameson <me@hjkos.com> Date: Mon, 26 Nov 2018 00:19:28 +0300 Subject: [PATCH 64/96] cosmetic fixes --- src/App.scss | 9 +++++--- src/components/font_control/font_control.vue | 23 +++++++++++++++----- src/i18n/en.json | 3 ++- 3 files changed, 26 insertions(+), 9 deletions(-) diff --git a/src/App.scss b/src/App.scss index 45491fd2..50645ec8 100644 --- a/src/App.scss +++ b/src/App.scss @@ -115,7 +115,7 @@ input, textarea, .select { font-family: sans-serif; font-family: var(--inputFont, sans-serif); font-size: 14px; - padding: 8px 7px; + padding: 8px .5em; box-sizing: border-box; display: inline-block; position: relative; @@ -147,10 +147,13 @@ input, textarea, .select { appearance: none; background: transparent; border: none; - margin: 0; color: $fallback--text; color: var(--text, $fallback--text); - padding: 4px 2em 3px 3px; + margin: 0; + padding: 0 2em 0 .2em; + font-family: sans-serif; + font-family: var(--inputFont, sans-serif); + font-size: 14px; width: 100%; z-index: 1; height: 29px; diff --git a/src/components/font_control/font_control.vue b/src/components/font_control/font_control.vue index 004b5546..e97a2640 100644 --- a/src/components/font_control/font_control.vue +++ b/src/components/font_control/font_control.vue @@ -1,5 +1,5 @@ <template> -<div class="font-control style-control"> +<div class="font-control style-control" :class="{ custom: isCustom }"> <label :for="preset === 'custom' ? name : name + '-font-switcher'" class="label"> {{label}} </label> @@ -16,18 +16,18 @@ :disabled="!present" v-model="preset" class="font-switcher" - id="name + '-font-switcher'"> + :id="name + '-font-switcher'"> <option v-for="option in availableOptions" :value="option"> - {{ option }} + {{ option === 'custom' ? $t('settings.style.fonts.custom') : option }} </option> </select> <i class="icon-down-open"/> </label> <input - v-if="preset === 'custom'" + v-if="isCustom" class="custom-font" type="text" - id="name" + :id="name" v-model="family"> </div> </template> @@ -71,6 +71,9 @@ export default { this.$emit('input', this.lValue) } }, + isCustom () { + return this.preset === 'custom' + }, preset: { get () { console.log(this.family) @@ -97,5 +100,15 @@ export default { input.custom-font { min-width: 10em; } + &.custom { + .select { + border-top-right-radius: 0; + border-bottom-right-radius: 0; + } + .custom-font { + border-top-left-radius: 0; + border-bottom-left-radius: 0; + } + } } </style> diff --git a/src/i18n/en.json b/src/i18n/en.json index 6f439f65..c70c488c 100644 --- a/src/i18n/en.json +++ b/src/i18n/en.json @@ -248,7 +248,8 @@ }, "family": "Font name", "size": "Size (in px)", - "weight": "Weight (boldness)" + "weight": "Weight (boldness)", + "custom": "Custom" } } }, From d7eec4c30d93b8a87fa912543889bd106398042d Mon Sep 17 00:00:00 2001 From: Henry Jameson <me@hjkos.com> Date: Mon, 26 Nov 2018 00:23:07 +0300 Subject: [PATCH 65/96] more styles --- src/components/status/status.vue | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/components/status/status.vue b/src/components/status/status.vue index 7d4ecbc7..8682d996 100644 --- a/src/components/status/status.vue +++ b/src/components/status/status.vue @@ -340,7 +340,10 @@ pre { overflow: auto; - font-family: var(--postCodeFont, sans-serif); + } + + code, samp, kbd, var, pre { + font-family: var(--postCodeFont, monospace); } p { From 572c874f5cd1e997de70c7771075ca1444f95549 Mon Sep 17 00:00:00 2001 From: Henry Jameson <me@hjkos.com> Date: Mon, 26 Nov 2018 02:29:08 +0300 Subject: [PATCH 66/96] theme separation --- src/components/font_control/font_control.vue | 1 - .../style_switcher/style_switcher.js | 29 +- static/styles.json | 848 +----------------- static/themes/redmond-xx-se.json | 285 ++++++ static/themes/redmond-xx.json | 274 ++++++ static/themes/redmond-xxi.json | 285 ++++++ 6 files changed, 873 insertions(+), 849 deletions(-) create mode 100644 static/themes/redmond-xx-se.json create mode 100644 static/themes/redmond-xx.json create mode 100644 static/themes/redmond-xxi.json diff --git a/src/components/font_control/font_control.vue b/src/components/font_control/font_control.vue index e97a2640..85f19eea 100644 --- a/src/components/font_control/font_control.vue +++ b/src/components/font_control/font_control.vue @@ -76,7 +76,6 @@ export default { }, preset: { get () { - console.log(this.family) if (this.family === 'serif' || this.family === 'sans-serif' || this.family === 'monospace' || diff --git a/src/components/style_switcher/style_switcher.js b/src/components/style_switcher/style_switcher.js index bbd28bdc..d833341f 100644 --- a/src/components/style_switcher/style_switcher.js +++ b/src/components/style_switcher/style_switcher.js @@ -106,7 +106,31 @@ export default { window.fetch('/static/styles.json') .then((data) => data.json()) .then((themes) => { - self.availableStyles = themes + return Promise.all(Object.entries(themes).map(([k, v]) => { + if (typeof v === 'object') { + return Promise.resolve([k, v]) + } else if (typeof v === 'string') { + return window.fetch(v) + .then((data) => data.json()) + .then((theme) => { + return [k, theme] + }) + .catch((e) => { + console.error(e) + return [] + }) + } + })) + }) + .then((promises) => { + return promises + .filter(([k, v]) => v) + .reduce((acc, [k, v]) => { + acc[k] = v + return acc + }, {}) + }).then((themesComplete) => { + self.availableStyles = themesComplete }) }, mounted () { @@ -184,7 +208,6 @@ export default { }, previewTheme () { if (!this.preview.theme.colors) return { colors: {}, opacity: {}, radii: {}, shadows: {}, fonts: {} } - console.log(this.preview.theme) return this.preview.theme }, // This needs optimization maybe @@ -515,8 +538,6 @@ export default { handler () { try { this.previewFonts = generateFonts({ fonts: this.fontsLocal }) - console.log('BENIS') - console.log(this.previewFonts) this.fontsInvalid = false } catch (e) { this.fontsInvalid = true diff --git a/static/styles.json b/static/styles.json index a53eeaa1..9204c717 100644 --- a/static/styles.json +++ b/static/styles.json @@ -6,848 +6,8 @@ "ir-black": [ "Ir Black", "#000000", "#242422", "#b5b3aa", "#ff6c60", "#FF6C60", "#A8FF60", "#96CBFE", "#FFFFB6" ], "monokai": [ "Monokai", "#272822", "#383830", "#f8f8f2", "#f92672", "#F92672", "#a6e22e", "#66d9ef", "#f4bf75" ], "mammal": [ "Mammal", "#272c37", "#444b5d", "#f8f8f8", "#9bacc8", "#7f3142", "#2bd850", "#2b90d9", "#ca8f04" ], - "redmond-xx": { - "_pleroma_theme_version": 2, - "name": "Redmond XX", - "theme": { - "shadows": { - "panel": [ - { - "x": "-1", - "y": "-1", - "blur": "0", - "spread": 0, - "color": "#000000", - "alpha": "1", - "inset": true - }, - { - "x": "1", - "y": "1", - "blur": "0", - "spread": 0, - "color": "#dfdfdf", - "alpha": "1", - "inset": true - }, - { - "x": "-2", - "y": "-2", - "blur": "0", - "spread": 0, - "color": "#848484", - "alpha": "1", - "inset": true - }, - { - "x": "2", - "y": "2", - "blur": "0", - "spread": 0, - "color": "#FFFFFF", - "alpha": "1", - "inset": true - }, - { - "x": "0", - "y": "0", - "blur": "0", - "spread": "3", - "color": "--bg", - "alpha": "1", - "inset": true - } - ], - "button": [ - { - "x": "-1", - "y": "-1", - "blur": "0", - "spread": 0, - "color": "#000000", - "alpha": "1", - "inset": true - }, - { - "x": "1", - "y": "1", - "blur": "0", - "spread": 0, - "color": "#FFFFFF", - "alpha": "1", - "inset": true - }, - { - "x": "-2", - "y": "-2", - "blur": "0", - "spread": 0, - "color": "#848484", - "alpha": "1", - "inset": true - }, - { - "x": "2", - "y": "2", - "blur": "0", - "spread": 0, - "color": "#dfdfdf", - "alpha": "1", - "inset": true - }, - { - "x": "0", - "y": "0", - "blur": "0", - "spread": "3", - "color": "--bg", - "alpha": "1", - "inset": true - } - ], - "buttonHover": [ - { - "x": "-1", - "y": "-1", - "blur": "0", - "spread": 0, - "color": "#000000", - "alpha": "1", - "inset": true - }, - { - "x": "1", - "y": "1", - "blur": "0", - "spread": 0, - "color": "#FFFFFF", - "alpha": "1", - "inset": true - }, - { - "x": "-2", - "y": "-2", - "blur": "0", - "spread": 0, - "color": "#848484", - "alpha": "1", - "inset": true - }, - { - "x": "2", - "y": "2", - "blur": "0", - "spread": 0, - "color": "#dfdfdf", - "alpha": "1", - "inset": true - }, - { - "x": "0", - "y": "0", - "blur": "0", - "spread": "3", - "color": "--bg", - "alpha": "1", - "inset": true - } - ], - "buttonPressed": [ - { - "x": "1", - "y": "1", - "blur": "0", - "spread": 0, - "color": "#000000", - "alpha": "1", - "inset": true - }, - { - "x": "-1", - "y": "-1", - "blur": "0", - "spread": 0, - "color": "#FFFFFF", - "alpha": "1", - "inset": true - }, - { - "x": "2", - "y": "2", - "blur": "0", - "spread": 0, - "color": "#848484", - "alpha": "1", - "inset": true - }, - { - "x": "-2", - "y": "-2", - "blur": "0", - "spread": 0, - "color": "#dfdfdf", - "alpha": "1", - "inset": true - }, - { - "x": "0", - "y": "0", - "blur": "0", - "spread": "3", - "color": "--bg", - "alpha": "1", - "inset": true - } - ], - "input": [ - { - "x": "-1", - "y": "-1", - "blur": "0", - "spread": 0, - "color": "#FFFFFF", - "alpha": "1", - "inset": true - }, - { - "x": "1", - "y": "1", - "blur": "0", - "spread": 0, - "color": "#848484", - "alpha": "1", - "inset": true - }, - { - "x": "-2", - "y": "-2", - "blur": "0", - "spread": 0, - "color": "#dfdfdf", - "alpha": "1", - "inset": true - }, - { - "x": "2", - "y": "2", - "blur": "0", - "spread": 0, - "color": "#000000", - "alpha": "1", - "inset": true - }, - { - "x": "0", - "y": "0", - "blur": "0", - "spread": "3", - "color": "--input", - "alpha": "1", - "inset": true - } - ] - }, - "opacity": { - "input": "1" - }, - "colors": { - "bg": "#c0c0c0", - "text": "#000000", - "link": "#0000ff", - "fg": "#c0c0c0", - "panel": "#000080", - "input": "#ffffff", - "topBar": "#000080", - "topBarLink": "#ffffff", - "btn": "#c0c0c0", - "faint": "#3f3f3f", - "faintLink": "#404080", - "border": "#808080", - "cRed": "#FF0000", - "cBlue": "#008080", - "cGreen": "#00FF00", - "cOrange": "#808000" - }, - "radii": { - "btn": "0", - "input": "0", - "checkbox": "0", - "panel": "0", - "avatar": "0", - "avatarAlt": "0", - "tooltip": "0", - "attachment": "0" - } - } - }, - "redmond-xx-se": { - "_pleroma_theme_version": 2, - "name": "Redmond XX SE", - "theme": { - "shadows": { - "panel": [ - { - "x": "-1", - "y": "-1", - "blur": "0", - "spread": 0, - "color": "#000000", - "alpha": "1", - "inset": true - }, - { - "x": "1", - "y": "1", - "blur": "0", - "spread": 0, - "color": "#dfdfdf", - "alpha": "1", - "inset": true - }, - { - "x": "-2", - "y": "-2", - "blur": "0", - "spread": 0, - "color": "#848484", - "alpha": "1", - "inset": true - }, - { - "x": "2", - "y": "2", - "blur": "0", - "spread": 0, - "color": "#FFFFFF", - "alpha": "1", - "inset": true - }, - { - "x": "0", - "y": "0", - "blur": "0", - "spread": "3", - "color": "--bg", - "alpha": "1", - "inset": true - } - ], - "button": [ - { - "x": "-1", - "y": "-1", - "blur": "0", - "spread": 0, - "color": "#000000", - "alpha": "1", - "inset": true - }, - { - "x": "1", - "y": "1", - "blur": "0", - "spread": 0, - "color": "#FFFFFF", - "alpha": "1", - "inset": true - }, - { - "x": "-2", - "y": "-2", - "blur": "0", - "spread": 0, - "color": "#848484", - "alpha": "1", - "inset": true - }, - { - "x": "2", - "y": "2", - "blur": "0", - "spread": 0, - "color": "#dfdfdf", - "alpha": "1", - "inset": true - }, - { - "x": "0", - "y": "0", - "blur": "0", - "spread": "3", - "color": "--bg", - "alpha": "1", - "inset": true - } - ], - "buttonHover": [ - { - "x": "-1", - "y": "-1", - "blur": "0", - "spread": 0, - "color": "#000000", - "alpha": "1", - "inset": true - }, - { - "x": "1", - "y": "1", - "blur": "0", - "spread": 0, - "color": "#FFFFFF", - "alpha": "1", - "inset": true - }, - { - "x": "-2", - "y": "-2", - "blur": "0", - "spread": 0, - "color": "#848484", - "alpha": "1", - "inset": true - }, - { - "x": "2", - "y": "2", - "blur": "0", - "spread": 0, - "color": "#dfdfdf", - "alpha": "1", - "inset": true - }, - { - "x": "0", - "y": "0", - "blur": "0", - "spread": "3", - "color": "--bg", - "alpha": "1", - "inset": true - } - ], - "buttonPressed": [ - { - "x": "1", - "y": "1", - "blur": "0", - "spread": 0, - "color": "#000000", - "alpha": "1", - "inset": true - }, - { - "x": "-1", - "y": "-1", - "blur": "0", - "spread": 0, - "color": "#FFFFFF", - "alpha": "1", - "inset": true - }, - { - "x": "2", - "y": "2", - "blur": "0", - "spread": 0, - "color": "#848484", - "alpha": "1", - "inset": true - }, - { - "x": "-2", - "y": "-2", - "blur": "0", - "spread": 0, - "color": "#dfdfdf", - "alpha": "1", - "inset": true - }, - { - "x": "0", - "y": "0", - "blur": "0", - "spread": "3", - "color": "--bg", - "alpha": "1", - "inset": true - } - ], - "input": [ - { - "x": "-1", - "y": "-1", - "blur": "0", - "spread": 0, - "color": "#FFFFFF", - "alpha": "1", - "inset": true - }, - { - "x": "1", - "y": "1", - "blur": "0", - "spread": 0, - "color": "#848484", - "alpha": "1", - "inset": true - }, - { - "x": "-2", - "y": "-2", - "blur": "0", - "spread": 0, - "color": "#dfdfdf", - "alpha": "1", - "inset": true - }, - { - "x": "2", - "y": "2", - "blur": "0", - "spread": 0, - "color": "#000000", - "alpha": "1", - "inset": true - }, - { - "x": "0", - "y": "0", - "blur": "0", - "spread": "3", - "color": "--input", - "alpha": "1", - "inset": true - } - ], - "panelHeader": [ - { - "x": "-2200", - "y": 0, - "blur": "200", - "spread": "-2000", - "inset": true, - "color": "#1084d0", - "alpha": 1 - } - ] - }, - "opacity": { - "input": "1" - }, - "colors": { - "bg": "#c0c0c0", - "text": "#000000", - "link": "#0000ff", - "fg": "#c0c0c0", - "panel": "#000080", - "input": "#ffffff", - "topBar": "#000080", - "topBarLink": "#ffffff", - "btn": "#c0c0c0", - "faint": "#3f3f3f", - "faintLink": "#404080", - "border": "#808080", - "cRed": "#FF0000", - "cBlue": "#008080", - "cGreen": "#00FF00", - "cOrange": "#808000" - }, - "radii": { - "btn": "0", - "input": "0", - "checkbox": "0", - "panel": "0", - "avatar": "0", - "avatarAlt": "0", - "tooltip": "0", - "attachment": "0" - } - } - }, - "redmond-xxi": { - "_pleroma_theme_version": 2, - "name": "Redmond XXI", - "theme": { - "shadows": { - "panel": [ - { - "x": "-1", - "y": "-1", - "blur": "0", - "spread": 0, - "color": "#000000", - "alpha": "1", - "inset": true - }, - { - "x": "1", - "y": "1", - "blur": "0", - "spread": 0, - "color": "#dfdfdf", - "alpha": "1", - "inset": true - }, - { - "x": "-2", - "y": "-2", - "blur": "0", - "spread": 0, - "color": "#848484", - "alpha": "1", - "inset": true - }, - { - "x": "2", - "y": "2", - "blur": "0", - "spread": 0, - "color": "#FFFFFF", - "alpha": "1", - "inset": true - }, - { - "x": "0", - "y": "0", - "blur": "0", - "spread": "3", - "color": "--bg", - "alpha": "1", - "inset": true - } - ], - "button": [ - { - "x": "-1", - "y": "-1", - "blur": "0", - "spread": 0, - "color": "#000000", - "alpha": "1", - "inset": true - }, - { - "x": "1", - "y": "1", - "blur": "0", - "spread": 0, - "color": "#FFFFFF", - "alpha": "1", - "inset": true - }, - { - "x": "-2", - "y": "-2", - "blur": "0", - "spread": 0, - "color": "#848484", - "alpha": "1", - "inset": true - }, - { - "x": "2", - "y": "2", - "blur": "0", - "spread": 0, - "color": "#dfdfdf", - "alpha": "1", - "inset": true - }, - { - "x": "0", - "y": "0", - "blur": "0", - "spread": "3", - "color": "--bg", - "alpha": "1", - "inset": true - } - ], - "buttonHover": [ - { - "x": "-1", - "y": "-1", - "blur": "0", - "spread": 0, - "color": "#000000", - "alpha": "1", - "inset": true - }, - { - "x": "1", - "y": "1", - "blur": "0", - "spread": 0, - "color": "#FFFFFF", - "alpha": "1", - "inset": true - }, - { - "x": "-2", - "y": "-2", - "blur": "0", - "spread": 0, - "color": "#848484", - "alpha": "1", - "inset": true - }, - { - "x": "2", - "y": "2", - "blur": "0", - "spread": 0, - "color": "#dfdfdf", - "alpha": "1", - "inset": true - }, - { - "x": "0", - "y": "0", - "blur": "0", - "spread": "3", - "color": "--bg", - "alpha": "1", - "inset": true - } - ], - "buttonPressed": [ - { - "x": "1", - "y": "1", - "blur": "0", - "spread": 0, - "color": "#000000", - "alpha": "1", - "inset": true - }, - { - "x": "-1", - "y": "-1", - "blur": "0", - "spread": 0, - "color": "#FFFFFF", - "alpha": "1", - "inset": true - }, - { - "x": "2", - "y": "2", - "blur": "0", - "spread": 0, - "color": "#848484", - "alpha": "1", - "inset": true - }, - { - "x": "-2", - "y": "-2", - "blur": "0", - "spread": 0, - "color": "#dfdfdf", - "alpha": "1", - "inset": true - }, - { - "x": "0", - "y": "0", - "blur": "0", - "spread": "3", - "color": "--bg", - "alpha": "1", - "inset": true - } - ], - "input": [ - { - "x": "-1", - "y": "-1", - "blur": "0", - "spread": 0, - "color": "#FFFFFF", - "alpha": "1", - "inset": true - }, - { - "x": "1", - "y": "1", - "blur": "0", - "spread": 0, - "color": "#848484", - "alpha": "1", - "inset": true - }, - { - "x": "-2", - "y": "-2", - "blur": "0", - "spread": 0, - "color": "#dfdfdf", - "alpha": "1", - "inset": true - }, - { - "x": "2", - "y": "2", - "blur": "0", - "spread": 0, - "color": "#000000", - "alpha": "1", - "inset": true - }, - { - "x": "0", - "y": "0", - "blur": "0", - "spread": "3", - "color": "--input", - "alpha": "1", - "inset": true - } - ], - "panelHeader": [ - { - "x": "-2200", - "y": 0, - "blur": "200", - "spread": "-2000", - "inset": true, - "color": "#a5cef7", - "alpha": 1 - } - ] - }, - "opacity": { - "input": "1" - }, - "colors": { - "bg": "#d6d6ce", - "text": "#000000", - "link": "#0000ff", - "fg": "#d6d6ce", - "panel": "#042967", - "input": "#ffffff", - "topBar": "#042967", - "topBarLink": "#ffffff", - "btn": "#d6d6ce", - "faint": "#3f3f3f", - "faintLink": "#404080", - "border": "#808080", - "cRed": "#FF0000", - "cBlue": "#008080", - "cGreen": "#00FF00", - "cOrange": "#808000" - }, - "radii": { - "btn": "0", - "input": "0", - "checkbox": "0", - "panel": "0", - "avatar": "0", - "avatarAlt": "0", - "tooltip": "0", - "attachment": "0" - } - } - } + + "redmond-xx": "./static/themes/redmond-xx.json", + "redmond-xx-se": "./static/themes/redmond-xx-se.json", + "redmond-xxi": "./static/themes/redmond-xxi.json" } diff --git a/static/themes/redmond-xx-se.json b/static/themes/redmond-xx-se.json new file mode 100644 index 00000000..e4cdbdee --- /dev/null +++ b/static/themes/redmond-xx-se.json @@ -0,0 +1,285 @@ +{ + "_pleroma_theme_version": 2, + "name": "Redmond XX SE", + "theme": { + "shadows": { + "panel": [ + { + "x": "-1", + "y": "-1", + "blur": "0", + "spread": 0, + "color": "#000000", + "alpha": "1", + "inset": true + }, + { + "x": "1", + "y": "1", + "blur": "0", + "spread": 0, + "color": "#dfdfdf", + "alpha": "1", + "inset": true + }, + { + "x": "-2", + "y": "-2", + "blur": "0", + "spread": 0, + "color": "#848484", + "alpha": "1", + "inset": true + }, + { + "x": "2", + "y": "2", + "blur": "0", + "spread": 0, + "color": "#FFFFFF", + "alpha": "1", + "inset": true + }, + { + "x": "0", + "y": "0", + "blur": "0", + "spread": "3", + "color": "--bg", + "alpha": "1", + "inset": true + } + ], + "button": [ + { + "x": "-1", + "y": "-1", + "blur": "0", + "spread": 0, + "color": "#000000", + "alpha": "1", + "inset": true + }, + { + "x": "1", + "y": "1", + "blur": "0", + "spread": 0, + "color": "#FFFFFF", + "alpha": "1", + "inset": true + }, + { + "x": "-2", + "y": "-2", + "blur": "0", + "spread": 0, + "color": "#848484", + "alpha": "1", + "inset": true + }, + { + "x": "2", + "y": "2", + "blur": "0", + "spread": 0, + "color": "#dfdfdf", + "alpha": "1", + "inset": true + }, + { + "x": "0", + "y": "0", + "blur": "0", + "spread": "3", + "color": "--bg", + "alpha": "1", + "inset": true + } + ], + "buttonHover": [ + { + "x": "-1", + "y": "-1", + "blur": "0", + "spread": 0, + "color": "#000000", + "alpha": "1", + "inset": true + }, + { + "x": "1", + "y": "1", + "blur": "0", + "spread": 0, + "color": "#FFFFFF", + "alpha": "1", + "inset": true + }, + { + "x": "-2", + "y": "-2", + "blur": "0", + "spread": 0, + "color": "#848484", + "alpha": "1", + "inset": true + }, + { + "x": "2", + "y": "2", + "blur": "0", + "spread": 0, + "color": "#dfdfdf", + "alpha": "1", + "inset": true + }, + { + "x": "0", + "y": "0", + "blur": "0", + "spread": "3", + "color": "--bg", + "alpha": "1", + "inset": true + } + ], + "buttonPressed": [ + { + "x": "1", + "y": "1", + "blur": "0", + "spread": 0, + "color": "#000000", + "alpha": "1", + "inset": true + }, + { + "x": "-1", + "y": "-1", + "blur": "0", + "spread": 0, + "color": "#FFFFFF", + "alpha": "1", + "inset": true + }, + { + "x": "2", + "y": "2", + "blur": "0", + "spread": 0, + "color": "#848484", + "alpha": "1", + "inset": true + }, + { + "x": "-2", + "y": "-2", + "blur": "0", + "spread": 0, + "color": "#dfdfdf", + "alpha": "1", + "inset": true + }, + { + "x": "0", + "y": "0", + "blur": "0", + "spread": "3", + "color": "--bg", + "alpha": "1", + "inset": true + } + ], + "input": [ + { + "x": "-1", + "y": "-1", + "blur": "0", + "spread": 0, + "color": "#FFFFFF", + "alpha": "1", + "inset": true + }, + { + "x": "1", + "y": "1", + "blur": "0", + "spread": 0, + "color": "#848484", + "alpha": "1", + "inset": true + }, + { + "x": "-2", + "y": "-2", + "blur": "0", + "spread": 0, + "color": "#dfdfdf", + "alpha": "1", + "inset": true + }, + { + "x": "2", + "y": "2", + "blur": "0", + "spread": 0, + "color": "#000000", + "alpha": "1", + "inset": true + }, + { + "x": "0", + "y": "0", + "blur": "0", + "spread": "3", + "color": "--input", + "alpha": "1", + "inset": true + } + ], + "panelHeader": [ + { + "x": "-2200", + "y": 0, + "blur": "200", + "spread": "-2000", + "inset": true, + "color": "#1084d0", + "alpha": 1 + } + ] + }, + "opacity": { + "input": "1" + }, + "colors": { + "bg": "#c0c0c0", + "text": "#000000", + "link": "#0000ff", + "fg": "#c0c0c0", + "panel": "#000080", + "input": "#ffffff", + "topBar": "#000080", + "topBarLink": "#ffffff", + "btn": "#c0c0c0", + "faint": "#3f3f3f", + "faintLink": "#404080", + "border": "#808080", + "cRed": "#FF0000", + "cBlue": "#008080", + "cGreen": "#00FF00", + "cOrange": "#808000" + }, + "radii": { + "btn": "0", + "input": "0", + "checkbox": "0", + "panel": "0", + "avatar": "0", + "avatarAlt": "0", + "tooltip": "0", + "attachment": "0" + } + } +} diff --git a/static/themes/redmond-xx.json b/static/themes/redmond-xx.json new file mode 100644 index 00000000..96dff499 --- /dev/null +++ b/static/themes/redmond-xx.json @@ -0,0 +1,274 @@ +{ + "_pleroma_theme_version": 2, + "name": "Redmond XX", + "theme": { + "shadows": { + "panel": [ + { + "x": "-1", + "y": "-1", + "blur": "0", + "spread": 0, + "color": "#000000", + "alpha": "1", + "inset": true + }, + { + "x": "1", + "y": "1", + "blur": "0", + "spread": 0, + "color": "#dfdfdf", + "alpha": "1", + "inset": true + }, + { + "x": "-2", + "y": "-2", + "blur": "0", + "spread": 0, + "color": "#848484", + "alpha": "1", + "inset": true + }, + { + "x": "2", + "y": "2", + "blur": "0", + "spread": 0, + "color": "#FFFFFF", + "alpha": "1", + "inset": true + }, + { + "x": "0", + "y": "0", + "blur": "0", + "spread": "3", + "color": "--bg", + "alpha": "1", + "inset": true + } + ], + "button": [ + { + "x": "-1", + "y": "-1", + "blur": "0", + "spread": 0, + "color": "#000000", + "alpha": "1", + "inset": true + }, + { + "x": "1", + "y": "1", + "blur": "0", + "spread": 0, + "color": "#FFFFFF", + "alpha": "1", + "inset": true + }, + { + "x": "-2", + "y": "-2", + "blur": "0", + "spread": 0, + "color": "#848484", + "alpha": "1", + "inset": true + }, + { + "x": "2", + "y": "2", + "blur": "0", + "spread": 0, + "color": "#dfdfdf", + "alpha": "1", + "inset": true + }, + { + "x": "0", + "y": "0", + "blur": "0", + "spread": "3", + "color": "--bg", + "alpha": "1", + "inset": true + } + ], + "buttonHover": [ + { + "x": "-1", + "y": "-1", + "blur": "0", + "spread": 0, + "color": "#000000", + "alpha": "1", + "inset": true + }, + { + "x": "1", + "y": "1", + "blur": "0", + "spread": 0, + "color": "#FFFFFF", + "alpha": "1", + "inset": true + }, + { + "x": "-2", + "y": "-2", + "blur": "0", + "spread": 0, + "color": "#848484", + "alpha": "1", + "inset": true + }, + { + "x": "2", + "y": "2", + "blur": "0", + "spread": 0, + "color": "#dfdfdf", + "alpha": "1", + "inset": true + }, + { + "x": "0", + "y": "0", + "blur": "0", + "spread": "3", + "color": "--bg", + "alpha": "1", + "inset": true + } + ], + "buttonPressed": [ + { + "x": "1", + "y": "1", + "blur": "0", + "spread": 0, + "color": "#000000", + "alpha": "1", + "inset": true + }, + { + "x": "-1", + "y": "-1", + "blur": "0", + "spread": 0, + "color": "#FFFFFF", + "alpha": "1", + "inset": true + }, + { + "x": "2", + "y": "2", + "blur": "0", + "spread": 0, + "color": "#848484", + "alpha": "1", + "inset": true + }, + { + "x": "-2", + "y": "-2", + "blur": "0", + "spread": 0, + "color": "#dfdfdf", + "alpha": "1", + "inset": true + }, + { + "x": "0", + "y": "0", + "blur": "0", + "spread": "3", + "color": "--bg", + "alpha": "1", + "inset": true + } + ], + "input": [ + { + "x": "-1", + "y": "-1", + "blur": "0", + "spread": 0, + "color": "#FFFFFF", + "alpha": "1", + "inset": true + }, + { + "x": "1", + "y": "1", + "blur": "0", + "spread": 0, + "color": "#848484", + "alpha": "1", + "inset": true + }, + { + "x": "-2", + "y": "-2", + "blur": "0", + "spread": 0, + "color": "#dfdfdf", + "alpha": "1", + "inset": true + }, + { + "x": "2", + "y": "2", + "blur": "0", + "spread": 0, + "color": "#000000", + "alpha": "1", + "inset": true + }, + { + "x": "0", + "y": "0", + "blur": "0", + "spread": "3", + "color": "--input", + "alpha": "1", + "inset": true + } + ] + }, + "opacity": { + "input": "1" + }, + "colors": { + "bg": "#c0c0c0", + "text": "#000000", + "link": "#0000ff", + "fg": "#c0c0c0", + "panel": "#000080", + "input": "#ffffff", + "topBar": "#000080", + "topBarLink": "#ffffff", + "btn": "#c0c0c0", + "faint": "#3f3f3f", + "faintLink": "#404080", + "border": "#808080", + "cRed": "#FF0000", + "cBlue": "#008080", + "cGreen": "#00FF00", + "cOrange": "#808000" + }, + "radii": { + "btn": "0", + "input": "0", + "checkbox": "0", + "panel": "0", + "avatar": "0", + "avatarAlt": "0", + "tooltip": "0", + "attachment": "0" + } + } +} diff --git a/static/themes/redmond-xxi.json b/static/themes/redmond-xxi.json new file mode 100644 index 00000000..1469cc6b --- /dev/null +++ b/static/themes/redmond-xxi.json @@ -0,0 +1,285 @@ +{ + "_pleroma_theme_version": 2, + "name": "Redmond XXI", + "theme": { + "shadows": { + "panel": [ + { + "x": "-1", + "y": "-1", + "blur": "0", + "spread": 0, + "color": "#000000", + "alpha": "1", + "inset": true + }, + { + "x": "1", + "y": "1", + "blur": "0", + "spread": 0, + "color": "#dfdfdf", + "alpha": "1", + "inset": true + }, + { + "x": "-2", + "y": "-2", + "blur": "0", + "spread": 0, + "color": "#848484", + "alpha": "1", + "inset": true + }, + { + "x": "2", + "y": "2", + "blur": "0", + "spread": 0, + "color": "#FFFFFF", + "alpha": "1", + "inset": true + }, + { + "x": "0", + "y": "0", + "blur": "0", + "spread": "3", + "color": "--bg", + "alpha": "1", + "inset": true + } + ], + "button": [ + { + "x": "-1", + "y": "-1", + "blur": "0", + "spread": 0, + "color": "#000000", + "alpha": "1", + "inset": true + }, + { + "x": "1", + "y": "1", + "blur": "0", + "spread": 0, + "color": "#FFFFFF", + "alpha": "1", + "inset": true + }, + { + "x": "-2", + "y": "-2", + "blur": "0", + "spread": 0, + "color": "#848484", + "alpha": "1", + "inset": true + }, + { + "x": "2", + "y": "2", + "blur": "0", + "spread": 0, + "color": "#dfdfdf", + "alpha": "1", + "inset": true + }, + { + "x": "0", + "y": "0", + "blur": "0", + "spread": "3", + "color": "--bg", + "alpha": "1", + "inset": true + } + ], + "buttonHover": [ + { + "x": "-1", + "y": "-1", + "blur": "0", + "spread": 0, + "color": "#000000", + "alpha": "1", + "inset": true + }, + { + "x": "1", + "y": "1", + "blur": "0", + "spread": 0, + "color": "#FFFFFF", + "alpha": "1", + "inset": true + }, + { + "x": "-2", + "y": "-2", + "blur": "0", + "spread": 0, + "color": "#848484", + "alpha": "1", + "inset": true + }, + { + "x": "2", + "y": "2", + "blur": "0", + "spread": 0, + "color": "#dfdfdf", + "alpha": "1", + "inset": true + }, + { + "x": "0", + "y": "0", + "blur": "0", + "spread": "3", + "color": "--bg", + "alpha": "1", + "inset": true + } + ], + "buttonPressed": [ + { + "x": "1", + "y": "1", + "blur": "0", + "spread": 0, + "color": "#000000", + "alpha": "1", + "inset": true + }, + { + "x": "-1", + "y": "-1", + "blur": "0", + "spread": 0, + "color": "#FFFFFF", + "alpha": "1", + "inset": true + }, + { + "x": "2", + "y": "2", + "blur": "0", + "spread": 0, + "color": "#848484", + "alpha": "1", + "inset": true + }, + { + "x": "-2", + "y": "-2", + "blur": "0", + "spread": 0, + "color": "#dfdfdf", + "alpha": "1", + "inset": true + }, + { + "x": "0", + "y": "0", + "blur": "0", + "spread": "3", + "color": "--bg", + "alpha": "1", + "inset": true + } + ], + "input": [ + { + "x": "-1", + "y": "-1", + "blur": "0", + "spread": 0, + "color": "#FFFFFF", + "alpha": "1", + "inset": true + }, + { + "x": "1", + "y": "1", + "blur": "0", + "spread": 0, + "color": "#848484", + "alpha": "1", + "inset": true + }, + { + "x": "-2", + "y": "-2", + "blur": "0", + "spread": 0, + "color": "#dfdfdf", + "alpha": "1", + "inset": true + }, + { + "x": "2", + "y": "2", + "blur": "0", + "spread": 0, + "color": "#000000", + "alpha": "1", + "inset": true + }, + { + "x": "0", + "y": "0", + "blur": "0", + "spread": "3", + "color": "--input", + "alpha": "1", + "inset": true + } + ], + "panelHeader": [ + { + "x": "-2200", + "y": 0, + "blur": "200", + "spread": "-2000", + "inset": true, + "color": "#a5cef7", + "alpha": 1 + } + ] + }, + "opacity": { + "input": "1" + }, + "colors": { + "bg": "#d6d6ce", + "text": "#000000", + "link": "#0000ff", + "fg": "#d6d6ce", + "panel": "#042967", + "input": "#ffffff", + "topBar": "#042967", + "topBarLink": "#ffffff", + "btn": "#d6d6ce", + "faint": "#3f3f3f", + "faintLink": "#404080", + "border": "#808080", + "cRed": "#FF0000", + "cBlue": "#008080", + "cGreen": "#00FF00", + "cOrange": "#808000" + }, + "radii": { + "btn": "0", + "input": "0", + "checkbox": "0", + "panel": "0", + "avatar": "0", + "avatarAlt": "0", + "tooltip": "0", + "attachment": "0" + } + } +} From 9a9dc47fc573af28429a641bf5408ead1c0d33ec Mon Sep 17 00:00:00 2001 From: Henry Jameson <me@hjkos.com> Date: Mon, 26 Nov 2018 03:19:04 +0300 Subject: [PATCH 67/96] better preview, collateral fixes --- src/App.scss | 27 ++++++++ .../notifications/notifications.scss | 17 ------ .../notifications/notifications.vue | 2 +- src/components/status/status.vue | 1 + .../style_switcher/style_switcher.scss | 25 +++++++- .../style_switcher/style_switcher.vue | 61 +++++++++++++++++-- src/components/timeline/timeline.vue | 5 +- src/i18n/en.json | 13 ++++ 8 files changed, 123 insertions(+), 28 deletions(-) diff --git a/src/App.scss b/src/App.scss index 50645ec8..8c9df0ba 100644 --- a/src/App.scss +++ b/src/App.scss @@ -381,6 +381,12 @@ main-router { font-size: 1.3em; } + .faint { + background-color: transparent; + color: $fallback--faint; + color: var(--panelFaint, $fallback--faint); + } + .alert { white-space: nowrap; text-overflow: ellipsis; @@ -509,6 +515,27 @@ nav { flex-grow: 0; } } +.badge { + display: inline-block; + border-radius: 99px; + min-width: 22px; + max-width: 22px; + min-height: 22px; + max-height: 22px; + font-size: 15px; + line-height: 22px; + text-align: center; + vertical-align: middle; + white-space: nowrap; + padding: 0; + + &.badge-notification { + background-color: $fallback--cRed; + background-color: var(--badgeNotification, $fallback--cRed); + color: white; + color: var(--badgeNotificationText, white); + } +} .alert { margin: 0.35em; diff --git a/src/components/notifications/notifications.scss b/src/components/notifications/notifications.scss index 3f22b690..87c89f6a 100644 --- a/src/components/notifications/notifications.scss +++ b/src/components/notifications/notifications.scss @@ -4,23 +4,6 @@ // a bit of a hack to allow scrolling below notifications padding-bottom: 15em; - .unseen-count { - display: inline-block; - background-color: $fallback--cRed; - background-color: var(--badgeNotification, $fallback--cRed); - border-radius: 99px; - min-width: 22px; - max-width: 22px; - min-height: 22px; - max-height: 22px; - color: white; - color: var(--badgeNotificationText, white); - font-size: 15px; - line-height: 22px; - text-align: center; - vertical-align: middle - } - .loadmore-error { color: $fallback--text; color: var(--text, $fallback--text); diff --git a/src/components/notifications/notifications.vue b/src/components/notifications/notifications.vue index eb6c4dd0..64f18720 100644 --- a/src/components/notifications/notifications.vue +++ b/src/components/notifications/notifications.vue @@ -4,7 +4,7 @@ <div class="panel-heading"> <div class="title"> {{$t('notifications.notifications')}} - <span class="unseen-count" v-if="unseenCount">{{unseenCount}}</span> + <span class="badge badge-notification unseen-count" v-if="unseenCount">{{unseenCount}}</span> </div> <div @click.prevent class="loadmore-error alert error" v-if="error"> {{$t('timeline.error_fetching')}} diff --git a/src/components/status/status.vue b/src/components/status/status.vue index 8682d996..0edc7b71 100644 --- a/src/components/status/status.vue +++ b/src/components/status/status.vue @@ -471,6 +471,7 @@ .avatar { width: 48px; height: 48px; + box-shadow: var(--avatarStatusShadow); border-radius: $fallback--avatarRadius; border-radius: var(--avatarRadius, $fallback--avatarRadius); overflow: hidden; diff --git a/src/components/style_switcher/style_switcher.scss b/src/components/style_switcher/style_switcher.scss index 9c4f4ecd..2c33224b 100644 --- a/src/components/style_switcher/style_switcher.scss +++ b/src/components/style_switcher/style_switcher.scss @@ -163,11 +163,32 @@ background-size: cover; background-position: 50% 50%; + .separator { + margin: 1em; + border-bottom: 1px solid; + border-color: $fallback--border; + border-color: var(--border, $fallback--border); + } + + .panel-heading { + .badge, .alert, .btn, .faint { + margin-left: 1em; + } + .flex-spacer { + flex: 1; + } + } + .checkbox { + display: inline-flex; + align-items: baseline; + margin-right: 1em; + } + .btn { margin-left: 0; - margin-top: 1em; + padding: 0 1em; + min-width: 3em; min-height: 30px; - width: 10em; } } diff --git a/src/components/style_switcher/style_switcher.vue b/src/components/style_switcher/style_switcher.vue index a444c6a7..2a7756ed 100644 --- a/src/components/style_switcher/style_switcher.vue +++ b/src/components/style_switcher/style_switcher.vue @@ -46,27 +46,80 @@ v-model="keepRoundness"> <label for="keep-roundness">{{$t('settings.style.switcher.keep_roundness')}}</label> </span> + <span> + <input + id="keep-fonts" + type="checkbox" + v-model="keepFonts"> + <label for="keep-fonts">{{$t('settings.style.switcher.keep_fonts')}}</label> + </span> <p>{{$t('settings.style.switcher.save_load_hint')}}</p> </div> </div> <div class="preview-container"> <div class="panel dummy" :style="previewRules"> - <div class="panel-heading">Preview</div> + <div class="panel-heading"> + {{$t('settings.style.preview.header')}} + <span class="badge badge-notification"> + 99 + </span> + <span class="alert error"> + {{$t('settings.style.preview.error')}} + </span> + <button class="btn"> + {{$t('settings.style.preview.button')}} + </button> + <span class="flex-spacer"/> + <span class="faint"> + {{$t('settings.style.preview.header_faint')}} + </span> + </div> <div class="panel-body theme-preview-content"> <div class="avatar"> ( ͡° ͜ʖ ͡°) </div> <h4>Content</h4> + <br> - A bunch of more content and - <a style="color: var(--link)">a nice lil' link</a> + + <i18n path="settings.style.preview.text"> + <a style="color: var(--link)"> + {{$t('settings.style.preview.link')}} + </a> + </i18n> + <i style="color: var(--cBlue)" class="icon-reply"/> <i style="color: var(--cGreen)" class="icon-retweet"/> <i style="color: var(--cRed)" class="icon-cancel"/> <i style="color: var(--cOrange)" class="icon-star"/> + <br> - <button class="btn">Button</button> + <br> + + <input :value="$t('settings.style.preview.error')" type="text"> + <span class="alert error"> + {{$t('settings.style.preview.error')}} + </span> + + <br> + <br> + + <span class="checkbox"> + <input checked="very yes" type="checkbox" id="preview_checkbox"> + <label for="preview_checkbox">{{$t('settings.style.preview.checkbox')}}</label> + </span> + <button class="btn"> + {{$t('settings.style.preview.button')}} + </button> + + <div class="separator"></div> + + <i18n path="settings.style.preview.fine_print" tag="span" class="faint"> + <a style="color: var(--faintLink)"> + {{$t('settings.style.preview.faint_link')}} + </a> + </i18n> </div> </div> </div> diff --git a/src/components/timeline/timeline.vue b/src/components/timeline/timeline.vue index 39f1b5bc..b69a09fd 100644 --- a/src/components/timeline/timeline.vue +++ b/src/components/timeline/timeline.vue @@ -58,10 +58,7 @@ .timeline { .loadmore-text { - opacity: 0.8; - background-color: transparent; - color: $fallback--faint; - color: var(--panelFaint, $fallback--faint); + opacity: 1; } } diff --git a/src/i18n/en.json b/src/i18n/en.json index c70c488c..0530b66d 100644 --- a/src/i18n/en.json +++ b/src/i18n/en.json @@ -170,6 +170,7 @@ "keep_shadows": "Keep shadows", "keep_opacity": "Keep opacity", "keep_roundness": "Keep roundness", + "keep_fonts": "Keep fonts", "save_load_hint": "\"Keep\" options preserve currently set options when selecting or loading themes, it also stores said options when exporting a theme.", "reset": "Reset", "clear_all": "Clear all", @@ -250,6 +251,18 @@ "size": "Size (in px)", "weight": "Weight (boldness)", "custom": "Custom" + }, + "preview": { + "header": "Preview of header", + "error": "Example error", + "button": "Button", + "text": "A bunch of more content and {0}", + "input": "Just landed in L.A.", + "faint_link": "helpful manual", + "fine_print": "Read our {0} to learn nothing useful!", + "header_faint": "This is fine", + "checkbox": "I have skimmed over terms and conditions", + "link": "a nice lil' link" } } }, From 08838774e41b9beba8f884da15ab1314eddf28f8 Mon Sep 17 00:00:00 2001 From: Henry Jameson <me@hjkos.com> Date: Mon, 26 Nov 2018 03:51:12 +0300 Subject: [PATCH 68/96] redmond update --- static/themes/redmond-xx-se.json | 38 ++++++++++----- static/themes/redmond-xx.json | 18 ++++++- static/themes/redmond-xxi.json | 83 +++++++++++++------------------- 3 files changed, 75 insertions(+), 64 deletions(-) diff --git a/static/themes/redmond-xx-se.json b/static/themes/redmond-xx-se.json index e4cdbdee..70ee89d1 100644 --- a/static/themes/redmond-xx-se.json +++ b/static/themes/redmond-xx-se.json @@ -50,6 +50,26 @@ "inset": true } ], + "panelHeader": [ + { + "x": 0, + "y": 0, + "blur": 0, + "spread": "3", + "inset": true, + "color": "#c0c0c0", + "alpha": 1 + }, + { + "x": "-2200", + "y": 0, + "blur": "200", + "spread": "-2000", + "inset": true, + "color": "#1084d0", + "alpha": 1 + } + ], "button": [ { "x": "-1", @@ -237,21 +257,12 @@ "alpha": "1", "inset": true } - ], - "panelHeader": [ - { - "x": "-2200", - "y": 0, - "blur": "200", - "spread": "-2000", - "inset": true, - "color": "#1084d0", - "alpha": 1 - } ] }, + "fonts": {}, "opacity": { - "input": "1" + "input": "1", + "faint": "1" }, "colors": { "bg": "#c0c0c0", @@ -259,6 +270,7 @@ "link": "#0000ff", "fg": "#c0c0c0", "panel": "#000080", + "panelFaint": "#c0c0c0", "input": "#ffffff", "topBar": "#000080", "topBarLink": "#ffffff", @@ -268,7 +280,7 @@ "border": "#808080", "cRed": "#FF0000", "cBlue": "#008080", - "cGreen": "#00FF00", + "cGreen": "#008000", "cOrange": "#808000" }, "radii": { diff --git a/static/themes/redmond-xx.json b/static/themes/redmond-xx.json index 96dff499..4fd6a369 100644 --- a/static/themes/redmond-xx.json +++ b/static/themes/redmond-xx.json @@ -50,6 +50,17 @@ "inset": true } ], + "panelHeader": [ + { + "x": 0, + "y": 0, + "blur": 0, + "spread": "3", + "inset": true, + "color": "#c0c0c0", + "alpha": 1 + } + ], "button": [ { "x": "-1", @@ -239,8 +250,10 @@ } ] }, + "fonts": {}, "opacity": { - "input": "1" + "input": "1", + "faint": "1" }, "colors": { "bg": "#c0c0c0", @@ -248,6 +261,7 @@ "link": "#0000ff", "fg": "#c0c0c0", "panel": "#000080", + "panelFaint": "#c0c0c0", "input": "#ffffff", "topBar": "#000080", "topBarLink": "#ffffff", @@ -257,7 +271,7 @@ "border": "#808080", "cRed": "#FF0000", "cBlue": "#008080", - "cGreen": "#00FF00", + "cGreen": "#008000", "cOrange": "#808000" }, "radii": { diff --git a/static/themes/redmond-xxi.json b/static/themes/redmond-xxi.json index 1469cc6b..d10bf138 100644 --- a/static/themes/redmond-xxi.json +++ b/static/themes/redmond-xxi.json @@ -9,7 +9,7 @@ "y": "-1", "blur": "0", "spread": 0, - "color": "#000000", + "color": "#404040", "alpha": "1", "inset": true }, @@ -50,13 +50,33 @@ "inset": true } ], + "panelHeader": [ + { + "x": 0, + "y": 0, + "blur": 0, + "spread": "3", + "inset": true, + "color": "#d6d6ce", + "alpha": 1 + }, + { + "x": "-2200", + "y": 0, + "blur": "200", + "spread": "-2000", + "inset": true, + "color": "#a5cef7", + "alpha": 1 + } + ], "button": [ { "x": "-1", "y": "-1", "blur": "0", "spread": 0, - "color": "#000000", + "color": "#404040", "alpha": "1", "inset": true }, @@ -78,15 +98,6 @@ "alpha": "1", "inset": true }, - { - "x": "2", - "y": "2", - "blur": "0", - "spread": 0, - "color": "#dfdfdf", - "alpha": "1", - "inset": true - }, { "x": "0", "y": "0", @@ -103,7 +114,7 @@ "y": "-1", "blur": "0", "spread": 0, - "color": "#000000", + "color": "#404040", "alpha": "1", "inset": true }, @@ -125,15 +136,6 @@ "alpha": "1", "inset": true }, - { - "x": "2", - "y": "2", - "blur": "0", - "spread": 0, - "color": "#dfdfdf", - "alpha": "1", - "inset": true - }, { "x": "0", "y": "0", @@ -150,7 +152,7 @@ "y": "1", "blur": "0", "spread": 0, - "color": "#000000", + "color": "#404040", "alpha": "1", "inset": true }, @@ -172,15 +174,6 @@ "alpha": "1", "inset": true }, - { - "x": "-2", - "y": "-2", - "blur": "0", - "spread": 0, - "color": "#dfdfdf", - "alpha": "1", - "inset": true - }, { "x": "0", "y": "0", @@ -215,7 +208,7 @@ "y": "-2", "blur": "0", "spread": 0, - "color": "#dfdfdf", + "color": "#d4d0c8", "alpha": "1", "inset": true }, @@ -224,7 +217,7 @@ "y": "2", "blur": "0", "spread": 0, - "color": "#000000", + "color": "#404040", "alpha": "1", "inset": true }, @@ -237,21 +230,12 @@ "alpha": "1", "inset": true } - ], - "panelHeader": [ - { - "x": "-2200", - "y": 0, - "blur": "200", - "spread": "-2000", - "inset": true, - "color": "#a5cef7", - "alpha": 1 - } ] }, + "fonts": {}, "opacity": { - "input": "1" + "input": "1", + "faint": "1" }, "colors": { "bg": "#d6d6ce", @@ -259,6 +243,7 @@ "link": "#0000ff", "fg": "#d6d6ce", "panel": "#042967", + "panelFaint": "#FFFFFF", "input": "#ffffff", "topBar": "#042967", "topBarLink": "#ffffff", @@ -266,10 +251,10 @@ "faint": "#3f3f3f", "faintLink": "#404080", "border": "#808080", - "cRed": "#FF0000", - "cBlue": "#008080", - "cGreen": "#00FF00", - "cOrange": "#808000" + "cRed": "#c42726", + "cBlue": "#6699cc", + "cGreen": "#669966", + "cOrange": "#cc6633" }, "radii": { "btn": "0", From bb39b99d65bf897e073bc809ccc924f1b0ecc58b Mon Sep 17 00:00:00 2001 From: Henry Jameson <me@hjkos.com> Date: Mon, 26 Nov 2018 20:12:59 +0300 Subject: [PATCH 69/96] fix panel link color, fix broken user profiles --- src/App.scss | 10 ++++++++++ src/components/style_switcher/style_switcher.js | 4 ++++ src/components/style_switcher/style_switcher.vue | 4 +++- src/components/user_card_content/user_card_content.vue | 5 ++--- src/services/style_setter/style_setter.js | 1 + 5 files changed, 20 insertions(+), 4 deletions(-) diff --git a/src/App.scss b/src/App.scss index 8c9df0ba..15dec7ec 100644 --- a/src/App.scss +++ b/src/App.scss @@ -407,6 +407,11 @@ main-router { min-width: 1px; align-self: stretch; } + + a { + color: $fallback--link; + color: var(--panelLink, $fallback--link) + } } .panel-heading.stub { @@ -417,6 +422,11 @@ main-router { .panel-footer { border-radius: 0 0 $fallback--panelRadius $fallback--panelRadius; border-radius: 0 0 var(--panelRadius, $fallback--panelRadius) var(--panelRadius, $fallback--panelRadius); + + a { + color: $fallback--link; + color: var(--panelLink, $fallback--link) + } } .panel-body > p { diff --git a/src/components/style_switcher/style_switcher.js b/src/components/style_switcher/style_switcher.js index d833341f..0cceee4c 100644 --- a/src/components/style_switcher/style_switcher.js +++ b/src/components/style_switcher/style_switcher.js @@ -62,6 +62,7 @@ export default { panelColorLocal: undefined, panelTextColorLocal: undefined, + panelLinkColorLocal: undefined, panelFaintColorLocal: undefined, panelOpacityLocal: undefined, @@ -155,6 +156,7 @@ export default { panel: this.panelColorLocal, panelText: this.panelTextColorLocal, + panelLink: this.panelLinkColorLocal, panelFaint: this.panelFaintColorLocal, input: this.inputColorLocal, @@ -230,6 +232,7 @@ export default { const fgs = { text: hex2rgb(colors.text), panelText: hex2rgb(colors.panelText), + panelLink: hex2rgb(colors.panelLink), btnText: hex2rgb(colors.btnText), topBarText: hex2rgb(colors.topBarText), inputText: hex2rgb(colors.inputText), @@ -268,6 +271,7 @@ export default { tintText: getContrastRatio(alphaBlend(bgs.bg, 0.5, fgs.panelText), fgs.text), panelText: getContrastRatio(alphaBlend(bgs.panel, opacity.panel, fgs.panelText), fgs.panelText), + panelLink: getContrastRatio(alphaBlend(bgs.panel, opacity.panel, fgs.panelLink), fgs.panelLink), btnText: getContrastRatio(alphaBlend(bgs.btn, opacity.btn, fgs.btnText), fgs.btnText), diff --git a/src/components/style_switcher/style_switcher.vue b/src/components/style_switcher/style_switcher.vue index 2a7756ed..157a8534 100644 --- a/src/components/style_switcher/style_switcher.vue +++ b/src/components/style_switcher/style_switcher.vue @@ -183,8 +183,10 @@ <h4>{{ $t('settings.style.advanced_colors.panel_header') }}</h4> <ColorInput name="panelColor" v-model="panelColorLocal" :fallback="fgColorLocal" :label="$t('settings.background')"/> <OpacityInput name="panelOpacity" v-model="panelOpacityLocal" :fallback="previewTheme.opacity.panel || 1"/> - <ColorInput name="panelTextColor" v-model="panelTextColorLocal" :fallback="previewTheme.colors.panelText" :label="$t('settings.links')"/> + <ColorInput name="panelTextColor" v-model="panelTextColorLocal" :fallback="previewTheme.colors.panelText" :label="$t('settings.text')"/> <ContrastRatio :contrast="previewContrast.panelText" large="1"/> + <ColorInput name="panelLinkColor" v-model="panelLinkColorLocal" :fallback="previewTheme.colors.panelLink" :label="$t('settings.links')"/> + <ContrastRatio :contrast="previewContrast.panelLink" large="1"/> </div> <div class="color-item"> <h4>{{ $t('settings.style.advanced_colors.top_bar') }}</h4> diff --git a/src/components/user_card_content/user_card_content.vue b/src/components/user_card_content/user_card_content.vue index bb1e314f..5529948e 100644 --- a/src/components/user_card_content/user_card_content.vue +++ b/src/components/user_card_content/user_card_content.vue @@ -105,10 +105,9 @@ <span v-if="!hideUserStatsLocal">{{user.followers_count}}</span> </div> </div> + <p v-if="!hideBio && user.description_html" class="profile-bio" v-html="user.description_html"></p> + <p v-else-if="!hideBio" class="profile-bio">{{ user.description }}</p> </div> - <p v-if="!hideBio && user.description_html" class="profile-bio" v-html="user.description_html"></p> - <p v-else-if="!hideBio" class="profile-bio">{{ user.description }}</p> -</div> </div> </template> diff --git a/src/services/style_setter/style_setter.js b/src/services/style_setter/style_setter.js index f2c9c13e..7c375206 100644 --- a/src/services/style_setter/style_setter.js +++ b/src/services/style_setter/style_setter.js @@ -172,6 +172,7 @@ const generateColors = (input) => { colors.panel = col.panel || Object.assign({}, col.fg) colors.panelText = col.panelText || getTextColor(colors.panel, colors.fgText) + colors.panelLink = col.panelLink || getTextColor(colors.panel, colors.fgLink) colors.panelFaint = col.panelFaint || getTextColor(colors.panel, colors.faint) colors.topBar = col.topBar || Object.assign({}, col.fg) From d64f4ab363b8fd8b6fe4542abe5f991178f4d3d4 Mon Sep 17 00:00:00 2001 From: Henry Jameson <me@hjkos.com> Date: Mon, 26 Nov 2018 20:14:53 +0300 Subject: [PATCH 70/96] fix preview input text using wrong string --- src/components/style_switcher/style_switcher.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/style_switcher/style_switcher.vue b/src/components/style_switcher/style_switcher.vue index 157a8534..655e0589 100644 --- a/src/components/style_switcher/style_switcher.vue +++ b/src/components/style_switcher/style_switcher.vue @@ -97,7 +97,7 @@ <br> <br> - <input :value="$t('settings.style.preview.error')" type="text"> + <input :value="$t('settings.style.preview.input')" type="text"> <span class="alert error"> {{$t('settings.style.preview.error')}} </span> From f039b79e5ac7da33c3664241e0d20d3c6964872f Mon Sep 17 00:00:00 2001 From: Henry Jameson <me@hjkos.com> Date: Mon, 26 Nov 2018 20:25:14 +0300 Subject: [PATCH 71/96] unbreak user profiles --- src/components/user_card_content/user_card_content.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/components/user_card_content/user_card_content.js b/src/components/user_card_content/user_card_content.js index 064c984d..e7ca21c7 100644 --- a/src/components/user_card_content/user_card_content.js +++ b/src/components/user_card_content/user_card_content.js @@ -12,7 +12,10 @@ export default { }, computed: { headingStyle () { - const color = this.$store.state.config.customTheme.colors.bg + const color = this.$store.state.config.customTheme.colors ? + this.$store.state.config.customTheme.colors.bg : // v2 + this.$store.state.config.colors.bg // v1 + if (color) { const rgb = (typeof color === 'string') ? hex2rgb(color) : color const tintColor = `rgba(${Math.floor(rgb.r)}, ${Math.floor(rgb.g)}, ${Math.floor(rgb.b)}, .5)` From 2ebc06e30f009204eb289057730f9fa4a148d499 Mon Sep 17 00:00:00 2001 From: Henry Jameson <me@hjkos.com> Date: Mon, 26 Nov 2018 21:07:22 +0300 Subject: [PATCH 72/96] fixed keep checkboxes working when exporting --- .../style_switcher/style_switcher.js | 51 ++++++++++++++----- src/i18n/en.json | 2 +- 2 files changed, 40 insertions(+), 13 deletions(-) diff --git a/src/components/style_switcher/style_switcher.js b/src/components/style_switcher/style_switcher.js index 0cceee4c..ccdb4c4f 100644 --- a/src/components/style_switcher/style_switcher.js +++ b/src/components/style_switcher/style_switcher.js @@ -332,16 +332,34 @@ export default { }, methods: { exportCurrentTheme () { + const saveEverything = !this.keepFonts && !this.keepShadows && !this.keepColors && !this.keepOpacity && !this.keepRoundness + const theme = { + shadows: this.shadowsLocal, + fonts: this.fontsLocal, + opacity: this.currentOpacity, + colors: this.currentColors, + radii: this.currentRadii + } + + if (!this.keepFonts && !saveEverything) { + delete theme.fonts + } + if (!this.keepShadows && !saveEverything) { + delete theme.shadows + } + if (!this.keepOpacity && !saveEverything) { + delete theme.opacity + } + if (!this.keepColors && !saveEverything) { + delete theme.colors + } + if (!this.keepRoundness && !saveEverything) { + delete theme.radii + } + const stringified = JSON.stringify({ // To separate from other random JSON files and possible future theme formats - _pleroma_theme_version: 2, - theme: { - shadows: this.shadowsLocal, - fonts: this.fontsLocal, - opacity: this.currentOpacity, - colors: this.currentColors, - radii: this.currentRadii - } + _pleroma_theme_version: 2, theme }, null, 2) // Pretty-print and indent with 2 spaces // Create an invisible link with a data url and simulate a click @@ -404,7 +422,9 @@ export default { }, clearAll () { - this.normalizeLocalState(this.$store.state.config.customTheme) + const state = this.$store.state.config.customTheme + const version = state.colors ? 2 : 'l1' + this.normalizeLocalState(this.$store.state.config.customTheme, version) }, // Clears all the extra stuff when loading V1 theme @@ -442,9 +462,13 @@ export default { }, /** - * This applies stored theme data onto form. + * This applies stored theme data onto form. Supports three versions of data: + * v2 (version = 2) - newer version of themes. + * v1 (version = 1) - older version of themes (import from file) + * v1l (version = l1) - older version of theme (load from local storage) + * v1 and v1l differ because of way themes were stored/exported. * @param {Object} input - input data - * @param {Number} version - version of data. 0 means try to guess based on data. + * @param {Number} version - version of data. 0 means try to guess based on data. "l1" means v1, locastorage type */ normalizeLocalState (input, version = 0) { const colors = input.colors || input @@ -465,6 +489,8 @@ export default { } } + console.log(version) + // Stuff that differs between V1 and V2 if (version === 1) { this.fgColorLocal = rgb2hex(colors.btn) @@ -472,7 +498,7 @@ export default { } const keys = new Set(version !== 1 ? Object.keys(colors) : []) - if (version === 1) { + if (version === 1 || version === 'l1') { // V1 ignores the rest this.clearV1() keys @@ -483,6 +509,7 @@ export default { .add('cGreen') .add('cOrange') } + keys.forEach(key => { this[key + 'ColorLocal'] = rgb2hex(colors[key]) }) diff --git a/src/i18n/en.json b/src/i18n/en.json index ab7b954a..74e7a556 100644 --- a/src/i18n/en.json +++ b/src/i18n/en.json @@ -183,7 +183,7 @@ "keep_opacity": "Keep opacity", "keep_roundness": "Keep roundness", "keep_fonts": "Keep fonts", - "save_load_hint": "\"Keep\" options preserve currently set options when selecting or loading themes, it also stores said options when exporting a theme.", + "save_load_hint": "\"Keep\" options preserve currently set options when selecting or loading themes, it also stores said options when exporting a theme. When all checkboxes unset, exporting theme will save everything.", "reset": "Reset", "clear_all": "Clear all", "clear_opacity": "Clear opacity" From f8e17cbdc58651b17a4f5639d3719a7e533b0d8b Mon Sep 17 00:00:00 2001 From: Henry Jameson <me@hjkos.com> Date: Mon, 26 Nov 2018 21:22:44 +0300 Subject: [PATCH 73/96] lint fix --- src/components/user_card_content/user_card_content.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/components/user_card_content/user_card_content.js b/src/components/user_card_content/user_card_content.js index e7ca21c7..254d1666 100644 --- a/src/components/user_card_content/user_card_content.js +++ b/src/components/user_card_content/user_card_content.js @@ -12,9 +12,9 @@ export default { }, computed: { headingStyle () { - const color = this.$store.state.config.customTheme.colors ? - this.$store.state.config.customTheme.colors.bg : // v2 - this.$store.state.config.colors.bg // v1 + const color = this.$store.state.config.customTheme.colors + ? this.$store.state.config.customTheme.colors.bg // v2 + : this.$store.state.config.colors.bg // v1 if (color) { const rgb = (typeof color === 'string') ? hex2rgb(color) : color From b45fc6c6523b1332c6422a5dc6eff95c11a32690 Mon Sep 17 00:00:00 2001 From: Henry Jameson <me@hjkos.com> Date: Tue, 27 Nov 2018 04:54:59 +0300 Subject: [PATCH 74/96] updated preview window --- .../style_switcher/style_switcher.scss | 124 ++++++++++++------ .../style_switcher/style_switcher.vue | 108 ++++++++------- src/components/timeline/timeline.vue | 2 +- src/i18n/en.json | 3 +- 4 files changed, 150 insertions(+), 87 deletions(-) diff --git a/src/components/style_switcher/style_switcher.scss b/src/components/style_switcher/style_switcher.scss index 2c33224b..baf2b73a 100644 --- a/src/components/style_switcher/style_switcher.scss +++ b/src/components/style_switcher/style_switcher.scss @@ -163,32 +163,95 @@ background-size: cover; background-position: 50% 50%; - .separator { - margin: 1em; - border-bottom: 1px solid; - border-color: $fallback--border; - border-color: var(--border, $fallback--border); - } + .dummy { + .post { + font-family: var(--postFont); + display: flex; - .panel-heading { - .badge, .alert, .btn, .faint { - margin-left: 1em; - } - .flex-spacer { - flex: 1; - } - } - .checkbox { - display: inline-flex; - align-items: baseline; - margin-right: 1em; - } + .content { + flex: 1; - .btn { - margin-left: 0; - padding: 0 1em; - min-width: 3em; - min-height: 30px; + h4 { + margin-bottom: .25em; + } + + .icons { + margin-top: .5em; + display: flex; + + i { + margin-right: 1em; + } + } + } + } + + .after-post { + margin-top: 1em; + display: flex; + align-items: center; + } + + .avatar, .avatar-alt{ + background: linear-gradient(135deg, #b8e1fc 0%,#a9d2f3 10%,#90bae4 25%,#90bcea 37%,#90bff0 50%,#6ba8e5 51%,#a2daf5 83%,#bdf3fd 100%); + color: black; + font-family: sans-serif; + text-align: center; + margin-right: 1em; + } + + .avatar-alt { + flex: 0 auto; + margin-left: 28px; + font-size: 12px; + width: 20px; + height: 20px; + line-height: 20px; + border-radius: $fallback--avatarAltRadius; + border-radius: var(--avatarAltRadius, $fallback--avatarAltRadius); + } + + .avatar { + flex: 0 auto; + width: 48px; + height: 48px; + font-size: 14px; + line-height: 48px; + } + + .actions { + display: flex; + align-items: baseline; + + .checkbox { + display: inline-flex; + align-items: baseline; + margin-right: 1em; + flex: 1; + } + } + + .separator { + margin: 1em; + border-bottom: 1px solid; + border-color: $fallback--border; + border-color: var(--border, $fallback--border); + } + + .panel-heading { + .badge, .alert, .btn, .faint { + margin-left: 1em; + } + .flex-spacer { + flex: 1; + } + } + .btn { + margin-left: 0; + padding: 0 1em; + min-width: 3em; + min-height: 30px; + } } } @@ -259,17 +322,4 @@ margin-left: .25em; margin-right: .25em; } - - .dummy { - .avatar { - background: linear-gradient(135deg, #b8e1fc 0%,#a9d2f3 10%,#90bae4 25%,#90bcea 37%,#90bff0 50%,#6ba8e5 51%,#a2daf5 83%,#bdf3fd 100%); - color: black; - text-align: center; - height: 48px; - line-height: 48px; - width: 48px; - float: left; - margin-right: 1em; - } - } } diff --git a/src/components/style_switcher/style_switcher.vue b/src/components/style_switcher/style_switcher.vue index 655e0589..fa173b98 100644 --- a/src/components/style_switcher/style_switcher.vue +++ b/src/components/style_switcher/style_switcher.vue @@ -60,66 +60,78 @@ <div class="preview-container"> <div class="panel dummy" :style="previewRules"> <div class="panel-heading"> - {{$t('settings.style.preview.header')}} - <span class="badge badge-notification"> - 99 - </span> - <span class="alert error"> - {{$t('settings.style.preview.error')}} - </span> - <button class="btn"> - {{$t('settings.style.preview.button')}} - </button> - <span class="flex-spacer"/> + <div class="title"> + {{$t('settings.style.preview.header')}} + <span class="badge badge-notification"> + 99 + </span> + </div> <span class="faint"> {{$t('settings.style.preview.header_faint')}} </span> - </div> - <div class="panel-body theme-preview-content"> - <div class="avatar"> - ( ͡° ͜ʖ ͡°) - </div> - <h4>Content</h4> - - <br> - - <i18n path="settings.style.preview.text"> - <a style="color: var(--link)"> - {{$t('settings.style.preview.link')}} - </a> - </i18n> - - <i style="color: var(--cBlue)" class="icon-reply"/> - <i style="color: var(--cGreen)" class="icon-retweet"/> - <i style="color: var(--cRed)" class="icon-cancel"/> - <i style="color: var(--cOrange)" class="icon-star"/> - - <br> - <br> - - <input :value="$t('settings.style.preview.input')" type="text"> <span class="alert error"> {{$t('settings.style.preview.error')}} </span> - - <br> - <br> - - <span class="checkbox"> - <input checked="very yes" type="checkbox" id="preview_checkbox"> - <label for="preview_checkbox">{{$t('settings.style.preview.checkbox')}}</label> - </span> <button class="btn"> {{$t('settings.style.preview.button')}} </button> + </div> + <div class="panel-body theme-preview-content"> + <div class="post"> + <div class="avatar"> + ( ͡° ͜ʖ ͡°) + </div> + <div class="content"> + <h4> + Content + </h4> + <i18n path="settings.style.preview.text"> + <code style="font-family: var(--postCodeFont)"> + {{$t('settings.style.preview.mono')}} + </code> + <a style="color: var(--link)"> + {{$t('settings.style.preview.link')}} + </a> + </i18n> + + <div class="icons"> + <i style="color: var(--cBlue)" class="icon-reply"/> + <i style="color: var(--cGreen)" class="icon-retweet"/> + <i style="color: var(--cOrange)" class="icon-star"/> + <i style="color: var(--cRed)" class="icon-cancel"/> + </div> + </div> + </div> + + <div class="after-post"> + <div class="avatar-alt"> + :^) + </div> + <div class="content"> + <i18n path="settings.style.preview.fine_print" tag="span" class="faint"> + <a style="color: var(--faintLink)"> + {{$t('settings.style.preview.faint_link')}} + </a> + </i18n> + </div> + </div> <div class="separator"></div> - <i18n path="settings.style.preview.fine_print" tag="span" class="faint"> - <a style="color: var(--faintLink)"> - {{$t('settings.style.preview.faint_link')}} - </a> - </i18n> + <span class="alert error"> + {{$t('settings.style.preview.error')}} + </span> + <input :value="$t('settings.style.preview.input')" type="text"> + + <div class="actions"> + <span class="checkbox"> + <input checked="very yes" type="checkbox" id="preview_checkbox"> + <label for="preview_checkbox">{{$t('settings.style.preview.checkbox')}}</label> + </span> + <button class="btn"> + {{$t('settings.style.preview.button')}} + </button> + </div> </div> </div> </div> diff --git a/src/components/timeline/timeline.vue b/src/components/timeline/timeline.vue index b69a09fd..bc7f74c2 100644 --- a/src/components/timeline/timeline.vue +++ b/src/components/timeline/timeline.vue @@ -10,7 +10,7 @@ <button @click.prevent="showNewStatuses" class="loadmore-button" v-if="timeline.newStatusCount > 0 && !timelineError"> {{$t('timeline.show_new')}}{{newStatusCountStr}} </button> - <div @click.prevent class="loadmore-text" v-if="!timeline.newStatusCount > 0 && !timelineError"> + <div @click.prevent class="loadmore-text faint" v-if="!timeline.newStatusCount > 0 && !timelineError"> {{$t('timeline.up_to_date')}} </div> </div> diff --git a/src/i18n/en.json b/src/i18n/en.json index 74e7a556..8847b11e 100644 --- a/src/i18n/en.json +++ b/src/i18n/en.json @@ -268,7 +268,8 @@ "header": "Preview of header", "error": "Example error", "button": "Button", - "text": "A bunch of more content and {0}", + "text": "A bunch of more {0} and {1}", + "mono": "content", "input": "Just landed in L.A.", "faint_link": "helpful manual", "fine_print": "Read our {0} to learn nothing useful!", From 406df4399b630268c1028664f3b818571d6f8e4f Mon Sep 17 00:00:00 2001 From: Henry Jameson <me@hjkos.com> Date: Fri, 30 Nov 2018 16:39:07 +0300 Subject: [PATCH 75/96] avatars shadows, also allows drop-shadow use --- src/components/notification/notification.js | 3 +- src/components/notification/notification.vue | 2 +- .../notifications/notifications.scss | 6 ++++ src/components/status/status.js | 3 +- src/components/status/status.vue | 13 ++++++++- .../style_switcher/style_switcher.vue | 12 ++++++++ .../user_card_content/user_card_content.js | 3 +- .../user_card_content/user_card_content.vue | 7 ++++- src/i18n/en.json | 7 +++++ src/modules/interface.js | 6 ++++ src/services/style_setter/style_setter.js | 28 +++++++++++++++++-- 11 files changed, 82 insertions(+), 8 deletions(-) diff --git a/src/components/notification/notification.js b/src/components/notification/notification.js index c786f2cc..4dea63bb 100644 --- a/src/components/notification/notification.js +++ b/src/components/notification/notification.js @@ -6,7 +6,8 @@ import { highlightClass, highlightStyle } from '../../services/user_highlighter/ const Notification = { data () { return { - userExpanded: false + userExpanded: false, + betterShadow: this.$store.state.interface.browserSupport.cssFilter } }, props: [ diff --git a/src/components/notification/notification.vue b/src/components/notification/notification.vue index 72c1ca69..f98afbe0 100644 --- a/src/components/notification/notification.vue +++ b/src/components/notification/notification.vue @@ -2,7 +2,7 @@ <status v-if="notification.type === 'mention'" :compact="true" :statusoid="notification.status"></status> <div class="non-mention" :class="[userClass, { highlighted: userStyle }]" :style="[ userStyle ]"v-else> <a class='avatar-container' :href="notification.action.user.statusnet_profile_url" @click.stop.prevent.capture="toggleUserExpanded"> - <StillImage class='avatar-compact' :src="notification.action.user.profile_image_url_original"/> + <StillImage class='avatar-compact' :class="{'better-shadow': betterShadow}" :src="notification.action.user.profile_image_url_original"/> </a> <div class='notification-right'> <div class="usercard notification-usercard" v-if="userExpanded"> diff --git a/src/components/notifications/notifications.scss b/src/components/notifications/notifications.scss index 87c89f6a..d17ae25d 100644 --- a/src/components/notifications/notifications.scss +++ b/src/components/notifications/notifications.scss @@ -49,11 +49,17 @@ .avatar-compact { width: 32px; height: 32px; + box-shadow: var(--avatarStatusShadow); border-radius: $fallback--avatarAltRadius; border-radius: var(--avatarAltRadius, $fallback--avatarAltRadius); overflow: hidden; line-height: 0; + &.better-shadow { + box-shadow: none; + filter: drop-shadow(var(--avatarStatusShadowFilter)) + } + &.animated::before { display: none; } diff --git a/src/components/status/status.js b/src/components/status/status.js index 10716583..725bc3f8 100644 --- a/src/components/status/status.js +++ b/src/components/status/status.js @@ -33,7 +33,8 @@ const Status = { showingTall: false, expandingSubject: typeof this.$store.state.config.collapseMessageWithSubject === 'undefined' ? !this.$store.state.instance.collapseMessageWithSubject - : !this.$store.state.config.collapseMessageWithSubject + : !this.$store.state.config.collapseMessageWithSubject, + betterShadow: this.$store.state.interface.browserSupport.cssFilter } }, computed: { diff --git a/src/components/status/status.vue b/src/components/status/status.vue index 4541c560..26be335c 100644 --- a/src/components/status/status.vue +++ b/src/components/status/status.vue @@ -21,7 +21,7 @@ <div :class="[userClass, { highlighted: userStyle, 'is-retweet': retweet }]" :style="[ userStyle ]" class="media status"> <div v-if="!noHeading" class="media-left"> <a :href="status.user.statusnet_profile_url" @click.stop.prevent.capture="toggleUserExpanded"> - <StillImage class='avatar' :class="{'avatar-compact': compact}" :src="status.user.profile_image_url_original"/> + <StillImage class='avatar' :class="{'avatar-compact': compact, 'better-shadow': betterShadow}" :src="status.user.profile_image_url_original"/> </a> </div> <div class="status-body"> @@ -464,8 +464,14 @@ .status .avatar-compact { width: 32px; height: 32px; + box-shadow: var(--avatarStatusShadow); border-radius: $fallback--avatarAltRadius; border-radius: var(--avatarAltRadius, $fallback--avatarAltRadius); + + &.better-shadow { + box-shadow: none; + filter: drop-shadow(var(--avatarStatusShadowFilter)) + } } .avatar { @@ -477,6 +483,11 @@ overflow: hidden; position: relative; + &.better-shadow { + box-shadow: none; + filter: drop-shadow(var(--avatarStatusShadowFilter)) + } + img { width: 100%; height: 100%; diff --git a/src/components/style_switcher/style_switcher.vue b/src/components/style_switcher/style_switcher.vue index fa173b98..66fe0f6b 100644 --- a/src/components/style_switcher/style_switcher.vue +++ b/src/components/style_switcher/style_switcher.vue @@ -278,6 +278,18 @@ <button class="btn" @click="clearShadows">{{$t('settings.style.switcher.clear_all')}}</button> </div> <shadow-control :ready="!!currentShadowFallback" :fallback="currentShadowFallback" v-model="currentShadow"/> + <div v-if="shadowSelected === 'avatar' || shadowSelected === 'avatarStatus'"> + <i18n path="settings.style.shadows.filter_hint.always_drop_shadow" tag="p"> + <code>filter: drop-shadow()</code> + </i18n> + <i18n path="settings.style.shadows.filter_hint.text" tag="p"> + <code>drop-shadow</code> + <code>spread-radius</code> + <code>inset</code> + </i18n> + <p>{{$t('settings.style.shadows.filter_hint.inset_ignored')}}</p> + <p>{{$t('settings.style.shadows.filter_hint.spread_zero')}}</p> + </div> </div> <div :label="$t('settings.style.fonts._tab_label')" class="fonts-container"> <div class="tab-header"> diff --git a/src/components/user_card_content/user_card_content.js b/src/components/user_card_content/user_card_content.js index 254d1666..97cd4983 100644 --- a/src/components/user_card_content/user_card_content.js +++ b/src/components/user_card_content/user_card_content.js @@ -7,7 +7,8 @@ export default { return { hideUserStatsLocal: typeof this.$store.state.config.hideUserStats === 'undefined' ? this.$store.state.instance.hideUserStats - : this.$store.state.config.hideUserStats + : this.$store.state.config.hideUserStats, + betterShadow: this.$store.state.interface.browserSupport.cssFilter } }, computed: { diff --git a/src/components/user_card_content/user_card_content.vue b/src/components/user_card_content/user_card_content.vue index 5529948e..cca418ff 100644 --- a/src/components/user_card_content/user_card_content.vue +++ b/src/components/user_card_content/user_card_content.vue @@ -10,7 +10,7 @@ </a> <div class='container'> <router-link :to="{ name: 'user-profile', params: { id: user.id } }"> - <StillImage class="avatar" :src="user.profile_image_url_original"/> + <StillImage class="avatar" :class='{ "better-shadow": betterShadow }' :src="user.profile_image_url_original"/> </router-link> <div class="name-and-screen-name"> <div :title="user.name" class='user-name' v-if="user.name_html" v-html="user.name_html"></div> @@ -159,6 +159,11 @@ box-shadow: var(--avatarShadow); object-fit: cover; + &.better-shadow { + box-shadow: none; + filter: drop-shadow(var(--avatarStatusShadowFilter)) + } + &.animated::before { display: none; } diff --git a/src/i18n/en.json b/src/i18n/en.json index 8847b11e..7f5a2a4f 100644 --- a/src/i18n/en.json +++ b/src/i18n/en.json @@ -236,6 +236,13 @@ "spread": "Spread", "inset": "Inset", "hint": "For shadows you can also use --variable as a color value to use CSS3 variables. Please note that setting opacity won't work in this case.", + "filter_hint": { + "always_drop_shadow": "Warning, this shadow always uses {0} when browser supports it.", + "text": "Please note that {0} does not support {1} parameter and {2} keyword.", + "spread_zero": "Shadows with spread > 0 will appear as if it was set to zero", + "inset_ignored": "Inset shadows using will be ignored", + "inset_substituted": "Inset shadows will be substituted with {1} equivalent" + }, "components": { "panel": "Panel", "panelHeader": "Panel header", diff --git a/src/modules/interface.js b/src/modules/interface.js index 07489685..132fb08d 100644 --- a/src/modules/interface.js +++ b/src/modules/interface.js @@ -4,6 +4,12 @@ const defaultState = { settings: { currentSaveStateNotice: null, noticeClearTimeout: null + }, + browserSupport: { + cssFilter: window.CSS && window.CSS.supports && ( + window.CSS.supports('filter', 'drop-shadow(0 0)') || + window.CSS.supports('-webkit-filter', 'drop-shadow(0 0)') + ) } } diff --git a/src/services/style_setter/style_setter.js b/src/services/style_setter/style_setter.js index 7c375206..cff81c40 100644 --- a/src/services/style_setter/style_setter.js +++ b/src/services/style_setter/style_setter.js @@ -110,6 +110,24 @@ const getCssShadow = (input) => { ]).join(' ')).join(', ') } +const getCssShadowFilter = (input) => { + if (input.length === 0) { + return 'none' + } + + return input + // drop-shadow doesn't support inset or spread + .filter((shad) => console.log(shad) || !shad.inset && Number(shad.spread) === 0) + .map((shad) => [ + shad.x, + shad.y, + // drop-shadow's blur is twice as strong compared to box-shadow + shad.blur / 2 + ].map(_ => _ + 'px').concat([ + getCssColor(shad.color, shad.alpha) + ]).join(' ')).join(', ') +} + const getCssColor = (input, a) => { let rgb = {} if (typeof input === 'object') { @@ -384,7 +402,12 @@ const generateShadows = (input) => { return { rules: { - shadows: Object.entries(shadows).map(([k, v]) => `--${k}Shadow: ${getCssShadow(v)}`).join(';') + shadows: Object + .entries(shadows) + // TODO for v2.1: if shadow doesn't have non-inset shadows with spread > 0 - optionally + // convert all non-inset shadows into filter: drop-shadow() to boost performance + .map(([k, v]) => `--${k}Shadow: ${getCssShadow(v)}; --${k}ShadowFilter: ${getCssShadowFilter(v)}`) + .join(';') }, theme: { shadows @@ -467,5 +490,6 @@ export { generateFonts, generatePreset, composePreset, - getCssShadow + getCssShadow, + getCssShadowFilter } From 77ac42d9190934c8e4f1fa7dfef087c58ccd3990 Mon Sep 17 00:00:00 2001 From: Henry Jameson <me@hjkos.com> Date: Sat, 1 Dec 2018 14:47:42 +0300 Subject: [PATCH 76/96] fix retweeter avatar not getting proper shadow --- src/components/status/status.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/status/status.vue b/src/components/status/status.vue index 26be335c..6597d56b 100644 --- a/src/components/status/status.vue +++ b/src/components/status/status.vue @@ -9,7 +9,7 @@ </template> <template v-else> <div v-if="retweet && !noHeading" :class="[repeaterClass, { highlighted: repeaterStyle }]" :style="[repeaterStyle]" class="media container retweet-info"> - <StillImage v-if="retweet" class='avatar' :src="statusoid.user.profile_image_url_original"/> + <StillImage v-if="retweet" class='avatar' :class='{ "better-shadow": betterShadow }' :src="statusoid.user.profile_image_url_original"/> <div class="media-body faint"> <a v-if="retweeterHtml" :href="statusoid.user.statusnet_profile_url" class="user-name" :title="'@'+statusoid.user.screen_name" v-html="retweeterHtml"></a> <a v-else :href="statusoid.user.statusnet_profile_url" class="user-name" :title="'@'+statusoid.user.screen_name">{{retweeter}}</a> From bee738c815f287c4605eafd52c5565cdb07d5721 Mon Sep 17 00:00:00 2001 From: Henry Jameson <me@hjkos.com> Date: Sun, 2 Dec 2018 08:47:55 +0300 Subject: [PATCH 77/96] making inset shadows work on avatars again --- .../notifications/notifications.scss | 4 +-- src/components/status/status.vue | 8 ++--- .../style_switcher/style_switcher.vue | 7 ++-- .../user_card_content/user_card_content.vue | 4 +-- src/i18n/en.json | 6 ++-- src/services/style_setter/style_setter.js | 32 ++++++++++++------- 6 files changed, 36 insertions(+), 25 deletions(-) diff --git a/src/components/notifications/notifications.scss b/src/components/notifications/notifications.scss index d17ae25d..a6468e01 100644 --- a/src/components/notifications/notifications.scss +++ b/src/components/notifications/notifications.scss @@ -56,8 +56,8 @@ line-height: 0; &.better-shadow { - box-shadow: none; - filter: drop-shadow(var(--avatarStatusShadowFilter)) + box-shadow: var(--avatarStatusShadowInset); + filter: var(--avatarStatusShadowFilter) } &.animated::before { diff --git a/src/components/status/status.vue b/src/components/status/status.vue index 6597d56b..428383e3 100644 --- a/src/components/status/status.vue +++ b/src/components/status/status.vue @@ -469,8 +469,8 @@ border-radius: var(--avatarAltRadius, $fallback--avatarAltRadius); &.better-shadow { - box-shadow: none; - filter: drop-shadow(var(--avatarStatusShadowFilter)) + box-shadow: var(--avatarStatusShadowInset); + filter: var(--avatarStatusShadowFilter) } } @@ -484,8 +484,8 @@ position: relative; &.better-shadow { - box-shadow: none; - filter: drop-shadow(var(--avatarStatusShadowFilter)) + box-shadow: var(--avatarStatusShadowInset); + filter: var(--avatarStatusShadowFilter) } img { diff --git a/src/components/style_switcher/style_switcher.vue b/src/components/style_switcher/style_switcher.vue index 66fe0f6b..c0a7da69 100644 --- a/src/components/style_switcher/style_switcher.vue +++ b/src/components/style_switcher/style_switcher.vue @@ -282,12 +282,15 @@ <i18n path="settings.style.shadows.filter_hint.always_drop_shadow" tag="p"> <code>filter: drop-shadow()</code> </i18n> - <i18n path="settings.style.shadows.filter_hint.text" tag="p"> + <p>{{$t('settings.style.shadows.filter_hint.avatar_inset')}}</p> + <i18n path="settings.style.shadows.filter_hint.drop_shadow_syntax" tag="p"> <code>drop-shadow</code> <code>spread-radius</code> <code>inset</code> </i18n> - <p>{{$t('settings.style.shadows.filter_hint.inset_ignored')}}</p> + <i18n path="settings.style.shadows.filter_hint.inset_classic" tag="p"> + <code>box-shadow</code> + </i18n> <p>{{$t('settings.style.shadows.filter_hint.spread_zero')}}</p> </div> </div> diff --git a/src/components/user_card_content/user_card_content.vue b/src/components/user_card_content/user_card_content.vue index cca418ff..e8b0ebf9 100644 --- a/src/components/user_card_content/user_card_content.vue +++ b/src/components/user_card_content/user_card_content.vue @@ -160,8 +160,8 @@ object-fit: cover; &.better-shadow { - box-shadow: none; - filter: drop-shadow(var(--avatarStatusShadowFilter)) + box-shadow: var(--avatarShadowInset); + filter: var(--avatarShadowFilter) } &.animated::before { diff --git a/src/i18n/en.json b/src/i18n/en.json index 7f5a2a4f..39da04d8 100644 --- a/src/i18n/en.json +++ b/src/i18n/en.json @@ -238,10 +238,10 @@ "hint": "For shadows you can also use --variable as a color value to use CSS3 variables. Please note that setting opacity won't work in this case.", "filter_hint": { "always_drop_shadow": "Warning, this shadow always uses {0} when browser supports it.", - "text": "Please note that {0} does not support {1} parameter and {2} keyword.", + "drop_shadow_syntax": "{0} does not support {1} parameter and {2} keyword.", + "avatar_inset": "Please note that combining both inset and non-inset shadows on avatars might give unexpected results with transparent avatars.", "spread_zero": "Shadows with spread > 0 will appear as if it was set to zero", - "inset_ignored": "Inset shadows using will be ignored", - "inset_substituted": "Inset shadows will be substituted with {1} equivalent" + "inset_classic": "Inset shadows will be using {0}" }, "components": { "panel": "Panel", diff --git a/src/services/style_setter/style_setter.js b/src/services/style_setter/style_setter.js index cff81c40..44a36c88 100644 --- a/src/services/style_setter/style_setter.js +++ b/src/services/style_setter/style_setter.js @@ -94,20 +94,22 @@ const setColors = (input, commit) => { commit('setOption', { name: 'colors', value: theme.colors }) } -const getCssShadow = (input) => { +const getCssShadow = (input, usesDropShadow) => { if (input.length === 0) { return 'none' } - return input.map((shad) => [ - shad.x, - shad.y, - shad.blur, - shad.spread - ].map(_ => _ + 'px').concat([ - getCssColor(shad.color, shad.alpha), - shad.inset ? 'inset' : '' - ]).join(' ')).join(', ') + return input + .filter(_ => usesDropShadow ? _.inset : _) + .map((shad) => [ + shad.x, + shad.y, + shad.blur, + shad.spread + ].map(_ => _ + 'px').concat([ + getCssColor(shad.color, shad.alpha), + shad.inset ? 'inset' : '' + ]).join(' ')).join(', ') } const getCssShadowFilter = (input) => { @@ -125,7 +127,9 @@ const getCssShadowFilter = (input) => { shad.blur / 2 ].map(_ => _ + 'px').concat([ getCssColor(shad.color, shad.alpha) - ]).join(' ')).join(', ') + ]).join(' ')) + .map(_ => `drop-shadow(${_})`) + .join(' ') } const getCssColor = (input, a) => { @@ -406,7 +410,11 @@ const generateShadows = (input) => { .entries(shadows) // TODO for v2.1: if shadow doesn't have non-inset shadows with spread > 0 - optionally // convert all non-inset shadows into filter: drop-shadow() to boost performance - .map(([k, v]) => `--${k}Shadow: ${getCssShadow(v)}; --${k}ShadowFilter: ${getCssShadowFilter(v)}`) + .map(([k, v]) => [ + `--${k}Shadow: ${getCssShadow(v)}`, + `--${k}ShadowFilter: ${getCssShadowFilter(v)}`, + `--${k}ShadowInset: ${getCssShadow(v, true)}` + ].join(';')) .join(';') }, theme: { From 67ca21b2e6c5f76d1db3e7cfdd1c0ef019156172 Mon Sep 17 00:00:00 2001 From: Henry Jameson <me@hjkos.com> Date: Sun, 2 Dec 2018 09:38:40 +0300 Subject: [PATCH 78/96] localization strings, fixes --- .../style_switcher/style_switcher.scss | 9 +- .../style_switcher/style_switcher.vue | 4 +- src/i18n/en.json | 2 +- src/i18n/ru.json | 112 +++++++++++++++++- 4 files changed, 119 insertions(+), 8 deletions(-) diff --git a/src/components/style_switcher/style_switcher.scss b/src/components/style_switcher/style_switcher.scss index baf2b73a..4db1a295 100644 --- a/src/components/style_switcher/style_switcher.scss +++ b/src/components/style_switcher/style_switcher.scss @@ -49,7 +49,7 @@ } &:not([type=number]):not([type=text]) { - align-self: center; + align-self: flex-start; } } } @@ -113,6 +113,7 @@ p { flex: 1; margin: 0; + margin-right: .5em; } margin-bottom: 1em; @@ -148,7 +149,7 @@ flex-wrap: wrap; margin-top: .5em; span { - margin: 0 .5em; + margin: 0 .5em .5em; } } @@ -204,8 +205,8 @@ flex: 0 auto; margin-left: 28px; font-size: 12px; - width: 20px; - height: 20px; + min-width: 20px; + min-height: 20px; line-height: 20px; border-radius: $fallback--avatarAltRadius; border-radius: var(--avatarAltRadius, $fallback--avatarAltRadius); diff --git a/src/components/style_switcher/style_switcher.vue b/src/components/style_switcher/style_switcher.vue index c0a7da69..814a6f17 100644 --- a/src/components/style_switcher/style_switcher.vue +++ b/src/components/style_switcher/style_switcher.vue @@ -83,7 +83,7 @@ </div> <div class="content"> <h4> - Content + {{$t('settings.style.preview.content')}} </h4> <i18n path="settings.style.preview.text"> @@ -224,7 +224,7 @@ </div> <div class="color-item"> <h4>{{ $t('settings.style.advanced_colors.borders') }}</h4> - <ColorInput name="borderColor" v-model="borderColorLocal" :fallback="previewTheme.colors.border" label="Color"/> + <ColorInput name="borderColor" v-model="borderColorLocal" :fallback="previewTheme.colors.border" :label="$t('settings.style.common.color')"/> <OpacityInput name="borderOpacity" v-model="borderOpacityLocal" :fallback="previewTheme.opacity.border || 1"/> </div> <div class="color-item"> diff --git a/src/i18n/en.json b/src/i18n/en.json index 39da04d8..56179868 100644 --- a/src/i18n/en.json +++ b/src/i18n/en.json @@ -207,7 +207,6 @@ "common_colors": { "_tab_label": "Common", "main": "Common colors", - "foreground": "Panel header, top bar, buttons, text fields", "foreground_hint": "See \"Advanced\" tab for more detailed control", "rgbo": "Icons, accents, badges" }, @@ -273,6 +272,7 @@ }, "preview": { "header": "Preview of header", + "content": "Content", "error": "Example error", "button": "Button", "text": "A bunch of more {0} and {1}", diff --git a/src/i18n/ru.json b/src/i18n/ru.json index 921bf67e..63c0bd2a 100644 --- a/src/i18n/ru.json +++ b/src/i18n/ru.json @@ -138,8 +138,118 @@ "text": "Текст", "theme": "Тема", "theme_help": "Используйте шестнадцатеричные коды цветов (#rrggbb) для настройки темы.", + "theme_help_v2_1": "Вы так же можете перепоределить цвета определенных компонентов нажав соотв. галочку. Используйте кнопку \"Очистить всё\" чтобы снять все переопределения", + "theme_help_v2_2": "Под некоторыми полями ввода это идикаторы контрастности, наведите на них мышью чтобы узнать больше. Приспользовании прозрачности контраст расчитывается для наихудшего варианта.", "tooltipRadius": "Всплывающие подсказки/уведомления", - "user_settings": "Настройки пользователя" + "user_settings": "Настройки пользователя", + "style": { + "switcher": { + "keep_shadows": "Оставить тени", + "keep_opacity": "Оставить прозрачность", + "keep_roundness": "Оставить скругление", + "keep_fonts": "Оставить шрифты", + "save_load_hint": "Опции \"оставить...\" позволяют сохранить текущие настройки при выборе другой темы или импорта её из файла. Так же они влияют на то какие компоненты будут сохранены при экспорте темы. Когда все галочки сняты все компоненты будут экспортированы.", + "reset": "Сбросить", + "clear_all": "Очистить всё", + "clear_opacity": "Очистить прозрачность" + }, + "common": { + "color": "Цвет", + "opacity": "Прозрачность", + "contrast": { + "hint": "Уровень контраста: {ratio}, что {level} {context}", + "level": { + "aa": "соответствует гайдлайну Level AA (минимальный)", + "aaa": "соответствует гайдлайну Level AAA (рекомендуемый)", + "bad": "не соответствует каким либо гайдлайнам" + }, + "context": { + "18pt": "для крупного (18pt+) текста", + "text": "для текста" + } + } + }, + "common_colors": { + "_tab_label": "Общие", + "main": "Общие цвета", + "foreground_hint": "См. вкладку \"Дополнительно\" для более детального контроля", + "rgbo": "Иконки, акценты, ярылки" + }, + "advanced_colors": { + "_tab_label": "Дополнительно", + "alert": "Фон уведомлений", + "alert_error": "Ошибки", + "badge": "Фон значков", + "badge_notification": "Уведомления", + "panel_header": "Заголовок панели", + "top_bar": "Верняя полоска", + "borders": "Границы", + "buttons": "Кнопки", + "inputs": "Поля ввода", + "faint_text": "Маловажный текст" + }, + "radii": { + "_tab_label": "Скругление" + }, + "shadows": { + "_tab_label": "Светотень", + "component": "Компонент", + "override": "Переопределить", + "shadow_id": "Тень №{value}", + "blur": "Размытие", + "spread": "Разброс", + "inset": "Внутренняя", + "hint": "Для теней вы так же можете использовать --variable в качестве цвета чтобы использовать CSS3-переменные. В таком случае прозрачность работать не будет.", + "filter_hint": { + "always_drop_shadow": "Внимание, эта тень всегда использует {0} когда браузер поддерживает это", + "drop_shadow_syntax": "{0} не поддерживает параметр {1} и ключевое слово {2}", + "avatar_inset": "Одновременное использование внутренних и внешних теней на (прозрачных) аватарках может дать не те результаты что вы ожидаете", + "spread_zero": "Тени с разбросом > 0 будут выглядеть как если бы разброс установлен в 0", + "inset_classic": "Внутренние тени будут использовать {0}" + }, + "components": { + "panel": "Панель", + "panelHeader": "Заголовок панели", + "topBar": "Верхняя полоска", + "avatar": "Аватарка (профиль)", + "avatarStatus": "Аватарка (в ленте)", + "popup": "Всплывающие подсказки", + "button": "Кнопки", + "buttonHover": "Кнопки (наведен курсор)", + "buttonPressed": "Кнопки (нажата)", + "buttonPressedHover": "Кнопки (нажата+наведен курсор)", + "input": "Поля ввода" + } + }, + "fonts": { + "_tab_label": "Шрифты", + "help": "Выберите тип шрифта для использования в интерфейсе. При выборе варианта \"другой\" надо ввести название шрифта в точности как он называется в системе.", + "components": { + "interface": "Интерфейс", + "input": "Поля ввода", + "post": "Текст постов", + "postCode": "Моноширинный текст в посте (форматирование)" + }, + "family": "Шрифт", + "size": "Размер (в пикселях)", + "weight": "Ширина", + "custom": "Другой" + }, + "preview": { + "header": "Пример", + "content": "Контент", + "error": "Ошибка стоп 000", + "button": "Кнопка", + "text": "Еще немного {0} и масенькая {1}", + "mono": "контента", + "input": "Что нового?", + "faint_link": "Его придется убрать", + "fine_print": "Если проблемы остались — ваш гуртовщик мыши плохо стоит. {0}.", + "header_faint": "Все идет по плану", + "checkbox": "Я подтверждаю что не было ни единого разрыва", + "link": "ссылка" + } + } }, "timeline": { "collapse": "Свернуть", From 80c0745558cd5dc69364bc19ee5dc1b8b3ddc3e2 Mon Sep 17 00:00:00 2001 From: Henry Jameson <me@hjkos.com> Date: Sun, 2 Dec 2018 10:22:25 +0300 Subject: [PATCH 79/96] some more themes, fixes --- .../style_switcher/style_switcher.js | 3 +- .../style_switcher/style_switcher.scss | 6 + .../style_switcher/style_switcher.vue | 2 +- static/styles.json | 4 +- static/themes/breezy-dark.json | 139 ++++++++++++++++++ static/themes/breezy-light.json | 139 ++++++++++++++++++ 6 files changed, 289 insertions(+), 4 deletions(-) create mode 100644 static/themes/breezy-dark.json create mode 100644 static/themes/breezy-light.json diff --git a/src/components/style_switcher/style_switcher.js b/src/components/style_switcher/style_switcher.js index ccdb4c4f..d09185fa 100644 --- a/src/components/style_switcher/style_switcher.js +++ b/src/components/style_switcher/style_switcher.js @@ -497,10 +497,9 @@ export default { this.textColorLocal = rgb2hex(colors.fg) } + this.clearV1() const keys = new Set(version !== 1 ? Object.keys(colors) : []) if (version === 1 || version === 'l1') { - // V1 ignores the rest - this.clearV1() keys .add('bg') .add('link') diff --git a/src/components/style_switcher/style_switcher.scss b/src/components/style_switcher/style_switcher.scss index 4db1a295..c8c5d9dd 100644 --- a/src/components/style_switcher/style_switcher.scss +++ b/src/components/style_switcher/style_switcher.scss @@ -135,6 +135,12 @@ display: flex; justify-content: center; align-items: baseline; + flex-wrap: wrap; + + .presets, + .import-export { + margin-bottom: .5em; + } .import-export { display: flex; diff --git a/src/components/style_switcher/style_switcher.vue b/src/components/style_switcher/style_switcher.vue index 814a6f17..9de60f7b 100644 --- a/src/components/style_switcher/style_switcher.vue +++ b/src/components/style_switcher/style_switcher.vue @@ -2,7 +2,7 @@ <div class="style-switcher"> <div class="presets-container"> <div class="save-load"> - <div> + <div class="presets"> {{$t('settings.presets')}} <label for="preset-switcher" class='select'> <select id="preset-switcher" v-model="selected" class="preset-switcher"> diff --git a/static/styles.json b/static/styles.json index 9204c717..b6bd6530 100644 --- a/static/styles.json +++ b/static/styles.json @@ -9,5 +9,7 @@ "redmond-xx": "./static/themes/redmond-xx.json", "redmond-xx-se": "./static/themes/redmond-xx-se.json", - "redmond-xxi": "./static/themes/redmond-xxi.json" + "redmond-xxi": "./static/themes/redmond-xxi.json", + "breezy-dark": "./static/themes/breezy-dark.json", + "breezy-light": "./static/themes/breezy-light.json" } diff --git a/static/themes/breezy-dark.json b/static/themes/breezy-dark.json new file mode 100644 index 00000000..6119bf88 --- /dev/null +++ b/static/themes/breezy-dark.json @@ -0,0 +1,139 @@ +{ + "_pleroma_theme_version": 2, + "name": "Breezy Dark (beta)", + "theme": { + "shadows": { + "panel": [ + { + "x": "1", + "y": "2", + "blur": "6", + "spread": 0, + "color": "#000000", + "alpha": 0.6 + } + ], + "button": [ + { + "x": 0, + "y": "0", + "blur": "0", + "spread": "1", + "color": "#ffffff", + "alpha": "0.15", + "inset": true + }, + { + "x": "1", + "y": "1", + "blur": "1", + "spread": 0, + "color": "#000000", + "alpha": "0.3", + "inset": false + } + ], + "panelHeader": [ + { + "x": 0, + "y": "40", + "blur": "40", + "spread": "-40", + "inset": true, + "color": "#ffffff", + "alpha": "0.1" + } + ], + "buttonHover": [ + { + "x": 0, + "y": "0", + "blur": 0, + "spread": "1", + "color": "--link", + "alpha": "0.3", + "inset": true + }, + { + "x": "1", + "y": "1", + "blur": "1", + "spread": 0, + "color": "#000000", + "alpha": "0.3", + "inset": false + } + ], + "buttonPressed": [ + { + "x": 0, + "y": 0, + "blur": "0", + "spread": "50", + "color": "--faint", + "alpha": 1, + "inset": true + }, + { + "x": 0, + "y": "0", + "blur": 0, + "spread": "1", + "color": "#ffffff", + "alpha": 0.2, + "inset": true + }, + { + "x": "1", + "y": "1", + "blur": 0, + "spread": 0, + "color": "#000000", + "alpha": "0.3", + "inset": false + } + ], + "input": [ + { + "x": 0, + "y": "0", + "blur": 0, + "spread": "1", + "color": "#FFFFFF", + "alpha": "0.2", + "inset": true + } + ] + }, + "fonts": {}, + "opacity": { + "input": "1", + "panel": "0" + }, + "colors": { + "bg": "#31363b", + "text": "#eff0f1", + "link": "#3daee9", + "fg": "#31363b", + "panel": "#31363b", + "input": "#232629", + "topBarLink": "#eff0f1", + "btn": "#31363b", + "border": "#4c545b", + "cRed": "#da4453", + "cBlue": "#3daee9", + "cGreen": "#27ae60", + "cOrange": "#f67400" + }, + "radii": { + "btn": "2", + "input": "2", + "checkbox": "1", + "panel": "2", + "avatar": "2", + "avatarAlt": "2", + "tooltip": "2", + "attachment": "2" + } + } +} diff --git a/static/themes/breezy-light.json b/static/themes/breezy-light.json new file mode 100644 index 00000000..becf704f --- /dev/null +++ b/static/themes/breezy-light.json @@ -0,0 +1,139 @@ +{ + "_pleroma_theme_version": 2, + "name": "Breezy Light (beta)", + "theme": { + "shadows": { + "panel": [ + { + "x": "1", + "y": "2", + "blur": "6", + "spread": 0, + "color": "#000000", + "alpha": 0.6 + } + ], + "button": [ + { + "x": 0, + "y": "0", + "blur": "0", + "spread": "1", + "color": "#000000", + "alpha": "0.3", + "inset": true + }, + { + "x": "1", + "y": "1", + "blur": "1", + "spread": 0, + "color": "#000000", + "alpha": "0.3", + "inset": false + } + ], + "panelHeader": [ + { + "x": 0, + "y": "40", + "blur": "40", + "spread": "-40", + "inset": true, + "color": "#ffffff", + "alpha": "0.1" + } + ], + "buttonHover": [ + { + "x": 0, + "y": "0", + "blur": 0, + "spread": "1", + "color": "--link", + "alpha": "0.3", + "inset": true + }, + { + "x": "1", + "y": "1", + "blur": "1", + "spread": 0, + "color": "#000000", + "alpha": "0.3", + "inset": false + } + ], + "buttonPressed": [ + { + "x": 0, + "y": 0, + "blur": "0", + "spread": "50", + "color": "--faint", + "alpha": 1, + "inset": true + }, + { + "x": 0, + "y": "0", + "blur": 0, + "spread": "1", + "color": "#ffffff", + "alpha": 0.2, + "inset": true + }, + { + "x": "1", + "y": "1", + "blur": 0, + "spread": 0, + "color": "#000000", + "alpha": "0.3", + "inset": false + } + ], + "input": [ + { + "x": 0, + "y": "0", + "blur": 0, + "spread": "1", + "color": "#000000", + "alpha": "0.2", + "inset": true + } + ] + }, + "fonts": {}, + "opacity": { + "input": "1" + }, + "colors": { + "bg": "#eff0f1", + "text": "#232627", + "link": "#2980b9", + "fg": "#bcc2c7", + "panel": "#475057", + "panelText": "#fcfcfc", + "input": "#fcfcfc", + "topBar": "#475057", + "topBarLink": "#eff0f1", + "btn": "#eff0f1", + "cRed": "#da4453", + "cBlue": "#2980b9", + "cGreen": "#27ae60", + "cOrange": "#f67400" + }, + "radii": { + "btn": "2", + "input": "2", + "checkbox": "1", + "panel": "2", + "avatar": "2", + "avatarAlt": "2", + "tooltip": "2", + "attachment": "2" + } + } +} From 1e56cec2aaf8683a47d67ee657659b95c7a4d3b6 Mon Sep 17 00:00:00 2001 From: Henry Jameson <me@hjkos.com> Date: Sun, 2 Dec 2018 10:23:41 +0300 Subject: [PATCH 80/96] missing string --- src/i18n/ru.json | 1 + 1 file changed, 1 insertion(+) diff --git a/src/i18n/ru.json b/src/i18n/ru.json index 63c0bd2a..58a6eae5 100644 --- a/src/i18n/ru.json +++ b/src/i18n/ru.json @@ -100,6 +100,7 @@ "import_followers_from_a_csv_file": "Импортировать читаемых из файла .csv", "import_theme": "Загрузить Тему", "inputRadius": "Поля ввода", + "checkboxRadius": "Чекбоксы", "interfaceLanguage": "Язык интерфейса", "limited_availability": "Не доступно в вашем браузере", "links": "Ссылки", From d756455c3446f74df922f27d53081a5f307968f8 Mon Sep 17 00:00:00 2001 From: Henry Jameson <me@hjkos.com> Date: Sun, 2 Dec 2018 12:56:02 +0300 Subject: [PATCH 81/96] todo --- src/components/shadow_control/shadow_control.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/components/shadow_control/shadow_control.js b/src/components/shadow_control/shadow_control.js index a1484d09..44e4a22f 100644 --- a/src/components/shadow_control/shadow_control.js +++ b/src/components/shadow_control/shadow_control.js @@ -14,6 +14,7 @@ export default { data () { return { selectedId: 0, + // TODO there are some bugs regarding display of array (it's not getting updated when deleting for some reason) cValue: this.value || this.fallback || [] } }, From dd4deae66e5bb1b856898f9d1a4195b52427cdb0 Mon Sep 17 00:00:00 2001 From: Henry Jameson <me@hjkos.com> Date: Sun, 2 Dec 2018 15:03:51 +0300 Subject: [PATCH 82/96] fallback for some weird case on my phone --- src/services/style_setter/style_setter.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/services/style_setter/style_setter.js b/src/services/style_setter/style_setter.js index 44a36c88..33aba32c 100644 --- a/src/services/style_setter/style_setter.js +++ b/src/services/style_setter/style_setter.js @@ -205,10 +205,10 @@ const generateColors = (input) => { colors.icon = mixrgb(colors.bg, colors.text) - colors.cBlue = col.cBlue - colors.cRed = col.cRed - colors.cGreen = col.cGreen - colors.cOrange = col.cOrange + colors.cBlue = col.cBlue || hex2rgb('#0000FF') + colors.cRed = col.cRed || hex2rgb('#FF0000') + colors.cGreen = col.cGreen || hex2rgb('#00FF00') + colors.cOrange = col.cOrange || hex2rgb('#E3FF00') colors.alertError = col.alertError || Object.assign({}, col.cRed) colors.alertErrorText = getTextColor(alphaBlend(colors.alertError, opacity.alert, colors.bg), colors.text) From fad19c3c2faccefd51f0aa5905cfb6df38ae9744 Mon Sep 17 00:00:00 2001 From: Henry Jameson <me@hjkos.com> Date: Sun, 2 Dec 2018 15:10:18 +0300 Subject: [PATCH 83/96] fix --- src/services/style_setter/style_setter.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/services/style_setter/style_setter.js b/src/services/style_setter/style_setter.js index 33aba32c..5720d02d 100644 --- a/src/services/style_setter/style_setter.js +++ b/src/services/style_setter/style_setter.js @@ -119,7 +119,7 @@ const getCssShadowFilter = (input) => { return input // drop-shadow doesn't support inset or spread - .filter((shad) => console.log(shad) || !shad.inset && Number(shad.spread) === 0) + .filter((shad) => !shad.inset && Number(shad.spread) === 0) .map((shad) => [ shad.x, shad.y, From e95b6c7e538e5b65ee81fd5e5d3cb905609c6bab Mon Sep 17 00:00:00 2001 From: Henry Jameson <me@hjkos.com> Date: Sun, 2 Dec 2018 15:20:25 +0300 Subject: [PATCH 84/96] fix --- src/services/style_setter/style_setter.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/services/style_setter/style_setter.js b/src/services/style_setter/style_setter.js index 5720d02d..445d3c49 100644 --- a/src/services/style_setter/style_setter.js +++ b/src/services/style_setter/style_setter.js @@ -210,11 +210,11 @@ const generateColors = (input) => { colors.cGreen = col.cGreen || hex2rgb('#00FF00') colors.cOrange = col.cOrange || hex2rgb('#E3FF00') - colors.alertError = col.alertError || Object.assign({}, col.cRed) + colors.alertError = col.alertError || Object.assign({}, colors.cRed) colors.alertErrorText = getTextColor(alphaBlend(colors.alertError, opacity.alert, colors.bg), colors.text) colors.alertErrorPanelText = getTextColor(alphaBlend(colors.alertError, opacity.alert, colors.panel), colors.panelText) - colors.badgeNotification = col.badgeNotification || Object.assign({}, col.cRed) + colors.badgeNotification = col.badgeNotification || Object.assign({}, colors.cRed) colors.badgeNotificationText = contrastRatio(colors.badgeNotification).rgb Object.entries(opacity).forEach(([ k, v ]) => { From 6636c0f551a146622a1db40582140c59d3eb650d Mon Sep 17 00:00:00 2001 From: Henry Jameson <me@hjkos.com> Date: Wed, 5 Dec 2018 12:01:24 +0300 Subject: [PATCH 85/96] mobile fixes --- src/components/style_switcher/style_switcher.scss | 6 ++++++ src/components/tab_switcher/tab_switcher.scss | 4 +++- src/i18n/en.json | 2 +- 3 files changed, 10 insertions(+), 2 deletions(-) diff --git a/src/components/style_switcher/style_switcher.scss b/src/components/style_switcher/style_switcher.scss index c8c5d9dd..f53f00a0 100644 --- a/src/components/style_switcher/style_switcher.scss +++ b/src/components/style_switcher/style_switcher.scss @@ -248,6 +248,12 @@ .panel-heading { .badge, .alert, .btn, .faint { margin-left: 1em; + white-space: nowrap; + } + .faint { + text-overflow: ellipsis; + min-width: 2em; + overflow-x: hidden; } .flex-spacer { flex: 1; diff --git a/src/components/tab_switcher/tab_switcher.scss b/src/components/tab_switcher/tab_switcher.scss index d0e5ea87..fbd3321b 100644 --- a/src/components/tab_switcher/tab_switcher.scss +++ b/src/components/tab_switcher/tab_switcher.scss @@ -11,7 +11,8 @@ position: relative; justify-content: center; width: 100%; - overflow: hidden; + overflow-y: hidden; + overflow-x: auto; padding-top: 5px; height: 32px; box-sizing: border-box; @@ -33,6 +34,7 @@ border-bottom-left-radius: 0; border-bottom-right-radius: 0; padding: 5px 1em 99px; + white-space: nowrap; &:not(.active) { z-index: 4; diff --git a/src/i18n/en.json b/src/i18n/en.json index 890ed6c4..c0d30b59 100644 --- a/src/i18n/en.json +++ b/src/i18n/en.json @@ -273,7 +273,7 @@ "custom": "Custom" }, "preview": { - "header": "Preview of header", + "header": "Preview", "content": "Content", "error": "Example error", "button": "Button", From aeecd2b09b7c31644a2c601fc1b8d123e2b263b0 Mon Sep 17 00:00:00 2001 From: Henry Jameson <me@hjkos.com> Date: Tue, 11 Dec 2018 00:56:15 +0300 Subject: [PATCH 86/96] separate font control js --- src/components/font_control/font_control.js | 58 +++++++++++++++++++ src/components/font_control/font_control.vue | 61 +------------------- 2 files changed, 59 insertions(+), 60 deletions(-) create mode 100644 src/components/font_control/font_control.js diff --git a/src/components/font_control/font_control.js b/src/components/font_control/font_control.js new file mode 100644 index 00000000..8e2b0e45 --- /dev/null +++ b/src/components/font_control/font_control.js @@ -0,0 +1,58 @@ +import { set } from 'vue' + +export default { + props: [ + 'name', 'label', 'value', 'fallback', 'options', 'no-inherit' + ], + data () { + return { + lValue: this.value, + availableOptions: [ + this.noInherit ? '' : 'inherit', + 'custom', + ...(this.options || []), + 'serif', + 'monospace', + 'sans-serif' + ].filter(_ => _) + } + }, + beforeUpdate () { + this.lValue = this.value + }, + computed: { + present () { + return typeof this.lValue !== 'undefined' + }, + dValue () { + return this.lValue || this.fallback || {} + }, + family: { + get () { + return this.dValue.family + }, + set (v) { + set(this.lValue, 'family', v) + this.$emit('input', this.lValue) + } + }, + isCustom () { + return this.preset === 'custom' + }, + preset: { + get () { + if (this.family === 'serif' || + this.family === 'sans-serif' || + this.family === 'monospace' || + this.family === 'inherit') { + return this.family + } else { + return 'custom' + } + }, + set (v) { + this.family = v === 'custom' ? '' : v + } + } + } +} diff --git a/src/components/font_control/font_control.vue b/src/components/font_control/font_control.vue index 85f19eea..ed36b280 100644 --- a/src/components/font_control/font_control.vue +++ b/src/components/font_control/font_control.vue @@ -32,66 +32,7 @@ </div> </template> -<script> -import { set } from 'vue' - -export default { - props: [ - 'name', 'label', 'value', 'fallback', 'options', 'no-inherit' - ], - data () { - return { - lValue: this.value, - availableOptions: [ - this.noInherit ? '' : 'inherit', - 'custom', - ...(this.options || []), - 'serif', - 'monospace', - 'sans-serif' - ].filter(_ => _) - } - }, - beforeUpdate () { - this.lValue = this.value - }, - computed: { - present () { - return typeof this.lValue !== 'undefined' - }, - dValue () { - return this.lValue || this.fallback || {} - }, - family: { - get () { - return this.dValue.family - }, - set (v) { - set(this.lValue, 'family', v) - this.$emit('input', this.lValue) - } - }, - isCustom () { - return this.preset === 'custom' - }, - preset: { - get () { - if (this.family === 'serif' || - this.family === 'sans-serif' || - this.family === 'monospace' || - this.family === 'inherit') { - return this.family - } else { - return 'custom' - } - }, - set (v) { - this.family = v === 'custom' ? '' : v - } - } - } -} -</script> +<script src="./font_control.js" ></script> <style lang="scss"> @import '../../_variables.scss'; From fe2fe092361b60c9d585f80b53d906b501c9722e Mon Sep 17 00:00:00 2001 From: Henry Jameson <me@hjkos.com> Date: Tue, 11 Dec 2018 01:38:20 +0300 Subject: [PATCH 87/96] fixed v2 setting as default theme --- .../style_switcher/style_switcher.js | 34 ++-------- src/services/style_setter/style_setter.js | 63 ++++++++++++++----- 2 files changed, 53 insertions(+), 44 deletions(-) diff --git a/src/components/style_switcher/style_switcher.js b/src/components/style_switcher/style_switcher.js index d09185fa..2bbf74db 100644 --- a/src/components/style_switcher/style_switcher.js +++ b/src/components/style_switcher/style_switcher.js @@ -1,6 +1,6 @@ import { rgb2hex, hex2rgb, getContrastRatio, alphaBlend } from '../../services/color_convert/color_convert.js' import { set, delete as del } from 'vue' -import { generateColors, generateShadows, generateRadii, generateFonts, composePreset } from '../../services/style_setter/style_setter.js' +import { generateColors, generateShadows, generateRadii, generateFonts, composePreset, getThemes } from '../../services/style_setter/style_setter.js' import ColorInput from '../color_input/color_input.vue' import RangeInput from '../range_input/range_input.vue' import OpacityInput from '../opacity_input/opacity_input.vue' @@ -104,35 +104,9 @@ export default { created () { const self = this - window.fetch('/static/styles.json') - .then((data) => data.json()) - .then((themes) => { - return Promise.all(Object.entries(themes).map(([k, v]) => { - if (typeof v === 'object') { - return Promise.resolve([k, v]) - } else if (typeof v === 'string') { - return window.fetch(v) - .then((data) => data.json()) - .then((theme) => { - return [k, theme] - }) - .catch((e) => { - console.error(e) - return [] - }) - } - })) - }) - .then((promises) => { - return promises - .filter(([k, v]) => v) - .reduce((acc, [k, v]) => { - acc[k] = v - return acc - }, {}) - }).then((themesComplete) => { - self.availableStyles = themesComplete - }) + getThemes().then((themesComplete) => { + self.availableStyles = themesComplete + }) }, mounted () { this.normalizeLocalState(this.$store.state.config.customTheme) diff --git a/src/services/style_setter/style_setter.js b/src/services/style_setter/style_setter.js index 445d3c49..783a693a 100644 --- a/src/services/style_setter/style_setter.js +++ b/src/services/style_setter/style_setter.js @@ -449,11 +449,43 @@ const generatePreset = (input) => { return composePreset(colors, radii, shadows, fonts) } -const setPreset = (val, commit) => { - window.fetch('/static/styles.json') +const getThemes = () => { + return window.fetch('/static/styles.json') .then((data) => data.json()) .then((themes) => { - const theme = themes[val] ? themes[val] : themes['pleroma-dark'] + return Promise.all(Object.entries(themes).map(([k, v]) => { + if (typeof v === 'object') { + return Promise.resolve([k, v]) + } else if (typeof v === 'string') { + return window.fetch(v) + .then((data) => data.json()) + .then((theme) => { + return [k, theme] + }) + .catch((e) => { + console.error(e) + return [] + }) + } + })) + }) + .then((promises) => { + return promises + .filter(([k, v]) => v) + .reduce((acc, [k, v]) => { + acc[k] = v + return acc + }, {}) + }) +} + +const setPreset = (val, commit) => { + getThemes().then((themes) => { + const theme = themes[val] ? themes[val] : themes['pleroma-dark'] + const isV1 = Array.isArray(theme) + const data = isV1 ? {} : theme.theme + + if (isV1) { const bgRgb = hex2rgb(theme[1]) const fgRgb = hex2rgb(theme[2]) const textRgb = hex2rgb(theme[3]) @@ -464,7 +496,7 @@ const setPreset = (val, commit) => { const cBlueRgb = hex2rgb(theme[7] || '#0000FF') const cOrangeRgb = hex2rgb(theme[8] || '#E3FF00') - const colors = { + data.colors = { bg: bgRgb, fg: fgRgb, text: textRgb, @@ -474,17 +506,19 @@ const setPreset = (val, commit) => { cGreen: cGreenRgb, cOrange: cOrangeRgb } + } - // This is a hack, this function is only called during initial load. - // We want to cancel loading the theme from config.json if we're already - // loading a theme from the persisted state. - // Needed some way of dealing with the async way of things. - // load config -> set preset -> wait for styles.json to load -> - // load persisted state -> set colors -> styles.json loaded -> set colors - if (!window.themeLoaded) { - setColors({ colors }, commit) - } - }) + console.log(data) + // This is a hack, this function is only called during initial load. + // We want to cancel loading the theme from config.json if we're already + // loading a theme from the persisted state. + // Needed some way of dealing with the async way of things. + // load config -> set preset -> wait for styles.json to load -> + // load persisted state -> set colors -> styles.json loaded -> set colors + if (!window.themeLoaded) { + setColors(data, commit) + } + }) } export { @@ -497,6 +531,7 @@ export { generateShadows, generateFonts, generatePreset, + getThemes, composePreset, getCssShadow, getCssShadowFilter From 4b25475b5766580d7645bb5e8fcb7f229ed6c620 Mon Sep 17 00:00:00 2001 From: Henry Jameson <me@hjkos.com> Date: Tue, 11 Dec 2018 01:39:18 +0300 Subject: [PATCH 88/96] setColors -> applyTheme. For sanity. Also disabled export because nobody uses it and should not use anyway. --- src/services/style_setter/style_setter.js | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/services/style_setter/style_setter.js b/src/services/style_setter/style_setter.js index 783a693a..5d988732 100644 --- a/src/services/style_setter/style_setter.js +++ b/src/services/style_setter/style_setter.js @@ -71,7 +71,7 @@ const getTextColor = function (bg, text, preserve) { return text } -const setColors = (input, commit) => { +const applyTheme = (input, commit) => { const { rules, theme } = generatePreset(input) const head = document.head const body = document.body @@ -516,7 +516,7 @@ const setPreset = (val, commit) => { // load config -> set preset -> wait for styles.json to load -> // load persisted state -> set colors -> styles.json loaded -> set colors if (!window.themeLoaded) { - setColors(data, commit) + applyTheme(data, commit) } }) } @@ -524,7 +524,6 @@ const setPreset = (val, commit) => { export { setStyle, setPreset, - setColors, getTextColor, generateColors, generateRadii, From 73aa9153d96ed50293dbbbeb8691a71d9f911bfc Mon Sep 17 00:00:00 2001 From: Henry Jameson <me@hjkos.com> Date: Tue, 11 Dec 2018 01:40:19 +0300 Subject: [PATCH 89/96] cleanup --- src/components/style_switcher/style_switcher.js | 2 -- src/services/style_setter/style_setter.js | 1 - 2 files changed, 3 deletions(-) diff --git a/src/components/style_switcher/style_switcher.js b/src/components/style_switcher/style_switcher.js index 2bbf74db..adcbee25 100644 --- a/src/components/style_switcher/style_switcher.js +++ b/src/components/style_switcher/style_switcher.js @@ -463,8 +463,6 @@ export default { } } - console.log(version) - // Stuff that differs between V1 and V2 if (version === 1) { this.fgColorLocal = rgb2hex(colors.btn) diff --git a/src/services/style_setter/style_setter.js b/src/services/style_setter/style_setter.js index 5d988732..1e1b3fce 100644 --- a/src/services/style_setter/style_setter.js +++ b/src/services/style_setter/style_setter.js @@ -508,7 +508,6 @@ const setPreset = (val, commit) => { } } - console.log(data) // This is a hack, this function is only called during initial load. // We want to cancel loading the theme from config.json if we're already // loading a theme from the persisted state. From 90a567066139377a3fd385151eed2e022555cb22 Mon Sep 17 00:00:00 2001 From: Henry Jameson <me@hjkos.com> Date: Tue, 11 Dec 2018 01:41:19 +0300 Subject: [PATCH 90/96] removed unused function from color_convert --- src/services/color_convert/color_convert.js | 9 --------- 1 file changed, 9 deletions(-) diff --git a/src/services/color_convert/color_convert.js b/src/services/color_convert/color_convert.js index 58c434fa..7576c518 100644 --- a/src/services/color_convert/color_convert.js +++ b/src/services/color_convert/color_convert.js @@ -113,14 +113,6 @@ const hex2rgb = (hex) => { } : null } -const rgbstr2hex = (rgb) => { - if (rgb[0] === '#') { - return rgb - } - rgb = rgb.match(/\d+/g) - return `#${((Number(rgb[0]) << 16) + (Number(rgb[1]) << 8) + Number(rgb[2])).toString(16)}` -} - const mixrgb = (a, b) => { return Object.keys(a).reduce((acc, k) => { acc[k] = (a[k] + b[k]) / 2 @@ -133,7 +125,6 @@ export { hex2rgb, mixrgb, invert, - rgbstr2hex, getContrastRatio, alphaBlend } From a17ac74df71a9de23a82825680d7154927b033ee Mon Sep 17 00:00:00 2001 From: Henry Jameson <me@hjkos.com> Date: Tue, 11 Dec 2018 02:05:22 +0300 Subject: [PATCH 91/96] revert that, it's actually used, i'm an idiot --- src/services/style_setter/style_setter.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/services/style_setter/style_setter.js b/src/services/style_setter/style_setter.js index 1e1b3fce..10e7ed9b 100644 --- a/src/services/style_setter/style_setter.js +++ b/src/services/style_setter/style_setter.js @@ -523,6 +523,7 @@ const setPreset = (val, commit) => { export { setStyle, setPreset, + applyTheme, getTextColor, generateColors, generateRadii, From 51dccb788798364bbb662d378f2aa2647f1845cf Mon Sep 17 00:00:00 2001 From: Henry Jameson <me@hjkos.com> Date: Tue, 11 Dec 2018 02:46:17 +0300 Subject: [PATCH 92/96] separated preview and exported from style_switcher --- .../export_import/export_import.vue | 75 ++++++++++++++ src/components/style_switcher/preview.vue | 78 +++++++++++++++ .../style_switcher/style_switcher.js | 97 +++++++------------ .../style_switcher/style_switcher.vue | 93 +++--------------- src/modules/config.js | 4 +- 5 files changed, 200 insertions(+), 147 deletions(-) create mode 100644 src/components/export_import/export_import.vue create mode 100644 src/components/style_switcher/preview.vue diff --git a/src/components/export_import/export_import.vue b/src/components/export_import/export_import.vue new file mode 100644 index 00000000..9914d54a --- /dev/null +++ b/src/components/export_import/export_import.vue @@ -0,0 +1,75 @@ +<template> +<div class="import-export"> + <button class="btn" @click="exportData">{{ exportLabel }}</button> + <button class="btn" @click="importData">{{ importLabel }}</button> + <p v-if="importFailed" class="import-warning">{{ importFailedText }}</p> +</div> +</template> + +<script> +export default { + props: [ + 'exportObject', + 'importLabel', + 'exportLabel', + 'importFailedText', + 'validator', + 'onImport', + 'onImportFailure' + ], + data () { + return { + importFailed: false + } + }, + methods: { + exportData () { + const stringified = JSON.stringify(this.exportObject) // Pretty-print and indent with 2 spaces + + // Create an invisible link with a data url and simulate a click + const e = document.createElement('a') + e.setAttribute('download', 'pleroma_theme.json') + e.setAttribute('href', 'data:application/json;base64,' + window.btoa(stringified)) + e.style.display = 'none' + + document.body.appendChild(e) + e.click() + document.body.removeChild(e) + }, + importData () { + this.importFailed = false + const filePicker = document.createElement('input') + filePicker.setAttribute('type', 'file') + filePicker.setAttribute('accept', '.json') + + filePicker.addEventListener('change', event => { + if (event.target.files[0]) { + // eslint-disable-next-line no-undef + const reader = new FileReader() + reader.onload = ({target}) => { + try { + const parsed = JSON.parse(target.result) + const valid = this.validator(parsed) + if (valid) { + this.onImport(parsed) + } else { + this.importFailed = true + // this.onImportFailure(valid) + } + } catch (e) { + // This will happen both if there is a JSON syntax error or the theme is missing components + this.importFailed = true + // this.onImportFailure(e) + } + } + reader.readAsText(event.target.files[0]) + } + }) + + document.body.appendChild(filePicker) + filePicker.click() + document.body.removeChild(filePicker) + } + } +} +</script> diff --git a/src/components/style_switcher/preview.vue b/src/components/style_switcher/preview.vue new file mode 100644 index 00000000..09a136e9 --- /dev/null +++ b/src/components/style_switcher/preview.vue @@ -0,0 +1,78 @@ +<template> +<div class="panel dummy"> + <div class="panel-heading"> + <div class="title"> + {{$t('settings.style.preview.header')}} + <span class="badge badge-notification"> + 99 + </span> + </div> + <span class="faint"> + {{$t('settings.style.preview.header_faint')}} + </span> + <span class="alert error"> + {{$t('settings.style.preview.error')}} + </span> + <button class="btn"> + {{$t('settings.style.preview.button')}} + </button> + </div> + <div class="panel-body theme-preview-content"> + <div class="post"> + <div class="avatar"> + ( ͡° ͜ʖ ͡°) + </div> + <div class="content"> + <h4> + {{$t('settings.style.preview.content')}} + </h4> + + <i18n path="settings.style.preview.text"> + <code style="font-family: var(--postCodeFont)"> + {{$t('settings.style.preview.mono')}} + </code> + <a style="color: var(--link)"> + {{$t('settings.style.preview.link')}} + </a> + </i18n> + + <div class="icons"> + <i style="color: var(--cBlue)" class="icon-reply"/> + <i style="color: var(--cGreen)" class="icon-retweet"/> + <i style="color: var(--cOrange)" class="icon-star"/> + <i style="color: var(--cRed)" class="icon-cancel"/> + </div> + </div> + </div> + + <div class="after-post"> + <div class="avatar-alt"> + :^) + </div> + <div class="content"> + <i18n path="settings.style.preview.fine_print" tag="span" class="faint"> + <a style="color: var(--faintLink)"> + {{$t('settings.style.preview.faint_link')}} + </a> + </i18n> + </div> + </div> + <div class="separator"></div> + + <span class="alert error"> + {{$t('settings.style.preview.error')}} + </span> + <input :value="$t('settings.style.preview.input')" type="text"> + + <div class="actions"> + <span class="checkbox"> + <input checked="very yes" type="checkbox" id="preview_checkbox"> + <label for="preview_checkbox">{{$t('settings.style.preview.checkbox')}}</label> + </span> + <button class="btn"> + {{$t('settings.style.preview.button')}} + </button> + </div> + </div> +</div> +</template> diff --git a/src/components/style_switcher/style_switcher.js b/src/components/style_switcher/style_switcher.js index adcbee25..50cd1e6f 100644 --- a/src/components/style_switcher/style_switcher.js +++ b/src/components/style_switcher/style_switcher.js @@ -8,6 +8,8 @@ import ShadowControl from '../shadow_control/shadow_control.vue' import FontControl from '../font_control/font_control.vue' import ContrastRatio from '../contrast_ratio/contrast_ratio.vue' import TabSwitcher from '../tab_switcher/tab_switcher.jsx' +import Preview from './preview.vue' +import ExportImport from '../export_import/export_import.vue' // List of color values used in v1 const v1OnlyNames = [ @@ -26,7 +28,6 @@ export default { return { availableStyles: [], selected: this.$store.state.config.theme, - invalidThemeImported: false, previewShadows: {}, previewColors: {}, @@ -293,20 +294,11 @@ export default { }, themeValid () { return !this.shadowsInvalid && !this.colorsInvalid && !this.radiiInvalid - } - }, - components: { - ColorInput, - OpacityInput, - RangeInput, - ContrastRatio, - ShadowControl, - FontControl, - TabSwitcher - }, - methods: { - exportCurrentTheme () { + }, + exportedTheme () { const saveEverything = !this.keepFonts && !this.keepShadows && !this.keepColors && !this.keepOpacity && !this.keepRoundness + + // TODO change into delete-less version. const theme = { shadows: this.shadowsLocal, fonts: this.fontsLocal, @@ -331,57 +323,24 @@ export default { delete theme.radii } - const stringified = JSON.stringify({ + return { // To separate from other random JSON files and possible future theme formats _pleroma_theme_version: 2, theme - }, null, 2) // Pretty-print and indent with 2 spaces - - // Create an invisible link with a data url and simulate a click - const e = document.createElement('a') - e.setAttribute('download', 'pleroma_theme.json') - e.setAttribute('href', 'data:application/json;base64,' + window.btoa(stringified)) - e.style.display = 'none' - - document.body.appendChild(e) - e.click() - document.body.removeChild(e) - }, - - importTheme () { - this.invalidThemeImported = false - const filePicker = document.createElement('input') - filePicker.setAttribute('type', 'file') - filePicker.setAttribute('accept', '.json') - - filePicker.addEventListener('change', event => { - if (event.target.files[0]) { - // eslint-disable-next-line no-undef - const reader = new FileReader() - reader.onload = ({target}) => { - try { - const parsed = JSON.parse(target.result) - if (parsed._pleroma_theme_version === 1) { - this.normalizeLocalState(parsed, 1) - } else if (parsed._pleroma_theme_version === 2) { - this.normalizeLocalState(parsed.theme, 2) - } else { - // A theme from the future, spooky - this.invalidThemeImported = true - } - } catch (e) { - // This will happen both if there is a JSON syntax error or the theme is missing components - this.invalidThemeImported = true - } - } - reader.readAsText(event.target.files[0]) - } - }) - - document.body.appendChild(filePicker) - filePicker.click() - document.body.removeChild(filePicker) - }, - + } + } + }, + components: { + ColorInput, + OpacityInput, + RangeInput, + ContrastRatio, + ShadowControl, + FontControl, + TabSwitcher, + Preview, + ExportImport + }, + methods: { setCustomTheme () { this.$store.dispatch('setOption', { name: 'customTheme', @@ -394,7 +353,17 @@ export default { } }) }, - + onImport (parsed) { + if (parsed._pleroma_theme_version === 1) { + this.normalizeLocalState(parsed, 1) + } else if (parsed._pleroma_theme_version === 2) { + this.normalizeLocalState(parsed.theme, 2) + } + }, + importValidator (parsed) { + const version = parsed._pleroma_theme_version + return version >= 1 || version <= 2 + }, clearAll () { const state = this.$store.state.config.customTheme const version = state.colors ? 2 : 'l1' diff --git a/src/components/style_switcher/style_switcher.vue b/src/components/style_switcher/style_switcher.vue index 9de60f7b..730bfef0 100644 --- a/src/components/style_switcher/style_switcher.vue +++ b/src/components/style_switcher/style_switcher.vue @@ -18,11 +18,14 @@ <i class="icon-down-open"/> </label> </div> - <div class="import-export"> - <button class="btn" @click="exportCurrentTheme">{{ $t('settings.export_theme') }}</button> - <button class="btn" @click="importTheme">{{ $t('settings.import_theme') }}</button> - <p v-if="invalidThemeImported" class="import-warning">{{ $t('settings.invalid_theme_imported') }}</p> - </div> + <export-import + :exportObject='exportedTheme' + :exportLabel='$t("settings.export_theme")' + :importLabel='$t("settings.import_theme")' + :importFailedText='$t("settings.invalid_theme_imported")' + :onImport='onImport' + :validator='importValidator' + /> </div> <div class="save-load-options"> <span> @@ -58,82 +61,7 @@ </div> <div class="preview-container"> - <div class="panel dummy" :style="previewRules"> - <div class="panel-heading"> - <div class="title"> - {{$t('settings.style.preview.header')}} - <span class="badge badge-notification"> - 99 - </span> - </div> - <span class="faint"> - {{$t('settings.style.preview.header_faint')}} - </span> - <span class="alert error"> - {{$t('settings.style.preview.error')}} - </span> - <button class="btn"> - {{$t('settings.style.preview.button')}} - </button> - </div> - <div class="panel-body theme-preview-content"> - <div class="post"> - <div class="avatar"> - ( ͡° ͜ʖ ͡°) - </div> - <div class="content"> - <h4> - {{$t('settings.style.preview.content')}} - </h4> - - <i18n path="settings.style.preview.text"> - <code style="font-family: var(--postCodeFont)"> - {{$t('settings.style.preview.mono')}} - </code> - <a style="color: var(--link)"> - {{$t('settings.style.preview.link')}} - </a> - </i18n> - - <div class="icons"> - <i style="color: var(--cBlue)" class="icon-reply"/> - <i style="color: var(--cGreen)" class="icon-retweet"/> - <i style="color: var(--cOrange)" class="icon-star"/> - <i style="color: var(--cRed)" class="icon-cancel"/> - </div> - </div> - </div> - - <div class="after-post"> - <div class="avatar-alt"> - :^) - </div> - <div class="content"> - <i18n path="settings.style.preview.fine_print" tag="span" class="faint"> - <a style="color: var(--faintLink)"> - {{$t('settings.style.preview.faint_link')}} - </a> - </i18n> - </div> - </div> - <div class="separator"></div> - - <span class="alert error"> - {{$t('settings.style.preview.error')}} - </span> - <input :value="$t('settings.style.preview.input')" type="text"> - - <div class="actions"> - <span class="checkbox"> - <input checked="very yes" type="checkbox" id="preview_checkbox"> - <label for="preview_checkbox">{{$t('settings.style.preview.checkbox')}}</label> - </span> - <button class="btn"> - {{$t('settings.style.preview.button')}} - </button> - </div> - </div> - </div> + <preview :style="previewRules"/> </div> <keep-alive> @@ -235,6 +163,7 @@ <OpacityInput name="faintOpacity" v-model="faintOpacityLocal" :fallback="previewTheme.opacity.faint || 0.5"/> </div> </div> + <div :label="$t('settings.style.radii._tab_label')" class="radius-container"> <div class="tab-header"> <p>{{$t('settings.radii_help')}}</p> @@ -249,6 +178,7 @@ <RangeInput name="attachmentRadius" :label="$t('settings.attachmentRadius')" v-model="attachmentRadiusLocal" :fallback="previewTheme.radii.attachment" max="50" hardMin="0"/> <RangeInput name="tooltipRadius" :label="$t('settings.tooltipRadius')" v-model="tooltipRadiusLocal" :fallback="previewTheme.radii.tooltip" max="50" hardMin="0"/> </div> + <div :label="$t('settings.style.shadows._tab_label')" class="shadow-container"> <div class="tab-header shadow-selector"> <div class="select-container"> @@ -294,6 +224,7 @@ <p>{{$t('settings.style.shadows.filter_hint.spread_zero')}}</p> </div> </div> + <div :label="$t('settings.style.fonts._tab_label')" class="fonts-container"> <div class="tab-header"> <p>{{$t('settings.style.fonts.help')}}</p> diff --git a/src/modules/config.js b/src/modules/config.js index fb9b3ca6..45ac8f65 100644 --- a/src/modules/config.js +++ b/src/modules/config.js @@ -1,5 +1,5 @@ import { set, delete as del } from 'vue' -import { setPreset, setColors } from '../services/style_setter/style_setter.js' +import { setPreset, applyTheme } from '../services/style_setter/style_setter.js' const browserLocale = (window.navigator.language || 'en').split('-')[0] @@ -57,7 +57,7 @@ const config = { setPreset(value, commit) break case 'customTheme': - setColors(value, commit) + applyTheme(value, commit) } } } From c189a08dffd2373172d9fd34af5954af146c2f36 Mon Sep 17 00:00:00 2001 From: Henry Jameson <me@hjkos.com> Date: Tue, 11 Dec 2018 16:36:06 +0300 Subject: [PATCH 93/96] added keep-colors option --- .../style_switcher/style_switcher.js | 88 ++++++++++--------- .../style_switcher/style_switcher.scss | 9 +- .../style_switcher/style_switcher.vue | 15 +++- src/i18n/en.json | 1 + src/i18n/ru.json | 1 + 5 files changed, 62 insertions(+), 52 deletions(-) diff --git a/src/components/style_switcher/style_switcher.js b/src/components/style_switcher/style_switcher.js index 50cd1e6f..6a4e1cba 100644 --- a/src/components/style_switcher/style_switcher.js +++ b/src/components/style_switcher/style_switcher.js @@ -38,6 +38,7 @@ export default { colorsInvalid: true, radiiInvalid: true, + keepColor: false, keepShadows: false, keepOpacity: false, keepRoundness: false, @@ -296,31 +297,30 @@ export default { return !this.shadowsInvalid && !this.colorsInvalid && !this.radiiInvalid }, exportedTheme () { - const saveEverything = !this.keepFonts && !this.keepShadows && !this.keepColors && !this.keepOpacity && !this.keepRoundness + const saveEverything = ( + !this.keepFonts && + !this.keepShadows && + !this.keepOpacity && + !this.keepRoundness && + !this.keepColor + ) - // TODO change into delete-less version. - const theme = { - shadows: this.shadowsLocal, - fonts: this.fontsLocal, - opacity: this.currentOpacity, - colors: this.currentColors, - radii: this.currentRadii - } + const theme = {} - if (!this.keepFonts && !saveEverything) { - delete theme.fonts + if (this.keepFonts || saveEverything) { + theme.fonts = this.fontsLocal } - if (!this.keepShadows && !saveEverything) { - delete theme.shadows + if (this.keepShadows || saveEverything) { + theme.shadows = this.shadowsLocal } - if (!this.keepOpacity && !saveEverything) { - delete theme.opacity + if (this.keepOpacity || saveEverything) { + theme.opacity = this.currentOpacity } - if (!this.keepColors && !saveEverything) { - delete theme.colors + if (this.keepColor || saveEverything) { + theme.colors = this.currentColors } - if (!this.keepRoundness && !saveEverything) { - delete theme.radii + if (this.keepRoundness || saveEverything) { + theme.radii = this.currentRadii } return { @@ -438,21 +438,23 @@ export default { this.textColorLocal = rgb2hex(colors.fg) } - this.clearV1() - const keys = new Set(version !== 1 ? Object.keys(colors) : []) - if (version === 1 || version === 'l1') { - keys - .add('bg') - .add('link') - .add('cRed') - .add('cBlue') - .add('cGreen') - .add('cOrange') - } + if (!this.keepColor) { + this.clearV1() + const keys = new Set(version !== 1 ? Object.keys(colors) : []) + if (version === 1 || version === 'l1') { + keys + .add('bg') + .add('link') + .add('cRed') + .add('cBlue') + .add('cGreen') + .add('cOrange') + } - keys.forEach(key => { - this[key + 'ColorLocal'] = rgb2hex(colors[key]) - }) + keys.forEach(key => { + this[key + 'ColorLocal'] = rgb2hex(colors[key]) + }) + } if (!this.keepRoundness) { this.clearRoundness() @@ -553,16 +555,18 @@ export default { this.clearOpacity() } - this.clearV1() + if (!this.keepColor) { + this.clearV1() - this.bgColorLocal = this.selected[1] - this.fgColorLocal = this.selected[2] - this.textColorLocal = this.selected[3] - this.linkColorLocal = this.selected[4] - this.cRedColorLocal = this.selected[5] - this.cGreenColorLocal = this.selected[6] - this.cBlueColorLocal = this.selected[7] - this.cOrangeColorLocal = this.selected[8] + this.bgColorLocal = this.selected[1] + this.fgColorLocal = this.selected[2] + this.textColorLocal = this.selected[3] + this.linkColorLocal = this.selected[4] + this.cRedColorLocal = this.selected[5] + this.cGreenColorLocal = this.selected[6] + this.cBlueColorLocal = this.selected[7] + this.cOrangeColorLocal = this.selected[8] + } } else if (this.selectedVersion >= 2) { this.normalizeLocalState(this.selected.theme, 2) } diff --git a/src/components/style_switcher/style_switcher.scss b/src/components/style_switcher/style_switcher.scss index f53f00a0..135c113a 100644 --- a/src/components/style_switcher/style_switcher.scss +++ b/src/components/style_switcher/style_switcher.scss @@ -54,11 +54,6 @@ } } - .import-warning { - color: $fallback--cRed; - color: var(--cRed, $fallback--cRed); - } - .tab-switcher { margin: 0 -1em; } @@ -154,8 +149,10 @@ .save-load-options { flex-wrap: wrap; margin-top: .5em; - span { + justify-content: center; + .keep-option { margin: 0 .5em .5em; + min-width: 25%; } } diff --git a/src/components/style_switcher/style_switcher.vue b/src/components/style_switcher/style_switcher.vue index 730bfef0..6acb7755 100644 --- a/src/components/style_switcher/style_switcher.vue +++ b/src/components/style_switcher/style_switcher.vue @@ -28,28 +28,35 @@ /> </div> <div class="save-load-options"> - <span> + <span class="keep-option"> + <input + id="keep-color" + type="checkbox" + v-model="keepColor"> + <label for="keep-color">{{$t('settings.style.switcher.keep_color')}}</label> + </span> + <span class="keep-option"> <input id="keep-shadows" type="checkbox" v-model="keepShadows"> <label for="keep-shadows">{{$t('settings.style.switcher.keep_shadows')}}</label> </span> - <span> + <span class="keep-option"> <input id="keep-opacity" type="checkbox" v-model="keepOpacity"> <label for="keep-opacity">{{$t('settings.style.switcher.keep_opacity')}}</label> </span> - <span> + <span class="keep-option"> <input id="keep-roundness" type="checkbox" v-model="keepRoundness"> <label for="keep-roundness">{{$t('settings.style.switcher.keep_roundness')}}</label> </span> - <span> + <span class="keep-option"> <input id="keep-fonts" type="checkbox" diff --git a/src/i18n/en.json b/src/i18n/en.json index a1fc43aa..97dfcb77 100644 --- a/src/i18n/en.json +++ b/src/i18n/en.json @@ -191,6 +191,7 @@ }, "style": { "switcher": { + "keep_color": "Keep colors", "keep_shadows": "Keep shadows", "keep_opacity": "Keep opacity", "keep_roundness": "Keep roundness", diff --git a/src/i18n/ru.json b/src/i18n/ru.json index 438e0608..c764005a 100644 --- a/src/i18n/ru.json +++ b/src/i18n/ru.json @@ -156,6 +156,7 @@ "user_settings": "Настройки пользователя", "style": { "switcher": { + "keep_color": "Оставить цвета", "keep_shadows": "Оставить тени", "keep_opacity": "Оставить прозрачность", "keep_roundness": "Оставить скругление", From 83b85cd412245b8bfb65ff650b5543da4974ae46 Mon Sep 17 00:00:00 2001 From: Henry Jameson <me@hjkos.com> Date: Tue, 11 Dec 2018 16:36:27 +0300 Subject: [PATCH 94/96] better layouting for import-export, error display fixes --- .../export_import/export_import.vue | 16 +++++++- .../style_switcher/style_switcher.vue | 38 ++++++++++--------- 2 files changed, 34 insertions(+), 20 deletions(-) diff --git a/src/components/export_import/export_import.vue b/src/components/export_import/export_import.vue index 9914d54a..451a2668 100644 --- a/src/components/export_import/export_import.vue +++ b/src/components/export_import/export_import.vue @@ -1,8 +1,11 @@ <template> -<div class="import-export"> +<div class="import-export-container"> + <slot name="before"/> <button class="btn" @click="exportData">{{ exportLabel }}</button> <button class="btn" @click="importData">{{ importLabel }}</button> - <p v-if="importFailed" class="import-warning">{{ importFailedText }}</p> + <slot name="afterButtons"/> + <p v-if="importFailed" class="alert error">{{ importFailedText }}</p> + <slot name="afterError"/> </div> </template> @@ -73,3 +76,12 @@ export default { } } </script> + +<style lang="scss"> +.import-export-container { + display: flex; + flex-wrap: wrap; + align-items: baseline; + justify-content: center; +} +</style> diff --git a/src/components/style_switcher/style_switcher.vue b/src/components/style_switcher/style_switcher.vue index 6acb7755..84963c81 100644 --- a/src/components/style_switcher/style_switcher.vue +++ b/src/components/style_switcher/style_switcher.vue @@ -2,30 +2,32 @@ <div class="style-switcher"> <div class="presets-container"> <div class="save-load"> - <div class="presets"> - {{$t('settings.presets')}} - <label for="preset-switcher" class='select'> - <select id="preset-switcher" v-model="selected" class="preset-switcher"> - <option v-for="style in availableStyles" - :value="style" - :style="{ - backgroundColor: style[1] || style.theme.colors.bg, - color: style[3] || style.theme.colors.text - }"> - {{style[0] || style.name}} - </option> - </select> - <i class="icon-down-open"/> - </label> - </div> <export-import :exportObject='exportedTheme' :exportLabel='$t("settings.export_theme")' :importLabel='$t("settings.import_theme")' :importFailedText='$t("settings.invalid_theme_imported")' :onImport='onImport' - :validator='importValidator' - /> + :validator='importValidator'> + <template slot="before"> + <div class="presets"> + {{$t('settings.presets')}} + <label for="preset-switcher" class='select'> + <select id="preset-switcher" v-model="selected" class="preset-switcher"> + <option v-for="style in availableStyles" + :value="style" + :style="{ + backgroundColor: style[1] || style.theme.colors.bg, + color: style[3] || style.theme.colors.text + }"> + {{style[0] || style.name}} + </option> + </select> + <i class="icon-down-open"/> + </label> + </div> + </template> + </export-import> </div> <div class="save-load-options"> <span class="keep-option"> From c3f8b713a77adbac73678f3f1c7a9ab937ca5904 Mon Sep 17 00:00:00 2001 From: Henry Jameson <me@hjkos.com> Date: Tue, 11 Dec 2018 16:37:03 +0300 Subject: [PATCH 95/96] fixed wrong height for selects --- src/App.scss | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/App.scss b/src/App.scss index 15dec7ec..5355d899 100644 --- a/src/App.scss +++ b/src/App.scss @@ -119,7 +119,7 @@ input, textarea, .select { box-sizing: border-box; display: inline-block; position: relative; - height: 29px; + height: 28px; line-height: 16px; hyphens: none; @@ -136,7 +136,7 @@ input, textarea, .select { height: 100%; color: $fallback--text; color: var(--text, $fallback--text); - line-height: 29px; + line-height: 28px; z-index: 0; pointer-events: none; } @@ -156,7 +156,7 @@ input, textarea, .select { font-size: 14px; width: 100%; z-index: 1; - height: 29px; + height: 28px; line-height: 16px; } From 8fcc4c67667b0951d6c0d28cec320bd4b2f8f107 Mon Sep 17 00:00:00 2001 From: Henry Jameson <me@hjkos.com> Date: Tue, 11 Dec 2018 19:09:00 +0300 Subject: [PATCH 96/96] fix --- static/styles.json | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/static/styles.json b/static/styles.json index b6bd6530..00ad6ae1 100644 --- a/static/styles.json +++ b/static/styles.json @@ -7,9 +7,9 @@ "monokai": [ "Monokai", "#272822", "#383830", "#f8f8f2", "#f92672", "#F92672", "#a6e22e", "#66d9ef", "#f4bf75" ], "mammal": [ "Mammal", "#272c37", "#444b5d", "#f8f8f8", "#9bacc8", "#7f3142", "#2bd850", "#2b90d9", "#ca8f04" ], - "redmond-xx": "./static/themes/redmond-xx.json", - "redmond-xx-se": "./static/themes/redmond-xx-se.json", - "redmond-xxi": "./static/themes/redmond-xxi.json", - "breezy-dark": "./static/themes/breezy-dark.json", - "breezy-light": "./static/themes/breezy-light.json" + "redmond-xx": "/static/themes/redmond-xx.json", + "redmond-xx-se": "/static/themes/redmond-xx-se.json", + "redmond-xxi": "/static/themes/redmond-xxi.json", + "breezy-dark": "/static/themes/breezy-dark.json", + "breezy-light": "/static/themes/breezy-light.json" }