Compare commits
9 commits
322cd6d5e0
...
f354de14cc
Author | SHA1 | Date | |
---|---|---|---|
|
f354de14cc | ||
|
48cc7ccc78 | ||
|
541affd459 | ||
|
a21f25ed8e | ||
|
3d77860e57 | ||
|
c937736fea | ||
|
24663b2f04 | ||
|
9e3e4ed429 | ||
|
81d9537f9d |
15 changed files with 718 additions and 556 deletions
|
@ -351,7 +351,12 @@ const afterStoreSetup = async ({ store, i18n }) => {
|
|||
store.dispatch('setInstanceOption', { name: 'server', value: server })
|
||||
|
||||
await setConfig({ store })
|
||||
await store.dispatch('applyTheme', { recompile: false })
|
||||
document.querySelector('#status').textContent = i18n.global.t('splash.theme')
|
||||
try {
|
||||
await store.dispatch('applyTheme').catch((e) => { console.error('Error setting theme', e) })
|
||||
} catch (e) {
|
||||
return Promise.reject(e)
|
||||
}
|
||||
|
||||
applyConfig(store.state.config)
|
||||
|
||||
|
|
115
src/components/select/select_motion.vue
Normal file
115
src/components/select/select_motion.vue
Normal file
|
@ -0,0 +1,115 @@
|
|||
<template>
|
||||
<div
|
||||
class="SelectMotion btn-group"
|
||||
>
|
||||
<button
|
||||
class="btn button-default"
|
||||
:disabled="disabled || shadowsAreNull"
|
||||
@click="add"
|
||||
>
|
||||
<FAIcon
|
||||
fixed-width
|
||||
icon="plus"
|
||||
/>
|
||||
</button>
|
||||
<button
|
||||
class="btn button-default"
|
||||
:disabled="disabled || !moveUpValid"
|
||||
:class="{ disabled: disabled || !moveUpValid }"
|
||||
@click="moveUp"
|
||||
>
|
||||
<FAIcon
|
||||
fixed-width
|
||||
icon="chevron-up"
|
||||
/>
|
||||
</button>
|
||||
<button
|
||||
class="btn button-default"
|
||||
:disabled="disabled || !moveDnValid"
|
||||
:class="{ disabled: disabled || !moveDnValid }"
|
||||
@click="moveDn"
|
||||
>
|
||||
<FAIcon
|
||||
fixed-width
|
||||
icon="chevron-down"
|
||||
/>
|
||||
</button>
|
||||
<button
|
||||
class="btn button-default"
|
||||
:disabled="disabled || !present"
|
||||
:class="{ disabled: disabled || !present }"
|
||||
@click="del"
|
||||
>
|
||||
<FAIcon
|
||||
fixed-width
|
||||
icon="times"
|
||||
/>
|
||||
</button>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { computed, defineEmits, defineProps } from 'vue'
|
||||
|
||||
const props = defineProps(['modelValue', 'selectedId', 'disabled', 'getAddValue'])
|
||||
const emit = defineEmits(['update:modelValue', 'update:selectedId'])
|
||||
|
||||
const moveUpValid = computed(() => {
|
||||
return props.selectedId > 0
|
||||
})
|
||||
|
||||
const present = computed(() => props.modelValue[props.selectedId] != null)
|
||||
|
||||
const moveUp = () => {
|
||||
const newModel = [...props.modelValue]
|
||||
const movable = newModel.splice(props.selectedId, 1)[0]
|
||||
newModel.splice(props.selectedId - 1, 0, movable)
|
||||
|
||||
emit('update:modelValue', newModel)
|
||||
emit('update:selectedId', props.selectedId - 1)
|
||||
}
|
||||
|
||||
const moveDnValid = computed(() => {
|
||||
return props.selectedId < props.modelValue.length - 1
|
||||
})
|
||||
const moveDn = () => {
|
||||
const newModel = [...props.modelValue]
|
||||
const movable = newModel.splice(props.selectedId.value, 1)[0]
|
||||
newModel.splice(props.selectedId + 1, 0, movable)
|
||||
|
||||
emit('update:modelValue', newModel)
|
||||
emit('update:selectedId', props.selectedId + 1)
|
||||
}
|
||||
|
||||
const add = () => {
|
||||
const newModel = [...props.modelValue]
|
||||
newModel.push(props.getAddValue())
|
||||
console.log(newModel)
|
||||
|
||||
emit('update:modelValue', newModel)
|
||||
emit('update:selectedId', Math.max(newModel.length - 1, 0))
|
||||
}
|
||||
|
||||
const del = () => {
|
||||
const newModel = [...props.modelValue]
|
||||
newModel.splice(props.selectedId, 1)
|
||||
|
||||
emit('update:modelValue', newModel)
|
||||
emit('update:selectedId', newModel.length === 0 ? undefined : Math.max(props.selectedId - 1, 0))
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.SelectMotion {
|
||||
flex: 0 0 auto;
|
||||
display: grid;
|
||||
grid-auto-columns: 1fr;
|
||||
grid-auto-flow: column;
|
||||
margin-top: 0.25em;
|
||||
|
||||
.button-default {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
}
|
||||
</style>
|
|
@ -34,7 +34,7 @@ const AppearanceTab = {
|
|||
return {
|
||||
availableStyles: [],
|
||||
availablePalettes: [],
|
||||
themeImporter: newImporter({
|
||||
fileImporter: newImporter({
|
||||
accept: '.json, .piss',
|
||||
validator: this.importValidator,
|
||||
onImport: this.onImport,
|
||||
|
@ -179,6 +179,10 @@ const AppearanceTab = {
|
|||
this.$store.dispatch('setOption', { name: 'interfaceLanguage', value: val })
|
||||
}
|
||||
},
|
||||
customThemeVersion () {
|
||||
const { themeVersion } = this.$store.state.interface
|
||||
return themeVersion
|
||||
},
|
||||
isCustomThemeUsed () {
|
||||
const { theme } = this.mergedConfig
|
||||
return theme === 'custom'
|
||||
|
@ -202,8 +206,8 @@ const AppearanceTab = {
|
|||
}
|
||||
})
|
||||
},
|
||||
importTheme () {
|
||||
this.themeImporter.importData()
|
||||
importFile () {
|
||||
this.fileImporter.importData()
|
||||
},
|
||||
onImport (parsed, filename) {
|
||||
if (filename.endsWith('.json')) {
|
||||
|
@ -234,14 +238,18 @@ const AppearanceTab = {
|
|||
const { palette } = this.mergedConfig
|
||||
return key === palette
|
||||
},
|
||||
importStyle () {
|
||||
|
||||
setStyle (name) {
|
||||
this.$store.dispatch('resetThemeV2')
|
||||
this.$store.dispatch('setTheme', name)
|
||||
this.$store.dispatch('applyTheme')
|
||||
},
|
||||
setTheme (name) {
|
||||
this.$store.dispatch('resetThemeV3')
|
||||
this.$store.dispatch('setTheme', name)
|
||||
this.$store.dispatch('applyTheme')
|
||||
},
|
||||
setPalette (name) {
|
||||
this.$store.dispatch('resetThemeV2')
|
||||
this.$store.dispatch('setPalette', name)
|
||||
this.$store.dispatch('applyTheme')
|
||||
},
|
||||
|
|
|
@ -24,6 +24,10 @@
|
|||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
grid-gap: 0.5em;
|
||||
|
||||
.unsupported-theme-v2 {
|
||||
grid-column: 1 / span 2;
|
||||
}
|
||||
}
|
||||
|
||||
.palette-entry {
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
<h2>{{ $t('settings.theme') }}</h2>
|
||||
<button
|
||||
class="btn button-default"
|
||||
@click="importTheme"
|
||||
@click="importFile"
|
||||
>
|
||||
<FAIcon icon="folder-open" />
|
||||
{{ $t('settings.style.themes3.editor.load_style') }}
|
||||
|
@ -30,7 +30,7 @@
|
|||
v-html="previewTheme('stock')"
|
||||
/>
|
||||
<!-- eslint-enable vue/no-v-text-v-html-on-component -->
|
||||
<preview />
|
||||
<preview id="theme-preview-stock" />
|
||||
<h4 class="theme-name">
|
||||
{{ $t('settings.style.stock_theme_used') }}
|
||||
<span class="alert neutral version">v3</span>
|
||||
|
@ -71,23 +71,30 @@
|
|||
</ul>
|
||||
<h3>{{ $t('settings.style.themes3.palette.label') }}</h3>
|
||||
<div class="palettes">
|
||||
<button
|
||||
v-for="p in availablePalettes"
|
||||
:key="p.name"
|
||||
class="btn button-default palette-entry"
|
||||
:class="{ toggled: isPaletteActive(p.key) }"
|
||||
@click="() => setPalette(p.key)"
|
||||
>
|
||||
<label>
|
||||
{{ p.name }}
|
||||
</label>
|
||||
<span
|
||||
v-for="c in palettesKeys"
|
||||
:key="c"
|
||||
class="palette-square"
|
||||
:style="{ backgroundColor: p[c], border: '1px solid ' + (p[c] ?? 'var(--text)') }"
|
||||
/>
|
||||
</button>
|
||||
<template v-if="customThemeVersion === 'v3'">
|
||||
<button
|
||||
v-for="p in availablePalettes"
|
||||
:key="p.name"
|
||||
class="btn button-default palette-entry"
|
||||
:class="{ toggled: isPaletteActive(p.key) }"
|
||||
@click="() => setPalette(p.key)"
|
||||
>
|
||||
<label>
|
||||
{{ p.name }}
|
||||
</label>
|
||||
<span
|
||||
v-for="c in palettesKeys"
|
||||
:key="c"
|
||||
class="palette-square"
|
||||
:style="{ backgroundColor: p[c], border: '1px solid ' + (p[c] ?? 'var(--text)') }"
|
||||
/>
|
||||
</button>
|
||||
</template>
|
||||
<template v-else-if="customThemeVersion === 'v2'">
|
||||
<div class="alert neutral theme-notice unsupported-theme-v2">
|
||||
{{ $t('settings.style.themes3.palette.v2_unsupported') }}
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
<div class="alert neutral theme-notice">
|
||||
|
|
|
@ -219,9 +219,13 @@ export default {
|
|||
return selectors.map(x => x.substring(1)).join('')
|
||||
})
|
||||
const previewCss = computed(() => {
|
||||
const scoped = getCssRules(previewRules)
|
||||
.map(simulatePseudoSelectors)
|
||||
return scoped.join('\n')
|
||||
try {
|
||||
const scoped = getCssRules(previewRules).map(simulatePseudoSelectors)
|
||||
return scoped.join('\n')
|
||||
} catch (e) {
|
||||
console.error('Invalid ruleset', e)
|
||||
return null
|
||||
}
|
||||
})
|
||||
|
||||
// ### Rules stuff aka meat and potatoes
|
||||
|
@ -415,17 +419,22 @@ export default {
|
|||
})
|
||||
|
||||
const updatePreview = () => {
|
||||
previewRules.splice(0, previewRules.length)
|
||||
previewRules.push(...init({
|
||||
inputRuleset: editorFriendlyToOriginal.value,
|
||||
initialStaticVars: {
|
||||
...palette.value
|
||||
},
|
||||
ultimateBackgroundColor: '#000000',
|
||||
rootComponentName: selectedComponentName.value,
|
||||
editMode: true,
|
||||
debug: true
|
||||
}).eager)
|
||||
try {
|
||||
const rules = init({
|
||||
inputRuleset: editorFriendlyToOriginal.value,
|
||||
initialStaticVars: {
|
||||
...palette.value
|
||||
},
|
||||
ultimateBackgroundColor: '#000000',
|
||||
rootComponentName: selectedComponentName.value,
|
||||
editMode: true,
|
||||
debug: true
|
||||
}).eager
|
||||
previewRules.splice(0, previewRules.length)
|
||||
previewRules.push(...rules)
|
||||
} catch (e) {
|
||||
console.error('Could not compile preview theme', e)
|
||||
}
|
||||
}
|
||||
|
||||
const updateSelectedComponent = () => {
|
||||
|
|
|
@ -51,231 +51,241 @@
|
|||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="setting-item component-editor">
|
||||
<div class="component-selector">
|
||||
<label for="component-selector">
|
||||
{{ $t('settings.style.themes3.editor.component_selector') }}
|
||||
{{ ' ' }}
|
||||
</label>
|
||||
<Select
|
||||
id="component-selector"
|
||||
v-model="selectedComponentKey"
|
||||
>
|
||||
<option
|
||||
v-for="key in componentKeys"
|
||||
:key="'component-' + key"
|
||||
:value="key"
|
||||
>
|
||||
{{ fallbackI18n($t(getFriendlyNamePath(componentsMap.get(key).name)), componentsMap.get(key).name) }}
|
||||
</option>
|
||||
</Select>
|
||||
</div>
|
||||
<tab-switcher>
|
||||
<div
|
||||
v-if="selectedComponentVariantsAll.length > 1"
|
||||
class="variant-selector"
|
||||
key="component"
|
||||
class="setting-item component-editor"
|
||||
:label="$t('settings.style.themes3.editor.component_tab')"
|
||||
>
|
||||
<label for="variant-selector">
|
||||
{{ $t('settings.style.themes3.editor.variant_selector') }}
|
||||
</label>
|
||||
<Select
|
||||
v-model="selectedVariant"
|
||||
>
|
||||
<option
|
||||
v-for="variant in selectedComponentVariantsAll"
|
||||
:key="'component-variant-' + variant"
|
||||
:value="variant"
|
||||
<div class="component-selector">
|
||||
<label for="component-selector">
|
||||
{{ $t('settings.style.themes3.editor.component_selector') }}
|
||||
{{ ' ' }}
|
||||
</label>
|
||||
<Select
|
||||
id="component-selector"
|
||||
v-model="selectedComponentKey"
|
||||
>
|
||||
{{ fallbackI18n($t(getVariantPath(selectedComponentName, variant)), variant) }}
|
||||
</option>
|
||||
</Select>
|
||||
</div>
|
||||
<div
|
||||
v-if="selectedComponentStates.length > 0"
|
||||
class="state-selector"
|
||||
>
|
||||
<label>
|
||||
{{ $t('settings.style.themes3.editor.states_selector') }}
|
||||
</label>
|
||||
<ul
|
||||
class="state-selector-list"
|
||||
<option
|
||||
v-for="key in componentKeys"
|
||||
:key="'component-' + key"
|
||||
:value="key"
|
||||
>
|
||||
{{ fallbackI18n($t(getFriendlyNamePath(componentsMap.get(key).name)), componentsMap.get(key).name) }}
|
||||
</option>
|
||||
</Select>
|
||||
</div>
|
||||
<div
|
||||
v-if="selectedComponentVariantsAll.length > 1"
|
||||
class="variant-selector"
|
||||
>
|
||||
<li
|
||||
v-for="state in selectedComponentStates"
|
||||
:key="'component-state-' + state"
|
||||
<label for="variant-selector">
|
||||
{{ $t('settings.style.themes3.editor.variant_selector') }}
|
||||
</label>
|
||||
<Select
|
||||
v-model="selectedVariant"
|
||||
>
|
||||
<option
|
||||
v-for="variant in selectedComponentVariantsAll"
|
||||
:key="'component-variant-' + variant"
|
||||
:value="variant"
|
||||
>
|
||||
{{ fallbackI18n($t(getVariantPath(selectedComponentName, variant)), variant) }}
|
||||
</option>
|
||||
</Select>
|
||||
</div>
|
||||
<div
|
||||
v-if="selectedComponentStates.length > 0"
|
||||
class="state-selector"
|
||||
>
|
||||
<label>
|
||||
{{ $t('settings.style.themes3.editor.states_selector') }}
|
||||
</label>
|
||||
<ul
|
||||
class="state-selector-list"
|
||||
>
|
||||
<li
|
||||
v-for="state in selectedComponentStates"
|
||||
:key="'component-state-' + state"
|
||||
>
|
||||
<Checkbox
|
||||
:value="selectedState.has(state)"
|
||||
@update:modelValue="(v) => updateSelectedStates(state, v)"
|
||||
>
|
||||
{{ fallbackI18n($t(getStatePath(selectedComponentName, state)), state) }}
|
||||
</Checkbox>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="preview-container">
|
||||
<!-- eslint-disable vue/no-v-html vue/no-v-text-v-html-on-component -->
|
||||
<component
|
||||
:is="'style'"
|
||||
v-html="previewCss"
|
||||
/>
|
||||
<!-- eslint-enable vue/no-v-html vue/no-v-text-v-html-on-component -->
|
||||
<ComponentPreview
|
||||
class="component-preview"
|
||||
:show-text="componentHas('Text')"
|
||||
:shadow-control="isShadowTabOpen"
|
||||
:preview-class="previewClass"
|
||||
:preview-style="editorHintStyle"
|
||||
:disabled="!editedSubShadow && typeof editedShadow !== 'string'"
|
||||
:shadow="editedSubShadow"
|
||||
@update:shadow="({ axis, value }) => updateSubShadow(axis, value)"
|
||||
/>
|
||||
</div>
|
||||
<tab-switcher
|
||||
ref="tabSwitcher"
|
||||
class="component-settings"
|
||||
:on-switch="onTabSwitch"
|
||||
>
|
||||
<div
|
||||
key="main"
|
||||
class="editor-tab"
|
||||
:label="$t('settings.style.themes3.editor.main_tab')"
|
||||
>
|
||||
<ColorInput
|
||||
v-model="editedBackgroundColor"
|
||||
:disabled="!isBackgroundColorPresent"
|
||||
:label="$t('settings.style.themes3.editor.background')"
|
||||
/>
|
||||
<Tooltip :text="$t('settings.style.themes3.editor.include_in_rule')">
|
||||
<Checkbox v-model="isBackgroundColorPresent" />
|
||||
</Tooltip>
|
||||
<OpacityInput
|
||||
v-model="editedOpacity"
|
||||
:disabled="!isOpacityPresent"
|
||||
:label="$t('settings.style.themes3.editor.opacity')"
|
||||
/>
|
||||
<Tooltip :text="$t('settings.style.themes3.editor.include_in_rule')">
|
||||
<Checkbox v-model="isOpacityPresent" />
|
||||
</Tooltip>
|
||||
<ColorInput
|
||||
v-if="componentHas('Text')"
|
||||
v-model="editedTextColor"
|
||||
:label="$t('settings.style.themes3.editor.text_color')"
|
||||
:disabled="!isTextColorPresent"
|
||||
/>
|
||||
<Tooltip
|
||||
v-if="componentHas('Text')"
|
||||
:text="$t('settings.style.themes3.editor.include_in_rule')"
|
||||
>
|
||||
<Checkbox v-model="isTextColorPresent" />
|
||||
</Tooltip>
|
||||
<div class="style-control suboption">
|
||||
<label
|
||||
for="textAuto"
|
||||
class="label"
|
||||
:class="{ faint: !isTextAutoPresent }"
|
||||
>
|
||||
{{ $t('settings.style.themes3.editor.text_auto.label') }}
|
||||
</label>
|
||||
<Select
|
||||
id="textAuto"
|
||||
v-model="editedTextAuto"
|
||||
:disabled="!isTextAutoPresent"
|
||||
>
|
||||
<option value="no-preserve">
|
||||
{{ $t('settings.style.themes3.editor.text_auto.no-preserve') }}
|
||||
</option>
|
||||
<option value="no-auto">
|
||||
{{ $t('settings.style.themes3.editor.text_auto.no-auto') }}
|
||||
</option>
|
||||
<option value="preserve">
|
||||
{{ $t('settings.style.themes3.editor.text_auto.preserve') }}
|
||||
</option>
|
||||
</Select>
|
||||
</div>
|
||||
<Tooltip
|
||||
v-if="componentHas('Text')"
|
||||
:text="$t('settings.style.themes3.editor.include_in_rule')"
|
||||
>
|
||||
<Checkbox v-model="isTextAutoPresent" />
|
||||
</Tooltip>
|
||||
<div>
|
||||
<ContrastRatio :contrast="getContrast(editedBackgroundColor, editedTextColor)" />
|
||||
</div>
|
||||
<div>
|
||||
<!-- spacer for missing checkbox -->
|
||||
</div>
|
||||
<ColorInput
|
||||
v-if="componentHas('Link')"
|
||||
v-model="editedLinkColor"
|
||||
:label="$t('settings.style.themes3.editor.link_color')"
|
||||
:disabled="!isLinkColorPresent"
|
||||
/>
|
||||
<Tooltip
|
||||
v-if="componentHas('Link')"
|
||||
:text="$t('settings.style.themes3.editor.include_in_rule')"
|
||||
>
|
||||
<Checkbox v-model="isLinkColorPresent" />
|
||||
</Tooltip>
|
||||
<ColorInput
|
||||
v-if="componentHas('Icon')"
|
||||
v-model="editedIconColor"
|
||||
:label="$t('settings.style.themes3.editor.icon_color')"
|
||||
:disabled="!isIconColorPresent"
|
||||
/>
|
||||
<Tooltip
|
||||
v-if="componentHas('Icon')"
|
||||
:text="$t('settings.style.themes3.editor.include_in_rule')"
|
||||
>
|
||||
<Checkbox v-model="isIconColorPresent" />
|
||||
</Tooltip>
|
||||
</div>
|
||||
<div
|
||||
key="shadow"
|
||||
class="editor-tab shadow-tab"
|
||||
:label="$t('settings.style.themes3.editor.shadows_tab')"
|
||||
>
|
||||
<Checkbox
|
||||
:value="selectedState.has(state)"
|
||||
@update:modelValue="(v) => updateSelectedStates(state, v)"
|
||||
v-model="isShadowPresent"
|
||||
class="style-control"
|
||||
>
|
||||
{{ fallbackI18n($t(getStatePath(selectedComponentName, state)), state) }}
|
||||
</Checkbox>
|
||||
</li>
|
||||
</ul>
|
||||
{{ $t('settings.style.themes3.editor.include_in_rule') }}
|
||||
</checkbox>
|
||||
<ShadowControl
|
||||
v-model="editedShadow"
|
||||
:disabled="!isShadowPresent"
|
||||
:no-preview="true"
|
||||
:separate-inset="shadowSelected === 'avatar' || shadowSelected === 'avatarStatus'"
|
||||
@subShadowSelected="onSubShadow"
|
||||
/>
|
||||
</div>
|
||||
</tab-switcher>
|
||||
</div>
|
||||
<div class="preview-container">
|
||||
<!-- eslint-disable vue/no-v-html vue/no-v-text-v-html-on-component -->
|
||||
<component
|
||||
:is="'style'"
|
||||
v-html="previewCss"
|
||||
/>
|
||||
<!-- eslint-enable vue/no-v-html vue/no-v-text-v-html-on-component -->
|
||||
<ComponentPreview
|
||||
class="component-preview"
|
||||
:show-text="componentHas('Text')"
|
||||
:shadow-control="isShadowTabOpen"
|
||||
:preview-class="previewClass"
|
||||
:preview-style="editorHintStyle"
|
||||
:disabled="!editedSubShadow"
|
||||
:shadow="editedSubShadow"
|
||||
@update:shadow="({ axis, value }) => updateSubShadow(axis, value)"
|
||||
/>
|
||||
</div>
|
||||
<tab-switcher
|
||||
ref="tabSwitcher"
|
||||
class="component-settings"
|
||||
:on-switch="onTabSwitch"
|
||||
<div
|
||||
key="palette"
|
||||
:label="$t('settings.style.themes3.editor.palette_tab')"
|
||||
class="setting-item palette-editor"
|
||||
>
|
||||
<div
|
||||
key="main"
|
||||
class="editor-tab"
|
||||
:label="$t('settings.style.themes3.editor.main_tab')"
|
||||
>
|
||||
<ColorInput
|
||||
v-model="editedBackgroundColor"
|
||||
:disabled="!isBackgroundColorPresent"
|
||||
:label="$t('settings.style.themes3.editor.background')"
|
||||
/>
|
||||
<Tooltip :text="$t('settings.style.themes3.editor.include_in_rule')">
|
||||
<Checkbox v-model="isBackgroundColorPresent" />
|
||||
</Tooltip>
|
||||
<OpacityInput
|
||||
v-model="editedOpacity"
|
||||
:disabled="!isOpacityPresent"
|
||||
:label="$t('settings.style.themes3.editor.opacity')"
|
||||
/>
|
||||
<Tooltip :text="$t('settings.style.themes3.editor.include_in_rule')">
|
||||
<Checkbox v-model="isOpacityPresent" />
|
||||
</Tooltip>
|
||||
<ColorInput
|
||||
v-if="componentHas('Text')"
|
||||
v-model="editedTextColor"
|
||||
:label="$t('settings.style.themes3.editor.text_color')"
|
||||
:disabled="!isTextColorPresent"
|
||||
/>
|
||||
<Tooltip
|
||||
v-if="componentHas('Text')"
|
||||
:text="$t('settings.style.themes3.editor.include_in_rule')"
|
||||
<div class="label">
|
||||
<label for="palette-selector">
|
||||
{{ $t('settings.style.themes3.palette.label') }}
|
||||
{{ ' ' }}
|
||||
</label>
|
||||
<Select
|
||||
id="palette-selector"
|
||||
v-model="editedPalette"
|
||||
>
|
||||
<Checkbox v-model="isTextColorPresent" />
|
||||
</Tooltip>
|
||||
<div class="style-control suboption">
|
||||
<label
|
||||
for="textAuto"
|
||||
class="label"
|
||||
:class="{ faint: !isTextAutoPresent }"
|
||||
<option
|
||||
key="dark"
|
||||
value="dark"
|
||||
>
|
||||
{{ $t('settings.style.themes3.editor.text_auto.label') }}
|
||||
</label>
|
||||
<Select
|
||||
id="textAuto"
|
||||
v-model="editedTextAuto"
|
||||
:disabled="!isTextAutoPresent"
|
||||
{{ $t('settings.style.themes3.palette.dark') }}
|
||||
</option>
|
||||
<option
|
||||
key="light"
|
||||
value="light"
|
||||
>
|
||||
<option value="no-preserve">
|
||||
{{ $t('settings.style.themes3.editor.text_auto.no-preserve') }}
|
||||
</option>
|
||||
<option value="no-auto">
|
||||
{{ $t('settings.style.themes3.editor.text_auto.no-auto') }}
|
||||
</option>
|
||||
<option value="preserve">
|
||||
{{ $t('settings.style.themes3.editor.text_auto.preserve') }}
|
||||
</option>
|
||||
</Select>
|
||||
</div>
|
||||
<Tooltip
|
||||
v-if="componentHas('Text')"
|
||||
:text="$t('settings.style.themes3.editor.include_in_rule')"
|
||||
>
|
||||
<Checkbox v-model="isTextAutoPresent" />
|
||||
</Tooltip>
|
||||
<div>
|
||||
<ContrastRatio :contrast="getContrast(editedBackgroundColor, editedTextColor)" />
|
||||
</div>
|
||||
<div>
|
||||
<!-- spacer for missing checkbox -->
|
||||
</div>
|
||||
<ColorInput
|
||||
v-if="componentHas('Link')"
|
||||
v-model="editedLinkColor"
|
||||
:label="$t('settings.style.themes3.editor.link_color')"
|
||||
:disabled="!isLinkColorPresent"
|
||||
/>
|
||||
<Tooltip
|
||||
v-if="componentHas('Link')"
|
||||
:text="$t('settings.style.themes3.editor.include_in_rule')"
|
||||
>
|
||||
<Checkbox v-model="isLinkColorPresent" />
|
||||
</Tooltip>
|
||||
<ColorInput
|
||||
v-if="componentHas('Icon')"
|
||||
v-model="editedIconColor"
|
||||
:label="$t('settings.style.themes3.editor.icon_color')"
|
||||
:disabled="!isIconColorPresent"
|
||||
/>
|
||||
<Tooltip
|
||||
v-if="componentHas('Icon')"
|
||||
:text="$t('settings.style.themes3.editor.include_in_rule')"
|
||||
>
|
||||
<Checkbox v-model="isIconColorPresent" />
|
||||
</Tooltip>
|
||||
{{ $t('settings.style.themes3.palette.light') }}
|
||||
</option>
|
||||
</Select>
|
||||
</div>
|
||||
<div
|
||||
key="shadow"
|
||||
class="editor-tab shadow-tab"
|
||||
:label="$t('settings.style.themes3.editor.shadows_tab')"
|
||||
>
|
||||
<Checkbox
|
||||
v-model="isShadowPresent"
|
||||
class="style-control"
|
||||
>
|
||||
{{ $t('settings.style.themes3.editor.include_in_rule') }}
|
||||
</checkbox>
|
||||
<ShadowControl
|
||||
v-model="editedShadow"
|
||||
:disabled="!isShadowPresent"
|
||||
:no-preview="true"
|
||||
:separate-inset="shadowSelected === 'avatar' || shadowSelected === 'avatarStatus'"
|
||||
@subShadowSelected="onSubShadow"
|
||||
/>
|
||||
</div>
|
||||
</tab-switcher>
|
||||
</div>
|
||||
<div class="setting-item palette-editor">
|
||||
<div class="label">
|
||||
<label for="palette-selector">
|
||||
{{ $t('settings.style.themes3.palette.label') }}
|
||||
{{ ' ' }}
|
||||
</label>
|
||||
<Select
|
||||
id="palette-selector"
|
||||
v-model="editedPalette"
|
||||
>
|
||||
<option
|
||||
key="dark"
|
||||
value="dark"
|
||||
>
|
||||
{{ $t('settings.style.themes3.palette.dark') }}
|
||||
</option>
|
||||
<option
|
||||
key="light"
|
||||
value="light"
|
||||
>
|
||||
{{ $t('settings.style.themes3.palette.light') }}
|
||||
</option>
|
||||
</Select>
|
||||
<PaletteEditor v-model="palette" />
|
||||
</div>
|
||||
<PaletteEditor v-model="palette" />
|
||||
</div>
|
||||
</tab-switcher>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import ColorInput from 'src/components/color_input/color_input.vue'
|
||||
import OpacityInput from 'src/components/opacity_input/opacity_input.vue'
|
||||
import Select from 'src/components/select/select.vue'
|
||||
import SelectMotion from 'src/components/select/select_motion.vue'
|
||||
import Checkbox from 'src/components/checkbox/checkbox.vue'
|
||||
import Popover from 'src/components/popover/popover.vue'
|
||||
import ComponentPreview from 'src/components/component_preview/component_preview.vue'
|
||||
|
@ -21,16 +22,22 @@ library.add(
|
|||
faPlus
|
||||
)
|
||||
|
||||
const toModel = (object = {}) => ({
|
||||
x: 0,
|
||||
y: 0,
|
||||
blur: 0,
|
||||
spread: 0,
|
||||
inset: false,
|
||||
color: '#000000',
|
||||
alpha: 1,
|
||||
...object
|
||||
})
|
||||
const toModel = (input) => {
|
||||
if (typeof input === 'object') {
|
||||
return {
|
||||
x: 0,
|
||||
y: 0,
|
||||
blur: 0,
|
||||
spread: 0,
|
||||
inset: false,
|
||||
color: '#000000',
|
||||
alpha: 1,
|
||||
...input
|
||||
}
|
||||
} else if (typeof input === 'string') {
|
||||
return input
|
||||
}
|
||||
}
|
||||
|
||||
export default {
|
||||
props: [
|
||||
|
@ -48,20 +55,34 @@ export default {
|
|||
ColorInput,
|
||||
OpacityInput,
|
||||
Select,
|
||||
SelectMotion,
|
||||
Checkbox,
|
||||
Popover,
|
||||
ComponentPreview
|
||||
},
|
||||
beforeUpdate () {
|
||||
this.cValue = (this.modelValue ?? this.fallback ?? []).map(toModel)
|
||||
},
|
||||
computed: {
|
||||
selected () {
|
||||
const selected = this.cValue[this.selectedId]
|
||||
if (selected) {
|
||||
return { ...selected }
|
||||
selectedType: {
|
||||
get () {
|
||||
return typeof this.selected
|
||||
},
|
||||
set (newType) {
|
||||
this.selected = toModel(newType === 'object' ? {} : '')
|
||||
}
|
||||
},
|
||||
selected: {
|
||||
get () {
|
||||
const selected = this.cValue[this.selectedId]
|
||||
if (selected && typeof selected === 'object') {
|
||||
return { ...selected }
|
||||
} else if (typeof selected === 'string') {
|
||||
return selected
|
||||
}
|
||||
return null
|
||||
},
|
||||
set (value) {
|
||||
this.cValue[this.selectedId] = toModel(value)
|
||||
this.$emit('update:modelValue', this.cValue)
|
||||
}
|
||||
return null
|
||||
},
|
||||
present () {
|
||||
return this.selected != null && !this.usingFallback
|
||||
|
@ -72,61 +93,55 @@ export default {
|
|||
currentFallback () {
|
||||
return this.fallback?.[this.selectedId]
|
||||
},
|
||||
moveUpValid () {
|
||||
return this.selectedId > 0
|
||||
},
|
||||
moveDnValid () {
|
||||
return this.selectedId < this.cValue.length - 1
|
||||
},
|
||||
usingFallback () {
|
||||
return this.modelValue == null
|
||||
},
|
||||
style () {
|
||||
if (this.separateInset) {
|
||||
return {
|
||||
filter: getCssShadowFilter(this.cValue),
|
||||
boxShadow: getCssShadow(this.cValue, true)
|
||||
try {
|
||||
if (this.separateInset) {
|
||||
return {
|
||||
filter: getCssShadowFilter(this.cValue),
|
||||
boxShadow: getCssShadow(this.cValue, true)
|
||||
}
|
||||
}
|
||||
return {
|
||||
boxShadow: getCssShadow(this.cValue)
|
||||
}
|
||||
} catch (e) {
|
||||
return {
|
||||
border: '1px solid red'
|
||||
}
|
||||
}
|
||||
return {
|
||||
boxShadow: getCssShadow(this.cValue)
|
||||
}
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
modelValue (value) {
|
||||
if (!value) this.cValue = (this.modelValue ?? this.fallback ?? []).map(toModel)
|
||||
},
|
||||
selected (value) {
|
||||
this.$emit('subShadowSelected', this.selectedId)
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
getNewSubshadow () {
|
||||
return toModel(this.selected)
|
||||
},
|
||||
onSelectChange (id) {
|
||||
this.selectedId = id
|
||||
},
|
||||
getSubshadowLabel (shadow, index) {
|
||||
if (typeof shadow === 'object') {
|
||||
return shadow?.name ?? this.$t('settings.style.shadows.shadow_id', { value: index })
|
||||
} else if (typeof shadow === 'string') {
|
||||
return shadow || this.$t('settings.style.shadows.empty_expression')
|
||||
}
|
||||
},
|
||||
updateProperty: throttle(function (prop, value) {
|
||||
this.cValue[this.selectedId][prop] = value
|
||||
if (prop === 'inset' && value === false && this.separateInset) {
|
||||
this.cValue[this.selectedId].spread = 0
|
||||
}
|
||||
this.$emit('update:modelValue', this.cValue)
|
||||
}, 100),
|
||||
add () {
|
||||
this.cValue.push(toModel(this.selected))
|
||||
this.selectedId = Math.max(this.cValue.length - 1, 0)
|
||||
this.$emit('update:modelValue', this.cValue)
|
||||
},
|
||||
del () {
|
||||
this.cValue.splice(this.selectedId, 1)
|
||||
this.selectedId = this.cValue.length === 0 ? undefined : Math.max(this.selectedId - 1, 0)
|
||||
this.$emit('update:modelValue', this.cValue)
|
||||
},
|
||||
moveUp () {
|
||||
const movable = this.cValue.splice(this.selectedId, 1)[0]
|
||||
this.cValue.splice(this.selectedId - 1, 0, movable)
|
||||
this.selectedId -= 1
|
||||
this.$emit('update:modelValue', this.cValue)
|
||||
},
|
||||
moveDn () {
|
||||
const movable = this.cValue.splice(this.selectedId, 1)[0]
|
||||
this.cValue.splice(this.selectedId + 1, 0, movable)
|
||||
this.selectedId += 1
|
||||
this.$emit('update:modelValue', this.cValue)
|
||||
}
|
||||
}, 100)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
justify-content: stretch;
|
||||
grid-gap: 0.25em;
|
||||
margin-bottom: 1em;
|
||||
width: 100%;
|
||||
|
||||
.shadow-switcher {
|
||||
order: 1;
|
||||
|
@ -16,19 +17,6 @@
|
|||
.shadow-list {
|
||||
flex: 1 0 auto;
|
||||
}
|
||||
|
||||
.arrange-buttons {
|
||||
flex: 0 0 auto;
|
||||
display: grid;
|
||||
grid-auto-columns: 1fr;
|
||||
grid-auto-flow: column;
|
||||
margin-top: 0.25em;
|
||||
|
||||
.button-default {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.shadow-tweak {
|
||||
|
@ -37,6 +25,9 @@
|
|||
min-width: 10em;
|
||||
margin-left: 0.125em;
|
||||
margin-right: 0.125em;
|
||||
display: grid;
|
||||
grid-template-rows: auto 1fr;
|
||||
grid-gap: 0.25em;
|
||||
|
||||
/* hack */
|
||||
.input-boolean {
|
||||
|
@ -52,6 +43,11 @@
|
|||
flex: 1 0 5em;
|
||||
}
|
||||
|
||||
.shadow-expression {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.id-control {
|
||||
align-items: stretch;
|
||||
|
||||
|
@ -100,6 +96,5 @@
|
|||
}
|
||||
|
||||
.inset-tooltip {
|
||||
padding: 0.5em;
|
||||
max-width: 30em;
|
||||
}
|
||||
|
|
|
@ -8,7 +8,6 @@
|
|||
class="shadow-preview"
|
||||
:shadow-control="true"
|
||||
:shadow="selected"
|
||||
:preview-style="style"
|
||||
:disabled="disabled || !present"
|
||||
@update:shadow="({ axis, value }) => updateProperty(axis, value)"
|
||||
/>
|
||||
|
@ -18,7 +17,7 @@
|
|||
v-model="selectedId"
|
||||
class="shadow-list"
|
||||
size="10"
|
||||
:disabled="shadowsAreNull"
|
||||
:disabled="disabled || shadowsAreNull"
|
||||
>
|
||||
<option
|
||||
v-for="(shadow, index) in cValue"
|
||||
|
@ -26,227 +25,208 @@
|
|||
:value="index"
|
||||
:class="{ '-active': index === Number(selectedId) }"
|
||||
>
|
||||
{{ shadow?.name ?? $t('settings.style.shadows.shadow_id', { value: index }) }}
|
||||
{{ getSubshadowLabel(shadow, index) }}
|
||||
</option>
|
||||
</Select>
|
||||
<div
|
||||
class="id-control btn-group arrange-buttons"
|
||||
>
|
||||
<button
|
||||
class="btn button-default"
|
||||
:disabled="disabled || shadowsAreNull"
|
||||
@click="add"
|
||||
>
|
||||
<FAIcon
|
||||
fixed-width
|
||||
icon="plus"
|
||||
/>
|
||||
</button>
|
||||
<button
|
||||
class="btn button-default"
|
||||
:disabled="disabled || !moveUpValid"
|
||||
:class="{ disabled: disabled || !moveUpValid }"
|
||||
@click="moveUp"
|
||||
>
|
||||
<FAIcon
|
||||
fixed-width
|
||||
icon="chevron-up"
|
||||
/>
|
||||
</button>
|
||||
<button
|
||||
class="btn button-default"
|
||||
:disabled="disabled || !moveDnValid"
|
||||
:class="{ disabled: disabled || !moveDnValid }"
|
||||
@click="moveDn"
|
||||
>
|
||||
<FAIcon
|
||||
fixed-width
|
||||
icon="chevron-down"
|
||||
/>
|
||||
</button>
|
||||
<button
|
||||
class="btn button-default"
|
||||
:disabled="disabled || !present"
|
||||
:class="{ disabled: disabled || !present }"
|
||||
@click="del"
|
||||
>
|
||||
<FAIcon
|
||||
fixed-width
|
||||
icon="times"
|
||||
/>
|
||||
</button>
|
||||
</div>
|
||||
<SelectMotion
|
||||
v-model="cValue"
|
||||
:selected-id="selectedId"
|
||||
:get-add-value="getNewSubshadow"
|
||||
:disabled="disabled"
|
||||
@update:selectedId="onSelectChange"
|
||||
/>
|
||||
</div>
|
||||
<div class="shadow-tweak">
|
||||
<div
|
||||
:class="{ disabled: disabled || !present }"
|
||||
class="name-control style-control"
|
||||
<Select
|
||||
v-model="selectedType"
|
||||
:disabled="disabled || !present"
|
||||
>
|
||||
<label
|
||||
for="name"
|
||||
class="label"
|
||||
:class="{ faint: disabled || !present }"
|
||||
>
|
||||
{{ $t('settings.style.shadows.name') }}
|
||||
</label>
|
||||
<input
|
||||
id="name"
|
||||
:value="selected?.name"
|
||||
:disabled="disabled || !present"
|
||||
<option value="object">
|
||||
{{ $t('settings.style.shadows.raw') }}
|
||||
</option>
|
||||
<option value="string">
|
||||
{{ $t('settings.style.shadows.expression') }}
|
||||
</option>
|
||||
</Select>
|
||||
<template v-if="selectedType === 'string'">
|
||||
<textarea
|
||||
v-model="selected"
|
||||
class="input shadow-expression"
|
||||
:disabled="disabled || shadowsAreNull"
|
||||
:class="{disabled: disabled || shadowsAreNull}"
|
||||
/>
|
||||
</template>
|
||||
<template v-else-if="selectedType === 'object'">
|
||||
<div
|
||||
:class="{ disabled: disabled || !present }"
|
||||
name="name"
|
||||
class="input input-string"
|
||||
@input="e => updateProperty('name', e.target.value)"
|
||||
class="name-control style-control"
|
||||
>
|
||||
</div>
|
||||
<div
|
||||
:disabled="disabled || !present"
|
||||
class="inset-control style-control"
|
||||
>
|
||||
<Checkbox
|
||||
id="inset"
|
||||
:value="selected?.inset"
|
||||
:disabled="disabled || !present"
|
||||
name="inset"
|
||||
class="input-inset input-boolean"
|
||||
@input="e => updateProperty('inset', e.target.checked)"
|
||||
>
|
||||
<template #before>
|
||||
{{ $t('settings.style.shadows.inset') }}
|
||||
</template>
|
||||
</Checkbox>
|
||||
</div>
|
||||
<div
|
||||
:disabled="disabled || !present"
|
||||
:class="{ disabled: disabled || !present }"
|
||||
class="blur-control style-control"
|
||||
>
|
||||
<label
|
||||
for="blur"
|
||||
class="label"
|
||||
:class="{ faint: disabled || !present }"
|
||||
>
|
||||
{{ $t('settings.style.shadows.blur') }}
|
||||
</label>
|
||||
<input
|
||||
id="blur"
|
||||
:value="selected?.blur"
|
||||
:disabled="disabled || !present"
|
||||
:class="{ disabled: disabled || !present }"
|
||||
name="blur"
|
||||
class="input input-range"
|
||||
type="range"
|
||||
max="20"
|
||||
min="0"
|
||||
@input="e => updateProperty('blur', e.target.value)"
|
||||
>
|
||||
<input
|
||||
:value="selected?.blur"
|
||||
class="input input-number -small"
|
||||
:disabled="disabled || !present"
|
||||
:class="{ disabled: disabled || !present }"
|
||||
type="number"
|
||||
min="0"
|
||||
@input="e => updateProperty('blur', e.target.value)"
|
||||
>
|
||||
</div>
|
||||
<div
|
||||
class="spread-control style-control"
|
||||
:class="{ disabled: disabled || !present || (separateInset && !selected?.inset) }"
|
||||
>
|
||||
<label
|
||||
for="spread"
|
||||
class="label"
|
||||
:class="{ faint: disabled || !present || (separateInset && !selected?.inset) }"
|
||||
>
|
||||
{{ $t('settings.style.shadows.spread') }}
|
||||
</label>
|
||||
<input
|
||||
id="spread"
|
||||
:value="selected?.spread"
|
||||
:disabled="disabled || !present || (separateInset && !selected?.inset)"
|
||||
:class="{ disabled: disabled || !present || (separateInset && !selected?.inset) }"
|
||||
name="spread"
|
||||
class="input input-range"
|
||||
type="range"
|
||||
max="20"
|
||||
min="-20"
|
||||
@input="e => updateProperty('spread', e.target.value)"
|
||||
>
|
||||
<input
|
||||
:value="selected?.spread"
|
||||
class="input input-number -small"
|
||||
:class="{ disabled: disabled || !present || (separateInset && !selected?.inset) }"
|
||||
:disabled="{ disabled: disabled || !present || (separateInset && !selected?.inset) }"
|
||||
type="number"
|
||||
@input="e => updateProperty('spread', e.target.value)"
|
||||
>
|
||||
</div>
|
||||
<ColorInput
|
||||
:model-value="selected?.color"
|
||||
:disabled="disabled || !present"
|
||||
:label="$t('settings.style.common.color')"
|
||||
:fallback="currentFallback?.color"
|
||||
:show-optional-tickbox="false"
|
||||
name="shadow"
|
||||
@update:modelValue="e => updateProperty('color', e)"
|
||||
/>
|
||||
<OpacityInput
|
||||
:model-value="selected?.alpha"
|
||||
:disabled="disabled || !present"
|
||||
@update:modelValue="e => updateProperty('alpha', e)"
|
||||
/>
|
||||
<i18n-t
|
||||
scope="global"
|
||||
keypath="settings.style.shadows.hintV3"
|
||||
:class="{ faint: disabled || !present }"
|
||||
tag="p"
|
||||
>
|
||||
<code>--variable,mod</code>
|
||||
</i18n-t>
|
||||
<Popover
|
||||
v-if="separateInset"
|
||||
trigger="hover"
|
||||
>
|
||||
<template #trigger>
|
||||
<div
|
||||
class="inset-alert alert warning"
|
||||
<label
|
||||
for="name"
|
||||
class="label"
|
||||
:class="{ faint: disabled || !present }"
|
||||
>
|
||||
<FAIcon icon="exclamation-triangle" />
|
||||
|
||||
{{ $t('settings.style.shadows.filter_hint.avatar_inset_short') }}
|
||||
</div>
|
||||
</template>
|
||||
<template #content>
|
||||
<div class="inset-tooltip">
|
||||
<i18n-t
|
||||
scope="global"
|
||||
keypath="settings.style.shadows.filter_hint.always_drop_shadow"
|
||||
tag="p"
|
||||
{{ $t('settings.style.shadows.name') }}
|
||||
</label>
|
||||
<input
|
||||
id="name"
|
||||
:value="selected?.name"
|
||||
:disabled="disabled || !present"
|
||||
:class="{ disabled: disabled || !present }"
|
||||
name="name"
|
||||
class="input input-string"
|
||||
@input="e => updateProperty('name', e.target.value)"
|
||||
>
|
||||
</div>
|
||||
<div
|
||||
:disabled="disabled || !present"
|
||||
class="inset-control style-control"
|
||||
>
|
||||
<Checkbox
|
||||
id="inset"
|
||||
:value="selected?.inset"
|
||||
:disabled="disabled || !present"
|
||||
name="inset"
|
||||
class="input-inset input-boolean"
|
||||
@input="e => updateProperty('inset', e.target.checked)"
|
||||
>
|
||||
<template #before>
|
||||
{{ $t('settings.style.shadows.inset') }}
|
||||
</template>
|
||||
</Checkbox>
|
||||
</div>
|
||||
<div
|
||||
:disabled="disabled || !present"
|
||||
:class="{ disabled: disabled || !present }"
|
||||
class="blur-control style-control"
|
||||
>
|
||||
<label
|
||||
for="blur"
|
||||
class="label"
|
||||
:class="{ faint: disabled || !present }"
|
||||
>
|
||||
{{ $t('settings.style.shadows.blur') }}
|
||||
</label>
|
||||
<input
|
||||
id="blur"
|
||||
:value="selected?.blur"
|
||||
:disabled="disabled || !present"
|
||||
:class="{ disabled: disabled || !present }"
|
||||
name="blur"
|
||||
class="input input-range"
|
||||
type="range"
|
||||
max="20"
|
||||
min="0"
|
||||
@input="e => updateProperty('blur', e.target.value)"
|
||||
>
|
||||
<input
|
||||
:value="selected?.blur"
|
||||
class="input input-number -small"
|
||||
:disabled="disabled || !present"
|
||||
:class="{ disabled: disabled || !present }"
|
||||
type="number"
|
||||
min="0"
|
||||
@input="e => updateProperty('blur', e.target.value)"
|
||||
>
|
||||
</div>
|
||||
<div
|
||||
class="spread-control style-control"
|
||||
:class="{ disabled: disabled || !present || (separateInset && !selected?.inset) }"
|
||||
>
|
||||
<label
|
||||
for="spread"
|
||||
class="label"
|
||||
:class="{ faint: disabled || !present || (separateInset && !selected?.inset) }"
|
||||
>
|
||||
{{ $t('settings.style.shadows.spread') }}
|
||||
</label>
|
||||
<input
|
||||
id="spread"
|
||||
:value="selected?.spread"
|
||||
:disabled="disabled || !present || (separateInset && !selected?.inset)"
|
||||
:class="{ disabled: disabled || !present || (separateInset && !selected?.inset) }"
|
||||
name="spread"
|
||||
class="input input-range"
|
||||
type="range"
|
||||
max="20"
|
||||
min="-20"
|
||||
@input="e => updateProperty('spread', e.target.value)"
|
||||
>
|
||||
<input
|
||||
:value="selected?.spread"
|
||||
class="input input-number -small"
|
||||
:class="{ disabled: disabled || !present || (separateInset && !selected?.inset) }"
|
||||
:disabled="{ disabled: disabled || !present || (separateInset && !selected?.inset) }"
|
||||
type="number"
|
||||
@input="e => updateProperty('spread', e.target.value)"
|
||||
>
|
||||
</div>
|
||||
<ColorInput
|
||||
:model-value="selected?.color"
|
||||
:disabled="disabled || !present"
|
||||
:label="$t('settings.style.common.color')"
|
||||
:fallback="currentFallback?.color"
|
||||
:show-optional-tickbox="false"
|
||||
name="shadow"
|
||||
@update:modelValue="e => updateProperty('color', e)"
|
||||
/>
|
||||
<OpacityInput
|
||||
:model-value="selected?.alpha"
|
||||
:disabled="disabled || !present"
|
||||
@update:modelValue="e => updateProperty('alpha', e)"
|
||||
/>
|
||||
<i18n-t
|
||||
scope="global"
|
||||
keypath="settings.style.shadows.hintV3"
|
||||
:class="{ faint: disabled || !present }"
|
||||
tag="p"
|
||||
>
|
||||
<code>--variable,mod</code>
|
||||
</i18n-t>
|
||||
<Popover
|
||||
v-if="separateInset"
|
||||
trigger="hover"
|
||||
>
|
||||
<template #trigger>
|
||||
<div
|
||||
class="inset-alert alert warning"
|
||||
>
|
||||
<code>filter: drop-shadow()</code>
|
||||
</i18n-t>
|
||||
<p>{{ $t('settings.style.shadows.filter_hint.avatar_inset') }}</p>
|
||||
<i18n-t
|
||||
scope="global"
|
||||
keypath="settings.style.shadows.filter_hint.drop_shadow_syntax"
|
||||
tag="p"
|
||||
>
|
||||
<code>drop-shadow</code>
|
||||
<code>spread-radius</code>
|
||||
<code>inset</code>
|
||||
</i18n-t>
|
||||
<i18n-t
|
||||
scope="global"
|
||||
keypath="settings.style.shadows.filter_hint.inset_classic"
|
||||
tag="p"
|
||||
>
|
||||
<code>box-shadow</code>
|
||||
</i18n-t>
|
||||
<p>{{ $t('settings.style.shadows.filter_hint.spread_zero') }}</p>
|
||||
</div>
|
||||
</template>
|
||||
</Popover>
|
||||
<FAIcon icon="exclamation-triangle" />
|
||||
|
||||
{{ $t('settings.style.shadows.filter_hint.avatar_inset_short') }}
|
||||
</div>
|
||||
</template>
|
||||
<template #content>
|
||||
<div class="inset-tooltip tooltip">
|
||||
<i18n-t
|
||||
scope="global"
|
||||
keypath="settings.style.shadows.filter_hint.always_drop_shadow"
|
||||
tag="p"
|
||||
>
|
||||
<code>filter: drop-shadow()</code>
|
||||
</i18n-t>
|
||||
<p>{{ $t('settings.style.shadows.filter_hint.avatar_inset') }}</p>
|
||||
<i18n-t
|
||||
scope="global"
|
||||
keypath="settings.style.shadows.filter_hint.drop_shadow_syntax"
|
||||
tag="p"
|
||||
>
|
||||
<code>drop-shadow</code>
|
||||
<code>spread-radius</code>
|
||||
<code>inset</code>
|
||||
</i18n-t>
|
||||
<i18n-t
|
||||
scope="global"
|
||||
keypath="settings.style.shadows.filter_hint.inset_classic"
|
||||
tag="p"
|
||||
>
|
||||
<code>box-shadow</code>
|
||||
</i18n-t>
|
||||
<p>{{ $t('settings.style.shadows.filter_hint.spread_zero') }}</p>
|
||||
</div>
|
||||
</template>
|
||||
</Popover>
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
|
|
@ -773,7 +773,8 @@
|
|||
"cOrange": "Orange color",
|
||||
"extra1": "Extra 1",
|
||||
"extra2": "Extra 2",
|
||||
"extra3": "Extra 3"
|
||||
"extra3": "Extra 3",
|
||||
"v2_unsupported": "Older v2 themes don't support palettes. Switch to v3 theme to make use of palettes",
|
||||
},
|
||||
"editor": {
|
||||
"title": "Style",
|
||||
|
@ -964,6 +965,9 @@
|
|||
"blur": "Blur",
|
||||
"spread": "Spread",
|
||||
"inset": "Inset",
|
||||
"raw": "Plain shadow",
|
||||
"expression": "Expression (advanced)",
|
||||
"empty_expression": "Empty expression",
|
||||
"hintV3": "For shadows you can also use the {0} notation to use other color slot.",
|
||||
"filter_hint": {
|
||||
"always_drop_shadow": "Warning, this shadow always uses {0} when browser supports it.",
|
||||
|
|
|
@ -5,6 +5,7 @@ import { convertTheme2To3 } from 'src/services/theme_data/theme2_to_theme3.js'
|
|||
const defaultState = {
|
||||
localFonts: null,
|
||||
themeApplied: false,
|
||||
themeVersion: 'v3',
|
||||
temporaryChangesTimeoutId: null, // used for temporary options that revert after a timeout
|
||||
temporaryChangesConfirm: () => {}, // used for applying temporary options
|
||||
temporaryChangesRevert: () => {}, // used for reverting temporary options
|
||||
|
@ -219,7 +220,8 @@ const interfaceMod = {
|
|||
return value
|
||||
} catch (e) {
|
||||
console.error('Could not fetch palettes index', e)
|
||||
return {}
|
||||
commit('setInstanceOption', { name: 'palettesIndex', value: { _error: e } })
|
||||
return Promise.resolve({})
|
||||
}
|
||||
},
|
||||
setPalette ({ dispatch, commit }, value) {
|
||||
|
@ -245,6 +247,7 @@ const interfaceMod = {
|
|||
return value
|
||||
} catch (e) {
|
||||
console.error('Could not fetch styles index', e)
|
||||
commit('setInstanceOption', { name: 'stylesIndex', value: { _error: e } })
|
||||
return Promise.resolve({})
|
||||
}
|
||||
},
|
||||
|
@ -271,6 +274,7 @@ const interfaceMod = {
|
|||
return value
|
||||
} catch (e) {
|
||||
console.error('Could not fetch themes index', e)
|
||||
commit('setInstanceOption', { name: 'themesIndex', value: { _error: e } })
|
||||
return Promise.resolve({})
|
||||
}
|
||||
},
|
||||
|
@ -307,7 +311,7 @@ const interfaceMod = {
|
|||
commit('setOption', { name: 'customThemeSource', value: null })
|
||||
},
|
||||
async applyTheme (
|
||||
{ dispatch, commit, rootState },
|
||||
{ dispatch, commit, rootState, state },
|
||||
{ recompile = true } = {}
|
||||
) {
|
||||
// If we're not not forced to recompile try using
|
||||
|
@ -373,31 +377,37 @@ const interfaceMod = {
|
|||
userThemeV2Snapshot = null
|
||||
|
||||
majorVersionUsed = 'v3'
|
||||
if (!palettesIndex || !stylesIndex) {
|
||||
const result = await Promise.all([
|
||||
dispatch('fetchPalettesIndex'),
|
||||
dispatch('fetchStylesIndex')
|
||||
])
|
||||
|
||||
palettesIndex = result[0]
|
||||
stylesIndex = result[1]
|
||||
}
|
||||
} else if (
|
||||
userThemeV2Name ||
|
||||
(userThemeV2Name ||
|
||||
userThemeV2Snapshot ||
|
||||
userThemeV2Source ||
|
||||
instanceThemeV2Name
|
||||
instanceThemeV2Name)
|
||||
) {
|
||||
majorVersionUsed = 'v2'
|
||||
} else {
|
||||
// if all fails fallback to v3
|
||||
majorVersionUsed = 'v3'
|
||||
}
|
||||
|
||||
if (majorVersionUsed === 'v3') {
|
||||
const result = await Promise.all([
|
||||
dispatch('fetchPalettesIndex'),
|
||||
dispatch('fetchStylesIndex')
|
||||
])
|
||||
|
||||
palettesIndex = result[0]
|
||||
stylesIndex = result[1]
|
||||
} else {
|
||||
// Promise.all just to be uniform with v3
|
||||
const result = await Promise.all([
|
||||
dispatch('fetchThemesIndex')
|
||||
])
|
||||
|
||||
themesIndex = result[0]
|
||||
} else {
|
||||
majorVersionUsed = 'v3'
|
||||
}
|
||||
|
||||
state.themeVersion = majorVersionUsed
|
||||
|
||||
let styleDataUsed = null
|
||||
let styleNameUsed = null
|
||||
let paletteDataUsed = null
|
||||
|
@ -520,7 +530,7 @@ const interfaceMod = {
|
|||
return result
|
||||
})()
|
||||
|
||||
const theme2ruleset = themeDataUsed && convertTheme2To3(normalizeThemeData(themeDataUsed))
|
||||
const theme2ruleset = themeDataUsed && convertTheme2To3(generatePreset(themeDataUsed).source)
|
||||
const hacks = []
|
||||
|
||||
Object.entries(theme3hacks).forEach(([key, value]) => {
|
||||
|
|
|
@ -144,8 +144,7 @@ export const tryLoadCache = () => {
|
|||
}
|
||||
}
|
||||
|
||||
export const applyTheme = async (input, onFinish = (data) => {}, debug) => {
|
||||
console.log('INPUT', input)
|
||||
export const applyTheme = (input, onFinish = (data) => {}, debug) => {
|
||||
const eagerStyles = createStyleSheet(EAGER_STYLE_ID)
|
||||
const lazyStyles = createStyleSheet(LAZY_STYLE_ID)
|
||||
|
||||
|
|
|
@ -44,8 +44,8 @@ const findShadow = (shadows, { dynamicVars, staticVars }) => {
|
|||
if (shadow.startsWith('$')) {
|
||||
targetShadow = process(shadow, shadowFunctions, { findColor, findShadow }, { dynamicVars, staticVars })
|
||||
} else if (shadow.startsWith('--')) {
|
||||
const [variable] = shadow.split(/,/g).map(str => str.trim()) // discarding modifier since it's not supported
|
||||
const variableSlot = variable.substring(2)
|
||||
// modifiers are completely unsupported here
|
||||
const variableSlot = shadow.substring(2)
|
||||
return findShadow(staticVars[variableSlot], { dynamicVars, staticVars })
|
||||
} else {
|
||||
targetShadow = parseShadow(shadow)
|
||||
|
@ -66,6 +66,7 @@ const findColor = (color, { dynamicVars, staticVars }) => {
|
|||
if (typeof color !== 'string' || (!color.startsWith('--') && !color.startsWith('$'))) return color
|
||||
let targetColor = null
|
||||
if (color.startsWith('--')) {
|
||||
// Modifier support is pretty much for v2 themes only
|
||||
const [variable, modifier] = color.split(/,/g).map(str => str.trim())
|
||||
const variableSlot = variable.substring(2)
|
||||
if (variableSlot === 'stack') {
|
||||
|
@ -421,7 +422,7 @@ export const init = ({
|
|||
break
|
||||
}
|
||||
case 'shadow': {
|
||||
const shadow = value.split(/,/g).map(s => s.trim())
|
||||
const shadow = value.split(/,/g).map(s => s.trim()).filter(x => x)
|
||||
dynamicVars[k] = shadow
|
||||
if (combination.component === rootComponentName) {
|
||||
staticVars[k.substring(2)] = shadow
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"pleroma-light": [ "Pleroma Light", "#f2f4f6", "#dbe0e8", "#304055", "#f86f0f", "#d31014", "#0fa00f", "#0095ff", "#ffa500" ],
|
||||
"pleroma-dark": [ "Pleroma Dark", "#121a24", "#182230", "#b9b9ba", "#d8a070", "#d31014", "#0fa00f", "#0095ff", "#ffa500" ],
|
||||
"pleroma-light": [ "Pleroma Light", "#f2f4f6", "#dbe0e8", "#304055", "#f86f0f", "#d31014", "#0fa00f", "#0095ff", "#ffa500" ],
|
||||
"classic-dark": {
|
||||
"name": "Classic Dark",
|
||||
"background": "#161c20",
|
||||
|
@ -12,6 +12,7 @@
|
|||
"cBlue": "#0095ff",
|
||||
"cOrange": "#ffa500"
|
||||
},
|
||||
"bird": [ "Bird", "#f8fafd", "#e6ecf0", "#14171a", "#0084b8", "#e0245e", "#17bf63", "#1b95e0", "#fab81e"],
|
||||
"pleroma-amoled": [ "Pleroma Dark AMOLED", "#000000", "#111111", "#b0b0b1", "#d8a070", "#aa0000", "#0fa00f", "#0095ff", "#d59500"],
|
||||
"tomorrow-night": {
|
||||
"name": "Tomorrow Night",
|
||||
|
@ -26,7 +27,6 @@
|
|||
"_cYellow": "#f0c674",
|
||||
"_cPurple": "#b294bb"
|
||||
},
|
||||
"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" ]
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue