|
|
@ -151,6 +151,7 @@ export const init = (extraRuleset, ultimateBackgroundColor) => {
|
|
|
|
|
|
|
|
|
|
|
|
const eagerRules = []
|
|
|
|
const eagerRules = []
|
|
|
|
const lazyRules = []
|
|
|
|
const lazyRules = []
|
|
|
|
|
|
|
|
const lazyPromises = []
|
|
|
|
|
|
|
|
|
|
|
|
const rulesetUnsorted = [
|
|
|
|
const rulesetUnsorted = [
|
|
|
|
...Object.values(components)
|
|
|
|
...Object.values(components)
|
|
|
@ -187,68 +188,16 @@ export const init = (extraRuleset, ultimateBackgroundColor) => {
|
|
|
|
|
|
|
|
|
|
|
|
const virtualComponents = new Set(Object.values(components).filter(c => c.virtual).map(c => c.name))
|
|
|
|
const virtualComponents = new Set(Object.values(components).filter(c => c.virtual).map(c => c.name))
|
|
|
|
|
|
|
|
|
|
|
|
let counter = 0
|
|
|
|
const processCombination = (combination, rules) => {
|
|
|
|
const promises = []
|
|
|
|
|
|
|
|
const processInnerComponent = (component, rules, parent) => {
|
|
|
|
|
|
|
|
const addRule = (rule) => {
|
|
|
|
const addRule = (rule) => {
|
|
|
|
rules.push(rule)
|
|
|
|
rules.push(rule)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const parentSelector = ruleToSelector(parent, true)
|
|
|
|
const selector = ruleToSelector(combination, true)
|
|
|
|
// const parentList = parent ? unroll(parent).reverse().map(c => c.component) : []
|
|
|
|
const cssSelector = ruleToSelector(combination)
|
|
|
|
// if (!component.virtual) {
|
|
|
|
|
|
|
|
// const path = [...parentList, component.name].join(' > ')
|
|
|
|
|
|
|
|
// console.log('Component ' + path + ' process starting')
|
|
|
|
|
|
|
|
// }
|
|
|
|
|
|
|
|
// const t0 = performance.now()
|
|
|
|
|
|
|
|
const {
|
|
|
|
|
|
|
|
validInnerComponents = [],
|
|
|
|
|
|
|
|
states: originalStates = {},
|
|
|
|
|
|
|
|
variants: originalVariants = {},
|
|
|
|
|
|
|
|
name
|
|
|
|
|
|
|
|
} = component
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Normalizing states and variants to always include "normal"
|
|
|
|
const parentSelector = selector.split(/ /g).slice(0, -1).join(' ')
|
|
|
|
const states = { normal: '', ...originalStates }
|
|
|
|
const soloSelector = selector.split(/ /g).slice(-1)[0]
|
|
|
|
const variants = { normal: '', ...originalVariants }
|
|
|
|
|
|
|
|
const innerComponents = (validInnerComponents).map(name => {
|
|
|
|
|
|
|
|
const result = components[name]
|
|
|
|
|
|
|
|
if (result === undefined) console.error(`Component ${component.name} references a component ${name} which does not exist!`)
|
|
|
|
|
|
|
|
return result
|
|
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Optimization: we only really need combinations without "normal" because all states implicitly have it
|
|
|
|
|
|
|
|
const permutationStateKeys = Object.keys(states).filter(s => s !== 'normal')
|
|
|
|
|
|
|
|
const stateCombinations = [
|
|
|
|
|
|
|
|
['normal'],
|
|
|
|
|
|
|
|
...getAllPossibleCombinations(permutationStateKeys)
|
|
|
|
|
|
|
|
.map(combination => ['normal', ...combination])
|
|
|
|
|
|
|
|
.filter(combo => {
|
|
|
|
|
|
|
|
// Optimization: filter out some hard-coded combinations that don't make sense
|
|
|
|
|
|
|
|
if (combo.indexOf('disabled') >= 0) {
|
|
|
|
|
|
|
|
return !(
|
|
|
|
|
|
|
|
combo.indexOf('hover') >= 0 ||
|
|
|
|
|
|
|
|
combo.indexOf('focused') >= 0 ||
|
|
|
|
|
|
|
|
combo.indexOf('pressed') >= 0
|
|
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
return true
|
|
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const stateVariantCombination = Object.keys(variants).map(variant => {
|
|
|
|
|
|
|
|
return stateCombinations.map(state => ({ variant, state }))
|
|
|
|
|
|
|
|
}).reduce((acc, x) => [...acc, ...x], [])
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
stateVariantCombination.forEach(combination => {
|
|
|
|
|
|
|
|
counter++
|
|
|
|
|
|
|
|
// const tt0 = performance.now()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
combination.component = component.name
|
|
|
|
|
|
|
|
const soloSelector = ruleToSelector(combination, true)
|
|
|
|
|
|
|
|
const soloCssSelector = ruleToSelector(combination)
|
|
|
|
|
|
|
|
const selector = [parentSelector, soloSelector].filter(x => x).join(' ')
|
|
|
|
|
|
|
|
const cssSelector = [parentSelector, soloCssSelector].filter(x => x).join(' ')
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const lowerLevelSelector = parentSelector
|
|
|
|
const lowerLevelSelector = parentSelector
|
|
|
|
const lowerLevelBackground = computed[lowerLevelSelector]?.background
|
|
|
|
const lowerLevelBackground = computed[lowerLevelSelector]?.background
|
|
|
@ -262,12 +211,10 @@ export const init = (extraRuleset, ultimateBackgroundColor) => {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Inheriting all of the applicable rules
|
|
|
|
// Inheriting all of the applicable rules
|
|
|
|
const existingRules = ruleset.filter(findRules({ component: component.name, ...combination, parent }))
|
|
|
|
const existingRules = ruleset.filter(findRules(combination))
|
|
|
|
const computedDirectives = existingRules.map(r => r.directives).reduce((acc, directives) => ({ ...acc, ...directives }), {})
|
|
|
|
const computedDirectives = existingRules.map(r => r.directives).reduce((acc, directives) => ({ ...acc, ...directives }), {})
|
|
|
|
const computedRule = {
|
|
|
|
const computedRule = {
|
|
|
|
component: component.name,
|
|
|
|
|
|
|
|
...combination,
|
|
|
|
...combination,
|
|
|
|
parent,
|
|
|
|
|
|
|
|
directives: computedDirectives
|
|
|
|
directives: computedDirectives
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
@ -275,10 +222,10 @@ export const init = (extraRuleset, ultimateBackgroundColor) => {
|
|
|
|
computed[selector].computedRule = computedRule
|
|
|
|
computed[selector].computedRule = computedRule
|
|
|
|
computed[selector].dynamicVars = dynamicVars
|
|
|
|
computed[selector].dynamicVars = dynamicVars
|
|
|
|
|
|
|
|
|
|
|
|
if (virtualComponents.has(component.name)) {
|
|
|
|
if (virtualComponents.has(combination.component)) {
|
|
|
|
const virtualName = [
|
|
|
|
const virtualName = [
|
|
|
|
'--',
|
|
|
|
'--',
|
|
|
|
component.name.toLowerCase(),
|
|
|
|
combination.component.toLowerCase(),
|
|
|
|
combination.variant === 'normal'
|
|
|
|
combination.variant === 'normal'
|
|
|
|
? ''
|
|
|
|
? ''
|
|
|
|
: combination.variant[0].toUpperCase() + combination.variant.slice(1).toLowerCase(),
|
|
|
|
: combination.variant[0].toUpperCase() + combination.variant.slice(1).toLowerCase(),
|
|
|
@ -323,7 +270,7 @@ export const init = (extraRuleset, ultimateBackgroundColor) => {
|
|
|
|
)
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
// Updating previously added rule
|
|
|
|
// Updating previously added rule
|
|
|
|
const earlyLowerLevelRules = rules.filter(findRules(parent, true))
|
|
|
|
const earlyLowerLevelRules = rules.filter(findRules(combination.parent, true))
|
|
|
|
const earlyLowerLevelRule = earlyLowerLevelRules.slice(-1)[0]
|
|
|
|
const earlyLowerLevelRule = earlyLowerLevelRules.slice(-1)[0]
|
|
|
|
|
|
|
|
|
|
|
|
const virtualDirectives = earlyLowerLevelRule.virtualDirectives || {}
|
|
|
|
const virtualDirectives = earlyLowerLevelRule.virtualDirectives || {}
|
|
|
@ -344,17 +291,26 @@ export const init = (extraRuleset, ultimateBackgroundColor) => {
|
|
|
|
|
|
|
|
|
|
|
|
if (computedDirectives.background) {
|
|
|
|
if (computedDirectives.background) {
|
|
|
|
let inheritRule = null
|
|
|
|
let inheritRule = null
|
|
|
|
const variantRules = ruleset.filter(findRules({ component: component.name, variant: combination.variant, parent }))
|
|
|
|
const variantRules = ruleset.filter(
|
|
|
|
|
|
|
|
findRules({
|
|
|
|
|
|
|
|
component: combination.component,
|
|
|
|
|
|
|
|
variant: combination.variant,
|
|
|
|
|
|
|
|
parent: combination.parent
|
|
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
)
|
|
|
|
const lastVariantRule = variantRules[variantRules.length - 1]
|
|
|
|
const lastVariantRule = variantRules[variantRules.length - 1]
|
|
|
|
if (lastVariantRule) {
|
|
|
|
if (lastVariantRule) {
|
|
|
|
inheritRule = lastVariantRule
|
|
|
|
inheritRule = lastVariantRule
|
|
|
|
} else {
|
|
|
|
} else {
|
|
|
|
const normalRules = ruleset.filter(findRules({ component: component.name, parent }))
|
|
|
|
const normalRules = ruleset.filter(findRules({
|
|
|
|
|
|
|
|
component: combination.component,
|
|
|
|
|
|
|
|
parent: combination.parent
|
|
|
|
|
|
|
|
}))
|
|
|
|
const lastNormalRule = normalRules[normalRules.length - 1]
|
|
|
|
const lastNormalRule = normalRules[normalRules.length - 1]
|
|
|
|
inheritRule = lastNormalRule
|
|
|
|
inheritRule = lastNormalRule
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const inheritSelector = ruleToSelector({ ...inheritRule, parent }, true)
|
|
|
|
const inheritSelector = ruleToSelector({ ...inheritRule, parent: combination.parent }, true)
|
|
|
|
const inheritedBackground = computed[inheritSelector].background
|
|
|
|
const inheritedBackground = computed[inheritSelector].background
|
|
|
|
|
|
|
|
|
|
|
|
dynamicVars.inheritedBackground = inheritedBackground
|
|
|
|
dynamicVars.inheritedBackground = inheritedBackground
|
|
|
@ -398,7 +354,7 @@ export const init = (extraRuleset, ultimateBackgroundColor) => {
|
|
|
|
case 'color': {
|
|
|
|
case 'color': {
|
|
|
|
const color = findColor(value[0], { dynamicVars, staticVars })
|
|
|
|
const color = findColor(value[0], { dynamicVars, staticVars })
|
|
|
|
dynamicVars[k] = color
|
|
|
|
dynamicVars[k] = color
|
|
|
|
if (component.name === 'Root') {
|
|
|
|
if (combination.component === 'Root') {
|
|
|
|
staticVars[k.substring(2)] = color
|
|
|
|
staticVars[k.substring(2)] = color
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break
|
|
|
|
break
|
|
|
@ -406,14 +362,14 @@ export const init = (extraRuleset, ultimateBackgroundColor) => {
|
|
|
|
case 'shadow': {
|
|
|
|
case 'shadow': {
|
|
|
|
const shadow = value
|
|
|
|
const shadow = value
|
|
|
|
dynamicVars[k] = shadow
|
|
|
|
dynamicVars[k] = shadow
|
|
|
|
if (component.name === 'Root') {
|
|
|
|
if (combination.component === 'Root') {
|
|
|
|
staticVars[k.substring(2)] = shadow
|
|
|
|
staticVars[k.substring(2)] = shadow
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
case 'generic': {
|
|
|
|
case 'generic': {
|
|
|
|
dynamicVars[k] = value
|
|
|
|
dynamicVars[k] = value
|
|
|
|
if (component.name === 'Root') {
|
|
|
|
if (combination.component === 'Root') {
|
|
|
|
staticVars[k.substring(2)] = value
|
|
|
|
staticVars[k.substring(2)] = value
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break
|
|
|
|
break
|
|
|
@ -424,50 +380,90 @@ export const init = (extraRuleset, ultimateBackgroundColor) => {
|
|
|
|
addRule({
|
|
|
|
addRule({
|
|
|
|
dynamicVars,
|
|
|
|
dynamicVars,
|
|
|
|
selector: cssSelector,
|
|
|
|
selector: cssSelector,
|
|
|
|
component: component.name,
|
|
|
|
|
|
|
|
...combination,
|
|
|
|
...combination,
|
|
|
|
parent,
|
|
|
|
|
|
|
|
directives: computedDirectives
|
|
|
|
directives: computedDirectives
|
|
|
|
})
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
innerComponents.forEach(innerComponent => {
|
|
|
|
const processInnerComponent = (component, parent) => {
|
|
|
|
if (innerComponent.lazy) {
|
|
|
|
const combinations = []
|
|
|
|
promises.push(new Promise((resolve, reject) => {
|
|
|
|
const {
|
|
|
|
setTimeout(() => {
|
|
|
|
validInnerComponents = [],
|
|
|
|
try {
|
|
|
|
states: originalStates = {},
|
|
|
|
processInnerComponent(innerComponent, lazyRules, { parent, component: name, ...combination })
|
|
|
|
variants: originalVariants = {}
|
|
|
|
resolve()
|
|
|
|
} = component
|
|
|
|
} catch (e) {
|
|
|
|
|
|
|
|
reject(e)
|
|
|
|
// Normalizing states and variants to always include "normal"
|
|
|
|
|
|
|
|
const states = { normal: '', ...originalStates }
|
|
|
|
|
|
|
|
const variants = { normal: '', ...originalVariants }
|
|
|
|
|
|
|
|
const innerComponents = (validInnerComponents).map(name => {
|
|
|
|
|
|
|
|
const result = components[name]
|
|
|
|
|
|
|
|
if (result === undefined) console.error(`Component ${component.name} references a component ${name} which does not exist!`)
|
|
|
|
|
|
|
|
return result
|
|
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Optimization: we only really need combinations without "normal" because all states implicitly have it
|
|
|
|
|
|
|
|
const permutationStateKeys = Object.keys(states).filter(s => s !== 'normal')
|
|
|
|
|
|
|
|
const stateCombinations = [
|
|
|
|
|
|
|
|
['normal'],
|
|
|
|
|
|
|
|
...getAllPossibleCombinations(permutationStateKeys)
|
|
|
|
|
|
|
|
.map(combination => ['normal', ...combination])
|
|
|
|
|
|
|
|
.filter(combo => {
|
|
|
|
|
|
|
|
// Optimization: filter out some hard-coded combinations that don't make sense
|
|
|
|
|
|
|
|
if (combo.indexOf('disabled') >= 0) {
|
|
|
|
|
|
|
|
return !(
|
|
|
|
|
|
|
|
combo.indexOf('hover') >= 0 ||
|
|
|
|
|
|
|
|
combo.indexOf('focused') >= 0 ||
|
|
|
|
|
|
|
|
combo.indexOf('pressed') >= 0
|
|
|
|
|
|
|
|
)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}, 0)
|
|
|
|
return true
|
|
|
|
}))
|
|
|
|
})
|
|
|
|
} else {
|
|
|
|
]
|
|
|
|
processInnerComponent(innerComponent, rules, { parent, component: name, ...combination })
|
|
|
|
|
|
|
|
|
|
|
|
const stateVariantCombination = Object.keys(variants).map(variant => {
|
|
|
|
|
|
|
|
return stateCombinations.map(state => ({ variant, state }))
|
|
|
|
|
|
|
|
}).reduce((acc, x) => [...acc, ...x], [])
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
stateVariantCombination.forEach(combination => {
|
|
|
|
|
|
|
|
combination.component = component.name
|
|
|
|
|
|
|
|
combination.lazy = component.lazy || parent?.lazy
|
|
|
|
|
|
|
|
combination.parent = parent
|
|
|
|
|
|
|
|
if (combination.state.indexOf('hover') >= 0) {
|
|
|
|
|
|
|
|
combination.lazy = true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
combinations.push(combination)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
innerComponents.forEach(innerComponent => {
|
|
|
|
|
|
|
|
combinations.push(...processInnerComponent(innerComponent, combination))
|
|
|
|
})
|
|
|
|
})
|
|
|
|
// const tt1 = performance.now()
|
|
|
|
|
|
|
|
// if (!component.virtual) {
|
|
|
|
|
|
|
|
// console.log('State-variant ' + combination.variant + ' : ' + combination.state.join('+') + ' procession time: ' + (tt1 - tt0) + 'ms')
|
|
|
|
|
|
|
|
// }
|
|
|
|
|
|
|
|
})
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
// const t1 = performance.now()
|
|
|
|
return combinations
|
|
|
|
// if (!component.virtual) {
|
|
|
|
|
|
|
|
// const path = [...parentList, component.name].join(' > ')
|
|
|
|
|
|
|
|
// console.log('Component ' + path + ' procession time: ' + (t1 - t0) + 'ms')
|
|
|
|
|
|
|
|
// }
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
processInnerComponent(components.Root, eagerRules)
|
|
|
|
const t0 = performance.now()
|
|
|
|
console.debug('Eager combinations processed:' + counter)
|
|
|
|
const combinations = processInnerComponent(components.Root)
|
|
|
|
const lazyExec = Promise.all(promises).then(() => {
|
|
|
|
const t1 = performance.now()
|
|
|
|
console.debug('Total combinations processed: ' + counter)
|
|
|
|
console.debug('Tree tranveral took ' + (t1 - t0) + ' ms')
|
|
|
|
}).then(() => lazyRules)
|
|
|
|
|
|
|
|
|
|
|
|
combinations.forEach((combination) => {
|
|
|
|
|
|
|
|
if (combination.lazy) {
|
|
|
|
|
|
|
|
lazyPromises.push(async () => processCombination(combination, lazyRules))
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
processCombination(combination, eagerRules)
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
const t2 = performance.now()
|
|
|
|
|
|
|
|
console.debug('Eager processing took ' + (t2 - t1) + ' ms')
|
|
|
|
|
|
|
|
|
|
|
|
return {
|
|
|
|
return {
|
|
|
|
lazy: lazyExec,
|
|
|
|
lazy: async () => {
|
|
|
|
|
|
|
|
await Promise.all(lazyPromises.map(x => x()))
|
|
|
|
|
|
|
|
return lazyRules
|
|
|
|
|
|
|
|
},
|
|
|
|
eager: eagerRules,
|
|
|
|
eager: eagerRules,
|
|
|
|
staticVars
|
|
|
|
staticVars
|
|
|
|
}
|
|
|
|
}
|
|
|
|