Merge branch 'themes3-grand-finale-maybe' into shigusegubu-themes3

This commit is contained in:
Henry Jameson 2024-10-28 21:23:14 +02:00
commit a023c44bee
9 changed files with 435 additions and 368 deletions

View file

@ -5,6 +5,10 @@
flex: 1 1 auto; flex: 1 1 auto;
} }
.opt {
margin-right: 0.5em;
}
&-field.input { &-field.input {
display: inline-flex; display: inline-flex;
flex: 0 0 0; flex: 0 0 0;

View file

@ -103,6 +103,7 @@
> >
</div> </div>
<ColorInput <ColorInput
v-if="!noColorControl"
class="input-color-input" class="input-color-input"
v-model="colorOverride" v-model="colorOverride"
fallback="#606060" fallback="#606060"
@ -128,19 +129,19 @@ export default {
'previewStyle', 'previewStyle',
'previewCss', 'previewCss',
'disabled', 'disabled',
'invalid' 'invalid',
'noColorControl'
], ],
emits: ['update:shadow'], emits: ['update:shadow'],
data () { data () {
return { return {
colorOverride: null, colorOverride: undefined,
lightGrid: false, lightGrid: false,
zoom: 100 zoom: 100
} }
}, },
computed: { computed: {
style () { style () {
console.log(this.previewStyle)
const result = [ const result = [
this.previewStyle, this.previewStyle,
`zoom: ${this.zoom / 100}` `zoom: ${this.zoom / 100}`

View file

@ -72,6 +72,7 @@ const moveUp = () => {
const moveDnValid = computed(() => { const moveDnValid = computed(() => {
return props.selectedId < props.modelValue.length - 1 return props.selectedId < props.modelValue.length - 1
}) })
const moveDn = () => { const moveDn = () => {
const newModel = [...props.modelValue] const newModel = [...props.modelValue]
const movable = newModel.splice(props.selectedId.value, 1)[0] const movable = newModel.splice(props.selectedId.value, 1)[0]

View file

@ -1,5 +1,6 @@
import { ref, reactive, computed, watch } from 'vue' import { ref, reactive, computed, watch, provide } from 'vue'
import { get, set } from 'lodash' import { useStore } from 'vuex'
import { get, set, unset } from 'lodash'
import Select from 'src/components/select/select.vue' import Select from 'src/components/select/select.vue'
import SelectMotion from 'src/components/select/select_motion.vue' import SelectMotion from 'src/components/select/select_motion.vue'
@ -15,12 +16,14 @@ 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 Preview from '../theme_tab/theme_preview.vue'
import VirtualDirectivesTab from './virtual_directives_tab.vue'
import { init, findColor } from 'src/services/theme_data/theme_data_3.service.js' import { init, findColor } from 'src/services/theme_data/theme_data_3.service.js'
import { import {
getCssRules, getCssRules,
getScopedVersion getScopedVersion
} from 'src/services/theme_data/css_utils.js' } from 'src/services/theme_data/css_utils.js'
import { serializeShadow, serialize } from 'src/services/theme_data/iss_serializer.js' import { serialize } from 'src/services/theme_data/iss_serializer.js'
import { deserializeShadow, deserialize } from 'src/services/theme_data/iss_deserializer.js' import { deserializeShadow, deserialize } from 'src/services/theme_data/iss_deserializer.js'
import { import {
rgb2hex, rgb2hex,
@ -70,10 +73,12 @@ export default {
PaletteEditor, PaletteEditor,
OpacityInput, OpacityInput,
ContrastRatio, ContrastRatio,
Preview Preview,
VirtualDirectivesTab
}, },
setup () { setup (props, context) {
const exports = {} const exports = {}
const store = useStore()
// All rules that are made by editor // All rules that are made by editor
const allEditedRules = reactive({}) const allEditedRules = reactive({})
@ -97,7 +102,7 @@ export default {
// ## Palette stuff // ## Palette stuff
const palettes = reactive([ const palettes = reactive([
{ {
name: 'dark', name: 'default',
bg: '#121a24', bg: '#121a24',
fg: '#182230', fg: '#182230',
text: '#b9b9ba', text: '#b9b9ba',
@ -147,6 +152,7 @@ export default {
}) })
exports.selectedPaletteId = selectedPaletteId exports.selectedPaletteId = selectedPaletteId
exports.selectedPalette = selectedPalette exports.selectedPalette = selectedPalette
provide('selectedPalette', selectedPalette)
exports.getNewPalette = () => ({ exports.getNewPalette = () => ({
name: 'new palette', name: 'new palette',
@ -186,19 +192,20 @@ export default {
const componentKeys = [...componentsMap.keys()] const componentKeys = [...componentsMap.keys()]
exports.componentKeys = componentKeys exports.componentKeys = componentKeys
// selection basis // Component list and selection
const selectedComponentKey = ref(componentsMap.keys().next().value) const selectedComponentKey = ref(componentsMap.keys().next().value)
exports.selectedComponentKey = selectedComponentKey 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)
// Selection basis
exports.selectedComponentVariants = computed(() => { exports.selectedComponentVariants = computed(() => {
return Object.keys({ normal: null, ...(selectedComponent.value.variants || {}) }) return Object.keys({ normal: null, ...(selectedComponent.value.variants || {}) })
}) })
const selectedComponentStatesAll = computed(() => {
return Object.keys({ normal: null, ...(selectedComponent.value.states || {}) })
})
exports.selectedComponentStates = computed(() => { exports.selectedComponentStates = computed(() => {
return selectedComponentStatesAll.value.filter(x => x !== 'normal') const all = Object.keys({ normal: null, ...(selectedComponent.value.states || {}) })
return all.filter(x => x !== 'normal')
}) })
// selection // selection
@ -214,6 +221,17 @@ export default {
} }
} }
// Reset variant and state on component change
const updateSelectedComponent = () => {
selectedVariant.value = 'normal'
selectedState.clear()
}
watch(
selectedComponentName,
updateSelectedComponent
)
// ### Rules stuff aka meat and potatoes // ### Rules stuff aka meat and potatoes
// 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
@ -270,13 +288,13 @@ export default {
return root return root
}) })
// Checkging whether component can support some "directives" which // Checking 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
exports.componentHas = (subComponent) => { exports.componentHas = (subComponent) => {
return !!selectedComponent.value.validInnerComponents?.find(x => x === subComponent) return !!selectedComponent.value.validInnerComponents?.find(x => x === subComponent)
} }
// Path is path for lodash's get and set // Path for lodash's get and set
const getPath = (component, directive) => { const getPath = (component, directive) => {
const pathSuffix = component ? `._children.${component}.normal.normal` : '' const pathSuffix = component ? `._children.${component}.normal.normal` : ''
const path = `${selectedComponentName.value}.${selectedVariant.value}.${normalizeStates([...selectedState])}${pathSuffix}.directives.${directive}` const path = `${selectedComponentName.value}.${selectedVariant.value}.${normalizeStates([...selectedState])}${pathSuffix}.directives.${directive}`
@ -296,7 +314,7 @@ export default {
) )
set(allEditedRules, getPath(component, directive), fallback ?? defaultValue) set(allEditedRules, getPath(component, directive), fallback ?? defaultValue)
} else { } else {
set(allEditedRules, getPath(component, directive), null) unset(allEditedRules, getPath(component, directive))
} }
} }
}) })
@ -313,14 +331,14 @@ export default {
usedRule = get(fallback, path) usedRule = get(fallback, path)
} }
if (directive === 'shadow') {
console.log('EDITED', usedRule)
console.log('PP', postProcess(usedRule))
}
return postProcess(usedRule) return postProcess(usedRule)
}, },
set (value) { set (value) {
set(allEditedRules, getPath(component, directive), value) if (value) {
set(allEditedRules, getPath(component, directive), value)
} else {
unset(allEditedRules, getPath(component, directive))
}
} }
}) })
@ -378,6 +396,7 @@ export default {
return null return null
}) })
} }
provide('normalizeShadows', normalizeShadows)
// Shadow is partially edited outside the ShadowControl // Shadow is partially edited outside the ShadowControl
// for better space utilization // for better space utilization
@ -428,57 +447,6 @@ export default {
} }
return styles.join('; ') 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')
exports.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 previewRules = reactive([])
exports.previewRules = previewRules
exports.previewCss = computed(() => {
try {
const scoped = getCssRules(previewRules).map(simulatePseudoSelectors)
return scoped.join('\n')
} catch (e) {
console.error('Invalid ruleset', e)
return null
}
})
const applicablePreviewRules = computed(() => {
return previewRules.filter(rule => {
const filterable = rule.parent ? rule.parent : rule
const variantMatches = filterable.variant === selectedVariant.value
const stateMatches = filterable.state.filter(x => x !== 'normal').every(x => selectedState.has(x))
return variantMatches && stateMatches
})
})
const previewColors = computed(() => ({
text: applicablePreviewRules.value.find(r => r.component === 'Text')?.virtualDirectives['--text'],
link: applicablePreviewRules.value.find(r => r.component === 'Link')?.virtualDirectives['--link'],
border: applicablePreviewRules.value.find(r => r.component === 'Border')?.virtualDirectives['--border'],
icon: applicablePreviewRules.value.find(r => r.component === 'Icon')?.virtualDirectives['--icon'],
background: applicablePreviewRules.value.find(r => r.parent == null)?.dynamicVars.stacked
}))
exports.previewColors = previewColors
const editorFriendlyToOriginal = computed(() => { const editorFriendlyToOriginal = computed(() => {
const resultRules = [] const resultRules = []
@ -514,64 +482,13 @@ export default {
}) })
} }
convert(selectedComponentName.value, allEditedRules[selectedComponentName.value]) componentsMap.values().forEach(({ name }) => {
convert(name, allEditedRules[name])
})
return resultRules return resultRules
}) })
const updatePreview = () => {
try {
const { name, ...paletteData } = selectedPalette.value
// This normally would be handled by Root but since we pass something
// else we have to make do ourselves
paletteData.accent = paletteData.accent || paletteData.link
paletteData.link = paletteData.link || paletteData.accent
const rules = init({
inputRuleset: editorFriendlyToOriginal.value,
initialStaticVars: {
...paletteData
},
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()
// export and import
watch(
allEditedRules,
updatePreview
)
watch(
palettes,
updatePreview
)
watch(
selectedPalette,
updatePreview
)
watch(
selectedComponentName,
updateSelectedComponent
)
// ## Variables
const allCustomVirtualDirectives = [...componentsMap.values()] const allCustomVirtualDirectives = [...componentsMap.values()]
.map(c => { .map(c => {
return c return c
@ -590,124 +507,29 @@ export default {
value: valVal.trim() value: valVal.trim()
} }
}) })
const virtualDirectives = reactive(allCustomVirtualDirectives)
const virtualDirectives = ref(allCustomVirtualDirectives)
exports.virtualDirectives = virtualDirectives exports.virtualDirectives = virtualDirectives
exports.updateVirtualDirectives = (value) => {
exports.onVirtualDirectivesUpdate = (e) => { virtualDirectives.value = value
virtualDirectives.splice(0, virtualDirectives.length)
virtualDirectives.push(...e)
} }
const selectedVirtualDirectiveId = ref(0)
exports.selectedVirtualDirectiveId = selectedVirtualDirectiveId
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) {
const newValType = value
let newValue
switch (value) {
case 'shadow':
newValue = '0 0 0 #000000 / 1'
break
case 'color':
newValue = '#000000'
break
default:
newValue = 'none'
}
const newName = virtualDirectives[selectedVirtualDirectiveId.value].name
virtualDirectives[selectedVirtualDirectiveId.value] = {
name: newName,
value: newValue,
valType: newValType
}
}
})
const draftVirtualDirectiveValid = ref(true)
const draftVirtualDirective = ref({})
exports.draftVirtualDirective = draftVirtualDirective
watch(
selectedVirtualDirective,
(directive) => {
switch (directive.valType) {
case 'shadow': {
if (Array.isArray(directive.value)) {
draftVirtualDirective.value = normalizeShadows(directive.value)
} else {
const splitShadow = directive.value.split(/,/g).map(x => x.trim())
draftVirtualDirective.value = normalizeShadows(splitShadow)
}
break
}
case 'color':
draftVirtualDirective.value = directive.value
break
default:
draftVirtualDirective.value = directive.value
break
}
},
{ immediate: true }
)
watch(
draftVirtualDirective,
(directive) => {
try {
switch (selectedVirtualDirective.value.valType) {
case 'shadow': {
virtualDirectives[selectedVirtualDirectiveId.value].value =
directive.map(x => serializeShadow(x)).join(', ')
break
}
default:
virtualDirectives[selectedVirtualDirectiveId.value].value = directive
}
draftVirtualDirectiveValid.value = true
} catch (e) {
console.error('Invalid virtual directive value', e)
draftVirtualDirectiveValid.value = false
}
},
{ immediate: true }
)
const virtualDirectivesOut = computed(() => { const virtualDirectivesOut = computed(() => {
return [ return [
'Root {', 'Root {',
...virtualDirectives.map(vd => ` --${vd.name}: ${vd.valType} | ${vd.value};`), ...virtualDirectives.value.map(vd => ` --${vd.name}: ${vd.valType} | ${vd.value};`),
'}' '}'
].join('\n') ].join('\n')
}) })
exports.getNewVirtualDirective = () => ({
name: 'newDirective',
valType: 'generic',
value: 'foobar'
})
exports.computeColor = (color) => { exports.computeColor = (color) => {
const computedColor = findColor(color, { dynamicVars: {}, staticVars: selectedPalette.value }) const computedColor = findColor(color, { dynamicVars: dynamicVars.value, staticVars: selectedPalette.value })
if (computedColor) { if (computedColor) {
return rgb2hex(computedColor) return rgb2hex(computedColor)
} }
return null return null
} }
provide('computeColor', exports.computeColor)
exports.contrast = computed(() => { exports.contrast = computed(() => {
return getContrast( return getContrast(
@ -716,48 +538,24 @@ export default {
) )
}) })
const overallPreviewRules = ref() const paletteRule = computed(() => {
exports.overallPreviewRules = overallPreviewRules const { name, ...rest } = selectedPalette.value
exports.updateOverallPreview = () => { return {
try { component: 'Root',
// This normally would be handled by Root but since we pass something directives: Object
// else we have to make do ourselves .entries(rest)
.filter(([k, v]) => v)
const { name, ...rest } = selectedPalette.value .map(([k, v]) => ['--' + k, v])
const paletteRule = { .reduce((acc, [k, v]) => ({ ...acc, [k]: `color | ${v}` }), {})
component: 'Root',
directives: Object
.entries(rest)
.map(([k, v]) => ['--' + k, v])
.reduce((acc, [k, v]) => ({ ...acc, [k]: `color | ${v}` }), {})
}
const virtualDirectivesRule = {
component: 'Root',
directives: Object.fromEntries(
virtualDirectives.map(vd => [`--${vd.name}`, `${vd.valType} | ${vd.value}`])
)
}
const rules = init({
inputRuleset: [
paletteRule,
virtualDirectivesRule,
...editorFriendlyToOriginal.value
],
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)
} }
} })
const virtualDirectivesRule = computed(() => ({
component: 'Root',
directives: Object.fromEntries(
virtualDirectives.value.map(vd => [`--${vd.name}`, `${vd.valType} | ${vd.value}`])
)
}))
// ## Export and Import // ## Export and Import
const styleExporter = newExporter({ const styleExporter = newExporter({
@ -782,14 +580,13 @@ export default {
exports.author.value = metaIn.author exports.author.value = metaIn.author
exports.website.value = metaIn.website exports.website.value = metaIn.website
virtualDirectives.splice(0, virtualDirectives.length)
const newVirtualDirectives = Object const newVirtualDirectives = Object
.entries(rootComponent.directives) .entries(rootComponent.directives)
.map(([name, value]) => { .map(([name, value]) => {
const [valType, valVal] = value.split('|').map(x => x.trim()) const [valType, valVal] = value.split('|').map(x => x.trim())
return { name: name.substring(2), valType, value: valVal } return { name: name.substring(2), valType, value: valVal }
}) })
virtualDirectives.push(...newVirtualDirectives) virtualDirectives.value = newVirtualDirectives
onPalettesUpdate(palettesIn.map(x => ({ name: x.variant, ...x.directives }))) onPalettesUpdate(palettesIn.map(x => ({ name: x.variant, ...x.directives })))
@ -801,6 +598,8 @@ export default {
allEditedRules allEditedRules
) )
}) })
exports.updateOverallPreview()
} }
}) })
@ -821,6 +620,133 @@ export default {
styleImporter.importData() styleImporter.importData()
} }
const exportRules = computed(() => [
paletteRule.value,
virtualDirectivesRule.value,
...editorFriendlyToOriginal.value
])
exports.applyStyle = () => {
store.dispatch('setStyleCustom', exportRules.value)
}
const overallPreviewRules = ref([])
exports.overallPreviewRules = overallPreviewRules
const overallPreviewCssRules = computed(() => getScopedVersion(
getCssRules(overallPreviewRules.value),
'#edited-style-preview'
).join('\n'))
exports.overallPreviewCssRules = overallPreviewCssRules
const updateOverallPreview = () => {
try {
overallPreviewRules.value = init({
inputRuleset: exportRules.value,
ultimateBackgroundColor: '#000000',
liteMode: true,
debug: true
}).eager
} catch (e) {
console.error('Could not compile preview theme', e)
return null
}
}
//
// Apart from "hover" we can't really show how component looks like in
// certain states, so we have to fake them.
const simulatePseudoSelectors = (css, prefix) => css
.replace(prefix, '.component-preview .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 = computed(() => {
const filtered = overallPreviewRules.value.filter(r => {
const componentMatch = r.component === selectedComponentName.value
const parentComponentMatch = r.parent?.component === selectedComponentName.value
if (!componentMatch && !parentComponentMatch) return false
const rule = parentComponentMatch ? r.parent : r
if (rule.component !== selectedComponentName.value) return false
if (rule.variant !== selectedVariant.value) return false
const ruleState = new Set(rule.state.filter(x => x !== 'normal'))
const differenceA = [...ruleState].filter(x => !selectedState.has(x))
const differenceB = [...selectedState].filter(x => !ruleState.has(x))
return (differenceA.length + differenceB.length) === 0
})
const sorted = [...filtered]
.filter(x => x.component === selectedComponentName.value)
.sort((a, b) => {
const aSelectorLength = a.selector.split(/ /g).length
const bSelectorLength = b.selector.split(/ /g).length
return aSelectorLength - bSelectorLength
})
const prefix = sorted[0].selector
return filtered.filter(x => x.selector.startsWith(prefix))
})
exports.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]
console.log('ORIG', original)
selectors.push(simulatePseudoSelectors(original))
})
}
return selectors.map(x => x.substring(1)).join('')
})
exports.previewCss = computed(() => {
try {
const prefix = previewRules.value[0].selector
const scoped = getCssRules(previewRules.value).map(x => simulatePseudoSelectors(x, prefix))
return scoped.join('\n')
} catch (e) {
console.error('Invalid ruleset', e)
return null
}
})
const dynamicVars = computed(() => {
return previewRules.value[0].dynamicVars
})
const previewColors = computed(() => {
const stacked = dynamicVars.value.stacked
const background = typeof stacked === 'string' ? stacked : rgb2hex(stacked)
return {
text: previewRules.value.find(r => r.component === 'Text')?.virtualDirectives['--text'],
link: previewRules.value.find(r => r.component === 'Link')?.virtualDirectives['--link'],
border: previewRules.value.find(r => r.component === 'Border')?.virtualDirectives['--border'],
icon: previewRules.value.find(r => r.component === 'Icon')?.virtualDirectives['--icon'],
background
}
})
exports.previewColors = previewColors
exports.updateOverallPreview = updateOverallPreview
updateOverallPreview()
watch(
[
allEditedRules,
palettes,
selectedPalette,
selectedState,
selectedVariant
],
updateOverallPreview
)
return exports return exports
} }
} }

View file

@ -7,7 +7,7 @@
<!-- eslint-disable vue/no-v-text-v-html-on-component --> <!-- eslint-disable vue/no-v-text-v-html-on-component -->
<component <component
:is="'style'" :is="'style'"
v-html="overallPreviewRules" v-html="overallPreviewCssRules"
/> />
<!-- eslint-enable vue/no-v-text-v-html-on-component --> <!-- eslint-enable vue/no-v-text-v-html-on-component -->
<Preview id="edited-style-preview" /> <Preview id="edited-style-preview" />
@ -41,7 +41,7 @@
</button> </button>
<button <button
class="btn button-default button-apply" class="btn button-default button-apply"
@click="applyTheme" @click="applyStyle"
> >
<FAIcon icon="check" /> <FAIcon icon="check" />
{{ $t('settings.style.themes3.editor.apply_preview') }} {{ $t('settings.style.themes3.editor.apply_preview') }}
@ -151,6 +151,7 @@
:preview-css="previewCss" :preview-css="previewCss"
:disabled="!editedSubShadow && typeof editedShadow !== 'string'" :disabled="!editedSubShadow && typeof editedShadow !== 'string'"
:shadow="editedSubShadow" :shadow="editedSubShadow"
:no-color-control="true"
@update:shadow="({ axis, value }) => updateSubShadow(axis, value)" @update:shadow="({ axis, value }) => updateSubShadow(axis, value)"
/> />
</div> </div>
@ -355,89 +356,13 @@
/> />
</div> </div>
</div> </div>
<div <VirtualDirectivesTab
key="variables" key="variables"
:label="$t('settings.style.themes3.editor.variables_tab')" :label="$t('settings.style.themes3.editor.variables_tab')"
class="setting-item list-editor variables-editor" :model-value="virtualDirectives"
> @update:modelValue="updateVirtualDirectives"
<label :normalize-shadows="normalizeShadows"
class="list-select-label" />
for="variables-selector"
>
{{ $t('settings.style.themes3.editor.variables.label') }}
{{ ' ' }}
</label>
<Select
id="variables-selector"
v-model="selectedVirtualDirectiveId"
class="list-select"
size="20"
>
<option
v-for="(p, index) in virtualDirectives"
:key="p.name"
:value="index"
>
{{ p.name }}
</option>
</Select>
<SelectMotion
class="list-select-movement"
:modelValue="virtualDirectives"
@update:modelValue="onVirtualDirectivesUpdate"
:selected-id="selectedVirtualDirectiveId"
:get-add-value="getNewVirtualDirective"
@update:selectedId="e => selectedVirtualDirectiveId = e"
/>
<div class="list-edit-area">
<div class="variable-selector">
<label
class="variable-name-label"
for="variables-selector"
>
{{ $t('settings.style.themes3.editor.variables.name_label') }}
{{ ' ' }}
</label>
<input
class="input"
v-model="selectedVirtualDirective.name"
>
<label
class="variable-type-label"
for="variables-selector"
>
{{ $t('settings.style.themes3.editor.variables.type_label') }}
{{ ' ' }}
</label>
<Select
v-model="selectedVirtualDirectiveValType"
>
<option value='shadow'>
{{ $t('settings.style.themes3.editor.variables.type_shadow') }}
</option>
<option value='color'>
{{ $t('settings.style.themes3.editor.variables.type_color') }}
</option>
<option value='generic'>
{{ $t('settings.style.themes3.editor.variables.type_generic') }}
</option>
</Select>
</div>
<ShadowControl
v-if="selectedVirtualDirectiveValType === 'shadow'"
v-model="draftVirtualDirective"
:static-vars="selectedPalette"
:compact="true"
/>
<ColorInput
v-if="selectedVirtualDirectiveValType === 'color'"
v-model="draftVirtualDirective"
:fallback="computeColor(draftVirtualDirective)"
:label="$t('settings.style.themes3.editor.variables.virtual_color')"
:hide-optional-checkbox="true"
/>
</div>
</div>
</tab-switcher> </tab-switcher>
</div> </div>
</template> </template>

View file

@ -0,0 +1,132 @@
import { ref, computed, watch, inject } from 'vue'
import Select from 'src/components/select/select.vue'
import SelectMotion from 'src/components/select/select_motion.vue'
import ShadowControl from 'src/components/shadow_control/shadow_control.vue'
import ColorInput from 'src/components/color_input/color_input.vue'
import { serializeShadow } from 'src/services/theme_data/iss_serializer.js'
// helper for debugging
// eslint-disable-next-line no-unused-vars
const toValue = (x) => JSON.parse(JSON.stringify(x === undefined ? 'null' : x))
export default {
components: {
Select,
SelectMotion,
ShadowControl,
ColorInput
},
props: ['modelValue'],
emits: ['update:modelValue'],
setup (props, context) {
const exports = {}
const emit = context.emit
exports.emit = emit
exports.computeColor = inject('computeColor')
exports.selectedPalette = inject('selectedPalette')
const selectedVirtualDirectiveId = ref(0)
exports.selectedVirtualDirectiveId = selectedVirtualDirectiveId
const selectedVirtualDirective = computed({
get () {
return props.modelValue[selectedVirtualDirectiveId.value]
},
set (value) {
const newVD = [...props.modelValue]
newVD[selectedVirtualDirectiveId.value] = value
emit('update:modelValue', newVD)
}
})
exports.selectedVirtualDirective = selectedVirtualDirective
exports.selectedVirtualDirectiveValType = computed({
get () {
return props.modelValue[selectedVirtualDirectiveId.value].valType
},
set (value) {
const newValType = value
let newValue
switch (value) {
case 'shadow':
newValue = '0 0 0 #000000 / 1'
break
case 'color':
newValue = '#000000'
break
default:
newValue = 'none'
}
const newName = props.modelValue[selectedVirtualDirectiveId.value].name
props.modelValue[selectedVirtualDirectiveId.value] = {
name: newName,
value: newValue,
valType: newValType
}
}
})
const draftVirtualDirectiveValid = ref(true)
const draftVirtualDirective = ref({})
exports.draftVirtualDirective = draftVirtualDirective
const normalizeShadows = inject('normalizeShadows')
watch(
selectedVirtualDirective,
(directive) => {
switch (directive.valType) {
case 'shadow': {
if (Array.isArray(directive.value)) {
draftVirtualDirective.value = normalizeShadows(directive.value)
} else {
const splitShadow = directive.value.split(/,/g).map(x => x.trim())
draftVirtualDirective.value = normalizeShadows(splitShadow)
}
break
}
case 'color':
draftVirtualDirective.value = directive.value
break
default:
draftVirtualDirective.value = directive.value
break
}
},
{ immediate: true }
)
watch(
draftVirtualDirective,
(directive) => {
try {
switch (selectedVirtualDirective.value.valType) {
case 'shadow': {
props.modelValue[selectedVirtualDirectiveId.value].value =
directive.map(x => serializeShadow(x)).join(', ')
break
}
default:
props.modelValue[selectedVirtualDirectiveId.value].value = directive
}
draftVirtualDirectiveValid.value = true
} catch (e) {
console.error('Invalid virtual directive value', e)
draftVirtualDirectiveValid.value = false
}
},
{ immediate: true }
)
exports.getNewVirtualDirective = () => ({
name: 'newDirective',
valType: 'generic',
value: 'foobar'
})
return exports
}
}

View file

@ -0,0 +1,83 @@
<script src="./virtual_directives_tab.js"></script>
<template>
<div class="setting-item list-editor variables-editor">
<label
class="list-select-label"
for="variables-selector"
>
{{ $t('settings.style.themes3.editor.variables.label') }}
{{ ' ' }}
</label>
<Select
id="variables-selector"
v-model="selectedVirtualDirectiveId"
class="list-select"
size="20"
>
<option
v-for="(p, index) in modelValue"
:key="p.name"
:value="index"
>
{{ p.name }}
</option>
</Select>
<SelectMotion
class="list-select-movement"
:model-value="modelValue"
@update:modelValue="e => emit('update:modelValue', e)"
:selected-id="selectedVirtualDirectiveId"
@update:selectedId="e => selectedVirtualDirectiveId = e"
:get-add-value="getNewVirtualDirective"
/>
<div class="list-edit-area">
<div class="variable-selector">
<label
class="variable-name-label"
for="variables-selector"
>
{{ $t('settings.style.themes3.editor.variables.name_label') }}
{{ ' ' }}
</label>
<input
class="input"
v-model="selectedVirtualDirective.name"
>
<label
class="variable-type-label"
for="variables-selector"
>
{{ $t('settings.style.themes3.editor.variables.type_label') }}
{{ ' ' }}
</label>
<Select
v-model="selectedVirtualDirectiveValType"
>
<option value='shadow'>
{{ $t('settings.style.themes3.editor.variables.type_shadow') }}
</option>
<option value='color'>
{{ $t('settings.style.themes3.editor.variables.type_color') }}
</option>
<option value='generic'>
{{ $t('settings.style.themes3.editor.variables.type_generic') }}
</option>
</Select>
</div>
<ShadowControl
v-if="selectedVirtualDirectiveValType === 'shadow'"
v-model="draftVirtualDirective"
:static-vars="selectedPalette"
:compact="true"
/>
<ColorInput
v-if="selectedVirtualDirectiveValType === 'color'"
v-model="draftVirtualDirective"
:fallback="computeColor(draftVirtualDirective)"
:label="$t('settings.style.themes3.editor.variables.virtual_color')"
:hide-optional-checkbox="true"
/>
</div>
</div>
</template>

View file

@ -351,10 +351,10 @@ const interfaceMod = {
let majorVersionUsed let majorVersionUsed
console.log( console.log(
`USER V3 palette: ${userPaletteName}, style: ${userStyleName} ` `USER V3 palette: ${userPaletteName}, style: ${userStyleName} , custom: ${!!userStyleCustomData}`
) )
console.log( console.log(
`USER V2 name: ${userThemeV2Name}, source: ${userThemeV2Source}, snapshot: ${userThemeV2Snapshot}` `USER V2 name: ${userThemeV2Name}, source: ${!!userThemeV2Source}, snapshot: ${!!userThemeV2Snapshot}`
) )
console.log(`INST V3 palette: ${instancePaletteName}, style: ${instanceStyleName}`) console.log(`INST V3 palette: ${instancePaletteName}, style: ${instanceStyleName}`)
@ -411,8 +411,8 @@ const interfaceMod = {
let styleDataUsed = null let styleDataUsed = null
let styleNameUsed = null let styleNameUsed = null
let paletteDataUsed = null let paletteDataUsed = null
let paletteNameUsed = null // let paletteNameUsed = null
let themeNameUsed = null // let themeNameUsed = null
let themeDataUsed = null let themeDataUsed = null
const getData = async (resource, index, customData, name) => { const getData = async (resource, index, customData, name) => {
@ -455,7 +455,7 @@ const interfaceMod = {
userPaletteCustomData, userPaletteCustomData,
userPaletteName || instancePaletteName userPaletteName || instancePaletteName
) )
paletteNameUsed = palette.nameUsed // paletteNameUsed = palette.nameUsed
paletteDataUsed = palette.dataUsed paletteDataUsed = palette.dataUsed
if (Array.isArray(paletteDataUsed)) { if (Array.isArray(paletteDataUsed)) {
const [ const [
@ -471,9 +471,8 @@ const interfaceMod = {
] = paletteDataUsed ] = paletteDataUsed
paletteDataUsed = { name, background, foreground, text, link, cRed, cBlue, cGreen, cOrange } paletteDataUsed = { name, background, foreground, text, link, cRed, cBlue, cGreen, cOrange }
} }
console.log('PAL', userPaletteName, paletteNameUsed)
console.log('PAL', paletteDataUsed)
console.log('USCD', userStyleCustomData)
const style = await getData( const style = await getData(
'style', 'style',
stylesIndex, stylesIndex,
@ -489,15 +488,13 @@ const interfaceMod = {
userThemeV2Source || userThemeV2Snapshot, userThemeV2Source || userThemeV2Snapshot,
userThemeV2Name || instanceThemeV2Name userThemeV2Name || instanceThemeV2Name
) )
themeNameUsed = theme.nameUsed // themeNameUsed = theme.nameUsed
themeDataUsed = theme.dataUsed themeDataUsed = theme.dataUsed
// Themes v2 editor support // Themes v2 editor support
commit('setInstanceOption', { name: 'themeData', value: themeDataUsed }) commit('setInstanceOption', { name: 'themeData', value: themeDataUsed })
} }
console.log('STYLE', styleNameUsed, paletteNameUsed, themeNameUsed)
// commit('setOption', { name: 'palette', value: paletteNameUsed }) // commit('setOption', { name: 'palette', value: paletteNameUsed })
// commit('setOption', { name: 'style', value: styleNameUsed }) // commit('setOption', { name: 'style', value: styleNameUsed })
// commit('setOption', { name: 'theme', value: themeNameUsed }) // commit('setOption', { name: 'theme', value: themeNameUsed })

View file

@ -173,8 +173,6 @@ export const getEngineChecksum = () => engineChecksum
* @param {boolean} onlyNormalState - only use components 'normal' states, meant for generating theme * @param {boolean} onlyNormalState - only use components 'normal' states, meant for generating theme
* previews since states are the biggest factor for compilation time and are completely unnecessary * previews since states are the biggest factor for compilation time and are completely unnecessary
* when previewing multiple themes at same time * when previewing multiple themes at same time
* @param {string} rootComponentName - [UNTESTED] which component to start from, meant for previewing a
* part of the theme (i.e. just the button) for themes 3 editor.
*/ */
export const init = ({ export const init = ({
inputRuleset, inputRuleset,
@ -183,9 +181,9 @@ export const init = ({
liteMode = false, liteMode = false,
editMode = false, editMode = false,
onlyNormalState = false, onlyNormalState = false,
rootComponentName = 'Root',
initialStaticVars = {} initialStaticVars = {}
}) => { }) => {
const rootComponentName = 'Root'
if (!inputRuleset) throw new Error('Ruleset is null or undefined!') if (!inputRuleset) throw new Error('Ruleset is null or undefined!')
const staticVars = { ...initialStaticVars } const staticVars = { ...initialStaticVars }
const stacked = {} const stacked = {}