Page Menu
Home
Phorge
Search
Configure Global Search
Log In
Files
F85831035
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Authored By
Unknown
Size
33 KB
Referenced Files
None
Subscribers
None
View Options
diff --git a/src/components/settings_modal/tabs/style_tab/style_tab.js b/src/components/settings_modal/tabs/style_tab/style_tab.js
index cbd25dfc23..f3d44bac5e 100644
--- a/src/components/settings_modal/tabs/style_tab/style_tab.js
+++ b/src/components/settings_modal/tabs/style_tab/style_tab.js
@@ -1,559 +1,575 @@
import { ref, reactive, computed, watch } from 'vue'
import { get, set } from 'lodash'
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 ComponentPreview from 'src/components/component_preview/component_preview.vue'
import StringSetting from '../../helpers/string_setting.vue'
import ShadowControl from 'src/components/shadow_control/shadow_control.vue'
import ColorInput from 'src/components/color_input/color_input.vue'
import PaletteEditor from 'src/components/palette_editor/palette_editor.vue'
import OpacityInput from 'src/components/opacity_input/opacity_input.vue'
import TabSwitcher from 'src/components/tab_switcher/tab_switcher.jsx'
import Tooltip from 'src/components/tooltip/tooltip.vue'
import ContrastRatio from 'src/components/contrast_ratio/contrast_ratio.vue'
import { init } from 'src/services/theme_data/theme_data_3.service.js'
import { getCssRules } from 'src/services/theme_data/css_utils.js'
import { serialize } from 'src/services/theme_data/iss_serializer.js'
import { deserialize } from 'src/services/theme_data/iss_deserializer.js'
import {
// rgb2hex,
hex2rgb,
getContrastRatio
} from 'src/services/color_convert/color_convert.js'
import { library } from '@fortawesome/fontawesome-svg-core'
import { faFloppyDisk, faFolderOpen, faFile } from '@fortawesome/free-solid-svg-icons'
// helper for debugging
// eslint-disable-next-line no-unused-vars
const toValue = (x) => JSON.parse(JSON.stringify(x === undefined ? 'null' : x))
// helper to make states comparable
const normalizeStates = (states) => ['normal', ...(states?.filter(x => x !== 'normal') || [])].join(':')
library.add(
faFile,
faFloppyDisk,
faFolderOpen
)
export default {
components: {
Select,
+ SelectMotion,
Checkbox,
Tooltip,
StringSetting,
ComponentPreview,
TabSwitcher,
ShadowControl,
ColorInput,
PaletteEditor,
OpacityInput,
ContrastRatio
},
setup () {
// ### Meta stuff
const name = ref('')
const author = ref('')
const license = ref('')
const website = ref('')
const metaOut = computed(() => {
return `@meta {
name: ${name.value};
author: ${author.value};
license: ${license.value};
website: ${website.value};
}`
})
// ### Palette stuff
const palettes = reactive({
light: {
bg: '#f2f6f9',
fg: '#d6dfed',
text: '#304055',
underlay: '#5d6086',
accent: '#f55b1b',
cBlue: '#0095ff',
cRed: '#d31014',
cGreen: '#0fa00f',
cOrange: '#ffa500',
border: '#d8e6f9'
},
dark: {
bg: '#121a24',
fg: '#182230',
text: '#b9b9ba',
link: '#d8a070',
accent: '#d8a070',
cRed: '#FF0000',
cBlue: '#0095ff',
cGreen: '#0fa00f',
cOrange: '#ffa500'
}
})
const palettesOut = computed(() => {
return Object.entries(palettes).map(([name, palette]) => {
const entries = Object
.entries(palette)
.map(([slot, data]) => ` ${slot}: ${data};`)
.join('\n')
return `@palette.${name} {\n${entries}\n}`
}).join('\n\n')
})
const editedPalette = ref('dark')
const palette = computed({
get () {
return palettes[editedPalette.value]
},
set (newPalette) {
palettes[editedPalette.value] = newPalette
}
})
+ const getNewPalette = () => ({
+ name: 'new palette',
+ bg: '#121a24',
+ fg: '#182230',
+ text: '#b9b9ba',
+ link: '#d8a070',
+ accent: '#d8a070',
+ cRed: '#FF0000',
+ cBlue: '#0095ff',
+ cGreen: '#0fa00f',
+ cOrange: '#ffa500'
+ })
+
// ### I18n stuff
// The paths in i18n are getting ridicously long, this effectively shortens them
const getI18nPath = (componentName) => `settings.style.themes3.editor.components.${componentName}`
// vue i18n doesn't seem to have (working) mechanic to have a fallback so we have to
// make do ourselves
const fallbackI18n = (translated, fallback) => {
if (translated.startsWith('settings.style.themes3')) {
return fallback
}
return translated
}
const getFriendlyNamePath = (componentName) => getI18nPath(componentName) + '.friendlyName'
const getVariantPath = (componentName, variant) => {
return variant === 'normal'
? 'settings.style.themes3.editor.components.normal.variant'
: `${getI18nPath(componentName)}.variants.${variant}`
}
const getStatePath = (componentName, state) => {
return state === 'normal'
? 'settings.style.themes3.editor.components.normal.state'
: `${getI18nPath(componentName)}.states.${state}`
}
// ### Initialization stuff
// Getting existing components
const componentsContext = require.context('src', true, /\.style.js(on)?$/)
const componentKeysAll = componentsContext.keys()
const componentsMap = new Map(
componentKeysAll
.map(
key => [key, componentsContext(key).default]
).filter(([key, component]) => !component.virtual && !component.notEditable)
)
const componentKeys = [...componentsMap.keys()]
// Initializing selected component and its computed descendants
const selectedComponentKey = ref(componentsMap.keys().next().value)
const selectedComponent = computed(() => componentsMap.get(selectedComponentKey.value))
const selectedComponentName = computed(() => selectedComponent.value.name)
const selectedVariant = ref('normal')
const selectedComponentVariantsAll = computed(() => {
return Object.keys({ normal: null, ...(selectedComponent.value.variants || {}) })
})
const selectedState = reactive(new Set())
const selectedComponentStatesAll = computed(() => {
return Object.keys({ normal: null, ...(selectedComponent.value.states || {}) })
})
const selectedComponentStates = computed(() => {
return selectedComponentStatesAll.value.filter(x => x !== 'normal')
})
const updateSelectedStates = (state, v) => {
if (v) {
selectedState.add(state)
} else {
selectedState.delete(state)
}
}
// ### Preview stuff
const editorHintStyle = computed(() => {
const editorHint = selectedComponent.value.editor
const styles = []
if (editorHint && Object.keys(editorHint).length > 0) {
if (editorHint.aspect != null) {
styles.push(`aspect-ratio: ${editorHint.aspect} !important;`)
}
if (editorHint.border != null) {
styles.push(`border-width: ${editorHint.border}px !important;`)
}
}
return styles.join('; ')
})
// Apart from "hover" we can't really show how component looks like in
// certain states, so we have to fake them.
const simulatePseudoSelectors = css => css
.replace(selectedComponent.value.selector, '.ComponentPreview .preview-block')
.replace(':active', '.preview-active')
.replace(':hover', '.preview-hover')
.replace(':active', '.preview-active')
.replace(':focus', '.preview-focus')
.replace(':focus-within', '.preview-focus-within')
.replace(':disabled', '.preview-disabled')
const previewRules = reactive([])
const previewClass = computed(() => {
const selectors = []
if (!!selectedComponent.value.variants?.normal || selectedVariant.value !== 'normal') {
selectors.push(selectedComponent.value.variants[selectedVariant.value])
}
if (selectedState.size > 0) {
selectedState.forEach(state => {
const original = selectedComponent.value.states[state]
selectors.push(simulatePseudoSelectors(original))
})
}
return selectors.map(x => x.substring(1)).join('')
})
const previewCss = computed(() => {
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
// The native structure of separate rules and the child -> parent
// relation isn't very convenient for editor, we replace the array
// and child -> parent structure with map and parent -> child structure
const editorFriendlyFallbackStructure = computed(() => {
const root = {}
componentKeys.forEach((componentKey) => {
const componentValue = componentsMap.get(componentKey)
const { defaultRules } = componentValue
defaultRules.forEach((rule) => {
const { parent: rParent } = rule
const parent = rParent ?? rule
const hasChildren = !!rParent
const child = hasChildren ? rule : null
const {
component: pComponent,
variant: pVariant = 'normal',
state: pState = [] // no relation to Intel CPUs whatsoever
} = parent
const pPath = `${hasChildren ? pComponent : componentValue.name}.${pVariant}.${normalizeStates(pState)}`
let output = get(root, pPath)
if (!output) {
set(root, pPath, {})
output = get(root, pPath)
}
if (hasChildren) {
output._children = output._children ?? {}
const {
component: cComponent,
variant: cVariant = 'normal',
state: cState = [],
directives
} = child
const cPath = `${cComponent}.${cVariant}.${normalizeStates(cState)}`
set(output._children, cPath, directives)
} else {
output.directives = parent.directives
}
})
})
return root
})
// All rules that are made by editor
const allEditedRules = reactive({})
// Checkging whether component can support some "directives" which
// are actually virtual subcomponents, i.e. Text, Link etc
const componentHas = (subComponent) => {
return !!selectedComponent.value.validInnerComponents?.find(x => x === subComponent)
}
// Path is path for lodash's get and set
const getPath = (component, directive) => {
const pathSuffix = component ? `._children.${component}.normal.normal` : ''
const path = `${selectedComponentName.value}.${selectedVariant.value}.${normalizeStates([...selectedState])}${pathSuffix}.directives.${directive}`
return path
}
// Templates for directives
const isElementPresent = (component, directive, defaultValue = '') => computed({
get () {
return get(allEditedRules, getPath(component, directive)) != null
},
set (value) {
if (value) {
const fallback = get(
editorFriendlyFallbackStructure.value,
getPath(component, directive)
)
set(allEditedRules, getPath(component, directive), fallback ?? defaultValue)
} else {
set(allEditedRules, getPath(component, directive), null)
}
}
})
const getEditedElement = (component, directive) => computed({
get () {
let usedRule
const fallback = editorFriendlyFallbackStructure.value
const real = allEditedRules
const path = getPath(component, directive)
usedRule = get(real, path) // get real
if (!usedRule) {
usedRule = get(fallback, path)
}
return usedRule
},
set (value) {
set(allEditedRules, getPath(component, directive), value)
}
})
// All the editable stuff for the component
const editedBackgroundColor = getEditedElement(null, 'background')
const editedOpacity = getEditedElement(null, 'opacity')
const editedTextColor = getEditedElement('Text', 'textColor')
const editedTextAuto = getEditedElement('Text', 'textAuto')
const editedLinkColor = getEditedElement('Link', 'textColor')
const editedIconColor = getEditedElement('Icon', 'textColor')
const editedShadow = getEditedElement(null, 'shadow')
// Shadow is partially edited outside the ShadowControl
// for better space utilization
const editedSubShadowId = ref(null)
const editedSubShadow = computed(() => {
if (editedShadow.value == null || editedSubShadowId.value == null) return null
return editedShadow.value[editedSubShadowId.value]
})
const updateSubShadow = (axis, value) => {
if (!editedSubShadow.value || editedSubShadowId.value == null) return
const newEditedShadow = [...editedShadow.value]
newEditedShadow[editedSubShadowId.value] = {
...newEditedShadow[editedSubShadowId.value],
[axis]: value
}
editedShadow.value = newEditedShadow
}
const onSubShadow = (id) => {
if (id != null) {
editedSubShadowId.value = id
} else {
editedSubShadow.value = null
}
}
// Whether specific directives present in the edited rule or not
// Somewhat serves double-duty as it creates/removes the directive
// when set
const isBackgroundColorPresent = isElementPresent(null, 'background', '#FFFFFF')
const isOpacityPresent = isElementPresent(null, 'opacity', 1)
const isTextColorPresent = isElementPresent('Text', 'textColor', '#000000')
const isTextAutoPresent = isElementPresent('Text', 'textAuto', '#000000')
const isLinkColorPresent = isElementPresent('Link', 'textColor', '#000080')
const isIconColorPresent = isElementPresent('Icon', 'textColor', '#909090')
const isShadowPresent = isElementPresent(null, 'shadow', [])
const editorFriendlyToOriginal = computed(() => {
const resultRules = []
const convert = (component, data = {}, parent) => {
const variants = Object.entries(data || {})
variants.forEach(([variant, variantData]) => {
const states = Object.entries(variantData)
states.forEach(([jointState, stateData]) => {
const state = jointState.split(/:/g)
const result = {
component,
variant,
state,
directives: stateData.directives || {}
}
if (parent) {
result.parent = {
component: parent
}
}
resultRules.push(result)
// Currently we only support single depth for simplicity's sake
if (!parent) {
Object.entries(stateData._children || {}).forEach(([cName, child]) => convert(cName, child, component))
}
})
})
}
convert(selectedComponentName.value, allEditedRules[selectedComponentName.value])
return resultRules
})
const updatePreview = () => {
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 = () => {
selectedVariant.value = 'normal'
selectedState.clear()
updatePreview()
}
updateSelectedComponent()
watch(
allEditedRules,
updatePreview
)
watch(
palettes,
updatePreview
)
watch(
editedPalette,
updatePreview
)
watch(
selectedComponentName,
updateSelectedComponent
)
// TODO this is VERY primitive right now, need to make it
// support variables, fallbacks etc.
const getContrast = (bg, text) => {
try {
const bgRgb = hex2rgb(bg)
const textRgb = hex2rgb(text)
const ratio = getContrastRatio(bgRgb, textRgb)
return {
// TODO this ideally should be part of <ContractRatio />
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
}
} catch (e) {
console.warn('Failure computing contrast', e)
return { error: e }
}
}
const isShadowTabOpen = ref(false)
const onTabSwitch = (tab) => {
isShadowTabOpen.value = tab === 'shadow'
}
const exportStyle = () => {
console.log('ORIG', toValue(editorFriendlyToOriginal.value))
console.log('SERI', serialize(editorFriendlyToOriginal.value))
const result = [
metaOut.value,
palettesOut.value,
serialize(editorFriendlyToOriginal.value)
].join('\n\n')
console.log('RESULT', result)
console.log('DESERI', deserialize(result))
}
return {
name,
author,
license,
website,
palette,
editedPalette,
+ getNewPalette,
componentKeys,
componentsMap,
selectedComponent,
selectedComponentName,
selectedComponentKey,
selectedComponentVariantsAll,
selectedComponentStates,
selectedVariant,
selectedState,
updateSelectedStates,
editedBackgroundColor,
editedOpacity,
editedTextColor,
editedTextAuto,
editedLinkColor,
editedIconColor,
editedShadow,
editedSubShadow,
onSubShadow,
updateSubShadow,
getContrast,
isBackgroundColorPresent,
isOpacityPresent,
isTextColorPresent,
isTextAutoPresent,
isLinkColorPresent,
isIconColorPresent,
isShadowPresent,
previewCss,
previewClass,
editorHintStyle,
getFriendlyNamePath,
fallbackI18n,
getVariantPath,
getStatePath,
componentHas,
isShadowTabOpen,
onTabSwitch,
exportStyle
}
}
}
diff --git a/src/components/settings_modal/tabs/style_tab/style_tab.scss b/src/components/settings_modal/tabs/style_tab/style_tab.scss
index 0306454fc7..deb087eef1 100644
--- a/src/components/settings_modal/tabs/style_tab/style_tab.scss
+++ b/src/components/settings_modal/tabs/style_tab/style_tab.scss
@@ -1,178 +1,181 @@
.StyleTab {
.style-control {
display: flex;
flex-wrap: wrap;
align-items: baseline;
margin-bottom: 0.5em;
.label {
margin-right: 0.5em;
flex: 1 1 0;
line-height: 2;
min-height: 2em;
}
&.suboption {
margin-left: 1em;
}
.color-input {
flex: 0 0 0;
}
input,
select {
min-width: 3em;
margin: 0;
flex: 0;
&[type="number"] {
min-width: 9em;
&.-small {
min-width: 5em;
}
}
&[type="range"] {
flex: 1;
min-width: 9em;
align-self: center;
margin: 0 0.25em;
}
&[type="checkbox"] + i {
height: 1.1em;
align-self: center;
}
}
}
.setting-item {
padding-bottom: 0;
.btn {
padding: 0 0.5em;
}
&:not(:first-child) {
margin-top: 0.5em;
}
&:not(:last-child) {
margin-bottom: 0.5em;
}
&.heading {
display: grid;
align-items: baseline;
grid-template-columns: 1fr auto auto auto;
grid-gap: 0.5em;
h2 {
flex: 1 0 auto;
}
}
&.metadata {
display: flex;
.setting-item {
flex: 2 0 auto;
}
li {
text-align: right;
}
}
}
.palette-editor {
- > .label:not(.Select) {
- font-weight: bold;
- justify-self: right;
+ display: grid;
+
+ .palette-selector {
+ &-label {
+ font-weight: bold;
+ }
}
}
.component-editor {
display: grid;
grid-template-columns: 6fr 3fr 4fr;
grid-template-rows: auto auto 1fr;
grid-gap: 0.5em;
grid-template-areas:
"component component variant"
"state state state"
"preview settings settings";
.component-selector {
grid-area: component;
align-self: center;
}
.component-selector,
.state-selector,
.variant-selector {
display: grid;
grid-template-columns: 1fr minmax(1fr, 10em);
grid-template-rows: auto;
grid-auto-flow: column;
grid-gap: 0.5em;
align-items: baseline;
> label:not(.Select) {
font-weight: bold;
justify-self: right;
}
}
.state-selector {
grid-area: state;
grid-template-columns: minmax(min-content, 7em) 1fr;
}
.variant-selector {
grid-area: variant;
}
.state-selector-list {
display: grid;
list-style: none;
grid-auto-flow: dense;
grid-template-columns: repeat(5, minmax(min-content, 1fr));
grid-auto-rows: 1fr;
grid-gap: 0.5em;
padding: 0;
margin: 0;
}
.preview-container {
--border: none;
--shadow: none;
--roundness: none;
grid-area: preview;
}
.component-settings {
grid-area: settings;
}
.editor-tab {
display: grid;
grid-template-columns: 1fr 2em;
grid-column-gap: 0.5em;
align-items: center;
grid-auto-rows: min-content;
grid-auto-flow: dense;
border-left: 1px solid var(--border);
border-right: 1px solid var(--border);
border-bottom: 1px solid var(--border);
padding: 0.5em;
}
.shadow-tab {
grid-template-columns: 1fr;
justify-items: center;
}
}
}
diff --git a/src/components/settings_modal/tabs/style_tab/style_tab.vue b/src/components/settings_modal/tabs/style_tab/style_tab.vue
index b73732f8aa..9951849aac 100644
--- a/src/components/settings_modal/tabs/style_tab/style_tab.vue
+++ b/src/components/settings_modal/tabs/style_tab/style_tab.vue
@@ -1,292 +1,297 @@
<script src="./style_tab.js">
</script>
<template>
<div class="StyleTab">
<div class="setting-item heading">
<h2>{{ $t('settings.style.themes3.editor.title') }}</h2>
<button
class="btn button-default"
@click="clearTheme"
>
<FAIcon icon="file" />
{{ $t('settings.style.themes3.editor.new_style') }}
</button>
<button
class="btn button-default"
@click="importStyle"
>
<FAIcon icon="folder-open" />
{{ $t('settings.style.themes3.editor.load_style') }}
</button>
<button
class="btn button-default"
@click="exportStyle"
>
<FAIcon icon="floppy-disk" />
{{ $t('settings.style.themes3.editor.save_style') }}
</button>
</div>
<div class="setting-item metadata">
<ul class="setting-list">
<li>
<StringSetting v-model="name">
{{ $t('settings.style.themes3.editor.style_name') }}
</StringSetting>
</li>
<li>
<StringSetting v-model="author">
{{ $t('settings.style.themes3.editor.style_author') }}
</StringSetting>
</li>
<li>
<StringSetting v-model="license">
{{ $t('settings.style.themes3.editor.style_license') }}
</StringSetting>
</li>
<li>
<StringSetting v-model="website">
{{ $t('settings.style.themes3.editor.style_website') }}
</StringSetting>
</li>
</ul>
</div>
<tab-switcher>
<div
key="component"
class="setting-item component-editor"
:label="$t('settings.style.themes3.editor.component_tab')"
>
<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>
<div
v-if="selectedComponentVariantsAll.length > 1"
class="variant-selector"
>
<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
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
key="palette"
:label="$t('settings.style.themes3.editor.palette_tab')"
class="setting-item palette-editor"
>
- <div class="label">
- <label for="palette-selector">
+ <label
+ class="palette-selector-label"
+ for="palette-selector"
+ >
{{ $t('settings.style.themes3.palette.label') }}
{{ ' ' }}
</label>
<Select
id="palette-selector"
v-model="editedPalette"
+ class="palette-list"
+ size="10"
>
<option
- key="dark"
- value="dark"
- >
- {{ $t('settings.style.themes3.palette.dark') }}
- </option>
- <option
- key="light"
- value="light"
+ v-for="(p, index) in palettes"
+ :key="p"
+ :value="index"
>
- {{ $t('settings.style.themes3.palette.light') }}
+ {{ p }}
</option>
</Select>
- </div>
+ <SelectMotion
+ class="palette-list-movement"
+ v-model="palettes"
+ :selected-id="editedPalette"
+ :get-add-value="getNewPalette"
+ @update:selectedId="e => editecPalette = e"
+ />
<PaletteEditor v-model="palette" />
</div>
</tab-switcher>
</div>
</template>
<style src="./style_tab.scss" lang="scss"></style>
File Metadata
Details
Attached
Mime Type
text/x-diff
Expires
Sun, Nov 1, 9:01 AM (5 h, 45 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
1802881
Default Alt Text
(33 KB)
Attached To
Mode
rPUFE pleroma-fe-upstream
Attached
Detach File
Event Timeline