2024-01-31 23:27:30 +00:00
|
|
|
import { convert, brightness } from 'chromatism'
|
2024-02-07 13:53:34 +00:00
|
|
|
import {
|
|
|
|
alphaBlend,
|
|
|
|
getTextColor,
|
|
|
|
rgba2css,
|
|
|
|
mixrgb,
|
|
|
|
relativeLuminance
|
|
|
|
} from '../color_convert/color_convert.js'
|
|
|
|
|
2024-02-19 16:48:49 +00:00
|
|
|
import {
|
|
|
|
colorFunctions,
|
|
|
|
shadowFunctions,
|
|
|
|
process
|
|
|
|
} from './theme3_slot_functions.js'
|
|
|
|
|
2024-02-09 17:37:22 +00:00
|
|
|
const DEBUG = false
|
|
|
|
|
2024-02-18 16:40:14 +00:00
|
|
|
// Ensuring the order of components
|
2024-01-18 12:35:25 +00:00
|
|
|
const components = {
|
2024-02-18 16:40:14 +00:00
|
|
|
Root: null,
|
|
|
|
Text: null,
|
|
|
|
FunText: null,
|
|
|
|
Link: null,
|
|
|
|
Icon: null,
|
2024-02-18 22:00:43 +00:00
|
|
|
Border: null,
|
2024-02-19 13:11:59 +00:00
|
|
|
Panel: null,
|
|
|
|
Chat: null,
|
|
|
|
ChatMessage: null
|
2024-02-07 13:53:34 +00:00
|
|
|
}
|
|
|
|
|
2024-02-19 16:48:49 +00:00
|
|
|
const findColor = (color, dynamicVars, staticVars) => {
|
|
|
|
if (typeof color !== 'string' || (!color.startsWith('--') && !color.startsWith('$'))) return color
|
|
|
|
let targetColor = null
|
|
|
|
if (color.startsWith('--')) {
|
|
|
|
const [variable, modifier] = color.split(/,/g).map(str => str.trim())
|
|
|
|
const variableSlot = variable.substring(2)
|
|
|
|
if (variableSlot === 'stack') {
|
|
|
|
const { r, g, b } = dynamicVars.stacked
|
|
|
|
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 {
|
|
|
|
switch (variableSlot) {
|
|
|
|
case 'inheritedBackground':
|
|
|
|
targetColor = convert(dynamicVars.inheritedBackground).rgb
|
|
|
|
break
|
|
|
|
case 'background':
|
|
|
|
targetColor = convert(dynamicVars.background).rgb
|
|
|
|
break
|
|
|
|
default:
|
|
|
|
targetColor = convert(staticVars[variableSlot]).rgb
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (modifier) {
|
|
|
|
const effectiveBackground = dynamicVars.lowerLevelBackground ?? targetColor
|
|
|
|
const isLightOnDark = relativeLuminance(convert(effectiveBackground).rgb) < 0.5
|
|
|
|
const mod = isLightOnDark ? 1 : -1
|
|
|
|
targetColor = brightness(Number.parseFloat(modifier) * mod, targetColor).rgb
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
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 opacity = directives.textOpacity
|
|
|
|
const backgroundColor = convert(dynamicVars.lowerLevelBackground).rgb
|
|
|
|
const textColor = convert(findColor(intendedTextColor, dynamicVars, staticVars)).rgb
|
|
|
|
if (opacity === null || opacity === undefined || opacity >= 1) {
|
|
|
|
return convert(textColor).hex
|
|
|
|
}
|
|
|
|
if (opacity === 0) {
|
|
|
|
return convert(backgroundColor).hex
|
|
|
|
}
|
|
|
|
const opacityMode = directives.textOpacityMode
|
|
|
|
switch (opacityMode) {
|
|
|
|
case 'fake':
|
|
|
|
return convert(alphaBlend(textColor, opacity, backgroundColor)).hex
|
|
|
|
case 'mixrgb':
|
|
|
|
return convert(mixrgb(backgroundColor, textColor)).hex
|
|
|
|
default:
|
|
|
|
return rgba2css({ a: opacity, ...textColor })
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-02-18 16:40:14 +00:00
|
|
|
// Loading all style.js[on] files dynamically
|
|
|
|
const componentsContext = require.context('src', true, /\.style.js(on)?$/)
|
|
|
|
componentsContext.keys().forEach(key => {
|
|
|
|
const component = componentsContext(key).default
|
2024-02-18 22:12:07 +00:00
|
|
|
if (components[component.name] != null) {
|
|
|
|
console.warn(`Component in file ${key} is trying to override existing component ${component.name}! You have collisions/duplicates!`)
|
|
|
|
}
|
2024-02-18 16:40:14 +00:00
|
|
|
components[component.name] = component
|
|
|
|
})
|
|
|
|
|
2024-02-07 13:53:34 +00:00
|
|
|
// "Unrolls" a tree structure of item: { parent: { ...item2, parent: { ...item3, parent: {...} } }}
|
|
|
|
// into an array [item2, item3] for iterating
|
|
|
|
const unroll = (item) => {
|
|
|
|
const out = []
|
2024-02-08 16:18:01 +00:00
|
|
|
let currentParent = item
|
2024-02-07 13:53:34 +00:00
|
|
|
while (currentParent) {
|
2024-02-15 18:20:27 +00:00
|
|
|
out.push(currentParent)
|
|
|
|
currentParent = currentParent.parent
|
2024-02-07 13:53:34 +00:00
|
|
|
}
|
|
|
|
return out
|
2024-01-18 12:35:25 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// This gives you an array of arrays of all possible unique (i.e. order-insensitive) combinations
|
2024-01-22 22:43:46 +00:00
|
|
|
export const getAllPossibleCombinations = (array) => {
|
2024-01-18 12:35:25 +00:00
|
|
|
const combos = [array.map(x => [x])]
|
|
|
|
for (let comboSize = 2; comboSize <= array.length; comboSize++) {
|
|
|
|
const previous = combos[combos.length - 1]
|
|
|
|
const selfSet = new Set()
|
|
|
|
const newCombos = previous.map(self => {
|
|
|
|
self.forEach(x => selfSet.add(x))
|
|
|
|
const nonSelf = array.filter(x => !selfSet.has(x))
|
|
|
|
return nonSelf.map(x => [...self, x])
|
|
|
|
})
|
|
|
|
const flatCombos = newCombos.reduce((acc, x) => [...acc, ...x], [])
|
|
|
|
combos.push(flatCombos)
|
|
|
|
}
|
|
|
|
return combos.reduce((acc, x) => [...acc, ...x], [])
|
|
|
|
}
|
|
|
|
|
2024-02-07 13:53:34 +00:00
|
|
|
// Converts rule, parents and their criteria into a CSS (or path if ignoreOutOfTreeSelector == true) selector
|
|
|
|
export const ruleToSelector = (rule, ignoreOutOfTreeSelector, isParent) => {
|
|
|
|
if (!rule && !isParent) return null
|
2024-01-22 22:43:46 +00:00
|
|
|
const component = components[rule.component]
|
2024-01-31 15:39:51 +00:00
|
|
|
const { states, variants, selector, outOfTreeSelector } = component
|
2024-01-22 22:43:46 +00:00
|
|
|
|
2024-01-23 17:18:55 +00:00
|
|
|
const applicableStates = ((rule.state || []).filter(x => x !== 'normal')).map(state => states[state])
|
2024-01-22 22:43:46 +00:00
|
|
|
|
|
|
|
const applicableVariantName = (rule.variant || 'normal')
|
|
|
|
let applicableVariant = ''
|
|
|
|
if (applicableVariantName !== 'normal') {
|
|
|
|
applicableVariant = variants[applicableVariantName]
|
2024-02-08 16:18:01 +00:00
|
|
|
} else {
|
|
|
|
applicableVariant = variants?.normal ?? ''
|
2024-01-22 22:43:46 +00:00
|
|
|
}
|
|
|
|
|
2024-01-31 15:39:51 +00:00
|
|
|
let realSelector
|
2024-02-07 13:53:34 +00:00
|
|
|
if (selector === ':root') {
|
|
|
|
realSelector = ''
|
|
|
|
} else if (isParent) {
|
2024-01-31 15:39:51 +00:00
|
|
|
realSelector = selector
|
|
|
|
} else {
|
2024-02-07 13:53:34 +00:00
|
|
|
if (outOfTreeSelector && !ignoreOutOfTreeSelector) realSelector = outOfTreeSelector
|
2024-01-31 15:39:51 +00:00
|
|
|
else realSelector = selector
|
|
|
|
}
|
|
|
|
|
|
|
|
const selectors = [realSelector, applicableVariant, ...applicableStates]
|
2024-01-22 22:43:46 +00:00
|
|
|
.toSorted((a, b) => {
|
|
|
|
if (a.startsWith(':')) return 1
|
2024-02-08 16:18:01 +00:00
|
|
|
if (/^[a-z]/.exec(a)) return -1
|
2024-01-31 15:39:51 +00:00
|
|
|
else return 0
|
2024-01-22 22:43:46 +00:00
|
|
|
})
|
|
|
|
.join('')
|
|
|
|
|
|
|
|
if (rule.parent) {
|
2024-02-07 13:53:34 +00:00
|
|
|
return (ruleToSelector(rule.parent, ignoreOutOfTreeSelector, true) + ' ' + selectors).trim()
|
2024-01-22 22:43:46 +00:00
|
|
|
}
|
2024-02-07 13:53:34 +00:00
|
|
|
return selectors.trim()
|
|
|
|
}
|
|
|
|
|
2024-02-07 16:54:00 +00:00
|
|
|
const combinationsMatch = (criteria, subject, strict) => {
|
2024-02-07 13:53:34 +00:00
|
|
|
if (criteria.component !== subject.component) return false
|
|
|
|
|
|
|
|
// All variants inherit from normal
|
2024-02-09 14:04:45 +00:00
|
|
|
if (subject.variant !== 'normal' || strict) {
|
2024-02-07 13:53:34 +00:00
|
|
|
if (criteria.variant !== subject.variant) return false
|
|
|
|
}
|
|
|
|
|
|
|
|
// Subject states > 1 essentially means state is "normal" and therefore matches
|
2024-02-09 17:37:22 +00:00
|
|
|
if (subject.state.length > 1 || strict) {
|
|
|
|
const subjectStatesSet = new Set(subject.state)
|
|
|
|
const criteriaStatesSet = new Set(criteria.state)
|
|
|
|
|
2024-02-07 13:53:34 +00:00
|
|
|
const setsAreEqual =
|
|
|
|
[...criteriaStatesSet].every(state => subjectStatesSet.has(state)) &&
|
|
|
|
[...subjectStatesSet].every(state => criteriaStatesSet.has(state))
|
|
|
|
|
|
|
|
if (!setsAreEqual) return false
|
|
|
|
}
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
|
2024-02-07 16:54:00 +00:00
|
|
|
const findRules = (criteria, strict) => subject => {
|
2024-02-07 13:53:34 +00:00
|
|
|
// If we searching for "general" rules - ignore "specific" ones
|
|
|
|
if (criteria.parent === null && !!subject.parent) return false
|
2024-02-07 16:54:00 +00:00
|
|
|
if (!combinationsMatch(criteria, subject, strict)) return false
|
2024-02-07 13:53:34 +00:00
|
|
|
|
|
|
|
if (criteria.parent !== undefined && criteria.parent !== null) {
|
2024-02-07 16:54:00 +00:00
|
|
|
if (!subject.parent && !strict) return true
|
2024-02-07 13:53:34 +00:00
|
|
|
const pathCriteria = unroll(criteria)
|
|
|
|
const pathSubject = unroll(subject)
|
|
|
|
if (pathCriteria.length < pathSubject.length) return false
|
|
|
|
|
|
|
|
// Search: .a .b .c
|
|
|
|
// Matches: .a .b .c; .b .c; .c; .z .a .b .c
|
|
|
|
// Does not match .a .b .c .d, .a .b .e
|
|
|
|
for (let i = 0; i < pathCriteria.length; i++) {
|
|
|
|
const criteriaParent = pathCriteria[i]
|
|
|
|
const subjectParent = pathSubject[i]
|
|
|
|
if (!subjectParent) return true
|
2024-02-07 16:54:00 +00:00
|
|
|
if (!combinationsMatch(criteriaParent, subjectParent, strict)) return false
|
2024-02-07 13:53:34 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return true
|
2024-01-22 22:43:46 +00:00
|
|
|
}
|
|
|
|
|
2024-02-19 16:48:49 +00:00
|
|
|
const normalizeCombination = rule => {
|
|
|
|
rule.variant = rule.variant ?? 'normal'
|
|
|
|
rule.state = [...new Set(['normal', ...(rule.state || [])])]
|
|
|
|
}
|
|
|
|
|
2024-01-31 15:39:51 +00:00
|
|
|
export const init = (extraRuleset, palette) => {
|
2024-02-08 16:18:01 +00:00
|
|
|
const stacked = {}
|
2024-02-07 13:53:34 +00:00
|
|
|
const computed = {}
|
|
|
|
|
2024-02-15 18:20:27 +00:00
|
|
|
const eagerRules = []
|
|
|
|
const lazyRules = []
|
2024-01-22 22:43:46 +00:00
|
|
|
|
2024-02-11 21:11:28 +00:00
|
|
|
const rulesetUnsorted = [
|
|
|
|
...Object.values(components)
|
|
|
|
.map(c => (c.defaultRules || []).map(r => ({ component: c.name, ...r })))
|
|
|
|
.reduce((acc, arr) => [...acc, ...arr], []),
|
|
|
|
...extraRuleset
|
|
|
|
].map(rule => {
|
|
|
|
normalizeCombination(rule)
|
|
|
|
let currentParent = rule.parent
|
|
|
|
while (currentParent) {
|
|
|
|
normalizeCombination(currentParent)
|
|
|
|
currentParent = currentParent.parent
|
|
|
|
}
|
2024-02-09 14:04:45 +00:00
|
|
|
|
|
|
|
return rule
|
|
|
|
})
|
2024-01-31 15:39:51 +00:00
|
|
|
|
2024-02-11 21:11:28 +00:00
|
|
|
const ruleset = rulesetUnsorted
|
|
|
|
.map((data, index) => ({ data, index }))
|
|
|
|
.sort(({ data: a, index: ai }, { data: b, index: bi }) => {
|
|
|
|
const parentsA = unroll(a).length
|
|
|
|
const parentsB = unroll(b).length
|
|
|
|
|
2024-02-18 22:00:43 +00:00
|
|
|
if (parentsA === parentsB) {
|
|
|
|
if (a.component === 'Text') return -1
|
|
|
|
if (b.component === 'Text') return 1
|
|
|
|
return ai - bi
|
|
|
|
}
|
2024-02-11 21:11:28 +00:00
|
|
|
if (parentsA === 0 && parentsB !== 0) return -1
|
|
|
|
if (parentsB === 0 && parentsA !== 0) return 1
|
2024-02-18 22:00:43 +00:00
|
|
|
return parentsA - parentsB
|
2024-02-11 21:11:28 +00:00
|
|
|
})
|
|
|
|
.map(({ data }) => data)
|
|
|
|
|
2024-02-07 13:53:34 +00:00
|
|
|
const virtualComponents = new Set(Object.values(components).filter(c => c.virtual).map(c => c.name))
|
|
|
|
|
2024-02-15 18:20:27 +00:00
|
|
|
let counter = 0
|
|
|
|
const promises = []
|
|
|
|
const processInnerComponent = (component, rules, parent) => {
|
|
|
|
const addRule = (rule) => {
|
|
|
|
rules.push(rule)
|
|
|
|
}
|
|
|
|
|
|
|
|
const parentSelector = ruleToSelector(parent, true)
|
2024-02-11 21:11:28 +00:00
|
|
|
// const parentList = parent ? unroll(parent).reverse().map(c => c.component) : []
|
|
|
|
// if (!component.virtual) {
|
|
|
|
// const path = [...parentList, component.name].join(' > ')
|
|
|
|
// console.log('Component ' + path + ' process starting')
|
|
|
|
// }
|
|
|
|
// const t0 = performance.now()
|
2024-01-18 12:35:25 +00:00
|
|
|
const {
|
2024-01-22 22:43:46 +00:00
|
|
|
validInnerComponents = [],
|
2024-01-18 12:35:25 +00:00
|
|
|
states: originalStates = {},
|
2024-01-22 22:43:46 +00:00
|
|
|
variants: originalVariants = {},
|
|
|
|
name
|
2024-01-18 12:35:25 +00:00
|
|
|
} = component
|
|
|
|
|
2024-02-07 13:53:34 +00:00
|
|
|
// Normalizing states and variants to always include "normal"
|
2024-01-18 12:35:25 +00:00
|
|
|
const states = { normal: '', ...originalStates }
|
|
|
|
const variants = { normal: '', ...originalVariants }
|
2024-02-18 16:40:14 +00:00
|
|
|
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
|
|
|
|
})
|
2024-01-18 12:35:25 +00:00
|
|
|
|
2024-02-09 14:04:45 +00:00
|
|
|
// Optimization: we only really need combinations without "normal" because all states implicitly have it
|
|
|
|
const permutationStateKeys = Object.keys(states).filter(s => s !== 'normal')
|
2024-02-15 18:20:27 +00:00
|
|
|
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
|
|
|
|
})
|
|
|
|
]
|
2024-02-08 16:18:01 +00:00
|
|
|
|
2024-01-18 12:35:25 +00:00
|
|
|
const stateVariantCombination = Object.keys(variants).map(variant => {
|
|
|
|
return stateCombinations.map(state => ({ variant, state }))
|
|
|
|
}).reduce((acc, x) => [...acc, ...x], [])
|
|
|
|
|
|
|
|
stateVariantCombination.forEach(combination => {
|
2024-02-15 18:20:27 +00:00
|
|
|
counter++
|
2024-02-11 21:11:28 +00:00
|
|
|
// const tt0 = performance.now()
|
2024-02-07 13:53:34 +00:00
|
|
|
|
2024-02-15 18:20:27 +00:00
|
|
|
combination.component = component.name
|
|
|
|
const soloSelector = ruleToSelector(combination, true)
|
2024-02-18 18:11:06 +00:00
|
|
|
const soloCssSelector = ruleToSelector(combination)
|
2024-02-15 18:20:27 +00:00
|
|
|
const selector = [parentSelector, soloSelector].filter(x => x).join(' ')
|
2024-02-18 18:11:06 +00:00
|
|
|
const cssSelector = [parentSelector, soloCssSelector].filter(x => x).join(' ')
|
2024-02-15 18:20:27 +00:00
|
|
|
|
|
|
|
const lowerLevelSelector = parentSelector
|
2024-02-08 16:18:01 +00:00
|
|
|
const lowerLevelBackground = computed[lowerLevelSelector]?.background
|
|
|
|
const lowerLevelVirtualDirectives = computed[lowerLevelSelector]?.virtualDirectives
|
|
|
|
const lowerLevelVirtualDirectivesRaw = computed[lowerLevelSelector]?.virtualDirectivesRaw
|
2024-02-07 16:54:00 +00:00
|
|
|
|
2024-02-19 16:48:49 +00:00
|
|
|
const dynamicVars = computed[selector] || {
|
2024-02-07 16:54:00 +00:00
|
|
|
lowerLevelBackground,
|
2024-02-08 16:18:01 +00:00
|
|
|
lowerLevelVirtualDirectives,
|
|
|
|
lowerLevelVirtualDirectivesRaw
|
2024-02-07 16:54:00 +00:00
|
|
|
}
|
|
|
|
|
2024-02-07 13:53:34 +00:00
|
|
|
// Inheriting all of the applicable rules
|
|
|
|
const existingRules = ruleset.filter(findRules({ component: component.name, ...combination, parent }))
|
2024-02-09 13:52:11 +00:00
|
|
|
const computedDirectives = existingRules.map(r => r.directives).reduce((acc, directives) => ({ ...acc, ...directives }), {})
|
2024-02-07 13:53:34 +00:00
|
|
|
const computedRule = {
|
|
|
|
component: component.name,
|
|
|
|
...combination,
|
|
|
|
parent,
|
|
|
|
directives: computedDirectives
|
|
|
|
}
|
2024-01-23 17:18:55 +00:00
|
|
|
|
2024-02-07 13:53:34 +00:00
|
|
|
computed[selector] = computed[selector] || {}
|
|
|
|
computed[selector].computedRule = computedRule
|
2024-02-18 18:11:06 +00:00
|
|
|
computed[selector].dynamicVars = dynamicVars
|
2024-02-07 13:53:34 +00:00
|
|
|
|
|
|
|
if (virtualComponents.has(component.name)) {
|
2024-01-31 15:39:51 +00:00
|
|
|
const virtualName = [
|
|
|
|
'--',
|
|
|
|
component.name.toLowerCase(),
|
|
|
|
combination.variant === 'normal'
|
|
|
|
? ''
|
|
|
|
: combination.variant[0].toUpperCase() + combination.variant.slice(1).toLowerCase(),
|
|
|
|
...combination.state.filter(x => x !== 'normal').toSorted().map(state => state[0].toUpperCase() + state.slice(1).toLowerCase())
|
|
|
|
].join('')
|
2024-01-23 17:18:55 +00:00
|
|
|
|
2024-02-07 13:53:34 +00:00
|
|
|
let inheritedTextColor = computedDirectives.textColor
|
2024-02-07 14:09:29 +00:00
|
|
|
let inheritedTextAuto = computedDirectives.textAuto
|
2024-02-07 13:53:34 +00:00
|
|
|
let inheritedTextOpacity = computedDirectives.textOpacity
|
|
|
|
let inheritedTextOpacityMode = computedDirectives.textOpacityMode
|
|
|
|
const lowerLevelTextSelector = [...selector.split(/ /g).slice(0, -1), soloSelector].join(' ')
|
|
|
|
const lowerLevelTextRule = computed[lowerLevelTextSelector]
|
2024-01-31 15:39:51 +00:00
|
|
|
|
2024-02-07 13:53:34 +00:00
|
|
|
if (inheritedTextColor == null || inheritedTextOpacity == null || inheritedTextOpacityMode == null) {
|
|
|
|
inheritedTextColor = computedDirectives.textColor ?? lowerLevelTextRule.textColor
|
2024-02-07 14:09:29 +00:00
|
|
|
inheritedTextAuto = computedDirectives.textAuto ?? lowerLevelTextRule.textAuto
|
2024-02-07 13:53:34 +00:00
|
|
|
inheritedTextOpacity = computedDirectives.textOpacity ?? lowerLevelTextRule.textOpacity
|
|
|
|
inheritedTextOpacityMode = computedDirectives.textOpacityMode ?? lowerLevelTextRule.textOpacityMode
|
2024-01-31 15:39:51 +00:00
|
|
|
}
|
2024-01-23 17:18:55 +00:00
|
|
|
|
2024-02-07 13:53:34 +00:00
|
|
|
const newTextRule = {
|
|
|
|
...computedRule,
|
|
|
|
directives: {
|
|
|
|
...computedRule.directives,
|
|
|
|
textColor: inheritedTextColor,
|
2024-02-07 14:09:29 +00:00
|
|
|
textAuto: inheritedTextAuto ?? 'preserve',
|
2024-02-07 13:53:34 +00:00
|
|
|
textOpacity: inheritedTextOpacity,
|
|
|
|
textOpacityMode: inheritedTextOpacityMode
|
|
|
|
}
|
2024-01-23 17:18:55 +00:00
|
|
|
}
|
2024-01-31 15:39:51 +00:00
|
|
|
|
2024-02-07 16:54:00 +00:00
|
|
|
dynamicVars.inheritedBackground = lowerLevelBackground
|
2024-02-18 22:00:43 +00:00
|
|
|
dynamicVars.stacked = convert(stacked[lowerLevelSelector]).rgb
|
2024-02-07 13:53:34 +00:00
|
|
|
|
2024-02-19 16:48:49 +00:00
|
|
|
const intendedTextColor = convert(findColor(inheritedTextColor, dynamicVars, palette)).rgb
|
2024-02-07 14:15:25 +00:00
|
|
|
const textColor = newTextRule.directives.textAuto === 'no-auto'
|
|
|
|
? intendedTextColor
|
|
|
|
: getTextColor(
|
2024-02-08 16:18:01 +00:00
|
|
|
convert(stacked[lowerLevelSelector]).rgb,
|
2024-02-07 14:15:25 +00:00
|
|
|
intendedTextColor,
|
|
|
|
newTextRule.directives.textAuto === 'preserve'
|
|
|
|
)
|
2024-01-31 15:39:51 +00:00
|
|
|
|
2024-02-07 16:54:00 +00:00
|
|
|
// Updating previously added rule
|
|
|
|
const earlyLowerLevelRules = rules.filter(findRules(parent, true))
|
|
|
|
const earlyLowerLevelRule = earlyLowerLevelRules.slice(-1)[0]
|
2024-02-07 13:53:34 +00:00
|
|
|
|
2024-02-07 16:54:00 +00:00
|
|
|
const virtualDirectives = earlyLowerLevelRule.virtualDirectives || {}
|
2024-02-08 16:18:01 +00:00
|
|
|
const virtualDirectivesRaw = earlyLowerLevelRule.virtualDirectivesRaw || {}
|
2024-02-07 13:53:34 +00:00
|
|
|
|
2024-02-07 16:54:00 +00:00
|
|
|
// Storing color data in lower layer to use as custom css properties
|
|
|
|
virtualDirectives[virtualName] = getTextColorAlpha(newTextRule.directives, textColor, dynamicVars)
|
2024-02-08 16:18:01 +00:00
|
|
|
virtualDirectivesRaw[virtualName] = textColor
|
2024-02-07 16:54:00 +00:00
|
|
|
earlyLowerLevelRule.virtualDirectives = virtualDirectives
|
2024-02-08 16:18:01 +00:00
|
|
|
earlyLowerLevelRule.virtualDirectivesRaw = virtualDirectivesRaw
|
|
|
|
computed[lowerLevelSelector].virtualDirectives = virtualDirectives
|
|
|
|
computed[lowerLevelSelector].virtualDirectivesRaw = virtualDirectivesRaw
|
2024-02-07 16:54:00 +00:00
|
|
|
|
|
|
|
// Debug: lets you see what it think background color should be
|
2024-02-09 17:37:22 +00:00
|
|
|
if (!DEBUG) return
|
2024-01-31 15:39:51 +00:00
|
|
|
|
|
|
|
const directives = {
|
|
|
|
textColor,
|
2024-02-08 16:18:01 +00:00
|
|
|
background: convert(computed[lowerLevelSelector].background).hex,
|
2024-01-31 15:39:51 +00:00
|
|
|
...inheritedTextOpacity
|
|
|
|
}
|
|
|
|
|
|
|
|
addRule({
|
2024-02-19 16:48:49 +00:00
|
|
|
dynamicVars,
|
2024-02-18 18:11:06 +00:00
|
|
|
selector: cssSelector,
|
2024-01-31 15:39:51 +00:00
|
|
|
virtual: true,
|
|
|
|
component: component.name,
|
2024-02-09 17:37:22 +00:00
|
|
|
parent,
|
2024-01-31 15:39:51 +00:00
|
|
|
...combination,
|
2024-02-07 13:53:34 +00:00
|
|
|
directives,
|
2024-02-08 16:18:01 +00:00
|
|
|
virtualDirectives,
|
|
|
|
virtualDirectivesRaw
|
2024-01-31 15:39:51 +00:00
|
|
|
})
|
2024-01-23 17:18:55 +00:00
|
|
|
} else {
|
2024-02-07 13:53:34 +00:00
|
|
|
computed[selector] = computed[selector] || {}
|
2024-02-13 00:09:43 +00:00
|
|
|
|
|
|
|
// TODO: DEFAULT TEXT COLOR
|
2024-02-18 22:00:43 +00:00
|
|
|
const lowerLevelStackedBackground = stacked[lowerLevelSelector] || convert('#FF00FF').rgb
|
2024-02-13 00:09:43 +00:00
|
|
|
|
2024-02-07 13:53:34 +00:00
|
|
|
if (computedDirectives.background) {
|
|
|
|
let inheritRule = null
|
|
|
|
const variantRules = ruleset.filter(findRules({ component: component.name, variant: combination.variant, parent }))
|
|
|
|
const lastVariantRule = variantRules[variantRules.length - 1]
|
|
|
|
if (lastVariantRule) {
|
|
|
|
inheritRule = lastVariantRule
|
|
|
|
} else {
|
|
|
|
const normalRules = ruleset.filter(findRules({ component: component.name, parent }))
|
|
|
|
const lastNormalRule = normalRules[normalRules.length - 1]
|
|
|
|
inheritRule = lastNormalRule
|
|
|
|
}
|
2024-01-31 15:39:51 +00:00
|
|
|
|
2024-02-07 13:53:34 +00:00
|
|
|
const inheritSelector = ruleToSelector({ ...inheritRule, parent }, true)
|
2024-02-08 16:18:01 +00:00
|
|
|
const inheritedBackground = computed[inheritSelector].background
|
2024-01-31 23:27:30 +00:00
|
|
|
|
2024-02-07 16:54:00 +00:00
|
|
|
dynamicVars.inheritedBackground = inheritedBackground
|
2024-01-31 15:39:51 +00:00
|
|
|
|
2024-02-19 16:48:49 +00:00
|
|
|
const rgb = convert(findColor(computedDirectives.background, dynamicVars, palette)).rgb
|
2024-01-31 15:39:51 +00:00
|
|
|
|
2024-02-08 16:18:01 +00:00
|
|
|
if (!stacked[selector]) {
|
|
|
|
let blend
|
|
|
|
const alpha = computedDirectives.opacity
|
|
|
|
if (alpha >= 1) {
|
|
|
|
blend = rgb
|
|
|
|
} else if (alpha <= 0) {
|
2024-02-18 22:00:43 +00:00
|
|
|
blend = lowerLevelStackedBackground
|
2024-02-08 16:18:01 +00:00
|
|
|
} else {
|
2024-02-18 22:00:43 +00:00
|
|
|
blend = alphaBlend(rgb, computedDirectives.opacity, lowerLevelStackedBackground)
|
2024-02-08 16:18:01 +00:00
|
|
|
}
|
|
|
|
stacked[selector] = blend
|
|
|
|
computed[selector].background = { ...rgb, a: computedDirectives.opacity ?? 1 }
|
2024-01-31 15:39:51 +00:00
|
|
|
}
|
2024-01-23 17:18:55 +00:00
|
|
|
}
|
2024-02-13 00:09:43 +00:00
|
|
|
|
2024-02-19 16:48:49 +00:00
|
|
|
if (computedDirectives.shadow) {
|
|
|
|
dynamicVars.shadow = (computedDirectives.shadow || []).map(shadow => {
|
|
|
|
let targetShadow
|
|
|
|
if (typeof shadow === 'string') {
|
|
|
|
if (shadow.startsWith('$')) {
|
|
|
|
targetShadow = process(shadow, shadowFunctions, findColor, dynamicVars, palette)
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
targetShadow = shadow
|
|
|
|
}
|
|
|
|
|
|
|
|
return {
|
|
|
|
...targetShadow,
|
|
|
|
color: findColor(targetShadow.color, dynamicVars, palette)
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2024-02-13 00:09:43 +00:00
|
|
|
if (!stacked[selector]) {
|
|
|
|
computedDirectives.background = 'transparent'
|
|
|
|
computedDirectives.opacity = 0
|
2024-02-18 22:00:43 +00:00
|
|
|
stacked[selector] = lowerLevelStackedBackground
|
|
|
|
computed[selector].background = { ...lowerLevelStackedBackground, a: 0 }
|
2024-02-13 00:09:43 +00:00
|
|
|
}
|
|
|
|
|
2024-02-18 22:00:43 +00:00
|
|
|
dynamicVars.stacked = lowerLevelStackedBackground
|
|
|
|
dynamicVars.background = computed[selector].background
|
2024-02-18 18:11:06 +00:00
|
|
|
|
2024-02-19 17:59:38 +00:00
|
|
|
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':
|
|
|
|
dynamicVars[k] = findColor(value, dynamicVars, palette)
|
|
|
|
}
|
|
|
|
})
|
|
|
|
|
2024-02-19 13:11:59 +00:00
|
|
|
addRule({
|
2024-02-19 16:48:49 +00:00
|
|
|
dynamicVars,
|
2024-02-19 13:11:59 +00:00
|
|
|
selector: cssSelector,
|
|
|
|
component: component.name,
|
|
|
|
...combination,
|
|
|
|
parent,
|
|
|
|
directives: computedDirectives
|
|
|
|
})
|
2024-01-31 15:39:51 +00:00
|
|
|
}
|
2024-02-08 16:18:01 +00:00
|
|
|
|
2024-02-15 18:20:27 +00:00
|
|
|
innerComponents.forEach(innerComponent => {
|
|
|
|
if (innerComponent.lazy) {
|
|
|
|
promises.push(new Promise((resolve, reject) => {
|
|
|
|
setTimeout(() => {
|
|
|
|
try {
|
|
|
|
processInnerComponent(innerComponent, lazyRules, { parent, component: name, ...combination })
|
|
|
|
resolve()
|
|
|
|
} catch (e) {
|
|
|
|
reject(e)
|
|
|
|
}
|
|
|
|
}, 0)
|
|
|
|
}))
|
|
|
|
} else {
|
|
|
|
processInnerComponent(innerComponent, rules, { parent, component: name, ...combination })
|
|
|
|
}
|
|
|
|
})
|
2024-02-11 21:11:28 +00:00
|
|
|
// const tt1 = performance.now()
|
|
|
|
// if (!component.virtual) {
|
|
|
|
// console.log('State-variant ' + combination.variant + ' : ' + combination.state.join('+') + ' procession time: ' + (tt1 - tt0) + 'ms')
|
|
|
|
// }
|
2024-01-18 12:35:25 +00:00
|
|
|
})
|
2024-02-08 16:18:01 +00:00
|
|
|
|
2024-02-11 21:11:28 +00:00
|
|
|
// const t1 = performance.now()
|
|
|
|
// if (!component.virtual) {
|
|
|
|
// const path = [...parentList, component.name].join(' > ')
|
|
|
|
// console.log('Component ' + path + ' procession time: ' + (t1 - t0) + 'ms')
|
|
|
|
// }
|
2024-01-18 12:35:25 +00:00
|
|
|
}
|
|
|
|
|
2024-02-15 18:20:27 +00:00
|
|
|
processInnerComponent(components.Root, eagerRules)
|
|
|
|
console.log('TOTAL COMBOS: ' + counter)
|
|
|
|
const lazyExec = Promise.all(promises).then(() => {
|
|
|
|
console.log('TOTAL COMBOS: ' + counter)
|
|
|
|
}).then(() => lazyRules)
|
2024-01-23 17:18:55 +00:00
|
|
|
|
|
|
|
return {
|
2024-02-15 18:20:27 +00:00
|
|
|
lazy: lazyExec,
|
2024-02-19 16:48:49 +00:00
|
|
|
eager: eagerRules
|
2024-01-23 17:18:55 +00:00
|
|
|
}
|
2024-01-18 12:35:25 +00:00
|
|
|
}
|