Merge branch 'themes3-grand-finale-maybe' into shigusegubu-themes3
This commit is contained in:
commit
0562fe1c44
13 changed files with 534 additions and 343 deletions
|
|
@ -11,7 +11,7 @@
|
||||||
{{ label }}
|
{{ label }}
|
||||||
</label>
|
</label>
|
||||||
<Checkbox
|
<Checkbox
|
||||||
v-if="typeof fallback !== 'undefined' && showOptionalTickbox"
|
v-if="typeof fallback !== 'undefined' && showOptionalCheckbox && !hideOptionalCheckbox"
|
||||||
:model-value="present"
|
:model-value="present"
|
||||||
:disabled="disabled"
|
:disabled="disabled"
|
||||||
class="opt"
|
class="opt"
|
||||||
|
|
@ -112,10 +112,16 @@ export default {
|
||||||
default: false
|
default: false
|
||||||
},
|
},
|
||||||
// Show "optional" tickbox, for when value might become mandatory
|
// Show "optional" tickbox, for when value might become mandatory
|
||||||
showOptionalTickbox: {
|
showOptionalCheckbox: {
|
||||||
required: false,
|
required: false,
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
default: true
|
default: true
|
||||||
|
},
|
||||||
|
// Force "optional" tickbox to hide
|
||||||
|
hideOptionalCheckbox: {
|
||||||
|
required: false,
|
||||||
|
type: Boolean,
|
||||||
|
default: false
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
emits: ['update:modelValue'],
|
emits: ['update:modelValue'],
|
||||||
|
|
|
||||||
|
|
@ -11,19 +11,50 @@
|
||||||
<!-- eslint-enable vue/no-v-html vue/no-v-text-v-html-on-component -->
|
<!-- eslint-enable vue/no-v-html vue/no-v-text-v-html-on-component -->
|
||||||
<label
|
<label
|
||||||
v-show="shadowControl"
|
v-show="shadowControl"
|
||||||
|
role="heading"
|
||||||
class="header"
|
class="header"
|
||||||
:class="{ faint: disabled }"
|
:class="{ faint: disabled }"
|
||||||
>
|
>
|
||||||
{{ $t('settings.style.shadows.offset') }}
|
{{ $t('settings.style.shadows.offset') }}
|
||||||
</label>
|
</label>
|
||||||
|
<label
|
||||||
|
v-show="shadowControl && !hideControls"
|
||||||
|
class="x-shift-number"
|
||||||
|
>
|
||||||
|
{{ $t('settings.style.shadows.offset-x') }}
|
||||||
|
<input
|
||||||
|
:value="shadow?.x"
|
||||||
|
:disabled="disabled"
|
||||||
|
:class="{ disabled }"
|
||||||
|
class="input input-number"
|
||||||
|
type="number"
|
||||||
|
@input="e => updateProperty('x', e.target.value)"
|
||||||
|
>
|
||||||
|
</label>
|
||||||
|
<label
|
||||||
|
class="y-shift-number"
|
||||||
|
v-show="shadowControl && !hideControls"
|
||||||
|
>
|
||||||
|
{{ $t('settings.style.shadows.offset-y') }}
|
||||||
|
<input
|
||||||
|
:value="shadow?.y"
|
||||||
|
:disabled="disabled"
|
||||||
|
:class="{ disabled }"
|
||||||
|
class="input input-number"
|
||||||
|
type="number"
|
||||||
|
@input="e => updateProperty('y', e.target.value)"
|
||||||
|
>
|
||||||
|
</label>
|
||||||
<input
|
<input
|
||||||
v-show="shadowControl && !hideControls"
|
v-show="shadowControl && !hideControls"
|
||||||
:value="shadow?.y"
|
:value="shadow?.x"
|
||||||
:disabled="disabled"
|
:disabled="disabled"
|
||||||
:class="{ disabled }"
|
:class="{ disabled }"
|
||||||
class="input input-number y-shift-number"
|
class="input input-range x-shift-slider"
|
||||||
type="number"
|
type="range"
|
||||||
@input="e => updateProperty('y', e.target.value)"
|
max="20"
|
||||||
|
min="-20"
|
||||||
|
@input="e => updateProperty('x', e.target.value)"
|
||||||
>
|
>
|
||||||
<input
|
<input
|
||||||
v-show="shadowControl && !hideControls"
|
v-show="shadowControl && !hideControls"
|
||||||
|
|
@ -43,7 +74,7 @@
|
||||||
<div
|
<div
|
||||||
class="preview-block"
|
class="preview-block"
|
||||||
:class="previewClass"
|
:class="previewClass"
|
||||||
:style="previewStyle"
|
:style="style"
|
||||||
>
|
>
|
||||||
{{ $t('settings.style.themes3.editor.test_string') }}
|
{{ $t('settings.style.themes3.editor.test_string') }}
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -53,43 +84,42 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<input
|
<div class="assists">
|
||||||
v-show="shadowControl && !hideControls"
|
<Checkbox
|
||||||
:value="shadow?.x"
|
v-model="lightGrid"
|
||||||
:disabled="disabled"
|
name="lightGrid"
|
||||||
:class="{ disabled }"
|
class="input-light-grid"
|
||||||
class="input input-number x-shift-number"
|
>
|
||||||
type="number"
|
{{ $t('settings.style.shadows.light_grid') }}
|
||||||
@input="e => updateProperty('x', e.target.value)"
|
</Checkbox>
|
||||||
>
|
<div class="style-control">
|
||||||
<input
|
<label class="label">
|
||||||
v-show="shadowControl && !hideControls"
|
{{ $t('settings.style.shadows.zoom') }}
|
||||||
:value="shadow?.x"
|
</label>
|
||||||
:disabled="disabled"
|
<input
|
||||||
:class="{ disabled }"
|
v-model="zoom"
|
||||||
class="input input-range x-shift-slider"
|
class="input input-number y-shift-number"
|
||||||
type="range"
|
type="number"
|
||||||
max="20"
|
>
|
||||||
min="-20"
|
</div>
|
||||||
@input="e => updateProperty('x', e.target.value)"
|
<ColorInput
|
||||||
>
|
class="input-color-input"
|
||||||
<Checkbox
|
v-model="colorOverride"
|
||||||
id="lightGrid"
|
fallback="#606060"
|
||||||
v-model="lightGrid"
|
:label="$t('settings.style.shadows.color_override')"
|
||||||
name="lightGrid"
|
/>
|
||||||
class="input-light-grid"
|
</div>
|
||||||
>
|
|
||||||
{{ $t('settings.style.shadows.light_grid') }}
|
|
||||||
</Checkbox>
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import Checkbox from 'src/components/checkbox/checkbox.vue'
|
import Checkbox from 'src/components/checkbox/checkbox.vue'
|
||||||
|
import ColorInput from 'src/components/color_input/color_input.vue'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
Checkbox
|
Checkbox,
|
||||||
|
ColorInput
|
||||||
},
|
},
|
||||||
props: [
|
props: [
|
||||||
'shadow',
|
'shadow',
|
||||||
|
|
@ -103,10 +133,21 @@ export default {
|
||||||
emits: ['update:shadow'],
|
emits: ['update:shadow'],
|
||||||
data () {
|
data () {
|
||||||
return {
|
return {
|
||||||
lightGrid: false
|
colorOverride: null,
|
||||||
|
lightGrid: false,
|
||||||
|
zoom: 100
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
|
style () {
|
||||||
|
console.log(this.previewStyle)
|
||||||
|
const result = [
|
||||||
|
this.previewStyle,
|
||||||
|
`zoom: ${this.zoom / 100}`
|
||||||
|
]
|
||||||
|
if (this.colorOverride) result.push(`--background: ${this.colorOverride}`)
|
||||||
|
return result
|
||||||
|
},
|
||||||
hideControls () {
|
hideControls () {
|
||||||
return typeof this.shadow === 'string'
|
return typeof this.shadow === 'string'
|
||||||
}
|
}
|
||||||
|
|
@ -121,16 +162,27 @@ export default {
|
||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
.ComponentPreview {
|
.ComponentPreview {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: 3em 1fr 3em;
|
grid-template-columns: 1em 1fr 1fr 1em;
|
||||||
grid-template-rows: 2em 1fr 2em;
|
grid-template-rows: 2em 1fr 1fr 1fr 1em 2em max-content;
|
||||||
grid-template-areas:
|
grid-template-areas:
|
||||||
". header y-num "
|
"header header header header "
|
||||||
". preview y-slide"
|
"preview preview preview y-slide"
|
||||||
"x-num x-slide . "
|
"preview preview preview y-slide"
|
||||||
"options options options";
|
"preview preview preview y-slide"
|
||||||
|
"x-slide x-slide x-slide . "
|
||||||
|
"x-num x-num y-num y-num "
|
||||||
|
"assists assists assists assists";
|
||||||
grid-gap: 0.5em;
|
grid-gap: 0.5em;
|
||||||
max-width: 25em;
|
|
||||||
max-height: 25em;
|
&:not(.-shadow-controls) {
|
||||||
|
grid-template-areas:
|
||||||
|
"header header header header "
|
||||||
|
"preview preview preview y-slide"
|
||||||
|
"preview preview preview y-slide"
|
||||||
|
"preview preview preview y-slide"
|
||||||
|
"assists assists assists assists";
|
||||||
|
grid-template-rows: 2em 1fr 1fr 1fr max-content;
|
||||||
|
}
|
||||||
|
|
||||||
.header {
|
.header {
|
||||||
grid-area: header;
|
grid-area: header;
|
||||||
|
|
@ -155,8 +207,15 @@ export default {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.assists {
|
||||||
|
grid-area: assists;
|
||||||
|
display: grid;
|
||||||
|
grid-auto-flow: rows;
|
||||||
|
grid-auto-rows: 2em;
|
||||||
|
grid-gap: 0.5em;
|
||||||
|
}
|
||||||
|
|
||||||
.input-light-grid {
|
.input-light-grid {
|
||||||
grid-area: options;
|
|
||||||
justify-self: center;
|
justify-self: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -166,6 +225,19 @@ export default {
|
||||||
|
|
||||||
.x-shift-number {
|
.x-shift-number {
|
||||||
grid-area: x-num;
|
grid-area: x-num;
|
||||||
|
justify-self: right;
|
||||||
|
}
|
||||||
|
|
||||||
|
.y-shift-number {
|
||||||
|
grid-area: y-num;
|
||||||
|
justify-self: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
.x-shift-number,
|
||||||
|
.y-shift-number {
|
||||||
|
input {
|
||||||
|
max-width: 4em;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.x-shift-slider {
|
.x-shift-slider {
|
||||||
|
|
@ -175,10 +247,6 @@ export default {
|
||||||
min-width: 10em;
|
min-width: 10em;
|
||||||
}
|
}
|
||||||
|
|
||||||
.y-shift-number {
|
|
||||||
grid-area: y-num;
|
|
||||||
}
|
|
||||||
|
|
||||||
.y-shift-slider {
|
.y-shift-slider {
|
||||||
grid-area: y-slide;
|
grid-area: y-slide;
|
||||||
writing-mode: vertical-lr;
|
writing-mode: vertical-lr;
|
||||||
|
|
|
||||||
|
|
@ -3,39 +3,44 @@
|
||||||
v-if="contrast"
|
v-if="contrast"
|
||||||
class="contrast-ratio"
|
class="contrast-ratio"
|
||||||
>
|
>
|
||||||
<span
|
<span v-if="showRatio">
|
||||||
:title="hint"
|
{{ contrast.text }}
|
||||||
|
</span>
|
||||||
|
<Tooltip
|
||||||
|
:text="hint"
|
||||||
class="rating"
|
class="rating"
|
||||||
>
|
>
|
||||||
<span v-if="contrast.aaa">
|
<span v-if="contrast.aaa">
|
||||||
<FAIcon icon="thumbs-up" />
|
<FAIcon icon="thumbs-up" :size="showRatio ? 'lg' : ''" />
|
||||||
</span>
|
</span>
|
||||||
<span v-if="!contrast.aaa && contrast.aa">
|
<span v-if="!contrast.aaa && contrast.aa">
|
||||||
<FAIcon icon="adjust" />
|
<FAIcon icon="adjust" :size="showRatio ? 'lg' : ''" />
|
||||||
</span>
|
</span>
|
||||||
<span v-if="!contrast.aaa && !contrast.aa">
|
<span v-if="!contrast.aaa && !contrast.aa">
|
||||||
<FAIcon icon="exclamation-triangle" />
|
<FAIcon icon="exclamation-triangle" :size="showRatio ? 'lg' : ''" />
|
||||||
</span>
|
</span>
|
||||||
</span>
|
</Tooltip>
|
||||||
<span
|
<Tooltip
|
||||||
v-if="contrast && large"
|
v-if="contrast && large"
|
||||||
|
:text="hint_18pt"
|
||||||
class="rating"
|
class="rating"
|
||||||
:title="hint_18pt"
|
|
||||||
>
|
>
|
||||||
<span v-if="contrast.laaa">
|
<span v-if="contrast.laaa">
|
||||||
<FAIcon icon="thumbs-up" />
|
<FAIcon icon="thumbs-up" :size="showRatio ? 'large' : ''" />
|
||||||
</span>
|
</span>
|
||||||
<span v-if="!contrast.laaa && contrast.laa">
|
<span v-if="!contrast.laaa && contrast.laa">
|
||||||
<FAIcon icon="adjust" />
|
<FAIcon icon="adjust" :size="showRatio ? 'lg' : ''" />
|
||||||
</span>
|
</span>
|
||||||
<span v-if="!contrast.laaa && !contrast.laa">
|
<span v-if="!contrast.laaa && !contrast.laa">
|
||||||
<FAIcon icon="exclamation-triangle" />
|
<FAIcon icon="exclamation-triangle" :size="showRatio ? 'lg' : ''" />
|
||||||
</span>
|
</span>
|
||||||
</span>
|
</Tooltip>
|
||||||
</span>
|
</span>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
import Tooltip from 'src/components/tooltip/tooltip.vue'
|
||||||
|
|
||||||
import { library } from '@fortawesome/fontawesome-svg-core'
|
import { library } from '@fortawesome/fontawesome-svg-core'
|
||||||
import {
|
import {
|
||||||
faAdjust,
|
faAdjust,
|
||||||
|
|
@ -62,8 +67,16 @@ export default {
|
||||||
required: false,
|
required: false,
|
||||||
type: Object,
|
type: Object,
|
||||||
default: () => ({})
|
default: () => ({})
|
||||||
|
},
|
||||||
|
showRatio: {
|
||||||
|
required: false,
|
||||||
|
type: Boolean,
|
||||||
|
default: false
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
components: {
|
||||||
|
Tooltip
|
||||||
|
},
|
||||||
computed: {
|
computed: {
|
||||||
hint () {
|
hint () {
|
||||||
const levelVal = this.contrast.aaa ? 'aaa' : (this.contrast.aa ? 'aa' : 'bad')
|
const levelVal = this.contrast.aaa ? 'aaa' : (this.contrast.aa ? 'aa' : 'bad')
|
||||||
|
|
@ -87,8 +100,7 @@ export default {
|
||||||
.contrast-ratio {
|
.contrast-ratio {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: flex-end;
|
justify-content: flex-end;
|
||||||
margin-top: -4px;
|
align-items: baseline;
|
||||||
margin-bottom: 5px;
|
|
||||||
|
|
||||||
.label {
|
.label {
|
||||||
margin-right: 1em;
|
margin-right: 1em;
|
||||||
|
|
@ -96,7 +108,6 @@ export default {
|
||||||
|
|
||||||
.rating {
|
.rating {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
text-align: center;
|
|
||||||
margin-left: 0.5em;
|
margin-left: 0.5em;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -75,9 +75,17 @@ const paletteKeys = [
|
||||||
'cBlue',
|
'cBlue',
|
||||||
'cGreen',
|
'cGreen',
|
||||||
'cOrange',
|
'cOrange',
|
||||||
|
'wallpaper',
|
||||||
'extra1',
|
'extra1',
|
||||||
'extra2',
|
'extra2',
|
||||||
'extra3'
|
'extra3',
|
||||||
|
'extra4',
|
||||||
|
'extra5',
|
||||||
|
'extra6',
|
||||||
|
'extra7',
|
||||||
|
'extra8',
|
||||||
|
'extra9',
|
||||||
|
'extra10'
|
||||||
]
|
]
|
||||||
|
|
||||||
const fallback = (key) => {
|
const fallback = (key) => {
|
||||||
|
|
@ -88,6 +96,9 @@ const fallback = (key) => {
|
||||||
return props.modelValue.accent
|
return props.modelValue.accent
|
||||||
}
|
}
|
||||||
if (key.startsWith('extra')) {
|
if (key.startsWith('extra')) {
|
||||||
|
return '#FF00FF'
|
||||||
|
}
|
||||||
|
if (key.startsWith('wallpaper')) {
|
||||||
return '#008080'
|
return '#008080'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -105,7 +116,7 @@ const updatePalette = (paletteKey, value) => {
|
||||||
display: grid;
|
display: grid;
|
||||||
justify-content: space-around;
|
justify-content: space-around;
|
||||||
grid-template-columns: repeat(4, 1fr);
|
grid-template-columns: repeat(4, 1fr);
|
||||||
grid-template-rows: repeat(3, 1fr) auto;
|
grid-template-rows: repeat(5, 1fr) auto;
|
||||||
grid-gap: 0.5em;
|
grid-gap: 0.5em;
|
||||||
align-items: baseline;
|
align-items: baseline;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -340,9 +340,7 @@ export default {
|
||||||
exports.editedBorderColor = getEditedElement('Border', 'textColor')
|
exports.editedBorderColor = getEditedElement('Border', 'textColor')
|
||||||
exports.isBorderColorPresent = isElementPresent('Border', 'textColor', '#909090')
|
exports.isBorderColorPresent = isElementPresent('Border', 'textColor', '#909090')
|
||||||
|
|
||||||
// TODO this is VERY primitive right now, need to make it
|
const getContrast = (bg, text) => {
|
||||||
// support variables, fallbacks etc.
|
|
||||||
exports.getContrast = (bg, text) => {
|
|
||||||
try {
|
try {
|
||||||
const bgRgb = hex2rgb(bg)
|
const bgRgb = hex2rgb(bg)
|
||||||
const textRgb = hex2rgb(text)
|
const textRgb = hex2rgb(text)
|
||||||
|
|
@ -464,6 +462,24 @@ export default {
|
||||||
return null
|
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 = []
|
||||||
|
|
||||||
|
|
@ -600,16 +616,23 @@ export default {
|
||||||
return virtualDirectives[selectedVirtualDirectiveId.value].valType
|
return virtualDirectives[selectedVirtualDirectiveId.value].valType
|
||||||
},
|
},
|
||||||
set (value) {
|
set (value) {
|
||||||
virtualDirectives[selectedVirtualDirectiveId.value].valType = value
|
const newValType = value
|
||||||
|
let newValue
|
||||||
switch (value) {
|
switch (value) {
|
||||||
case 'shadow':
|
case 'shadow':
|
||||||
virtualDirectives[selectedVirtualDirectiveId.value].value = '0 0 0 #000000'
|
newValue = '0 0 0 #000000 / 1'
|
||||||
break
|
break
|
||||||
case 'color':
|
case 'color':
|
||||||
virtualDirectives[selectedVirtualDirectiveId.value].value = '#000000'
|
newValue = '#000000'
|
||||||
break
|
break
|
||||||
default:
|
default:
|
||||||
virtualDirectives[selectedVirtualDirectiveId.value].value = 'none'
|
newValue = 'none'
|
||||||
|
}
|
||||||
|
const newName = virtualDirectives[selectedVirtualDirectiveId.value].name
|
||||||
|
virtualDirectives[selectedVirtualDirectiveId.value] = {
|
||||||
|
name: newName,
|
||||||
|
value: newValue,
|
||||||
|
valType: newValType
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
@ -664,6 +687,14 @@ export default {
|
||||||
{ immediate: true }
|
{ immediate: true }
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const virtualDirectivesOut = computed(() => {
|
||||||
|
return [
|
||||||
|
'Root {',
|
||||||
|
...virtualDirectives.map(vd => ` --${vd.name}: ${vd.valType} | ${vd.value};`),
|
||||||
|
'}'
|
||||||
|
].join('\n')
|
||||||
|
})
|
||||||
|
|
||||||
exports.getNewVirtualDirective = () => ({
|
exports.getNewVirtualDirective = () => ({
|
||||||
name: 'newDirective',
|
name: 'newDirective',
|
||||||
valType: 'generic',
|
valType: 'generic',
|
||||||
|
|
@ -678,6 +709,13 @@ export default {
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
exports.contrast = computed(() => {
|
||||||
|
return getContrast(
|
||||||
|
exports.computeColor(previewColors.value.background),
|
||||||
|
exports.computeColor(previewColors.value.text)
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
const overallPreviewRules = ref()
|
const overallPreviewRules = ref()
|
||||||
exports.overallPreviewRules = overallPreviewRules
|
exports.overallPreviewRules = overallPreviewRules
|
||||||
exports.updateOverallPreview = () => {
|
exports.updateOverallPreview = () => {
|
||||||
|
|
@ -694,10 +732,18 @@ export default {
|
||||||
.reduce((acc, [k, v]) => ({ ...acc, [k]: `color | ${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({
|
const rules = init({
|
||||||
inputRuleset: [
|
inputRuleset: [
|
||||||
...editorFriendlyToOriginal.value,
|
paletteRule,
|
||||||
paletteRule
|
virtualDirectivesRule,
|
||||||
|
...editorFriendlyToOriginal.value
|
||||||
],
|
],
|
||||||
ultimateBackgroundColor: '#000000',
|
ultimateBackgroundColor: '#000000',
|
||||||
liteMode: true,
|
liteMode: true,
|
||||||
|
|
@ -726,7 +772,8 @@ export default {
|
||||||
parser: (string) => deserialize(string),
|
parser: (string) => deserialize(string),
|
||||||
onImport (parsed, filename) {
|
onImport (parsed, filename) {
|
||||||
const editorComponents = parsed.filter(x => x.component.startsWith('@'))
|
const editorComponents = parsed.filter(x => x.component.startsWith('@'))
|
||||||
const rules = parsed.filter(x => !x.component.startsWith('@'))
|
const rootComponent = parsed.find(x => x.component === 'Root')
|
||||||
|
const rules = parsed.filter(x => !x.component.startsWith('@') && x.component !== 'Root')
|
||||||
const metaIn = editorComponents.find(x => x.component === '@meta').directives
|
const metaIn = editorComponents.find(x => x.component === '@meta').directives
|
||||||
const palettesIn = editorComponents.filter(x => x.component === '@palette')
|
const palettesIn = editorComponents.filter(x => x.component === '@palette')
|
||||||
|
|
||||||
|
|
@ -735,8 +782,16 @@ 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
|
||||||
|
.entries(rootComponent.directives)
|
||||||
|
.map(([name, value]) => {
|
||||||
|
const [valType, valVal] = value.split('|').map(x => x.trim())
|
||||||
|
return { name: name.substring(2), valType, value: valVal }
|
||||||
|
})
|
||||||
|
virtualDirectives.push(...newVirtualDirectives)
|
||||||
|
|
||||||
onPalettesUpdate(palettesIn.map(x => ({ name: x.variant, ...x.directives })))
|
onPalettesUpdate(palettesIn.map(x => ({ name: x.variant, ...x.directives })))
|
||||||
console.log('PALETTES', palettesIn)
|
|
||||||
|
|
||||||
Object.keys(allEditedRules).forEach((k) => delete allEditedRules[k])
|
Object.keys(allEditedRules).forEach((k) => delete allEditedRules[k])
|
||||||
|
|
||||||
|
|
@ -753,6 +808,7 @@ export default {
|
||||||
return [
|
return [
|
||||||
metaOut.value,
|
metaOut.value,
|
||||||
palettesOut.value,
|
palettesOut.value,
|
||||||
|
virtualDirectivesOut.value,
|
||||||
serialize(editorFriendlyToOriginal.value)
|
serialize(editorFriendlyToOriginal.value)
|
||||||
].join('\n\n')
|
].join('\n\n')
|
||||||
})
|
})
|
||||||
|
|
|
||||||
|
|
@ -166,27 +166,21 @@
|
||||||
>
|
>
|
||||||
<ColorInput
|
<ColorInput
|
||||||
v-model="editedBackgroundColor"
|
v-model="editedBackgroundColor"
|
||||||
:fallback="computeColor(editedBackgroundColor)"
|
:fallback="computeColor(editedBackgroundColor) ?? previewColors.background"
|
||||||
:disabled="!isBackgroundColorPresent"
|
:disabled="!isBackgroundColorPresent"
|
||||||
:label="$t('settings.style.themes3.editor.background')"
|
:label="$t('settings.style.themes3.editor.background')"
|
||||||
|
:hide-optional-checkbox="true"
|
||||||
/>
|
/>
|
||||||
<Tooltip :text="$t('settings.style.themes3.editor.include_in_rule')">
|
<Tooltip :text="$t('settings.style.themes3.editor.include_in_rule')">
|
||||||
<Checkbox v-model="isBackgroundColorPresent" />
|
<Checkbox v-model="isBackgroundColorPresent" />
|
||||||
</Tooltip>
|
</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
|
<ColorInput
|
||||||
v-if="componentHas('Text')"
|
v-if="componentHas('Text')"
|
||||||
v-model="editedTextColor"
|
v-model="editedTextColor"
|
||||||
:fallback="computeColor(editedTextColor)"
|
:fallback="computeColor(editedTextColor) ?? previewColors.text"
|
||||||
:label="$t('settings.style.themes3.editor.text_color')"
|
:label="$t('settings.style.themes3.editor.text_color')"
|
||||||
:disabled="!isTextColorPresent"
|
:disabled="!isTextColorPresent"
|
||||||
|
:hide-optional-checkbox="true"
|
||||||
/>
|
/>
|
||||||
<Tooltip
|
<Tooltip
|
||||||
v-if="componentHas('Text')"
|
v-if="componentHas('Text')"
|
||||||
|
|
@ -194,7 +188,10 @@
|
||||||
>
|
>
|
||||||
<Checkbox v-model="isTextColorPresent" />
|
<Checkbox v-model="isTextColorPresent" />
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
<div class="style-control suboption">
|
<div
|
||||||
|
v-if="componentHas('Text')"
|
||||||
|
class="style-control suboption"
|
||||||
|
>
|
||||||
<label
|
<label
|
||||||
for="textAuto"
|
for="textAuto"
|
||||||
class="label"
|
class="label"
|
||||||
|
|
@ -224,18 +221,27 @@
|
||||||
>
|
>
|
||||||
<Checkbox v-model="isTextAutoPresent" />
|
<Checkbox v-model="isTextAutoPresent" />
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
<div>
|
<div
|
||||||
<ContrastRatio :contrast="getContrast(editedBackgroundColor, editedTextColor)" />
|
class="style-control suboption"
|
||||||
|
v-if="componentHas('Text')"
|
||||||
|
>
|
||||||
|
<label class="label">
|
||||||
|
{{$t('settings.style.themes3.editor.contrast') }}
|
||||||
|
</label>
|
||||||
|
<ContrastRatio
|
||||||
|
:show-ratio="true"
|
||||||
|
:contrast="contrast"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div v-if="componentHas('Text')">
|
||||||
<!-- spacer for missing checkbox -->
|
|
||||||
</div>
|
</div>
|
||||||
<ColorInput
|
<ColorInput
|
||||||
v-if="componentHas('Link')"
|
v-if="componentHas('Link')"
|
||||||
v-model="editedLinkColor"
|
v-model="editedLinkColor"
|
||||||
:fallback="computeColor(editedLinkColor)"
|
:fallback="computeColor(editedLinkColor) ?? previewColors.link"
|
||||||
:label="$t('settings.style.themes3.editor.link_color')"
|
:label="$t('settings.style.themes3.editor.link_color')"
|
||||||
:disabled="!isLinkColorPresent"
|
:disabled="!isLinkColorPresent"
|
||||||
|
:hide-optional-checkbox="true"
|
||||||
/>
|
/>
|
||||||
<Tooltip
|
<Tooltip
|
||||||
v-if="componentHas('Link')"
|
v-if="componentHas('Link')"
|
||||||
|
|
@ -246,9 +252,10 @@
|
||||||
<ColorInput
|
<ColorInput
|
||||||
v-if="componentHas('Icon')"
|
v-if="componentHas('Icon')"
|
||||||
v-model="editedIconColor"
|
v-model="editedIconColor"
|
||||||
:fallback="computeColor(editedIconColor)"
|
:fallback="computeColor(editedIconColor) ?? previewColors.icon"
|
||||||
:label="$t('settings.style.themes3.editor.icon_color')"
|
:label="$t('settings.style.themes3.editor.icon_color')"
|
||||||
:disabled="!isIconColorPresent"
|
:disabled="!isIconColorPresent"
|
||||||
|
:hide-optional-checkbox="true"
|
||||||
/>
|
/>
|
||||||
<Tooltip
|
<Tooltip
|
||||||
v-if="componentHas('Icon')"
|
v-if="componentHas('Icon')"
|
||||||
|
|
@ -259,9 +266,10 @@
|
||||||
<ColorInput
|
<ColorInput
|
||||||
v-if="componentHas('Border')"
|
v-if="componentHas('Border')"
|
||||||
v-model="editedBorderColor"
|
v-model="editedBorderColor"
|
||||||
:fallback="computeColor(editedBorderColor)"
|
:fallback="computeColor(editedBorderColor) ?? previewColors.border"
|
||||||
:label="$t('settings.style.themes3.editor.Border_color')"
|
:label="$t('settings.style.themes3.editor.border_color')"
|
||||||
:disabled="!isBorderColorPresent"
|
:disabled="!isBorderColorPresent"
|
||||||
|
:hide-optional-checkbox="true"
|
||||||
/>
|
/>
|
||||||
<Tooltip
|
<Tooltip
|
||||||
v-if="componentHas('Border')"
|
v-if="componentHas('Border')"
|
||||||
|
|
@ -269,6 +277,14 @@
|
||||||
>
|
>
|
||||||
<Checkbox v-model="isBorderColorPresent" />
|
<Checkbox v-model="isBorderColorPresent" />
|
||||||
</Tooltip>
|
</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>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
key="shadow"
|
key="shadow"
|
||||||
|
|
@ -418,6 +434,7 @@
|
||||||
v-model="draftVirtualDirective"
|
v-model="draftVirtualDirective"
|
||||||
:fallback="computeColor(draftVirtualDirective)"
|
:fallback="computeColor(draftVirtualDirective)"
|
||||||
:label="$t('settings.style.themes3.editor.variables.virtual_color')"
|
:label="$t('settings.style.themes3.editor.variables.virtual_color')"
|
||||||
|
:hide-optional-checkbox="true"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -187,14 +187,14 @@
|
||||||
name="accentColor"
|
name="accentColor"
|
||||||
:fallback="previewTheme.colors?.link"
|
:fallback="previewTheme.colors?.link"
|
||||||
:label="$t('settings.accent')"
|
:label="$t('settings.accent')"
|
||||||
:show-optional-tickbox="typeof linkColorLocal !== 'undefined'"
|
:show-optional-checkbox="typeof linkColorLocal !== 'undefined'"
|
||||||
/>
|
/>
|
||||||
<ColorInput
|
<ColorInput
|
||||||
v-model="linkColorLocal"
|
v-model="linkColorLocal"
|
||||||
name="linkColor"
|
name="linkColor"
|
||||||
:fallback="previewTheme.colors?.accent"
|
:fallback="previewTheme.colors?.accent"
|
||||||
:label="$t('settings.links')"
|
:label="$t('settings.links')"
|
||||||
:show-optional-tickbox="typeof accentColorLocal !== 'undefined'"
|
:show-optional-checkbox="typeof accentColorLocal !== 'undefined'"
|
||||||
/>
|
/>
|
||||||
<ContrastRatio :contrast="previewContrast.bgLink" />
|
<ContrastRatio :contrast="previewContrast.bgLink" />
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -123,7 +123,7 @@ export default {
|
||||||
try {
|
try {
|
||||||
let result
|
let result
|
||||||
const serialized = this.cValue.map(x => serializeShadow(x)).join(',')
|
const serialized = this.cValue.map(x => serializeShadow(x)).join(',')
|
||||||
deserializeShadow(serialized) // validate
|
serialized.split(/,/).map(deserializeShadow) // validate
|
||||||
const expandedShadow = flattenDeep(findShadow(this.cValue, { dynamicVars: {}, staticVars: this.staticVars }))
|
const expandedShadow = flattenDeep(findShadow(this.cValue, { dynamicVars: {}, staticVars: this.staticVars }))
|
||||||
const fixedShadows = expandedShadow.map(x => ({ ...x, color: console.log(x) || rgb2hex(x.color) }))
|
const fixedShadows = expandedShadow.map(x => ({ ...x, color: console.log(x) || rgb2hex(x.color) }))
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -168,7 +168,7 @@
|
||||||
:disabled="disabled || !present"
|
:disabled="disabled || !present"
|
||||||
:label="$t('settings.style.common.color')"
|
:label="$t('settings.style.common.color')"
|
||||||
:fallback="getColorFallback"
|
:fallback="getColorFallback"
|
||||||
:show-optional-tickbox="false"
|
:show-optional-checkbox="false"
|
||||||
name="shadow"
|
name="shadow"
|
||||||
@update:modelValue="e => updateProperty('color', e)"
|
@update:modelValue="e => updateProperty('color', e)"
|
||||||
/>
|
/>
|
||||||
|
|
|
||||||
|
|
@ -770,9 +770,17 @@
|
||||||
"cBlue": "Blue color",
|
"cBlue": "Blue color",
|
||||||
"cGreen": "Green color",
|
"cGreen": "Green color",
|
||||||
"cOrange": "Orange color",
|
"cOrange": "Orange color",
|
||||||
|
"wallpaper": "Wallpaper",
|
||||||
"extra1": "Extra 1",
|
"extra1": "Extra 1",
|
||||||
"extra2": "Extra 2",
|
"extra2": "Extra 2",
|
||||||
"extra3": "Extra 3",
|
"extra3": "Extra 3",
|
||||||
|
"extra4": "Extra 4",
|
||||||
|
"extra5": "Extra 5",
|
||||||
|
"extra6": "Extra 6",
|
||||||
|
"extra7": "Extra 7",
|
||||||
|
"extra8": "Extra 8",
|
||||||
|
"extra9": "Extra 9",
|
||||||
|
"extra10": "Extra 10",
|
||||||
"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"
|
||||||
},
|
},
|
||||||
"editor": {
|
"editor": {
|
||||||
|
|
@ -793,6 +801,7 @@
|
||||||
"text_color": "Text color",
|
"text_color": "Text color",
|
||||||
"icon_color": "Icon color",
|
"icon_color": "Icon color",
|
||||||
"link_color": "Link color",
|
"link_color": "Link color",
|
||||||
|
"contrast": "Text contrast",
|
||||||
"border_color": "Border color",
|
"border_color": "Border color",
|
||||||
"include_in_rule": "Add to rule",
|
"include_in_rule": "Add to rule",
|
||||||
"test_string": "TEST",
|
"test_string": "TEST",
|
||||||
|
|
@ -940,7 +949,11 @@
|
||||||
"override": "Override",
|
"override": "Override",
|
||||||
"shadow_id": "Shadow #{value}",
|
"shadow_id": "Shadow #{value}",
|
||||||
"offset": "Shadow offset",
|
"offset": "Shadow offset",
|
||||||
|
"zoom": "Zoom",
|
||||||
|
"offset-x": "x:",
|
||||||
|
"offset-y": "y:",
|
||||||
"light_grid": "Use light checkerboard",
|
"light_grid": "Use light checkerboard",
|
||||||
|
"color_override": "Use different color",
|
||||||
"name": "Name",
|
"name": "Name",
|
||||||
"blur": "Blur",
|
"blur": "Blur",
|
||||||
"spread": "Spread",
|
"spread": "Spread",
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,7 @@ export const deserializeShadow = string => {
|
||||||
// spread (optional)
|
// spread (optional)
|
||||||
'(?:(-?[0-9]+(?:\\.[0-9]+)?)\\s+)?',
|
'(?:(-?[0-9]+(?:\\.[0-9]+)?)\\s+)?',
|
||||||
// either hex, variable or function
|
// either hex, variable or function
|
||||||
'(#[0-9a-f]{6}|--[a-z\\-_]+|\\$[a-z\\-()_]+)',
|
'(#[0-9a-f]{6}|--[a-z0-9\\-_]+|\\$[a-z0-9\\-()_]+)',
|
||||||
// opacity (optional)
|
// opacity (optional)
|
||||||
'(?:\\s+\\/\\s+([0-9]+(?:\\.[0-9]+)?)\\s*)?',
|
'(?:\\s+\\/\\s+([0-9]+(?:\\.[0-9]+)?)\\s*)?',
|
||||||
// name
|
// name
|
||||||
|
|
|
||||||
|
|
@ -354,10 +354,6 @@ export const convertTheme2To3 = (data) => {
|
||||||
newRules.push({ ...rule, state: ['toggled'] })
|
newRules.push({ ...rule, state: ['toggled'] })
|
||||||
newRules.push({ ...rule, state: ['toggled', 'focus'] })
|
newRules.push({ ...rule, state: ['toggled', 'focus'] })
|
||||||
newRules.push({ ...rule, state: ['pressed', 'focus'] })
|
newRules.push({ ...rule, state: ['pressed', 'focus'] })
|
||||||
}
|
|
||||||
if (key === 'buttonHover') {
|
|
||||||
newRules.push({ ...rule, state: ['toggled', 'hover'] })
|
|
||||||
newRules.push({ ...rule, state: ['pressed', 'hover'] })
|
|
||||||
newRules.push({ ...rule, state: ['toggled', 'focus', 'hover'] })
|
newRules.push({ ...rule, state: ['toggled', 'focus', 'hover'] })
|
||||||
newRules.push({ ...rule, state: ['pressed', 'focus', 'hover'] })
|
newRules.push({ ...rule, state: ['pressed', 'focus', 'hover'] })
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -63,54 +63,62 @@ export const findShadow = (shadows, { dynamicVars, staticVars }) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
export const findColor = (color, { dynamicVars, staticVars }) => {
|
export const findColor = (color, { dynamicVars, staticVars }) => {
|
||||||
if (typeof color !== 'string' || (!color.startsWith('--') && !color.startsWith('$'))) return color
|
try {
|
||||||
let targetColor = null
|
if (typeof color !== 'string' || (!color.startsWith('--') && !color.startsWith('$'))) return color
|
||||||
if (color.startsWith('--')) {
|
let targetColor = null
|
||||||
// Modifier support is pretty much for v2 themes only
|
if (color.startsWith('--')) {
|
||||||
const [variable, modifier] = color.split(/,/g).map(str => str.trim())
|
// Modifier support is pretty much for v2 themes only
|
||||||
const variableSlot = variable.substring(2)
|
const [variable, modifier] = color.split(/,/g).map(str => str.trim())
|
||||||
if (variableSlot === 'stack') {
|
const variableSlot = variable.substring(2)
|
||||||
const { r, g, b } = dynamicVars.stacked
|
if (variableSlot === 'stack') {
|
||||||
targetColor = { r, g, b }
|
const { r, g, b } = dynamicVars.stacked
|
||||||
} else if (variableSlot.startsWith('parent')) {
|
|
||||||
if (variableSlot === 'parent') {
|
|
||||||
const { r, g, b } = dynamicVars.lowerLevelBackground
|
|
||||||
targetColor = { r, g, b }
|
targetColor = { r, g, b }
|
||||||
|
} else if (variableSlot.startsWith('parent')) {
|
||||||
|
if (variableSlot === 'parent') {
|
||||||
|
const { r, g, b } = dynamicVars.lowerLevelBackground
|
||||||
|
targetColor = { r, g, b }
|
||||||
|
} else {
|
||||||
|
const virtualSlot = variableSlot.replace(/^parent/, '')
|
||||||
|
targetColor = convert(dynamicVars.lowerLevelVirtualDirectivesRaw[virtualSlot]).rgb
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
const virtualSlot = variableSlot.replace(/^parent/, '')
|
switch (variableSlot) {
|
||||||
targetColor = convert(dynamicVars.lowerLevelVirtualDirectivesRaw[virtualSlot]).rgb
|
case 'inheritedBackground':
|
||||||
|
targetColor = convert(dynamicVars.inheritedBackground).rgb
|
||||||
|
break
|
||||||
|
case 'background':
|
||||||
|
targetColor = convert(dynamicVars.background).rgb
|
||||||
|
break
|
||||||
|
default:
|
||||||
|
targetColor = convert(staticVars[variableSlot]).rgb
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
switch (variableSlot) {
|
if (modifier) {
|
||||||
case 'inheritedBackground':
|
const effectiveBackground = dynamicVars.lowerLevelBackground ?? targetColor
|
||||||
targetColor = convert(dynamicVars.inheritedBackground).rgb
|
const isLightOnDark = relativeLuminance(convert(effectiveBackground).rgb) < 0.5
|
||||||
break
|
const mod = isLightOnDark ? 1 : -1
|
||||||
case 'background':
|
targetColor = brightness(Number.parseFloat(modifier) * mod, targetColor).rgb
|
||||||
targetColor = convert(dynamicVars.background).rgb
|
|
||||||
break
|
|
||||||
default:
|
|
||||||
targetColor = convert(staticVars[variableSlot]).rgb
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (modifier) {
|
if (color.startsWith('$')) {
|
||||||
const effectiveBackground = dynamicVars.lowerLevelBackground ?? targetColor
|
try {
|
||||||
const isLightOnDark = relativeLuminance(convert(effectiveBackground).rgb) < 0.5
|
targetColor = process(color, colorFunctions, { findColor }, { dynamicVars, staticVars })
|
||||||
const mod = isLightOnDark ? 1 : -1
|
} catch (e) {
|
||||||
targetColor = brightness(Number.parseFloat(modifier) * mod, targetColor).rgb
|
console.error('Failure executing color function', e)
|
||||||
|
targetColor = '#FF00FF'
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
// Color references other color
|
||||||
|
return targetColor
|
||||||
|
} catch (e) {
|
||||||
|
throw new Error(`Couldn't find color "${color}", variables are:
|
||||||
|
Static:
|
||||||
|
${JSON.stringify(staticVars, null, 2)}
|
||||||
|
Dynamic:
|
||||||
|
${JSON.stringify(dynamicVars, null, 2)}`)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (color.startsWith('$')) {
|
|
||||||
try {
|
|
||||||
targetColor = process(color, colorFunctions, { findColor }, { dynamicVars, staticVars })
|
|
||||||
} catch (e) {
|
|
||||||
console.error('Failure executing color function', e)
|
|
||||||
targetColor = '#FF00FF'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Color references other color
|
|
||||||
return targetColor
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const getTextColorAlpha = (directives, intendedTextColor, dynamicVars, staticVars) => {
|
const getTextColorAlpha = (directives, intendedTextColor, dynamicVars, staticVars) => {
|
||||||
|
|
@ -241,212 +249,217 @@ export const init = ({
|
||||||
const nonEditableComponents = new Set(Object.values(components).filter(c => c.notEditable).map(c => c.name))
|
const nonEditableComponents = new Set(Object.values(components).filter(c => c.notEditable).map(c => c.name))
|
||||||
|
|
||||||
const processCombination = (combination) => {
|
const processCombination = (combination) => {
|
||||||
const selector = ruleToSelector(combination, true)
|
try {
|
||||||
const cssSelector = ruleToSelector(combination)
|
const selector = ruleToSelector(combination, true)
|
||||||
|
const cssSelector = ruleToSelector(combination)
|
||||||
|
|
||||||
const parentSelector = selector.split(/ /g).slice(0, -1).join(' ')
|
const parentSelector = selector.split(/ /g).slice(0, -1).join(' ')
|
||||||
const soloSelector = selector.split(/ /g).slice(-1)[0]
|
const soloSelector = selector.split(/ /g).slice(-1)[0]
|
||||||
|
|
||||||
const lowerLevelSelector = parentSelector
|
const lowerLevelSelector = parentSelector
|
||||||
let lowerLevelBackground = computed[lowerLevelSelector]?.background
|
let lowerLevelBackground = computed[lowerLevelSelector]?.background
|
||||||
if (editMode && !lowerLevelBackground) {
|
if (editMode && !lowerLevelBackground) {
|
||||||
// FIXME hack for editor until it supports handling component backgrounds
|
// FIXME hack for editor until it supports handling component backgrounds
|
||||||
lowerLevelBackground = '#00FFFF'
|
lowerLevelBackground = '#00FFFF'
|
||||||
}
|
}
|
||||||
const lowerLevelVirtualDirectives = computed[lowerLevelSelector]?.virtualDirectives
|
const lowerLevelVirtualDirectives = computed[lowerLevelSelector]?.virtualDirectives
|
||||||
const lowerLevelVirtualDirectivesRaw = computed[lowerLevelSelector]?.virtualDirectivesRaw
|
const lowerLevelVirtualDirectivesRaw = computed[lowerLevelSelector]?.virtualDirectivesRaw
|
||||||
|
|
||||||
const dynamicVars = computed[selector] || {
|
const dynamicVars = computed[selector] || {
|
||||||
lowerLevelBackground,
|
lowerLevelBackground,
|
||||||
lowerLevelVirtualDirectives,
|
lowerLevelVirtualDirectives,
|
||||||
lowerLevelVirtualDirectivesRaw
|
lowerLevelVirtualDirectivesRaw
|
||||||
}
|
|
||||||
|
|
||||||
// Inheriting all of the applicable rules
|
|
||||||
const existingRules = ruleset.filter(findRules(combination))
|
|
||||||
const computedDirectives =
|
|
||||||
existingRules
|
|
||||||
.map(r => r.directives)
|
|
||||||
.reduce((acc, directives) => ({ ...acc, ...directives }), {})
|
|
||||||
const computedRule = {
|
|
||||||
...combination,
|
|
||||||
directives: computedDirectives
|
|
||||||
}
|
|
||||||
|
|
||||||
computed[selector] = computed[selector] || {}
|
|
||||||
computed[selector].computedRule = computedRule
|
|
||||||
computed[selector].dynamicVars = dynamicVars
|
|
||||||
|
|
||||||
if (virtualComponents.has(combination.component)) {
|
|
||||||
const virtualName = [
|
|
||||||
'--',
|
|
||||||
combination.component.toLowerCase(),
|
|
||||||
combination.variant === 'normal'
|
|
||||||
? ''
|
|
||||||
: combination.variant[0].toUpperCase() + combination.variant.slice(1).toLowerCase(),
|
|
||||||
...sortBy(combination.state.filter(x => x !== 'normal')).map(state => state[0].toUpperCase() + state.slice(1).toLowerCase())
|
|
||||||
].join('')
|
|
||||||
|
|
||||||
let inheritedTextColor = computedDirectives.textColor
|
|
||||||
let inheritedTextAuto = computedDirectives.textAuto
|
|
||||||
let inheritedTextOpacity = computedDirectives.textOpacity
|
|
||||||
let inheritedTextOpacityMode = computedDirectives.textOpacityMode
|
|
||||||
const lowerLevelTextSelector = [...selector.split(/ /g).slice(0, -1), soloSelector].join(' ')
|
|
||||||
const lowerLevelTextRule = computed[lowerLevelTextSelector]
|
|
||||||
|
|
||||||
if (inheritedTextColor == null || inheritedTextOpacity == null || inheritedTextOpacityMode == null) {
|
|
||||||
inheritedTextColor = computedDirectives.textColor ?? lowerLevelTextRule.textColor
|
|
||||||
inheritedTextAuto = computedDirectives.textAuto ?? lowerLevelTextRule.textAuto
|
|
||||||
inheritedTextOpacity = computedDirectives.textOpacity ?? lowerLevelTextRule.textOpacity
|
|
||||||
inheritedTextOpacityMode = computedDirectives.textOpacityMode ?? lowerLevelTextRule.textOpacityMode
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const newTextRule = {
|
// Inheriting all of the applicable rules
|
||||||
...computedRule,
|
const existingRules = ruleset.filter(findRules(combination))
|
||||||
directives: {
|
const computedDirectives =
|
||||||
...computedRule.directives,
|
existingRules
|
||||||
textColor: inheritedTextColor,
|
.map(r => r.directives)
|
||||||
textAuto: inheritedTextAuto ?? 'preserve',
|
.reduce((acc, directives) => ({ ...acc, ...directives }), {})
|
||||||
textOpacity: inheritedTextOpacity,
|
const computedRule = {
|
||||||
textOpacityMode: inheritedTextOpacityMode
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
dynamicVars.inheritedBackground = lowerLevelBackground
|
|
||||||
dynamicVars.stacked = convert(stacked[lowerLevelSelector]).rgb
|
|
||||||
|
|
||||||
const intendedTextColor = convert(findColor(inheritedTextColor, { dynamicVars, staticVars })).rgb
|
|
||||||
const textColor = newTextRule.directives.textAuto === 'no-auto'
|
|
||||||
? intendedTextColor
|
|
||||||
: getTextColor(
|
|
||||||
convert(stacked[lowerLevelSelector]).rgb,
|
|
||||||
intendedTextColor,
|
|
||||||
newTextRule.directives.textAuto === 'preserve'
|
|
||||||
)
|
|
||||||
const virtualDirectives = computed[lowerLevelSelector].virtualDirectives || {}
|
|
||||||
const virtualDirectivesRaw = computed[lowerLevelSelector].virtualDirectivesRaw || {}
|
|
||||||
|
|
||||||
// Storing color data in lower layer to use as custom css properties
|
|
||||||
virtualDirectives[virtualName] = getTextColorAlpha(newTextRule.directives, textColor, dynamicVars)
|
|
||||||
virtualDirectivesRaw[virtualName] = textColor
|
|
||||||
|
|
||||||
computed[lowerLevelSelector].virtualDirectives = virtualDirectives
|
|
||||||
computed[lowerLevelSelector].virtualDirectivesRaw = virtualDirectivesRaw
|
|
||||||
|
|
||||||
return {
|
|
||||||
dynamicVars,
|
|
||||||
selector: cssSelector.split(/ /g).slice(0, -1).join(' '),
|
|
||||||
...combination,
|
|
||||||
directives: {},
|
|
||||||
virtualDirectives: {
|
|
||||||
[virtualName]: getTextColorAlpha(newTextRule.directives, textColor, dynamicVars)
|
|
||||||
},
|
|
||||||
virtualDirectivesRaw: {
|
|
||||||
[virtualName]: textColor
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
computed[selector] = computed[selector] || {}
|
|
||||||
|
|
||||||
// TODO: DEFAULT TEXT COLOR
|
|
||||||
const lowerLevelStackedBackground = stacked[lowerLevelSelector] || convert(ultimateBackgroundColor).rgb
|
|
||||||
|
|
||||||
if (computedDirectives.background) {
|
|
||||||
let inheritRule = null
|
|
||||||
const variantRules = ruleset.filter(
|
|
||||||
findRules({
|
|
||||||
component: combination.component,
|
|
||||||
variant: combination.variant,
|
|
||||||
parent: combination.parent
|
|
||||||
})
|
|
||||||
)
|
|
||||||
const lastVariantRule = variantRules[variantRules.length - 1]
|
|
||||||
if (lastVariantRule) {
|
|
||||||
inheritRule = lastVariantRule
|
|
||||||
} else {
|
|
||||||
const normalRules = ruleset.filter(findRules({
|
|
||||||
component: combination.component,
|
|
||||||
parent: combination.parent
|
|
||||||
}))
|
|
||||||
const lastNormalRule = normalRules[normalRules.length - 1]
|
|
||||||
inheritRule = lastNormalRule
|
|
||||||
}
|
|
||||||
|
|
||||||
const inheritSelector = ruleToSelector({ ...inheritRule, parent: combination.parent }, true)
|
|
||||||
const inheritedBackground = computed[inheritSelector].background
|
|
||||||
|
|
||||||
dynamicVars.inheritedBackground = inheritedBackground
|
|
||||||
|
|
||||||
const rgb = convert(findColor(computedDirectives.background, { dynamicVars, staticVars })).rgb
|
|
||||||
|
|
||||||
if (!stacked[selector]) {
|
|
||||||
let blend
|
|
||||||
const alpha = computedDirectives.opacity ?? 1
|
|
||||||
if (alpha >= 1) {
|
|
||||||
blend = rgb
|
|
||||||
} else if (alpha <= 0) {
|
|
||||||
blend = lowerLevelStackedBackground
|
|
||||||
} else {
|
|
||||||
blend = alphaBlend(rgb, computedDirectives.opacity, lowerLevelStackedBackground)
|
|
||||||
}
|
|
||||||
stacked[selector] = blend
|
|
||||||
computed[selector].background = { ...rgb, a: computedDirectives.opacity ?? 1 }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (computedDirectives.shadow) {
|
|
||||||
dynamicVars.shadow = flattenDeep(findShadow(flattenDeep(computedDirectives.shadow), { dynamicVars, staticVars }))
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!stacked[selector]) {
|
|
||||||
computedDirectives.background = 'transparent'
|
|
||||||
computedDirectives.opacity = 0
|
|
||||||
stacked[selector] = lowerLevelStackedBackground
|
|
||||||
computed[selector].background = { ...lowerLevelStackedBackground, a: 0 }
|
|
||||||
}
|
|
||||||
|
|
||||||
dynamicVars.stacked = stacked[selector]
|
|
||||||
dynamicVars.background = computed[selector].background
|
|
||||||
|
|
||||||
const dynamicSlots = Object.entries(computedDirectives).filter(([k, v]) => k.startsWith('--'))
|
|
||||||
|
|
||||||
dynamicSlots.forEach(([k, v]) => {
|
|
||||||
const [type, value] = v.split('|').map(x => x.trim()) // woah, Extreme!
|
|
||||||
switch (type) {
|
|
||||||
case 'color': {
|
|
||||||
const color = findColor(value, { dynamicVars, staticVars })
|
|
||||||
dynamicVars[k] = color
|
|
||||||
if (combination.component === rootComponentName) {
|
|
||||||
staticVars[k.substring(2)] = color
|
|
||||||
}
|
|
||||||
break
|
|
||||||
}
|
|
||||||
case 'shadow': {
|
|
||||||
const shadow = value.split(/,/g).map(s => s.trim()).filter(x => x)
|
|
||||||
dynamicVars[k] = shadow
|
|
||||||
if (combination.component === rootComponentName) {
|
|
||||||
staticVars[k.substring(2)] = shadow
|
|
||||||
}
|
|
||||||
break
|
|
||||||
}
|
|
||||||
case 'generic': {
|
|
||||||
dynamicVars[k] = value
|
|
||||||
if (combination.component === rootComponentName) {
|
|
||||||
staticVars[k.substring(2)] = value
|
|
||||||
}
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
const rule = {
|
|
||||||
dynamicVars,
|
|
||||||
selector: cssSelector,
|
|
||||||
...combination,
|
...combination,
|
||||||
directives: computedDirectives
|
directives: computedDirectives
|
||||||
}
|
}
|
||||||
|
|
||||||
return rule
|
computed[selector] = computed[selector] || {}
|
||||||
|
computed[selector].computedRule = computedRule
|
||||||
|
computed[selector].dynamicVars = dynamicVars
|
||||||
|
|
||||||
|
if (virtualComponents.has(combination.component)) {
|
||||||
|
const virtualName = [
|
||||||
|
'--',
|
||||||
|
combination.component.toLowerCase(),
|
||||||
|
combination.variant === 'normal'
|
||||||
|
? ''
|
||||||
|
: combination.variant[0].toUpperCase() + combination.variant.slice(1).toLowerCase(),
|
||||||
|
...sortBy(combination.state.filter(x => x !== 'normal')).map(state => state[0].toUpperCase() + state.slice(1).toLowerCase())
|
||||||
|
].join('')
|
||||||
|
|
||||||
|
let inheritedTextColor = computedDirectives.textColor
|
||||||
|
let inheritedTextAuto = computedDirectives.textAuto
|
||||||
|
let inheritedTextOpacity = computedDirectives.textOpacity
|
||||||
|
let inheritedTextOpacityMode = computedDirectives.textOpacityMode
|
||||||
|
const lowerLevelTextSelector = [...selector.split(/ /g).slice(0, -1), soloSelector].join(' ')
|
||||||
|
const lowerLevelTextRule = computed[lowerLevelTextSelector]
|
||||||
|
|
||||||
|
if (inheritedTextColor == null || inheritedTextOpacity == null || inheritedTextOpacityMode == null) {
|
||||||
|
inheritedTextColor = computedDirectives.textColor ?? lowerLevelTextRule.textColor
|
||||||
|
inheritedTextAuto = computedDirectives.textAuto ?? lowerLevelTextRule.textAuto
|
||||||
|
inheritedTextOpacity = computedDirectives.textOpacity ?? lowerLevelTextRule.textOpacity
|
||||||
|
inheritedTextOpacityMode = computedDirectives.textOpacityMode ?? lowerLevelTextRule.textOpacityMode
|
||||||
|
}
|
||||||
|
|
||||||
|
const newTextRule = {
|
||||||
|
...computedRule,
|
||||||
|
directives: {
|
||||||
|
...computedRule.directives,
|
||||||
|
textColor: inheritedTextColor,
|
||||||
|
textAuto: inheritedTextAuto ?? 'preserve',
|
||||||
|
textOpacity: inheritedTextOpacity,
|
||||||
|
textOpacityMode: inheritedTextOpacityMode
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dynamicVars.inheritedBackground = lowerLevelBackground
|
||||||
|
dynamicVars.stacked = convert(stacked[lowerLevelSelector]).rgb
|
||||||
|
|
||||||
|
const intendedTextColor = convert(findColor(inheritedTextColor, { dynamicVars, staticVars })).rgb
|
||||||
|
const textColor = newTextRule.directives.textAuto === 'no-auto'
|
||||||
|
? intendedTextColor
|
||||||
|
: getTextColor(
|
||||||
|
convert(stacked[lowerLevelSelector]).rgb,
|
||||||
|
intendedTextColor,
|
||||||
|
newTextRule.directives.textAuto === 'preserve'
|
||||||
|
)
|
||||||
|
const virtualDirectives = computed[lowerLevelSelector].virtualDirectives || {}
|
||||||
|
const virtualDirectivesRaw = computed[lowerLevelSelector].virtualDirectivesRaw || {}
|
||||||
|
|
||||||
|
// Storing color data in lower layer to use as custom css properties
|
||||||
|
virtualDirectives[virtualName] = getTextColorAlpha(newTextRule.directives, textColor, dynamicVars)
|
||||||
|
virtualDirectivesRaw[virtualName] = textColor
|
||||||
|
|
||||||
|
computed[lowerLevelSelector].virtualDirectives = virtualDirectives
|
||||||
|
computed[lowerLevelSelector].virtualDirectivesRaw = virtualDirectivesRaw
|
||||||
|
|
||||||
|
return {
|
||||||
|
dynamicVars,
|
||||||
|
selector: cssSelector.split(/ /g).slice(0, -1).join(' '),
|
||||||
|
...combination,
|
||||||
|
directives: {},
|
||||||
|
virtualDirectives: {
|
||||||
|
[virtualName]: getTextColorAlpha(newTextRule.directives, textColor, dynamicVars)
|
||||||
|
},
|
||||||
|
virtualDirectivesRaw: {
|
||||||
|
[virtualName]: textColor
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
computed[selector] = computed[selector] || {}
|
||||||
|
|
||||||
|
// TODO: DEFAULT TEXT COLOR
|
||||||
|
const lowerLevelStackedBackground = stacked[lowerLevelSelector] || convert(ultimateBackgroundColor).rgb
|
||||||
|
|
||||||
|
if (computedDirectives.background) {
|
||||||
|
let inheritRule = null
|
||||||
|
const variantRules = ruleset.filter(
|
||||||
|
findRules({
|
||||||
|
component: combination.component,
|
||||||
|
variant: combination.variant,
|
||||||
|
parent: combination.parent
|
||||||
|
})
|
||||||
|
)
|
||||||
|
const lastVariantRule = variantRules[variantRules.length - 1]
|
||||||
|
if (lastVariantRule) {
|
||||||
|
inheritRule = lastVariantRule
|
||||||
|
} else {
|
||||||
|
const normalRules = ruleset.filter(findRules({
|
||||||
|
component: combination.component,
|
||||||
|
parent: combination.parent
|
||||||
|
}))
|
||||||
|
const lastNormalRule = normalRules[normalRules.length - 1]
|
||||||
|
inheritRule = lastNormalRule
|
||||||
|
}
|
||||||
|
|
||||||
|
const inheritSelector = ruleToSelector({ ...inheritRule, parent: combination.parent }, true)
|
||||||
|
const inheritedBackground = computed[inheritSelector].background
|
||||||
|
|
||||||
|
dynamicVars.inheritedBackground = inheritedBackground
|
||||||
|
|
||||||
|
const rgb = convert(findColor(computedDirectives.background, { dynamicVars, staticVars })).rgb
|
||||||
|
|
||||||
|
if (!stacked[selector]) {
|
||||||
|
let blend
|
||||||
|
const alpha = computedDirectives.opacity ?? 1
|
||||||
|
if (alpha >= 1) {
|
||||||
|
blend = rgb
|
||||||
|
} else if (alpha <= 0) {
|
||||||
|
blend = lowerLevelStackedBackground
|
||||||
|
} else {
|
||||||
|
blend = alphaBlend(rgb, computedDirectives.opacity, lowerLevelStackedBackground)
|
||||||
|
}
|
||||||
|
stacked[selector] = blend
|
||||||
|
computed[selector].background = { ...rgb, a: computedDirectives.opacity ?? 1 }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (computedDirectives.shadow) {
|
||||||
|
dynamicVars.shadow = flattenDeep(findShadow(flattenDeep(computedDirectives.shadow), { dynamicVars, staticVars }))
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!stacked[selector]) {
|
||||||
|
computedDirectives.background = 'transparent'
|
||||||
|
computedDirectives.opacity = 0
|
||||||
|
stacked[selector] = lowerLevelStackedBackground
|
||||||
|
computed[selector].background = { ...lowerLevelStackedBackground, a: 0 }
|
||||||
|
}
|
||||||
|
|
||||||
|
dynamicVars.stacked = stacked[selector]
|
||||||
|
dynamicVars.background = computed[selector].background
|
||||||
|
|
||||||
|
const dynamicSlots = Object.entries(computedDirectives).filter(([k, v]) => k.startsWith('--'))
|
||||||
|
|
||||||
|
dynamicSlots.forEach(([k, v]) => {
|
||||||
|
const [type, value] = v.split('|').map(x => x.trim()) // woah, Extreme!
|
||||||
|
switch (type) {
|
||||||
|
case 'color': {
|
||||||
|
const color = findColor(value, { dynamicVars, staticVars })
|
||||||
|
dynamicVars[k] = color
|
||||||
|
if (combination.component === rootComponentName) {
|
||||||
|
staticVars[k.substring(2)] = color
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
case 'shadow': {
|
||||||
|
const shadow = value.split(/,/g).map(s => s.trim()).filter(x => x)
|
||||||
|
dynamicVars[k] = shadow
|
||||||
|
if (combination.component === rootComponentName) {
|
||||||
|
staticVars[k.substring(2)] = shadow
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
case 'generic': {
|
||||||
|
dynamicVars[k] = value
|
||||||
|
if (combination.component === rootComponentName) {
|
||||||
|
staticVars[k.substring(2)] = value
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const rule = {
|
||||||
|
dynamicVars,
|
||||||
|
selector: cssSelector,
|
||||||
|
...combination,
|
||||||
|
directives: computedDirectives
|
||||||
|
}
|
||||||
|
|
||||||
|
return rule
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
const { component, variant, state } = combination
|
||||||
|
throw new Error(`Error processing combination ${component}.${variant}:${state.join(':')}: ${e}`)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue