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

This commit is contained in:
Henry Jameson 2024-10-22 10:08:40 +03:00
commit 461eb8752d
15 changed files with 217 additions and 127 deletions

View file

@ -3,6 +3,12 @@
class="ComponentPreview" class="ComponentPreview"
:class="{ '-shadow-controls': shadowControl }" :class="{ '-shadow-controls': shadowControl }"
> >
<!-- eslint-disable vue/no-v-html vue/no-v-text-v-html-on-component -->
<component
:is="'style'"
v-html="previewCss"
/>
<!-- eslint-enable vue/no-v-html vue/no-v-text-v-html-on-component -->
<label <label
v-show="shadowControl" v-show="shadowControl"
class="header" class="header"
@ -11,7 +17,7 @@
{{ $t('settings.style.shadows.offset') }} {{ $t('settings.style.shadows.offset') }}
</label> </label>
<input <input
v-show="shadowControl" v-show="shadowControl && !hideControls"
:value="shadow?.y" :value="shadow?.y"
:disabled="disabled" :disabled="disabled"
:class="{ disabled }" :class="{ disabled }"
@ -20,7 +26,7 @@
@input="e => updateProperty('y', e.target.value)" @input="e => updateProperty('y', e.target.value)"
> >
<input <input
v-show="shadowControl" v-show="shadowControl && !hideControls"
:value="shadow?.y" :value="shadow?.y"
:disabled="disabled" :disabled="disabled"
:class="{ disabled }" :class="{ disabled }"
@ -41,9 +47,14 @@
> >
{{ $t('settings.style.themes3.editor.test_string') }} {{ $t('settings.style.themes3.editor.test_string') }}
</div> </div>
<div v-if="invalid" class="invalid-container">
<div class="alert error invalid-label">
{{ $t('settings.style.themes3.editor.invalid') }}
</div>
</div>
</div> </div>
<input <input
v-show="shadowControl" v-show="shadowControl && !hideControls"
:value="shadow?.x" :value="shadow?.x"
:disabled="disabled" :disabled="disabled"
:class="{ disabled }" :class="{ disabled }"
@ -52,7 +63,7 @@
@input="e => updateProperty('x', e.target.value)" @input="e => updateProperty('x', e.target.value)"
> >
<input <input
v-show="shadowControl" v-show="shadowControl && !hideControls"
:value="shadow?.x" :value="shadow?.x"
:disabled="disabled" :disabled="disabled"
:class="{ disabled }" :class="{ disabled }"
@ -85,7 +96,9 @@ export default {
'shadowControl', 'shadowControl',
'previewClass', 'previewClass',
'previewStyle', 'previewStyle',
'disabled' 'previewCss',
'disabled',
'invalid'
], ],
emits: ['update:shadow'], emits: ['update:shadow'],
data () { data () {
@ -93,9 +106,14 @@ export default {
lightGrid: false lightGrid: false
} }
}, },
computed: {
hideControls () {
return typeof this.shadow === 'string'
}
},
methods: { methods: {
updateProperty (axis, value) { updateProperty (axis, value) {
this.$emit('update:shadow', { axis, value }) this.$emit('update:shadow', { axis, value: Number(value) })
} }
} }
} }
@ -121,6 +139,22 @@ export default {
line-height: 2; line-height: 2;
} }
.invalid-container {
position: absolute;
top: 0;
bottom: 0;
left: 0;
right: 0;
display: grid;
align-items: center;
justify-items: center;
background-color: rgba(100 0 0 / 50%);
.alert {
padding: 0.5em 1em;
}
}
.input-light-grid { .input-light-grid {
grid-area: options; grid-area: options;
justify-self: center; justify-self: center;
@ -170,6 +204,7 @@ export default {
--__grid-color2-disabled: rgba(255 255 255 / 20%); --__grid-color2-disabled: rgba(255 255 255 / 20%);
} }
position: relative;
grid-area: preview; grid-area: preview;
aspect-ratio: 1; aspect-ratio: 1;
display: flex; display: flex;

View file

@ -1,6 +1,6 @@
export default { export default {
name: 'Modals', name: 'Modals',
selector: '.modal-view', selector: ['.modal-view', '#modal', '.shout-panel'],
lazy: true, lazy: true,
notEditable: true, notEditable: true,
validInnerComponents: [ validInnerComponents: [

View file

@ -1,6 +1,6 @@
export default { export default {
name: 'Scrollbar', name: 'Scrollbar',
selector: '::-webkit-scrollbar', selector: ['::-webkit-scrollbar-button', '::-webkit-scrollbar-thumb', '::-webkit-resizer'],
notEditable: true, // for now notEditable: true, // for now
defaultRules: [ defaultRules: [
{ {

View file

@ -4,7 +4,7 @@
> >
<button <button
class="btn button-default" class="btn button-default"
:disabled="disabled || shadowsAreNull" :disabled="disabled"
@click="add" @click="add"
> >
<FAIcon <FAIcon

View file

@ -21,7 +21,7 @@ import {
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 { serializeShadow, serialize } from 'src/services/theme_data/iss_serializer.js'
import { parseShadow, deserialize } from 'src/services/theme_data/iss_deserializer.js' import { deserializeShadow, deserialize } from 'src/services/theme_data/iss_deserializer.js'
import { import {
rgb2hex, rgb2hex,
hex2rgb, hex2rgb,
@ -371,7 +371,11 @@ export default {
return shadow return shadow
} }
if (typeof shadow === 'string') { if (typeof shadow === 'string') {
return parseShadow(shadow) try {
return deserializeShadow(shadow)
} catch (e) {
console.warn(e)
}
} }
return null return null
}) })
@ -580,6 +584,7 @@ export default {
const selectedVirtualDirectiveId = ref(0) const selectedVirtualDirectiveId = ref(0)
exports.selectedVirtualDirectiveId = selectedVirtualDirectiveId exports.selectedVirtualDirectiveId = selectedVirtualDirectiveId
const selectedVirtualDirective = computed({ const selectedVirtualDirective = computed({
get () { get () {
return virtualDirectives[selectedVirtualDirectiveId.value] return virtualDirectives[selectedVirtualDirectiveId.value]
@ -589,6 +594,7 @@ export default {
} }
}) })
exports.selectedVirtualDirective = selectedVirtualDirective exports.selectedVirtualDirective = selectedVirtualDirective
exports.selectedVirtualDirectiveValType = computed({ exports.selectedVirtualDirectiveValType = computed({
get () { get () {
return virtualDirectives[selectedVirtualDirectiveId.value].valType return virtualDirectives[selectedVirtualDirectiveId.value].valType
@ -607,35 +613,56 @@ export default {
} }
} }
}) })
exports.selectedVirtualDirectiveParsed = computed({
get () { const draftVirtualDirectiveValid = ref(true)
switch (selectedVirtualDirective.value.valType) { const draftVirtualDirective = ref({})
exports.draftVirtualDirective = draftVirtualDirective
watch(
selectedVirtualDirective,
(directive) => {
switch (directive.valType) {
case 'shadow': { case 'shadow': {
const directiveValue = selectedVirtualDirective.value.value if (Array.isArray(directive.value)) {
if (Array.isArray(directiveValue)) { draftVirtualDirective.value = normalizeShadows(directive.value)
return normalizeShadows(directiveValue)
} else { } else {
const splitShadow = directiveValue.split(/,/g).map(x => x.trim()) const splitShadow = directive.value.split(/,/g).map(x => x.trim())
return normalizeShadows(splitShadow) draftVirtualDirective.value = normalizeShadows(splitShadow)
} }
break
} }
case 'color': case 'color':
return selectedVirtualDirective.value.value draftVirtualDirective.value = directive.value
break
default: default:
return selectedVirtualDirective.value.value draftVirtualDirective.value = directive.value
break
} }
}, },
set (value) { { immediate: true }
)
watch(
draftVirtualDirective,
(directive) => {
try {
switch (selectedVirtualDirective.value.valType) { switch (selectedVirtualDirective.value.valType) {
case 'shadow': { case 'shadow': {
virtualDirectives[selectedVirtualDirectiveId.value].value = value.map(x => serializeShadow(x)).join(', ') virtualDirectives[selectedVirtualDirectiveId.value].value =
directive.map(x => serializeShadow(x)).join(', ')
break break
} }
default: default:
virtualDirectives[selectedVirtualDirectiveId.value].value = value virtualDirectives[selectedVirtualDirectiveId.value].value = directive
} }
draftVirtualDirectiveValid.value = true
} catch (e) {
console.error('Invalid virtual directive value', e)
draftVirtualDirectiveValid.value = false
} }
}) },
{ immediate: true }
)
exports.getNewVirtualDirective = () => ({ exports.getNewVirtualDirective = () => ({
name: 'newDirective', name: 'newDirective',

View file

@ -148,6 +148,7 @@
:shadow-control="isShadowTabOpen" :shadow-control="isShadowTabOpen"
:preview-class="previewClass" :preview-class="previewClass"
:preview-style="editorHintStyle" :preview-style="editorHintStyle"
:preview-css="previewCss"
:disabled="!editedSubShadow && typeof editedShadow !== 'string'" :disabled="!editedSubShadow && typeof editedShadow !== 'string'"
:shadow="editedSubShadow" :shadow="editedSubShadow"
@update:shadow="({ axis, value }) => updateSubShadow(axis, value)" @update:shadow="({ axis, value }) => updateSubShadow(axis, value)"
@ -285,7 +286,7 @@
:disabled="!isShadowPresent" :disabled="!isShadowPresent"
:no-preview="true" :no-preview="true"
:compact="true" :compact="true"
:separate-inset="shadowSelected === 'avatar' || shadowSelected === 'avatarStatus'" :static-vars="selectedPalette"
@subShadowSelected="onSubShadow" @subShadowSelected="onSubShadow"
/> />
</div> </div>
@ -408,14 +409,14 @@
</div> </div>
<ShadowControl <ShadowControl
v-if="selectedVirtualDirectiveValType === 'shadow'" v-if="selectedVirtualDirectiveValType === 'shadow'"
v-model="selectedVirtualDirectiveParsed" v-model="draftVirtualDirective"
:computeColor="computeColor" :static-vars="selectedPalette"
:compact="true" :compact="true"
/> />
<ColorInput <ColorInput
v-if="selectedVirtualDirectiveValType === 'color'" v-if="selectedVirtualDirectiveValType === 'color'"
v-model="selectedVirtualDirectiveParsed" v-model="draftVirtualDirective"
:fallback="computeColor(selectedVirtualDirectiveParsed)" :fallback="computeColor(draftVirtualDirective)"
:label="$t('settings.style.themes3.editor.variables.virtual_color')" :label="$t('settings.style.themes3.editor.variables.virtual_color')"
/> />
</div> </div>

View file

@ -957,6 +957,7 @@
v-model="currentShadow" v-model="currentShadow"
:separate-inset="shadowSelected === 'avatar' || shadowSelected === 'avatarStatus'" :separate-inset="shadowSelected === 'avatar' || shadowSelected === 'avatarStatus'"
:fallback="currentShadowFallback" :fallback="currentShadowFallback"
:static-vars="previewTheme.colors"
/> />
</div> </div>
<div <div

View file

@ -5,9 +5,13 @@ import SelectMotion from 'src/components/select/select_motion.vue'
import Checkbox from 'src/components/checkbox/checkbox.vue' import Checkbox from 'src/components/checkbox/checkbox.vue'
import Popover from 'src/components/popover/popover.vue' import Popover from 'src/components/popover/popover.vue'
import ComponentPreview from 'src/components/component_preview/component_preview.vue' import ComponentPreview from 'src/components/component_preview/component_preview.vue'
import { getCssShadow, getCssShadowFilter } from '../../services/theme_data/theme_data.service.js' import { rgb2hex } from 'src/services/color_convert/color_convert.js'
import { serializeShadow } from 'src/services/theme_data/iss_serializer.js'
import { deserializeShadow } from 'src/services/theme_data/iss_deserializer.js'
import { getCssShadow, getCssShadowFilter } from 'src/services/theme_data/css_utils.js'
import { findShadow, findColor } from 'src/services/theme_data/theme_data_3.service.js'
import { library } from '@fortawesome/fontawesome-svg-core' import { library } from '@fortawesome/fontawesome-svg-core'
import { throttle } from 'lodash' import { throttle, flattenDeep } from 'lodash'
import { import {
faTimes, faTimes,
faChevronDown, faChevronDown,
@ -46,13 +50,14 @@ export default {
'separateInset', 'separateInset',
'noPreview', 'noPreview',
'disabled', 'disabled',
'computeColor', 'staticVars',
'compact' 'compact'
], ],
emits: ['update:modelValue', 'subShadowSelected'], emits: ['update:modelValue', 'subShadowSelected'],
data () { data () {
return { return {
selectedId: 0 selectedId: 0,
invalid: false
} }
}, },
components: { components: {
@ -97,7 +102,7 @@ export default {
} }
}, },
present () { present () {
return this.selected != null && !this.usingFallback return this.selected != null && this.modelValue != null
}, },
shadowsAreNull () { shadowsAreNull () {
return this.modelValue == null return this.modelValue == null
@ -105,31 +110,38 @@ export default {
currentFallback () { currentFallback () {
return this.fallback?.[this.selectedId] return this.fallback?.[this.selectedId]
}, },
usingFallback () { getColorFallback () {
return this.modelValue == null if (this.staticVars && this.selected?.color) {
}, const computedColor = findColor(this.selected.color, { dynamicVars: {}, staticVars: this.staticVars }, true)
getFallback () { if (computedColor) return rgb2hex(computedColor)
if (typeof this.computeColor === 'function' && this.selected?.color) { return null
return this.computeColor(this.selected.color)
} else { } else {
return this.currentFallback?.color return this.currentFallback?.color
} }
}, },
style () { style () {
try { try {
let result
const serialized = this.cValue.map(x => serializeShadow(x)).join(',')
deserializeShadow(serialized) // validate
const expandedShadow = flattenDeep(findShadow(this.cValue, { dynamicVars: {}, staticVars: this.staticVars }))
const fixedShadows = expandedShadow.map(x => ({ ...x, color: console.log(x) || rgb2hex(x.color) }))
if (this.separateInset) { if (this.separateInset) {
return { result = {
filter: getCssShadowFilter(this.cValue), filter: getCssShadowFilter(fixedShadows),
boxShadow: getCssShadow(this.cValue, true) boxShadow: getCssShadow(fixedShadows, true)
}
} else {
result = {
boxShadow: getCssShadow(fixedShadows)
} }
} }
return { this.invalid = false
boxShadow: getCssShadow(this.cValue) return result
}
} catch (e) { } catch (e) {
return { console.error('Invalid shadow', e)
border: '1px solid red' this.invalid = true
}
} }
} }
}, },

View file

@ -5,9 +5,11 @@
> >
<ComponentPreview <ComponentPreview
v-if="!noPreview" v-if="!noPreview"
:invalid="invalid"
class="shadow-preview" class="shadow-preview"
:shadow-control="true" :shadow-control="true"
:shadow="selected" :shadow="selected"
:preview-style="style"
:disabled="disabled || !present" :disabled="disabled || !present"
@update:shadow="({ axis, value }) => updateProperty(axis, value)" @update:shadow="({ axis, value }) => updateProperty(axis, value)"
/> />
@ -165,7 +167,7 @@
:model-value="selected?.color" :model-value="selected?.color"
:disabled="disabled || !present" :disabled="disabled || !present"
:label="$t('settings.style.common.color')" :label="$t('settings.style.common.color')"
:fallback="getFallback" :fallback="getColorFallback"
:show-optional-tickbox="false" :show-optional-tickbox="false"
name="shadow" name="shadow"
@update:modelValue="e => updateProperty('color', e)" @update:modelValue="e => updateProperty('color', e)"

View file

@ -793,8 +793,10 @@
"text_color": "Text color", "text_color": "Text color",
"icon_color": "Icon color", "icon_color": "Icon color",
"link_color": "Link color", "link_color": "Link color",
"border_color": "Border color",
"include_in_rule": "Add to rule", "include_in_rule": "Add to rule",
"test_string": "TEST", "test_string": "TEST",
"invalid": "Invalid",
"refresh_preview": "Refresh preview", "refresh_preview": "Refresh preview",
"apply_preview": "Apply", "apply_preview": "Apply",
"text_auto": { "text_auto": {

View file

@ -56,22 +56,7 @@ export const generateTheme = (inputRuleset, callbacks, debug) => {
getCssRules(themes3.eager, debug).forEach(rule => { getCssRules(themes3.eager, debug).forEach(rule => {
// Hacks to support multiple selectors on same component // Hacks to support multiple selectors on same component
if (rule.match(/::-webkit-scrollbar-button/)) {
const parts = rule.split(/[{}]/g)
const newRule = [
parts[0],
', ',
parts[0].replace(/button/, 'thumb'),
', ',
parts[0].replace(/scrollbar-button/, 'resizer'),
' {',
parts[1],
'}'
].join('')
onNewRule(newRule, false)
} else {
onNewRule(rule, false) onNewRule(rule, false)
}
}) })
onEagerFinished() onEagerFinished()
@ -85,22 +70,7 @@ export const generateTheme = (inputRuleset, callbacks, debug) => {
const chunk = chunks[counter] const chunk = chunks[counter]
Promise.all(chunk.map(x => x())).then(result => { Promise.all(chunk.map(x => x())).then(result => {
getCssRules(result.filter(x => x), debug).forEach(rule => { getCssRules(result.filter(x => x), debug).forEach(rule => {
if (rule.match(/\.modal-view/)) {
const parts = rule.split(/[{}]/g)
const newRule = [
parts[0],
', ',
parts[0].replace(/\.modal-view/, '#modal'),
', ',
parts[0].replace(/\.modal-view/, '.shout-panel'),
' {',
parts[1],
'}'
].join('')
onNewRule(newRule, true)
} else {
onNewRule(rule, true) onNewRule(rule, true)
}
}) })
// const t1 = performance.now() // const t1 = performance.now()
// console.debug('Chunk ' + counter + ' took ' + (t1 - t0) + 'ms') // console.debug('Chunk ' + counter + ' took ' + (t1 - t0) + 'ms')

View file

@ -1,10 +1,11 @@
import { flattenDeep } from 'lodash' import { flattenDeep } from 'lodash'
export const parseShadow = string => { export const deserializeShadow = string => {
const modes = ['_full', 'inset', 'x', 'y', 'blur', 'spread', 'color', 'alpha'] const modes = ['_full', 'inset', 'x', 'y', 'blur', 'spread', 'color', 'alpha', 'name']
const regexPrep = [ const regexPrep = [
// inset keyword (optional) // inset keyword (optional)
'^(?:(inset)\\s+)?', '^',
'(?:(inset)\\s+)?',
// x // x
'(?:(-?[0-9]+(?:\\.[0-9]+)?)\\s+)', '(?:(-?[0-9]+(?:\\.[0-9]+)?)\\s+)',
// y // y
@ -16,7 +17,10 @@ export const parseShadow = string => {
// either hex, variable or function // either hex, variable or function
'(#[0-9a-f]{6}|--[a-z\\-_]+|\\$[a-z\\-()_]+)', '(#[0-9a-f]{6}|--[a-z\\-_]+|\\$[a-z\\-()_]+)',
// opacity (optional) // opacity (optional)
'(?:\\s+\\/\\s+([0-9]+(?:\\.[0-9]+)?)\\s*)?$' '(?:\\s+\\/\\s+([0-9]+(?:\\.[0-9]+)?)\\s*)?',
// name
'(?:\\s+#(\\w+)\\s*)?',
'$'
].join('') ].join('')
const regex = new RegExp(regexPrep, 'gis') // global, (stable) indices, single-string const regex = new RegExp(regexPrep, 'gis') // global, (stable) indices, single-string
const result = regex.exec(string) const result = regex.exec(string)
@ -24,11 +28,11 @@ export const parseShadow = string => {
if (string.startsWith('$') || string.startsWith('--')) { if (string.startsWith('$') || string.startsWith('--')) {
return string return string
} else { } else {
throw new Error(`Invalid shadow definition: ${string}`) throw new Error(`Invalid shadow definition: '${string}'`)
} }
} else { } else {
const numeric = new Set(['x', 'y', 'blur', 'spread', 'alpha']) const numeric = new Set(['x', 'y', 'blur', 'spread', 'alpha'])
const { x, y, blur, spread, alpha, inset, color } = Object.fromEntries(modes.map((mode, i) => { const { x, y, blur, spread, alpha, inset, color, name } = Object.fromEntries(modes.map((mode, i) => {
if (numeric.has(mode)) { if (numeric.has(mode)) {
const number = Number(result[i]) const number = Number(result[i])
if (Number.isNaN(number)) { if (Number.isNaN(number)) {
@ -43,7 +47,7 @@ export const parseShadow = string => {
} }
}).filter(([k, v]) => v !== false).slice(1)) }).filter(([k, v]) => v !== false).slice(1))
return { x, y, blur, spread, color, alpha, inset } return { x, y, blur, spread, color, alpha, inset, name }
} }
} }
// this works nearly the same as HTML tree converter // this works nearly the same as HTML tree converter
@ -150,7 +154,7 @@ export const deserialize = (input) => {
if (realValue === 'none') { if (realValue === 'none') {
realValue = [] realValue = []
} else { } else {
realValue = value.split(',').map(v => parseShadow(v.trim())) realValue = value.split(',').map(v => deserializeShadow(v.trim()))
} }
} if (!Number.isNaN(Number(value))) { } if (!Number.isNaN(Number(value))) {
realValue = Number(value) realValue = Number(value)

View file

@ -1,8 +1,13 @@
import { unroll } from './iss_utils.js' import { unroll } from './iss_utils.js'
import { deserializeShadow } from './iss_deserializer.js'
export const serializeShadow = (s, throwOnInvalid) => { export const serializeShadow = (s, throwOnInvalid) => {
if (typeof s === 'object') { if (typeof s === 'object') {
return `${s.inset ? 'inset ' : ''}${s.x} ${s.y} ${s.blur} ${s.spread} ${s.color} / ${s.alpha}` const inset = s.inset ? 'inset ' : ''
const name = s.name ? ` #${s.name} ` : ''
const result = `${inset}${s.x} ${s.y} ${s.blur} ${s.spread} ${s.color} / ${s.alpha}${name}`
deserializeShadow(result) // Verify that output is valid and parseable
return result
} else { } else {
return s return s
} }

View file

@ -56,43 +56,74 @@ export const getAllPossibleCombinations = (array) => {
* *
* @returns {String} CSS selector (or path) * @returns {String} CSS selector (or path)
*/ */
export const genericRuleToSelector = components => (rule, ignoreOutOfTreeSelector, isParent) => { export const genericRuleToSelector = components => (rule, ignoreOutOfTreeSelector, liteMode, children) => {
const isParent = !!children
if (!rule && !isParent) return null if (!rule && !isParent) return null
const component = components[rule.component] const component = components[rule.component]
const { states = {}, variants = {}, selector, outOfTreeSelector } = component const { states = {}, variants = {}, outOfTreeSelector } = component
const applicableStates = ((rule.state || []).filter(x => x !== 'normal')).map(state => states[state]) const expand = (array = [], subArray = []) => {
if (array.length === 0) return subArray.map(x => [x])
if (subArray.length === 0) return array.map(x => [x])
return array.map(a => {
return subArray.map(b => [a, b])
}).flat()
}
let componentSelectors = Array.isArray(component.selector) ? component.selector : [component.selector]
if (ignoreOutOfTreeSelector || liteMode) componentSelectors = [componentSelectors[0]]
componentSelectors = componentSelectors.map(selector => {
if (selector === ':root') {
return ''
} else if (isParent) {
return selector
} else {
if (outOfTreeSelector && !ignoreOutOfTreeSelector) return outOfTreeSelector
return selector
}
})
const applicableVariantName = (rule.variant || 'normal') const applicableVariantName = (rule.variant || 'normal')
let applicableVariant = '' let variantSelectors = null
if (applicableVariantName !== 'normal') { if (applicableVariantName !== 'normal') {
applicableVariant = variants[applicableVariantName] variantSelectors = variants[applicableVariantName]
} else { } else {
applicableVariant = variants?.normal ?? '' variantSelectors = variants?.normal ?? ''
} }
variantSelectors = Array.isArray(variantSelectors) ? variantSelectors : [variantSelectors]
if (ignoreOutOfTreeSelector || liteMode) variantSelectors = [variantSelectors[0]]
let realSelector const applicableStates = (rule.state || []).filter(x => x !== 'normal')
if (selector === ':root') { // const applicableStates = (rule.state || [])
realSelector = '' const statesSelectors = applicableStates.map(state => {
} else if (isParent) { const selector = states[state] || ''
realSelector = selector let arraySelector = Array.isArray(selector) ? selector : [selector]
} else { if (ignoreOutOfTreeSelector || liteMode) arraySelector = [arraySelector[0]]
if (outOfTreeSelector && !ignoreOutOfTreeSelector) realSelector = outOfTreeSelector arraySelector
else realSelector = selector
}
const selectors = [realSelector, applicableVariant, ...applicableStates]
.sort((a, b) => { .sort((a, b) => {
if (a.startsWith(':')) return 1 if (a.startsWith(':')) return 1
if (/^[a-z]/.exec(a)) return -1 if (/^[a-z]/.exec(a)) return -1
else return 0 else return 0
}) })
.join('') .join('')
return arraySelector
})
const statesSelectorsFlat = statesSelectors.reduce((acc, s) => {
return expand(acc, s).map(st => st.join(''))
}, [])
const componentVariant = expand(componentSelectors, variantSelectors).map(cv => cv.join(''))
const componentVariantStates = expand(componentVariant, statesSelectorsFlat).map(cvs => cvs.join(''))
const selectors = expand(componentVariantStates, children).map(cvsc => cvsc.join(' '))
/*
*/
if (rule.parent) { if (rule.parent) {
return (genericRuleToSelector(components)(rule.parent, ignoreOutOfTreeSelector, true) + ' ' + selectors).trim() return genericRuleToSelector(components)(rule.parent, ignoreOutOfTreeSelector, liteMode, selectors)
} }
return selectors.trim()
return selectors.join(', ').trim()
} }
/** /**

View file

@ -22,7 +22,7 @@ import {
normalizeCombination, normalizeCombination,
findRules findRules
} from './iss_utils.js' } from './iss_utils.js'
import { parseShadow } from './iss_deserializer.js' import { deserializeShadow } from './iss_deserializer.js'
// Ensuring the order of components // Ensuring the order of components
const components = { const components = {
@ -37,7 +37,7 @@ const components = {
ChatMessage: null ChatMessage: null
} }
const findShadow = (shadows, { dynamicVars, staticVars }) => { export const findShadow = (shadows, { dynamicVars, staticVars }) => {
return (shadows || []).map(shadow => { return (shadows || []).map(shadow => {
let targetShadow let targetShadow
if (typeof shadow === 'string') { if (typeof shadow === 'string') {
@ -48,7 +48,7 @@ const findShadow = (shadows, { dynamicVars, staticVars }) => {
const variableSlot = shadow.substring(2) const variableSlot = shadow.substring(2)
return findShadow(staticVars[variableSlot], { dynamicVars, staticVars }) return findShadow(staticVars[variableSlot], { dynamicVars, staticVars })
} else { } else {
targetShadow = parseShadow(shadow) targetShadow = deserializeShadow(shadow)
} }
} else { } else {
targetShadow = shadow targetShadow = shadow