Merge branch 'themes3-grand-finale-maybe' into shigusegubu-themes3
This commit is contained in:
commit
eac63e0c57
17 changed files with 477 additions and 254 deletions
|
|
@ -130,7 +130,7 @@ export default {
|
||||||
return this.modelValue === 'transparent'
|
return this.modelValue === 'transparent'
|
||||||
},
|
},
|
||||||
computedColor () {
|
computedColor () {
|
||||||
return this.modelValue && this.modelValue.startsWith('--')
|
return this.modelValue && (this.modelValue.startsWith('--') || this.modelValue.startsWith('$'))
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
|
|
||||||
|
|
@ -39,7 +39,7 @@
|
||||||
:class="previewClass"
|
:class="previewClass"
|
||||||
:style="previewStyle"
|
:style="previewStyle"
|
||||||
>
|
>
|
||||||
TEST
|
{{ $t('settings.style.themes3.editor.test_string') }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<input
|
<input
|
||||||
|
|
|
||||||
|
|
@ -28,7 +28,7 @@
|
||||||
<script setup>
|
<script setup>
|
||||||
import ColorInput from 'src/components/color_input/color_input.vue'
|
import ColorInput from 'src/components/color_input/color_input.vue'
|
||||||
import {
|
import {
|
||||||
// newImporter,
|
newImporter,
|
||||||
newExporter
|
newExporter
|
||||||
} from 'src/services/export_import/export_import.js'
|
} from 'src/services/export_import/export_import.js'
|
||||||
|
|
||||||
|
|
@ -46,23 +46,23 @@ library.add(
|
||||||
const props = defineProps(['modelValue'])
|
const props = defineProps(['modelValue'])
|
||||||
const emit = defineEmits(['update:modelValue'])
|
const emit = defineEmits(['update:modelValue'])
|
||||||
const paletteExporter = newExporter({
|
const paletteExporter = newExporter({
|
||||||
filename: 'pleroma.palette.json',
|
filename: 'pleroma_palette',
|
||||||
|
extension: 'json',
|
||||||
getExportedObject: () => props.modelValue
|
getExportedObject: () => props.modelValue
|
||||||
})
|
})
|
||||||
/*
|
const paletteImporter = newImporter({
|
||||||
const themeImporter = newImporter({
|
accept: '.json',
|
||||||
validator: importValidator,
|
onImport (parsed, filename) {
|
||||||
onImport,
|
emit('update:modelValue', parsed)
|
||||||
onImportFailure,
|
}
|
||||||
})
|
})
|
||||||
*/
|
|
||||||
|
|
||||||
const exportPalette = () => {
|
const exportPalette = () => {
|
||||||
paletteExporter.exportData()
|
paletteExporter.exportData()
|
||||||
}
|
}
|
||||||
|
|
||||||
const importPalette = () => {
|
const importPalette = () => {
|
||||||
// TODO
|
paletteImporter.importData()
|
||||||
}
|
}
|
||||||
|
|
||||||
const paletteKeys = [
|
const paletteKeys = [
|
||||||
|
|
|
||||||
|
|
@ -49,6 +49,7 @@ label.Select {
|
||||||
option {
|
option {
|
||||||
background-color: transparent;
|
background-color: transparent;
|
||||||
|
|
||||||
|
&:checked,
|
||||||
&.-active {
|
&.-active {
|
||||||
color: var(--selectionText);
|
color: var(--selectionText);
|
||||||
background-color: var(--selectionBackground);
|
background-color: var(--selectionBackground);
|
||||||
|
|
|
||||||
|
|
@ -170,7 +170,7 @@ export default {
|
||||||
},
|
},
|
||||||
configSink () {
|
configSink () {
|
||||||
if (this.path == null) {
|
if (this.path == null) {
|
||||||
return (k, v) => this.$emit('modelValue:update', v)
|
return (k, v) => this.$emit('update:modelValue', v)
|
||||||
}
|
}
|
||||||
switch (this.realSource) {
|
switch (this.realSource) {
|
||||||
case 'profile':
|
case 'profile':
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@
|
||||||
>
|
>
|
||||||
<label
|
<label
|
||||||
:for="path"
|
:for="path"
|
||||||
|
class="setting-label"
|
||||||
:class="{ 'faint': shouldBeDisabled }"
|
:class="{ 'faint': shouldBeDisabled }"
|
||||||
>
|
>
|
||||||
<template v-if="backendDescriptionLabel">
|
<template v-if="backendDescriptionLabel">
|
||||||
|
|
|
||||||
|
|
@ -13,23 +13,33 @@ import OpacityInput from 'src/components/opacity_input/opacity_input.vue'
|
||||||
import TabSwitcher from 'src/components/tab_switcher/tab_switcher.jsx'
|
import TabSwitcher from 'src/components/tab_switcher/tab_switcher.jsx'
|
||||||
import Tooltip from 'src/components/tooltip/tooltip.vue'
|
import Tooltip from 'src/components/tooltip/tooltip.vue'
|
||||||
import ContrastRatio from 'src/components/contrast_ratio/contrast_ratio.vue'
|
import ContrastRatio from 'src/components/contrast_ratio/contrast_ratio.vue'
|
||||||
|
import Preview from '../theme_tab/theme_preview.vue'
|
||||||
|
|
||||||
import { init } from 'src/services/theme_data/theme_data_3.service.js'
|
import { init, findColor } 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 { parseShadow /* , deserialize */ } from 'src/services/theme_data/iss_deserializer.js'
|
|
||||||
import {
|
import {
|
||||||
// rgb2hex,
|
getCssRules,
|
||||||
|
getScopedVersion
|
||||||
|
} from 'src/services/theme_data/css_utils.js'
|
||||||
|
import { serializeShadow, serialize } from 'src/services/theme_data/iss_serializer.js'
|
||||||
|
import { parseShadow, deserialize } from 'src/services/theme_data/iss_deserializer.js'
|
||||||
|
import {
|
||||||
|
rgb2hex,
|
||||||
hex2rgb,
|
hex2rgb,
|
||||||
getContrastRatio
|
getContrastRatio
|
||||||
} from 'src/services/color_convert/color_convert.js'
|
} from 'src/services/color_convert/color_convert.js'
|
||||||
import {
|
import {
|
||||||
// newImporter,
|
newImporter,
|
||||||
newExporter
|
newExporter
|
||||||
} from 'src/services/export_import/export_import.js'
|
} from 'src/services/export_import/export_import.js'
|
||||||
|
|
||||||
import { library } from '@fortawesome/fontawesome-svg-core'
|
import { library } from '@fortawesome/fontawesome-svg-core'
|
||||||
import { faFloppyDisk, faFolderOpen, faFile } from '@fortawesome/free-solid-svg-icons'
|
import {
|
||||||
|
faFloppyDisk,
|
||||||
|
faFolderOpen,
|
||||||
|
faFile,
|
||||||
|
faArrowsRotate,
|
||||||
|
faCheck
|
||||||
|
} from '@fortawesome/free-solid-svg-icons'
|
||||||
|
|
||||||
// helper for debugging
|
// helper for debugging
|
||||||
// eslint-disable-next-line no-unused-vars
|
// eslint-disable-next-line no-unused-vars
|
||||||
|
|
@ -41,7 +51,9 @@ const normalizeStates = (states) => ['normal', ...(states?.filter(x => x !== 'no
|
||||||
library.add(
|
library.add(
|
||||||
faFile,
|
faFile,
|
||||||
faFloppyDisk,
|
faFloppyDisk,
|
||||||
faFolderOpen
|
faFolderOpen,
|
||||||
|
faArrowsRotate,
|
||||||
|
faCheck
|
||||||
)
|
)
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
|
|
@ -57,25 +69,27 @@ export default {
|
||||||
ColorInput,
|
ColorInput,
|
||||||
PaletteEditor,
|
PaletteEditor,
|
||||||
OpacityInput,
|
OpacityInput,
|
||||||
ContrastRatio
|
ContrastRatio,
|
||||||
|
Preview
|
||||||
},
|
},
|
||||||
setup () {
|
setup () {
|
||||||
|
const exports = {}
|
||||||
// All rules that are made by editor
|
// All rules that are made by editor
|
||||||
const allEditedRules = reactive({})
|
const allEditedRules = reactive({})
|
||||||
|
|
||||||
// ## Meta stuff
|
// ## Meta stuff
|
||||||
const name = ref('')
|
exports.name = ref('')
|
||||||
const author = ref('')
|
exports.author = ref('')
|
||||||
const license = ref('')
|
exports.license = ref('')
|
||||||
const website = ref('')
|
exports.website = ref('')
|
||||||
|
|
||||||
const metaOut = computed(() => {
|
const metaOut = computed(() => {
|
||||||
return [
|
return [
|
||||||
'@meta {',
|
'@meta {',
|
||||||
` name: ${name.value};`,
|
` name: ${exports.name.value};`,
|
||||||
` author: ${author.value};`,
|
` author: ${exports.author.value};`,
|
||||||
` license: ${license.value};`,
|
` license: ${exports.license.value};`,
|
||||||
` website: ${website.value};`,
|
` website: ${exports.website.value};`,
|
||||||
'}'
|
'}'
|
||||||
].join('\n')
|
].join('\n')
|
||||||
})
|
})
|
||||||
|
|
@ -108,6 +122,20 @@ export default {
|
||||||
border: '#d8e6f9'
|
border: '#d8e6f9'
|
||||||
}
|
}
|
||||||
])
|
])
|
||||||
|
exports.palettes = palettes
|
||||||
|
|
||||||
|
// This is kinda dumb but you cannot "replace" reactive() object
|
||||||
|
// and so v-model simply fails when you try to chage (increase only?)
|
||||||
|
// length of the array. Since linter complains about mutating modelValue
|
||||||
|
// inside SelectMotion, the next best thing is to just wipe existing array
|
||||||
|
// and replace it with new one.
|
||||||
|
|
||||||
|
const onPalettesUpdate = (e) => {
|
||||||
|
palettes.splice(0, palettes.length)
|
||||||
|
palettes.push(...e)
|
||||||
|
}
|
||||||
|
exports.onPalettesUpdate = onPalettesUpdate
|
||||||
|
|
||||||
const selectedPaletteId = ref(0)
|
const selectedPaletteId = ref(0)
|
||||||
const selectedPalette = computed({
|
const selectedPalette = computed({
|
||||||
get () {
|
get () {
|
||||||
|
|
@ -117,7 +145,10 @@ export default {
|
||||||
palettes[selectedPaletteId.value] = newPalette
|
palettes[selectedPaletteId.value] = newPalette
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
const getNewPalette = () => ({
|
exports.selectedPaletteId = selectedPaletteId
|
||||||
|
exports.selectedPalette = selectedPalette
|
||||||
|
|
||||||
|
exports.getNewPalette = () => ({
|
||||||
name: 'new palette',
|
name: 'new palette',
|
||||||
bg: '#121a24',
|
bg: '#121a24',
|
||||||
fg: '#182230',
|
fg: '#182230',
|
||||||
|
|
@ -151,26 +182,31 @@ export default {
|
||||||
key => [key, componentsContext(key).default]
|
key => [key, componentsContext(key).default]
|
||||||
).filter(([key, component]) => !component.virtual && !component.notEditable)
|
).filter(([key, component]) => !component.virtual && !component.notEditable)
|
||||||
)
|
)
|
||||||
|
exports.componentsMap = componentsMap
|
||||||
const componentKeys = [...componentsMap.keys()]
|
const componentKeys = [...componentsMap.keys()]
|
||||||
|
exports.componentKeys = componentKeys
|
||||||
|
|
||||||
// selection basis
|
// selection basis
|
||||||
const selectedComponentKey = ref(componentsMap.keys().next().value)
|
const selectedComponentKey = ref(componentsMap.keys().next().value)
|
||||||
|
exports.selectedComponentKey = selectedComponentKey
|
||||||
const selectedComponent = computed(() => componentsMap.get(selectedComponentKey.value))
|
const selectedComponent = computed(() => componentsMap.get(selectedComponentKey.value))
|
||||||
const selectedComponentName = computed(() => selectedComponent.value.name)
|
const selectedComponentName = computed(() => selectedComponent.value.name)
|
||||||
const selectedComponentVariants = computed(() => {
|
exports.selectedComponentVariants = computed(() => {
|
||||||
return Object.keys({ normal: null, ...(selectedComponent.value.variants || {}) })
|
return Object.keys({ normal: null, ...(selectedComponent.value.variants || {}) })
|
||||||
})
|
})
|
||||||
const selectedComponentStatesAll = computed(() => {
|
const selectedComponentStatesAll = computed(() => {
|
||||||
return Object.keys({ normal: null, ...(selectedComponent.value.states || {}) })
|
return Object.keys({ normal: null, ...(selectedComponent.value.states || {}) })
|
||||||
})
|
})
|
||||||
const selectedComponentStates = computed(() => {
|
exports.selectedComponentStates = computed(() => {
|
||||||
return selectedComponentStatesAll.value.filter(x => x !== 'normal')
|
return selectedComponentStatesAll.value.filter(x => x !== 'normal')
|
||||||
})
|
})
|
||||||
|
|
||||||
// selection
|
// selection
|
||||||
const selectedVariant = ref('normal')
|
const selectedVariant = ref('normal')
|
||||||
|
exports.selectedVariant = selectedVariant
|
||||||
const selectedState = reactive(new Set())
|
const selectedState = reactive(new Set())
|
||||||
const updateSelectedStates = (state, v) => {
|
exports.selectedState = selectedState
|
||||||
|
exports.updateSelectedStates = (state, v) => {
|
||||||
if (v) {
|
if (v) {
|
||||||
selectedState.add(state)
|
selectedState.add(state)
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -182,47 +218,53 @@ export default {
|
||||||
// The native structure of separate rules and the child -> parent
|
// The native structure of separate rules and the child -> parent
|
||||||
// relation isn't very convenient for editor, we replace the array
|
// relation isn't very convenient for editor, we replace the array
|
||||||
// and child -> parent structure with map and parent -> child structure
|
// and child -> parent structure with map and parent -> child structure
|
||||||
|
const rulesToEditorFriendly = (rules, root = {}) => rules.reduce((acc, rule) => {
|
||||||
|
const { parent: rParent, component: rComponent } = 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 : rComponent}.${pVariant}.${normalizeStates(pState)}`
|
||||||
|
|
||||||
|
let output = get(acc, pPath)
|
||||||
|
if (!output) {
|
||||||
|
set(acc, pPath, {})
|
||||||
|
output = get(acc, pPath)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hasChildren) {
|
||||||
|
acc._children = acc._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 acc
|
||||||
|
}, root)
|
||||||
|
|
||||||
const editorFriendlyFallbackStructure = computed(() => {
|
const editorFriendlyFallbackStructure = computed(() => {
|
||||||
const root = {}
|
const root = {}
|
||||||
|
|
||||||
componentKeys.forEach((componentKey) => {
|
componentKeys.forEach((componentKey) => {
|
||||||
const componentValue = componentsMap.get(componentKey)
|
const componentValue = componentsMap.get(componentKey)
|
||||||
const { defaultRules } = componentValue
|
const { defaultRules, name } = componentValue
|
||||||
defaultRules.forEach((rule) => {
|
rulesToEditorFriendly(
|
||||||
const { parent: rParent } = rule
|
defaultRules.map((rule) => ({ ...rule, component: name })),
|
||||||
const parent = rParent ?? rule
|
root
|
||||||
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
|
return root
|
||||||
|
|
@ -230,7 +272,7 @@ export default {
|
||||||
|
|
||||||
// Checkging whether component can support some "directives" which
|
// Checkging whether component can support some "directives" which
|
||||||
// are actually virtual subcomponents, i.e. Text, Link etc
|
// are actually virtual subcomponents, i.e. Text, Link etc
|
||||||
const componentHas = (subComponent) => {
|
exports.componentHas = (subComponent) => {
|
||||||
return !!selectedComponent.value.validInnerComponents?.find(x => x === subComponent)
|
return !!selectedComponent.value.validInnerComponents?.find(x => x === subComponent)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -283,21 +325,24 @@ export default {
|
||||||
})
|
})
|
||||||
|
|
||||||
// All the editable stuff for the component
|
// All the editable stuff for the component
|
||||||
const editedBackgroundColor = getEditedElement(null, 'background')
|
exports.editedBackgroundColor = getEditedElement(null, 'background')
|
||||||
const isBackgroundColorPresent = isElementPresent(null, 'background', '#FFFFFF')
|
exports.isBackgroundColorPresent = isElementPresent(null, 'background', '#FFFFFF')
|
||||||
const editedOpacity = getEditedElement(null, 'opacity')
|
exports.editedOpacity = getEditedElement(null, 'opacity')
|
||||||
const isOpacityPresent = isElementPresent(null, 'opacity', 1)
|
exports.isOpacityPresent = isElementPresent(null, 'opacity', 1)
|
||||||
const editedTextColor = getEditedElement('Text', 'textColor')
|
exports.editedTextColor = getEditedElement('Text', 'textColor')
|
||||||
const isTextColorPresent = isElementPresent('Text', 'textColor', '#000000')
|
exports.isTextColorPresent = isElementPresent('Text', 'textColor', '#000000')
|
||||||
const editedTextAuto = getEditedElement('Text', 'textAuto')
|
exports.editedTextAuto = getEditedElement('Text', 'textAuto')
|
||||||
const isTextAutoPresent = isElementPresent('Text', 'textAuto', '#000000')
|
exports.isTextAutoPresent = isElementPresent('Text', 'textAuto', '#000000')
|
||||||
const editedLinkColor = getEditedElement('Link', 'textColor')
|
exports.editedLinkColor = getEditedElement('Link', 'textColor')
|
||||||
const isLinkColorPresent = isElementPresent('Link', 'textColor', '#000080')
|
exports.isLinkColorPresent = isElementPresent('Link', 'textColor', '#000080')
|
||||||
const editedIconColor = getEditedElement('Icon', 'textColor')
|
exports.editedIconColor = getEditedElement('Icon', 'textColor')
|
||||||
const isIconColorPresent = isElementPresent('Icon', 'textColor', '#909090')
|
exports.isIconColorPresent = isElementPresent('Icon', 'textColor', '#909090')
|
||||||
|
exports.editedBorderColor = getEditedElement('Border', 'textColor')
|
||||||
|
exports.isBorderColorPresent = isElementPresent('Border', 'textColor', '#909090')
|
||||||
|
|
||||||
// TODO this is VERY primitive right now, need to make it
|
// TODO this is VERY primitive right now, need to make it
|
||||||
// support variables, fallbacks etc.
|
// support variables, fallbacks etc.
|
||||||
const getContrast = (bg, text) => {
|
exports.getContrast = (bg, text) => {
|
||||||
try {
|
try {
|
||||||
const bgRgb = hex2rgb(bg)
|
const bgRgb = hex2rgb(bg)
|
||||||
const textRgb = hex2rgb(text)
|
const textRgb = hex2rgb(text)
|
||||||
|
|
@ -321,7 +366,6 @@ export default {
|
||||||
}
|
}
|
||||||
|
|
||||||
const normalizeShadows = (shadows) => {
|
const normalizeShadows = (shadows) => {
|
||||||
console.log('NORMALIZE')
|
|
||||||
return shadows?.map(shadow => {
|
return shadows?.map(shadow => {
|
||||||
if (typeof shadow === 'object') {
|
if (typeof shadow === 'object') {
|
||||||
return shadow
|
return shadow
|
||||||
|
|
@ -336,20 +380,23 @@ export default {
|
||||||
// Shadow is partially edited outside the ShadowControl
|
// Shadow is partially edited outside the ShadowControl
|
||||||
// for better space utilization
|
// for better space utilization
|
||||||
const editedShadow = getEditedElement(null, 'shadow', normalizeShadows)
|
const editedShadow = getEditedElement(null, 'shadow', normalizeShadows)
|
||||||
|
exports.editedShadow = editedShadow
|
||||||
const editedSubShadowId = ref(null)
|
const editedSubShadowId = ref(null)
|
||||||
|
exports.editedSubShadowId = editedSubShadowId
|
||||||
const editedSubShadow = computed(() => {
|
const editedSubShadow = computed(() => {
|
||||||
if (editedShadow.value == null || editedSubShadowId.value == null) return null
|
if (editedShadow.value == null || editedSubShadowId.value == null) return null
|
||||||
return editedShadow.value[editedSubShadowId.value]
|
return editedShadow.value[editedSubShadowId.value]
|
||||||
})
|
})
|
||||||
const isShadowPresent = isElementPresent(null, 'shadow', [])
|
exports.editedSubShadow = editedSubShadow
|
||||||
const onSubShadow = (id) => {
|
exports.isShadowPresent = isElementPresent(null, 'shadow', [])
|
||||||
|
exports.onSubShadow = (id) => {
|
||||||
if (id != null) {
|
if (id != null) {
|
||||||
editedSubShadowId.value = id
|
editedSubShadowId.value = id
|
||||||
} else {
|
} else {
|
||||||
editedSubShadow.value = null
|
editedSubShadow.value = null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const updateSubShadow = (axis, value) => {
|
exports.updateSubShadow = (axis, value) => {
|
||||||
if (!editedSubShadow.value || editedSubShadowId.value == null) return
|
if (!editedSubShadow.value || editedSubShadowId.value == null) return
|
||||||
const newEditedShadow = [...editedShadow.value]
|
const newEditedShadow = [...editedShadow.value]
|
||||||
|
|
||||||
|
|
@ -360,13 +407,13 @@ export default {
|
||||||
|
|
||||||
editedShadow.value = newEditedShadow
|
editedShadow.value = newEditedShadow
|
||||||
}
|
}
|
||||||
const isShadowTabOpen = ref(false)
|
exports.isShadowTabOpen = ref(false)
|
||||||
const onTabSwitch = (tab) => {
|
exports.onTabSwitch = (tab) => {
|
||||||
isShadowTabOpen.value = tab === 'shadow'
|
exports.isShadowTabOpen.value = tab === 'shadow'
|
||||||
}
|
}
|
||||||
|
|
||||||
// component preview
|
// component preview
|
||||||
const editorHintStyle = computed(() => {
|
exports.editorHintStyle = computed(() => {
|
||||||
const editorHint = selectedComponent.value.editor
|
const editorHint = selectedComponent.value.editor
|
||||||
const styles = []
|
const styles = []
|
||||||
if (editorHint && Object.keys(editorHint).length > 0) {
|
if (editorHint && Object.keys(editorHint).length > 0) {
|
||||||
|
|
@ -389,7 +436,7 @@ export default {
|
||||||
.replace(':focus', '.preview-focus')
|
.replace(':focus', '.preview-focus')
|
||||||
.replace(':focus-within', '.preview-focus-within')
|
.replace(':focus-within', '.preview-focus-within')
|
||||||
.replace(':disabled', '.preview-disabled')
|
.replace(':disabled', '.preview-disabled')
|
||||||
const previewClass = computed(() => {
|
exports.previewClass = computed(() => {
|
||||||
const selectors = []
|
const selectors = []
|
||||||
if (!!selectedComponent.value.variants?.normal || selectedVariant.value !== 'normal') {
|
if (!!selectedComponent.value.variants?.normal || selectedVariant.value !== 'normal') {
|
||||||
selectors.push(selectedComponent.value.variants[selectedVariant.value])
|
selectors.push(selectedComponent.value.variants[selectedVariant.value])
|
||||||
|
|
@ -403,7 +450,8 @@ export default {
|
||||||
return selectors.map(x => x.substring(1)).join('')
|
return selectors.map(x => x.substring(1)).join('')
|
||||||
})
|
})
|
||||||
const previewRules = reactive([])
|
const previewRules = reactive([])
|
||||||
const previewCss = computed(() => {
|
exports.previewRules = previewRules
|
||||||
|
exports.previewCss = computed(() => {
|
||||||
try {
|
try {
|
||||||
const scoped = getCssRules(previewRules).map(simulatePseudoSelectors)
|
const scoped = getCssRules(previewRules).map(simulatePseudoSelectors)
|
||||||
return scoped.join('\n')
|
return scoped.join('\n')
|
||||||
|
|
@ -523,9 +571,43 @@ export default {
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
const virtualDirectives = reactive(allCustomVirtualDirectives)
|
const virtualDirectives = reactive(allCustomVirtualDirectives)
|
||||||
|
exports.virtualDirectives = virtualDirectives
|
||||||
|
|
||||||
|
exports.onVirtualDirectivesUpdate = (e) => {
|
||||||
|
virtualDirectives.splice(0, virtualDirectives.length)
|
||||||
|
virtualDirectives.push(...e)
|
||||||
|
}
|
||||||
|
|
||||||
const selectedVirtualDirectiveId = ref(0)
|
const selectedVirtualDirectiveId = ref(0)
|
||||||
const selectedVirtualDirective = computed(() => virtualDirectives[selectedVirtualDirectiveId.value])
|
exports.selectedVirtualDirectiveId = selectedVirtualDirectiveId
|
||||||
const selectedVirtualDirectiveParsed = computed({
|
const selectedVirtualDirective = computed({
|
||||||
|
get () {
|
||||||
|
return virtualDirectives[selectedVirtualDirectiveId.value]
|
||||||
|
},
|
||||||
|
set (value) {
|
||||||
|
virtualDirectives[selectedVirtualDirectiveId.value].value = value
|
||||||
|
}
|
||||||
|
})
|
||||||
|
exports.selectedVirtualDirective = selectedVirtualDirective
|
||||||
|
exports.selectedVirtualDirectiveValType = computed({
|
||||||
|
get () {
|
||||||
|
return virtualDirectives[selectedVirtualDirectiveId.value].valType
|
||||||
|
},
|
||||||
|
set (value) {
|
||||||
|
virtualDirectives[selectedVirtualDirectiveId.value].valType = value
|
||||||
|
switch (value) {
|
||||||
|
case 'shadow':
|
||||||
|
virtualDirectives[selectedVirtualDirectiveId.value].value = '0 0 0 #000000'
|
||||||
|
break
|
||||||
|
case 'color':
|
||||||
|
virtualDirectives[selectedVirtualDirectiveId.value].value = '#000000'
|
||||||
|
break
|
||||||
|
default:
|
||||||
|
virtualDirectives[selectedVirtualDirectiveId.value].value = 'none'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
exports.selectedVirtualDirectiveParsed = computed({
|
||||||
get () {
|
get () {
|
||||||
switch (selectedVirtualDirective.value.valType) {
|
switch (selectedVirtualDirective.value.valType) {
|
||||||
case 'shadow': {
|
case 'shadow': {
|
||||||
|
|
@ -537,25 +619,109 @@ export default {
|
||||||
return normalizeShadows(splitShadow)
|
return normalizeShadows(splitShadow)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
case 'color':
|
||||||
|
return selectedVirtualDirective.value.value
|
||||||
default:
|
default:
|
||||||
return null
|
return selectedVirtualDirective.value.value
|
||||||
|
}
|
||||||
|
},
|
||||||
|
set (value) {
|
||||||
|
switch (selectedVirtualDirective.value.valType) {
|
||||||
|
case 'shadow': {
|
||||||
|
virtualDirectives[selectedVirtualDirectiveId.value].value = value.map(x => serializeShadow(x)).join(', ')
|
||||||
|
break
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
virtualDirectives[selectedVirtualDirectiveId.value].value = value
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
const getNewDirective = () => ({
|
exports.getNewVirtualDirective = () => ({
|
||||||
name: 'newDirective',
|
name: 'newDirective',
|
||||||
valType: 'generic',
|
valType: 'generic',
|
||||||
value: 'foobar'
|
value: 'foobar'
|
||||||
})
|
})
|
||||||
|
|
||||||
|
exports.computeColor = (color) => {
|
||||||
|
const computedColor = findColor(color, { dynamicVars: {}, staticVars: selectedPalette.value })
|
||||||
|
if (computedColor) {
|
||||||
|
return rgb2hex(computedColor)
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
const overallPreviewRules = ref()
|
||||||
|
exports.overallPreviewRules = overallPreviewRules
|
||||||
|
exports.updateOverallPreview = () => {
|
||||||
|
try {
|
||||||
|
// This normally would be handled by Root but since we pass something
|
||||||
|
// else we have to make do ourselves
|
||||||
|
|
||||||
|
const { name, ...rest } = selectedPalette.value
|
||||||
|
const paletteRule = {
|
||||||
|
component: 'Root',
|
||||||
|
directives: Object
|
||||||
|
.entries(rest)
|
||||||
|
.map(([k, v]) => ['--' + k, v])
|
||||||
|
.reduce((acc, [k, v]) => ({ ...acc, [k]: `color | ${v}` }), {})
|
||||||
|
}
|
||||||
|
|
||||||
|
const rules = init({
|
||||||
|
inputRuleset: [
|
||||||
|
...editorFriendlyToOriginal.value,
|
||||||
|
paletteRule
|
||||||
|
],
|
||||||
|
ultimateBackgroundColor: '#000000',
|
||||||
|
liteMode: true,
|
||||||
|
debug: true
|
||||||
|
}).eager
|
||||||
|
|
||||||
|
overallPreviewRules.value = getScopedVersion(
|
||||||
|
getCssRules(rules),
|
||||||
|
'#edited-style-preview'
|
||||||
|
).join('\n')
|
||||||
|
} catch (e) {
|
||||||
|
console.error('Could not compile preview theme', e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// ## Export and Import
|
// ## Export and Import
|
||||||
const styleExporter = newExporter({
|
const styleExporter = newExporter({
|
||||||
filename: name.value || 'pleroma_theme',
|
filename: () => exports.name.value ?? 'pleroma_theme',
|
||||||
mime: 'text/plain',
|
mime: 'text/plain',
|
||||||
extension: 'piss',
|
extension: 'piss',
|
||||||
getExportedObject: () => exportStyleData.value
|
getExportedObject: () => exportStyleData.value
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const styleImporter = newImporter({
|
||||||
|
accept: '.piss',
|
||||||
|
parser: (string) => deserialize(string),
|
||||||
|
onImport (parsed, filename) {
|
||||||
|
const editorComponents = parsed.filter(x => x.component.startsWith('@'))
|
||||||
|
const rules = parsed.filter(x => !x.component.startsWith('@'))
|
||||||
|
const metaIn = editorComponents.find(x => x.component === '@meta').directives
|
||||||
|
const palettesIn = editorComponents.filter(x => x.component === '@palette')
|
||||||
|
|
||||||
|
exports.name.value = metaIn.name
|
||||||
|
exports.license.value = metaIn.license
|
||||||
|
exports.author.value = metaIn.author
|
||||||
|
exports.website.value = metaIn.website
|
||||||
|
|
||||||
|
onPalettesUpdate(palettesIn.map(x => ({ name: x.variant, ...x.directives })))
|
||||||
|
console.log('PALETTES', palettesIn)
|
||||||
|
|
||||||
|
Object.keys(allEditedRules).forEach((k) => delete allEditedRules[k])
|
||||||
|
|
||||||
|
rules.forEach(rule => {
|
||||||
|
rulesToEditorFriendly(
|
||||||
|
[rule],
|
||||||
|
allEditedRules
|
||||||
|
)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
const exportStyleData = computed(() => {
|
const exportStyleData = computed(() => {
|
||||||
return [
|
return [
|
||||||
metaOut.value,
|
metaOut.value,
|
||||||
|
|
@ -563,80 +729,15 @@ export default {
|
||||||
serialize(editorFriendlyToOriginal.value)
|
serialize(editorFriendlyToOriginal.value)
|
||||||
].join('\n\n')
|
].join('\n\n')
|
||||||
})
|
})
|
||||||
const exportStyle = () => {
|
|
||||||
|
exports.exportStyle = () => {
|
||||||
styleExporter.exportData()
|
styleExporter.exportData()
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
exports.importStyle = () => {
|
||||||
// ## Meta
|
styleImporter.importData()
|
||||||
name,
|
|
||||||
author,
|
|
||||||
license,
|
|
||||||
website,
|
|
||||||
|
|
||||||
// ## Palette
|
|
||||||
palettes,
|
|
||||||
selectedPalette,
|
|
||||||
selectedPaletteId,
|
|
||||||
getNewPalette,
|
|
||||||
|
|
||||||
// ## Components
|
|
||||||
componentKeys,
|
|
||||||
componentsMap,
|
|
||||||
|
|
||||||
// selection basis
|
|
||||||
selectedComponent,
|
|
||||||
selectedComponentName,
|
|
||||||
selectedComponentKey,
|
|
||||||
selectedComponentVariants,
|
|
||||||
selectedComponentStates,
|
|
||||||
|
|
||||||
// selection
|
|
||||||
selectedVariant,
|
|
||||||
selectedState,
|
|
||||||
updateSelectedStates,
|
|
||||||
|
|
||||||
// component directives
|
|
||||||
componentHas,
|
|
||||||
|
|
||||||
// component colors
|
|
||||||
editedBackgroundColor,
|
|
||||||
isBackgroundColorPresent,
|
|
||||||
editedOpacity,
|
|
||||||
isOpacityPresent,
|
|
||||||
editedTextColor,
|
|
||||||
isTextColorPresent,
|
|
||||||
editedTextAuto,
|
|
||||||
isTextAutoPresent,
|
|
||||||
editedLinkColor,
|
|
||||||
isLinkColorPresent,
|
|
||||||
editedIconColor,
|
|
||||||
isIconColorPresent,
|
|
||||||
getContrast,
|
|
||||||
|
|
||||||
// component shadow
|
|
||||||
editedShadow,
|
|
||||||
editedSubShadow,
|
|
||||||
isShadowPresent,
|
|
||||||
onSubShadow,
|
|
||||||
updateSubShadow,
|
|
||||||
isShadowTabOpen,
|
|
||||||
onTabSwitch,
|
|
||||||
|
|
||||||
// component preview
|
|
||||||
editorHintStyle,
|
|
||||||
previewCss,
|
|
||||||
previewClass,
|
|
||||||
|
|
||||||
// ## Variables
|
|
||||||
virtualDirectives,
|
|
||||||
selectedVirtualDirective,
|
|
||||||
selectedVirtualDirectiveId,
|
|
||||||
selectedVirtualDirectiveParsed,
|
|
||||||
getNewDirective,
|
|
||||||
|
|
||||||
// ## Export and Import
|
|
||||||
exportStyle
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return exports
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -65,24 +65,60 @@
|
||||||
|
|
||||||
&.heading {
|
&.heading {
|
||||||
display: grid;
|
display: grid;
|
||||||
align-items: baseline;
|
grid-template:
|
||||||
grid-template-columns: 1fr auto auto auto;
|
"meta meta preview preview"
|
||||||
|
"meta meta preview preview"
|
||||||
|
"meta meta preview preview"
|
||||||
|
"meta meta preview preview"
|
||||||
|
"new new preview preview"
|
||||||
|
"load save refresh apply";
|
||||||
grid-gap: 0.5em;
|
grid-gap: 0.5em;
|
||||||
|
grid-template-columns: min-content min-content 6fr max-content;
|
||||||
|
grid-template-rows: repeat(4, min-content) repeat(2, 2em);
|
||||||
|
|
||||||
h2 {
|
ul.setting-list {
|
||||||
flex: 1 0 auto;
|
padding: 0;
|
||||||
}
|
margin: 0;
|
||||||
}
|
display: grid;
|
||||||
|
grid-template-rows: subgrid;
|
||||||
|
grid-area: meta;
|
||||||
|
|
||||||
&.metadata {
|
> li {
|
||||||
display: flex;
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
.setting-item {
|
.meta-field {
|
||||||
flex: 2 0 auto;
|
margin: 0;
|
||||||
|
|
||||||
|
.setting-label {
|
||||||
|
display: inline-block;
|
||||||
|
margin-bottom: 0.5em;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
li {
|
#edited-style-preview {
|
||||||
text-align: right;
|
grid-area: preview;
|
||||||
|
}
|
||||||
|
|
||||||
|
.button-save {
|
||||||
|
grid-area: save;
|
||||||
|
}
|
||||||
|
|
||||||
|
.button-load {
|
||||||
|
grid-area: load;
|
||||||
|
}
|
||||||
|
|
||||||
|
.button-new {
|
||||||
|
grid-area: new;
|
||||||
|
}
|
||||||
|
|
||||||
|
.button-refresh {
|
||||||
|
grid-area: refresh;
|
||||||
|
}
|
||||||
|
|
||||||
|
.button-apply {
|
||||||
|
grid-area: apply;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -119,9 +155,18 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.palettes-editor {
|
.palette-editor {
|
||||||
|
width: min-content;
|
||||||
|
|
||||||
.list-edit-area {
|
.list-edit-area {
|
||||||
|
display: grid;
|
||||||
align-self: baseline;
|
align-self: baseline;
|
||||||
|
grid-template-rows: subgrid;
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
}
|
||||||
|
|
||||||
|
.palette-editor-single {
|
||||||
|
grid-row: 2 / span 2;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -4,49 +4,66 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="StyleTab">
|
<div class="StyleTab">
|
||||||
<div class="setting-item heading">
|
<div class="setting-item heading">
|
||||||
<!-- TODO: This needs to go -->
|
<!-- eslint-disable vue/no-v-text-v-html-on-component -->
|
||||||
<h2>{{ $t('settings.style.themes3.editor.title') }}</h2>
|
<component
|
||||||
|
:is="'style'"
|
||||||
|
v-html="overallPreviewRules"
|
||||||
|
/>
|
||||||
|
<!-- eslint-enable vue/no-v-text-v-html-on-component -->
|
||||||
|
<Preview id="edited-style-preview" />
|
||||||
<button
|
<button
|
||||||
class="btn button-default"
|
class="btn button-default button-new"
|
||||||
@click="clearTheme"
|
@click="clearTheme"
|
||||||
>
|
>
|
||||||
<FAIcon icon="file" />
|
<FAIcon icon="file" />
|
||||||
{{ $t('settings.style.themes3.editor.new_style') }}
|
{{ $t('settings.style.themes3.editor.new_style') }}
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
class="btn button-default"
|
class="btn button-default button-load"
|
||||||
@click="importStyle"
|
@click="importStyle"
|
||||||
>
|
>
|
||||||
<FAIcon icon="folder-open" />
|
<FAIcon icon="folder-open" />
|
||||||
{{ $t('settings.style.themes3.editor.load_style') }}
|
{{ $t('settings.style.themes3.editor.load_style') }}
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
class="btn button-default"
|
class="btn button-default button-save"
|
||||||
@click="exportStyle"
|
@click="exportStyle"
|
||||||
>
|
>
|
||||||
<FAIcon icon="floppy-disk" />
|
<FAIcon icon="floppy-disk" />
|
||||||
{{ $t('settings.style.themes3.editor.save_style') }}
|
{{ $t('settings.style.themes3.editor.save_style') }}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
<button
|
||||||
<div class="setting-item metadata">
|
class="btn button-default button-refresh"
|
||||||
<ul class="setting-list">
|
@click="updateOverallPreview"
|
||||||
|
>
|
||||||
|
<FAIcon icon="arrows-rotate" />
|
||||||
|
{{ $t('settings.style.themes3.editor.refresh_preview') }}
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
class="btn button-default button-apply"
|
||||||
|
@click="applyTheme"
|
||||||
|
>
|
||||||
|
<FAIcon icon="check" />
|
||||||
|
{{ $t('settings.style.themes3.editor.apply_preview') }}
|
||||||
|
</button>
|
||||||
|
<ul class="setting-list style-metadata">
|
||||||
<li>
|
<li>
|
||||||
<StringSetting v-model="name">
|
<StringSetting class="meta-field" v-model="name">
|
||||||
{{ $t('settings.style.themes3.editor.style_name') }}
|
{{ $t('settings.style.themes3.editor.style_name') }}
|
||||||
</StringSetting>
|
</StringSetting>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<StringSetting v-model="author">
|
<StringSetting class="meta-field" v-model="author">
|
||||||
{{ $t('settings.style.themes3.editor.style_author') }}
|
{{ $t('settings.style.themes3.editor.style_author') }}
|
||||||
</StringSetting>
|
</StringSetting>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<StringSetting v-model="license">
|
<StringSetting class="meta-field" v-model="license">
|
||||||
{{ $t('settings.style.themes3.editor.style_license') }}
|
{{ $t('settings.style.themes3.editor.style_license') }}
|
||||||
</StringSetting>
|
</StringSetting>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<StringSetting v-model="website">
|
<StringSetting class="meta-field" v-model="website">
|
||||||
{{ $t('settings.style.themes3.editor.style_website') }}
|
{{ $t('settings.style.themes3.editor.style_website') }}
|
||||||
</StringSetting>
|
</StringSetting>
|
||||||
</li>
|
</li>
|
||||||
|
|
@ -148,6 +165,7 @@
|
||||||
>
|
>
|
||||||
<ColorInput
|
<ColorInput
|
||||||
v-model="editedBackgroundColor"
|
v-model="editedBackgroundColor"
|
||||||
|
:fallback="computeColor(editedBackgroundColor)"
|
||||||
:disabled="!isBackgroundColorPresent"
|
:disabled="!isBackgroundColorPresent"
|
||||||
:label="$t('settings.style.themes3.editor.background')"
|
:label="$t('settings.style.themes3.editor.background')"
|
||||||
/>
|
/>
|
||||||
|
|
@ -165,6 +183,7 @@
|
||||||
<ColorInput
|
<ColorInput
|
||||||
v-if="componentHas('Text')"
|
v-if="componentHas('Text')"
|
||||||
v-model="editedTextColor"
|
v-model="editedTextColor"
|
||||||
|
:fallback="computeColor(editedTextColor)"
|
||||||
:label="$t('settings.style.themes3.editor.text_color')"
|
:label="$t('settings.style.themes3.editor.text_color')"
|
||||||
:disabled="!isTextColorPresent"
|
:disabled="!isTextColorPresent"
|
||||||
/>
|
/>
|
||||||
|
|
@ -213,6 +232,7 @@
|
||||||
<ColorInput
|
<ColorInput
|
||||||
v-if="componentHas('Link')"
|
v-if="componentHas('Link')"
|
||||||
v-model="editedLinkColor"
|
v-model="editedLinkColor"
|
||||||
|
:fallback="computeColor(editedLinkColor)"
|
||||||
:label="$t('settings.style.themes3.editor.link_color')"
|
:label="$t('settings.style.themes3.editor.link_color')"
|
||||||
:disabled="!isLinkColorPresent"
|
:disabled="!isLinkColorPresent"
|
||||||
/>
|
/>
|
||||||
|
|
@ -225,6 +245,7 @@
|
||||||
<ColorInput
|
<ColorInput
|
||||||
v-if="componentHas('Icon')"
|
v-if="componentHas('Icon')"
|
||||||
v-model="editedIconColor"
|
v-model="editedIconColor"
|
||||||
|
:fallback="computeColor(editedIconColor)"
|
||||||
:label="$t('settings.style.themes3.editor.icon_color')"
|
:label="$t('settings.style.themes3.editor.icon_color')"
|
||||||
:disabled="!isIconColorPresent"
|
:disabled="!isIconColorPresent"
|
||||||
/>
|
/>
|
||||||
|
|
@ -234,6 +255,19 @@
|
||||||
>
|
>
|
||||||
<Checkbox v-model="isIconColorPresent" />
|
<Checkbox v-model="isIconColorPresent" />
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
|
<ColorInput
|
||||||
|
v-if="componentHas('Border')"
|
||||||
|
v-model="editedBorderColor"
|
||||||
|
:fallback="computeColor(editedBorderColor)"
|
||||||
|
:label="$t('settings.style.themes3.editor.Border_color')"
|
||||||
|
:disabled="!isBorderColorPresent"
|
||||||
|
/>
|
||||||
|
<Tooltip
|
||||||
|
v-if="componentHas('Border')"
|
||||||
|
:text="$t('settings.style.themes3.editor.include_in_rule')"
|
||||||
|
>
|
||||||
|
<Checkbox v-model="isBorderColorPresent" />
|
||||||
|
</Tooltip>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
key="shadow"
|
key="shadow"
|
||||||
|
|
@ -262,38 +296,47 @@
|
||||||
:label="$t('settings.style.themes3.editor.palette_tab')"
|
:label="$t('settings.style.themes3.editor.palette_tab')"
|
||||||
class="setting-item list-editor palette-editor"
|
class="setting-item list-editor palette-editor"
|
||||||
>
|
>
|
||||||
<label
|
<label
|
||||||
class="list-select-label"
|
class="list-select-label"
|
||||||
for="palette-selector"
|
for="palette-selector"
|
||||||
|
>
|
||||||
|
{{ $t('settings.style.themes3.palette.label') }}
|
||||||
|
{{ ' ' }}
|
||||||
|
</label>
|
||||||
|
<Select
|
||||||
|
id="palette-selector"
|
||||||
|
v-model="selectedPaletteId"
|
||||||
|
class="list-select"
|
||||||
|
size="4"
|
||||||
|
>
|
||||||
|
<option
|
||||||
|
v-for="(p, index) in palettes"
|
||||||
|
:key="p.name"
|
||||||
|
:value="index"
|
||||||
>
|
>
|
||||||
{{ $t('settings.style.themes3.palette.label') }}
|
{{ p.name }}
|
||||||
{{ ' ' }}
|
</option>
|
||||||
</label>
|
</Select>
|
||||||
<Select
|
<SelectMotion
|
||||||
id="palette-selector"
|
class="list-select-movement"
|
||||||
v-model="selectedPaletteId"
|
:modelValue="palettes"
|
||||||
class="list-select"
|
@update:modelValue="onPalettesUpdate"
|
||||||
size="4"
|
:selected-id="selectedPaletteId"
|
||||||
>
|
:get-add-value="getNewPalette"
|
||||||
<option
|
@update:selectedId="e => selectedPaletteId = e"
|
||||||
v-for="(p, index) in palettes"
|
|
||||||
:key="p.name"
|
|
||||||
:value="index"
|
|
||||||
>
|
|
||||||
{{ p.name }}
|
|
||||||
</option>
|
|
||||||
</Select>
|
|
||||||
<SelectMotion
|
|
||||||
class="list-select-movement"
|
|
||||||
v-model="palettes"
|
|
||||||
:get-add-value="getNewPalette"
|
|
||||||
:selected-id="selectedPaletteId"
|
|
||||||
@update:selectedId="e => selectedPaletteId = e"
|
|
||||||
/>
|
|
||||||
<PaletteEditor
|
|
||||||
class="list-edit-area"
|
|
||||||
v-model="selectedPalette"
|
|
||||||
/>
|
/>
|
||||||
|
<div class="list-edit-area">
|
||||||
|
<StringSetting
|
||||||
|
class="palette-name-input"
|
||||||
|
v-model="selectedPalette.name"
|
||||||
|
>
|
||||||
|
{{ $t('settings.style.themes3.palette.name_label') }}
|
||||||
|
</StringSetting>
|
||||||
|
<PaletteEditor
|
||||||
|
class="palette-editor-single"
|
||||||
|
v-model="selectedPalette"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
key="variables"
|
key="variables"
|
||||||
|
|
@ -304,14 +347,14 @@
|
||||||
class="list-select-label"
|
class="list-select-label"
|
||||||
for="variables-selector"
|
for="variables-selector"
|
||||||
>
|
>
|
||||||
{{ $t('settings.style.themes3.variables.label') }}
|
{{ $t('settings.style.themes3.editor.variables.label') }}
|
||||||
{{ ' ' }}
|
{{ ' ' }}
|
||||||
</label>
|
</label>
|
||||||
<Select
|
<Select
|
||||||
id="variables-selector"
|
id="variables-selector"
|
||||||
v-model="selectedVirtualDirectiveId"
|
v-model="selectedVirtualDirectiveId"
|
||||||
class="list-select"
|
class="list-select"
|
||||||
size="9"
|
size="20"
|
||||||
>
|
>
|
||||||
<option
|
<option
|
||||||
v-for="(p, index) in virtualDirectives"
|
v-for="(p, index) in virtualDirectives"
|
||||||
|
|
@ -323,7 +366,8 @@
|
||||||
</Select>
|
</Select>
|
||||||
<SelectMotion
|
<SelectMotion
|
||||||
class="list-select-movement"
|
class="list-select-movement"
|
||||||
v-model="virtualDirectives"
|
:modelValue="virtualDirectives"
|
||||||
|
@update:modelValue="onVirtualDirectivesUpdate"
|
||||||
:selected-id="selectedVirtualDirectiveId"
|
:selected-id="selectedVirtualDirectiveId"
|
||||||
:get-add-value="getNewVirtualDirective"
|
:get-add-value="getNewVirtualDirective"
|
||||||
@update:selectedId="e => selectedVirtualDirectiveId = e"
|
@update:selectedId="e => selectedVirtualDirectiveId = e"
|
||||||
|
|
@ -334,7 +378,7 @@
|
||||||
class="variable-name-label"
|
class="variable-name-label"
|
||||||
for="variables-selector"
|
for="variables-selector"
|
||||||
>
|
>
|
||||||
{{ $t('settings.style.themes3.variables.name_label') }}
|
{{ $t('settings.style.themes3.editor.variables.name_label') }}
|
||||||
{{ ' ' }}
|
{{ ' ' }}
|
||||||
</label>
|
</label>
|
||||||
<input
|
<input
|
||||||
|
|
@ -345,25 +389,36 @@
|
||||||
class="variable-type-label"
|
class="variable-type-label"
|
||||||
for="variables-selector"
|
for="variables-selector"
|
||||||
>
|
>
|
||||||
{{ $t('settings.style.themes3.variables.type_label') }}
|
{{ $t('settings.style.themes3.editor.variables.type_label') }}
|
||||||
{{ ' ' }}
|
{{ ' ' }}
|
||||||
</label>
|
</label>
|
||||||
<Select
|
<Select
|
||||||
v-model="selectedVirtualDirective.valType"
|
v-model="selectedVirtualDirectiveValType"
|
||||||
>
|
>
|
||||||
<option value='shadow'>
|
<option value='shadow'>
|
||||||
{{ $t('settings.style.themes3.variables.type_label') }}
|
{{ $t('settings.style.themes3.editor.variables.type_shadow') }}
|
||||||
shadow</option>
|
</option>
|
||||||
<option value='shadow'>color</option>
|
<option value='color'>
|
||||||
<option value='shadow'>generic</option>
|
{{ $t('settings.style.themes3.editor.variables.type_color') }}
|
||||||
|
</option>
|
||||||
|
<option value='generic'>
|
||||||
|
{{ $t('settings.style.themes3.editor.variables.type_generic') }}
|
||||||
|
</option>
|
||||||
</Select>
|
</Select>
|
||||||
</div>
|
</div>
|
||||||
<ShadowControl
|
<ShadowControl
|
||||||
v-if="selectedVirtualDirective.valType === 'shadow'"
|
v-if="selectedVirtualDirectiveValType === 'shadow'"
|
||||||
v-model="selectedVirtualDirectiveParsed"
|
v-model="selectedVirtualDirectiveParsed"
|
||||||
|
:computeColor="computeColor"
|
||||||
:compact="true"
|
:compact="true"
|
||||||
/>
|
/>
|
||||||
</div>
|
<ColorInput
|
||||||
|
v-if="selectedVirtualDirectiveValType === 'color'"
|
||||||
|
v-model="selectedVirtualDirectiveParsed"
|
||||||
|
:fallback="computeColor(selectedVirtualDirectiveParsed)"
|
||||||
|
:label="$t('settings.style.themes3.editor.variables.virtual_color')"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</tab-switcher>
|
</tab-switcher>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -46,6 +46,7 @@ export default {
|
||||||
'separateInset',
|
'separateInset',
|
||||||
'noPreview',
|
'noPreview',
|
||||||
'disabled',
|
'disabled',
|
||||||
|
'computeColor',
|
||||||
'compact'
|
'compact'
|
||||||
],
|
],
|
||||||
emits: ['update:modelValue', 'subShadowSelected'],
|
emits: ['update:modelValue', 'subShadowSelected'],
|
||||||
|
|
@ -107,6 +108,13 @@ export default {
|
||||||
usingFallback () {
|
usingFallback () {
|
||||||
return this.modelValue == null
|
return this.modelValue == null
|
||||||
},
|
},
|
||||||
|
getFallback () {
|
||||||
|
if (typeof this.computeColor === 'function' && this.selected?.color) {
|
||||||
|
return this.computeColor(this.selected.color)
|
||||||
|
} else {
|
||||||
|
return this.currentFallback?.color
|
||||||
|
}
|
||||||
|
},
|
||||||
style () {
|
style () {
|
||||||
try {
|
try {
|
||||||
if (this.separateInset) {
|
if (this.separateInset) {
|
||||||
|
|
|
||||||
|
|
@ -5,8 +5,6 @@
|
||||||
grid-template-areas: "selector preview tweak";
|
grid-template-areas: "selector preview tweak";
|
||||||
grid-gap: 0.5em;
|
grid-gap: 0.5em;
|
||||||
justify-content: stretch;
|
justify-content: stretch;
|
||||||
margin-bottom: 1em;
|
|
||||||
width: 100%;
|
|
||||||
|
|
||||||
&.-compact {
|
&.-compact {
|
||||||
grid-template-columns: 10em 1fr;
|
grid-template-columns: 10em 1fr;
|
||||||
|
|
@ -112,7 +110,7 @@
|
||||||
|
|
||||||
.shadow-preview {
|
.shadow-preview {
|
||||||
grid-area: preview;
|
grid-area: preview;
|
||||||
min-width: 10em;
|
min-width: 25em;
|
||||||
margin-left: 0.125em;
|
margin-left: 0.125em;
|
||||||
align-self: start;
|
align-self: start;
|
||||||
justify-self: center;
|
justify-self: center;
|
||||||
|
|
|
||||||
|
|
@ -165,7 +165,7 @@
|
||||||
:model-value="selected?.color"
|
:model-value="selected?.color"
|
||||||
:disabled="disabled || !present"
|
:disabled="disabled || !present"
|
||||||
:label="$t('settings.style.common.color')"
|
:label="$t('settings.style.common.color')"
|
||||||
:fallback="currentFallback?.color"
|
:fallback="getFallback"
|
||||||
:show-optional-tickbox="false"
|
:show-optional-tickbox="false"
|
||||||
name="shadow"
|
name="shadow"
|
||||||
@update:modelValue="e => updateProperty('color', e)"
|
@update:modelValue="e => updateProperty('color', e)"
|
||||||
|
|
|
||||||
|
|
@ -757,7 +757,8 @@
|
||||||
"themes3": {
|
"themes3": {
|
||||||
"define": "Override",
|
"define": "Override",
|
||||||
"palette": {
|
"palette": {
|
||||||
"label": "Palettes",
|
"label": "Color schemes",
|
||||||
|
"name_label": "Color scheme name",
|
||||||
"import": "Import",
|
"import": "Import",
|
||||||
"export": "Export",
|
"export": "Export",
|
||||||
"bg": "Panel background",
|
"bg": "Panel background",
|
||||||
|
|
@ -774,11 +775,6 @@
|
||||||
"extra3": "Extra 3",
|
"extra3": "Extra 3",
|
||||||
"v2_unsupported": "Older v2 themes don't support palettes. Switch to v3 theme to make use of palettes"
|
"v2_unsupported": "Older v2 themes don't support palettes. Switch to v3 theme to make use of palettes"
|
||||||
},
|
},
|
||||||
"variables": {
|
|
||||||
"label": "Variables",
|
|
||||||
"name_label": "Name:",
|
|
||||||
"type_label": "Type:"
|
|
||||||
},
|
|
||||||
"editor": {
|
"editor": {
|
||||||
"title": "Style",
|
"title": "Style",
|
||||||
"new_style": "New",
|
"new_style": "New",
|
||||||
|
|
@ -798,6 +794,9 @@
|
||||||
"icon_color": "Icon color",
|
"icon_color": "Icon color",
|
||||||
"link_color": "Link color",
|
"link_color": "Link color",
|
||||||
"include_in_rule": "Add to rule",
|
"include_in_rule": "Add to rule",
|
||||||
|
"test_string": "TEST",
|
||||||
|
"refresh_preview": "Refresh preview",
|
||||||
|
"apply_preview": "Apply",
|
||||||
"text_auto": {
|
"text_auto": {
|
||||||
"label": "Auto-contrast",
|
"label": "Auto-contrast",
|
||||||
"no-preserve": "Black or White",
|
"no-preserve": "Black or White",
|
||||||
|
|
@ -805,8 +804,17 @@
|
||||||
"no-auto": "Disabled"
|
"no-auto": "Disabled"
|
||||||
},
|
},
|
||||||
"component_tab": "Components style",
|
"component_tab": "Components style",
|
||||||
"palette_tab": "Color presets",
|
"palette_tab": "Color schemes",
|
||||||
"variables_tab": "Variables (Advanced)"
|
"variables_tab": "Variables (Advanced)",
|
||||||
|
"variables": {
|
||||||
|
"label": "Variables",
|
||||||
|
"name_label": "Name:",
|
||||||
|
"type_label": "Type:",
|
||||||
|
"type_shadow": "Shadow",
|
||||||
|
"type_color": "Color",
|
||||||
|
"type_generic": "Generic",
|
||||||
|
"virtual_color": "Variable color value"
|
||||||
|
},
|
||||||
},
|
},
|
||||||
"hacks": {
|
"hacks": {
|
||||||
"underlay_overrides": "Change underlay",
|
"underlay_overrides": "Change underlay",
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,8 @@ export const newExporter = ({
|
||||||
|
|
||||||
// Create an invisible link with a data url and simulate a click
|
// Create an invisible link with a data url and simulate a click
|
||||||
const e = document.createElement('a')
|
const e = document.createElement('a')
|
||||||
e.setAttribute('download', `${filename}.${extension}`)
|
const realFilename = typeof filename === 'function' ? filename() : filename
|
||||||
|
e.setAttribute('download', `${realFilename}.${extension}`)
|
||||||
e.setAttribute('href', `data:${mime};base64, ${window.btoa(stringified)}`)
|
e.setAttribute('href', `data:${mime};base64, ${window.btoa(stringified)}`)
|
||||||
e.style.display = 'none'
|
e.style.display = 'none'
|
||||||
|
|
||||||
|
|
@ -28,6 +29,7 @@ export const newExporter = ({
|
||||||
|
|
||||||
export const newImporter = ({
|
export const newImporter = ({
|
||||||
accept = '.json',
|
accept = '.json',
|
||||||
|
parser = (string) => JSON.parse(string),
|
||||||
onImport,
|
onImport,
|
||||||
onImportFailure,
|
onImportFailure,
|
||||||
validator = () => true
|
validator = () => true
|
||||||
|
|
@ -44,7 +46,7 @@ export const newImporter = ({
|
||||||
const reader = new FileReader()
|
const reader = new FileReader()
|
||||||
reader.onload = ({ target }) => {
|
reader.onload = ({ target }) => {
|
||||||
try {
|
try {
|
||||||
const parsed = JSON.parse(target.result)
|
const parsed = parser(target.result)
|
||||||
const validationResult = validator(parsed, filename)
|
const validationResult = validator(parsed, filename)
|
||||||
if (validationResult === true) {
|
if (validationResult === true) {
|
||||||
onImport(parsed, filename)
|
onImport(parsed, filename)
|
||||||
|
|
|
||||||
|
|
@ -21,7 +21,11 @@ export const parseShadow = string => {
|
||||||
const regex = new RegExp(regexPrep, 'gis') // global, (stable) indices, single-string
|
const regex = new RegExp(regexPrep, 'gis') // global, (stable) indices, single-string
|
||||||
const result = regex.exec(string)
|
const result = regex.exec(string)
|
||||||
if (result == null) {
|
if (result == null) {
|
||||||
return string
|
if (string.startsWith('$') || string.startsWith('--')) {
|
||||||
|
return string
|
||||||
|
} else {
|
||||||
|
throw new Error(`Invalid shadow definition: ${string}`)
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
const numeric = new Set(['x', 'y', 'blur', 'spread', 'alpha'])
|
const numeric = new Set(['x', 'y', 'blur', 'spread', 'alpha'])
|
||||||
const { x, y, blur, spread, alpha, inset, color } = Object.fromEntries(modes.map((mode, i) => {
|
const { x, y, blur, spread, alpha, inset, color } = Object.fromEntries(modes.map((mode, i) => {
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
import { unroll } from './iss_utils.js'
|
import { unroll } from './iss_utils.js'
|
||||||
|
|
||||||
const serializeShadow = s => {
|
export const serializeShadow = (s, throwOnInvalid) => {
|
||||||
if (typeof s === 'object') {
|
if (typeof s === 'object') {
|
||||||
return `${s.inset ? 'inset ' : ''}${s.x} ${s.y} ${s.blur} ${s.spread} ${s.color} / ${s.alpha}`
|
return `${s.inset ? 'inset ' : ''}${s.x} ${s.y} ${s.blur} ${s.spread} ${s.color} / ${s.alpha}`
|
||||||
} else {
|
} else {
|
||||||
|
|
|
||||||
|
|
@ -62,7 +62,7 @@ const findShadow = (shadows, { dynamicVars, staticVars }) => {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
const findColor = (color, { dynamicVars, staticVars }) => {
|
export const findColor = (color, { dynamicVars, staticVars }) => {
|
||||||
if (typeof color !== 'string' || (!color.startsWith('--') && !color.startsWith('$'))) return color
|
if (typeof color !== 'string' || (!color.startsWith('--') && !color.startsWith('$'))) return color
|
||||||
let targetColor = null
|
let targetColor = null
|
||||||
if (color.startsWith('--')) {
|
if (color.startsWith('--')) {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue