more dynamic vars, PROPER ICON COLORS

This commit is contained in:
Henry Jameson 2024-02-07 18:54:00 +02:00
parent 1229bbd855
commit a7d771e8c8
2 changed files with 55 additions and 40 deletions

View File

@ -6,10 +6,8 @@ export default {
{ {
component: 'Icon', component: 'Icon',
directives: { directives: {
textColor: '--text', textColor: '$blend(--parent, 0.5, --parent--text)',
// textAuto: 'no-auto', // doesn't work well with mixrgb? textAuto: 'no-auto'
textOpacity: 0.5,
textOpacityMode: 'mixrgb'
} }
} }
] ]

View File

@ -141,12 +141,12 @@ export const ruleToSelector = (rule, ignoreOutOfTreeSelector, isParent) => {
return selectors.trim() return selectors.trim()
} }
const combinationsMatch = (criteria, subject) => { const combinationsMatch = (criteria, subject, strict) => {
if (criteria.component !== subject.component) return false if (criteria.component !== subject.component) return false
// All variants inherit from normal // All variants inherit from normal
const subjectVariant = Object.prototype.hasOwnProperty.call(subject, 'variant') ? subject.variant : 'normal' const subjectVariant = Object.prototype.hasOwnProperty.call(subject, 'variant') ? subject.variant : 'normal'
if (subjectVariant !== 'normal') { if (subjectVariant !== 'normal' || strict) {
if (criteria.variant !== subject.variant) return false if (criteria.variant !== subject.variant) return false
} }
@ -154,7 +154,7 @@ const combinationsMatch = (criteria, subject) => {
const criteriaStatesSet = new Set(['normal', ...(criteria.state || [])]) const criteriaStatesSet = new Set(['normal', ...(criteria.state || [])])
// Subject states > 1 essentially means state is "normal" and therefore matches // Subject states > 1 essentially means state is "normal" and therefore matches
if (subjectStatesSet.size > 1) { if (subjectStatesSet.size > 1 || strict) {
const setsAreEqual = const setsAreEqual =
[...criteriaStatesSet].every(state => subjectStatesSet.has(state)) && [...criteriaStatesSet].every(state => subjectStatesSet.has(state)) &&
[...subjectStatesSet].every(state => criteriaStatesSet.has(state)) [...subjectStatesSet].every(state => criteriaStatesSet.has(state))
@ -164,13 +164,13 @@ const combinationsMatch = (criteria, subject) => {
return true return true
} }
const findRules = criteria => subject => { const findRules = (criteria, strict) => subject => {
// If we searching for "general" rules - ignore "specific" ones // If we searching for "general" rules - ignore "specific" ones
if (criteria.parent === null && !!subject.parent) return false if (criteria.parent === null && !!subject.parent) return false
if (!combinationsMatch(criteria, subject)) return false if (!combinationsMatch(criteria, subject, strict)) return false
if (criteria.parent !== undefined && criteria.parent !== null) { if (criteria.parent !== undefined && criteria.parent !== null) {
if (!subject.parent) return true if (!subject.parent && !strict) return true
const pathCriteria = unroll(criteria) const pathCriteria = unroll(criteria)
const pathSubject = unroll(subject) const pathSubject = unroll(subject)
if (pathCriteria.length < pathSubject.length) return false if (pathCriteria.length < pathSubject.length) return false
@ -182,7 +182,7 @@ const findRules = criteria => subject => {
const criteriaParent = pathCriteria[i] const criteriaParent = pathCriteria[i]
const subjectParent = pathSubject[i] const subjectParent = pathSubject[i]
if (!subjectParent) return true if (!subjectParent) return true
if (!combinationsMatch(criteriaParent, subjectParent)) return false if (!combinationsMatch(criteriaParent, subjectParent, strict)) return false
} }
} }
return true return true
@ -205,21 +205,24 @@ export const init = (extraRuleset, palette) => {
rules.push(rule) rules.push(rule)
} }
const findColor = (color, inheritedBackground, lowerLevelBackground) => { const findColor = (color, dynamicVars) => {
if (typeof color !== 'string' || (!color.startsWith('--') && !color.startsWith('$'))) return color if (typeof color !== 'string' || (!color.startsWith('--') && !color.startsWith('$'))) return color
let targetColor = null let targetColor = null
if (color.startsWith('--')) { if (color.startsWith('--')) {
const [variable, modifier] = color.split(/,/g).map(str => str.trim()) const [variable, modifier] = color.split(/,/g).map(str => str.trim())
const variableSlot = variable.substring(2) const variableSlot = variable.substring(2)
if (variableSlot.startsWith('parent')) { if (variableSlot.startsWith('parent')) {
// TODO support more than just background?
if (variableSlot === 'parent') { if (variableSlot === 'parent') {
targetColor = lowerLevelBackground targetColor = dynamicVars.lowerLevelBackground
} else {
const virtualSlot = variableSlot.replace(/^parent/, '')
targetColor = dynamicVars.lowerLevelVirtualDirectives[virtualSlot]
} }
} else { } else {
// TODO add support for --current prefix
switch (variableSlot) { switch (variableSlot) {
case 'background': case 'background':
targetColor = inheritedBackground targetColor = dynamicVars.inheritedBackground
break break
default: default:
targetColor = palette[variableSlot] targetColor = palette[variableSlot]
@ -227,12 +230,13 @@ export const init = (extraRuleset, palette) => {
} }
if (modifier) { if (modifier) {
const effectiveBackground = lowerLevelBackground ?? targetColor const effectiveBackground = dynamicVars.lowerLevelBackground ?? targetColor
const isLightOnDark = relativeLuminance(convert(effectiveBackground).rgb) < 0.5 const isLightOnDark = relativeLuminance(convert(effectiveBackground).rgb) < 0.5
const mod = isLightOnDark ? 1 : -1 const mod = isLightOnDark ? 1 : -1
targetColor = brightness(Number.parseFloat(modifier) * mod, targetColor).rgb targetColor = brightness(Number.parseFloat(modifier) * mod, targetColor).rgb
} }
} }
if (color.startsWith('$')) { if (color.startsWith('$')) {
try { try {
const { funcName, argsString } = /\$(?<funcName>\w+)\((?<argsString>[a-zA-Z0-9-,.'"\s]*)\)/.exec(color).groups const { funcName, argsString } = /\$(?<funcName>\w+)\((?<argsString>[a-zA-Z0-9-,.'"\s]*)\)/.exec(color).groups
@ -242,9 +246,10 @@ export const init = (extraRuleset, palette) => {
if (args.length !== 3) { if (args.length !== 3) {
throw new Error(`$blend requires 3 arguments, ${args.length} were provided`) throw new Error(`$blend requires 3 arguments, ${args.length} were provided`)
} }
const backgroundArg = findColor(args[2], inheritedBackground, lowerLevelBackground) const backgroundArg = convert(findColor(args[2], dynamicVars)).rgb
const foregroundArg = findColor(args[0], inheritedBackground, lowerLevelBackground) const foregroundArg = convert(findColor(args[0], dynamicVars)).rgb
const amount = Number(args[1]) const amount = Number(args[1])
console.log('ASS', backgroundArg, foregroundArg, amount)
targetColor = alphaBlend(backgroundArg, amount, foregroundArg) targetColor = alphaBlend(backgroundArg, amount, foregroundArg)
break break
} }
@ -260,17 +265,17 @@ export const init = (extraRuleset, palette) => {
const cssColorString = (color, alpha) => rgba2css({ ...convert(color).rgb, a: alpha }) const cssColorString = (color, alpha) => rgba2css({ ...convert(color).rgb, a: alpha })
const getTextColorAlpha = (rule, lowerColor, value) => { const getTextColorAlpha = (directives, intendedTextColor, dynamicVars) => {
const opacity = rule.directives.textOpacity const opacity = directives.textOpacity
const backgroundColor = convert(lowerColor).rgb const backgroundColor = convert(dynamicVars.lowerLevelBackground).rgb
const textColor = convert(findColor(value, backgroundColor)).rgb const textColor = convert(findColor(intendedTextColor, dynamicVars)).rgb
if (opacity === null || opacity === undefined || opacity >= 1) { if (opacity === null || opacity === undefined || opacity >= 1) {
return convert(textColor).hex return convert(textColor).hex
} }
if (opacity === 0) { if (opacity === 0) {
return convert(backgroundColor).hex return convert(backgroundColor).hex
} }
const opacityMode = rule.directives.textOpacityMode const opacityMode = directives.textOpacityMode
switch (opacityMode) { switch (opacityMode) {
case 'fake': case 'fake':
return convert(alphaBlend(textColor, opacity, backgroundColor)).hex return convert(alphaBlend(textColor, opacity, backgroundColor)).hex
@ -341,6 +346,16 @@ export const init = (extraRuleset, palette) => {
const soloSelector = ruleToSelector({ component: component.name, ...combination }, true) const soloSelector = ruleToSelector({ component: component.name, ...combination }, true)
const selector = ruleToSelector({ component: component.name, ...combination, parent }, true) const selector = ruleToSelector({ component: component.name, ...combination, parent }, true)
const lowerLevelSelector = selector.split(/ /g).slice(0, -1).join(' ')
const lowerLevelBackground = cache[lowerLevelSelector]?.background
const lowerLevelVirtualDirectives = cache[lowerLevelSelector]?.virtualDirectives
// console.log('ASS', lowerLevelVirtualDirectives)
const dynamicVars = {
lowerLevelBackground,
lowerLevelVirtualDirectives
}
// 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({ component: component.name, ...combination, parent }))
const { directives: computedDirectives } = existingRules.reduce((acc, rule) => merge(acc, rule), {}) const { directives: computedDirectives } = existingRules.reduce((acc, rule) => merge(acc, rule), {})
@ -389,11 +404,10 @@ export const init = (extraRuleset, palette) => {
} }
} }
const lowerLevelSelector = selector.split(/ /g).slice(0, -1).join(' ') dynamicVars.inheritedBackground = lowerLevelBackground
const lowerLevelBackground = cache[lowerLevelSelector].background
// TODO properly provide "parent" text color? // TODO properly provide "parent" text color?
const intendedTextColor = convert(findColor(inheritedTextColor, null, lowerLevelBackground)).rgb const intendedTextColor = convert(findColor(inheritedTextColor, dynamicVars)).rgb
const textColor = newTextRule.directives.textAuto === 'no-auto' const textColor = newTextRule.directives.textAuto === 'no-auto'
? intendedTextColor ? intendedTextColor
: getTextColor( : getTextColor(
@ -402,23 +416,25 @@ export const init = (extraRuleset, palette) => {
newTextRule.directives.textAuto === 'preserve' newTextRule.directives.textAuto === 'preserve'
) )
// Updating previously added rule
const earlyLowerLevelRules = rules.filter(findRules(parent, true))
const earlyLowerLevelRule = earlyLowerLevelRules.slice(-1)[0]
const virtualDirectives = earlyLowerLevelRule.virtualDirectives || {}
// Storing color data in lower layer to use as custom css properties // Storing color data in lower layer to use as custom css properties
cache[lowerLevelSelector].textDefined = cache[lowerLevelSelector].textDefined || {} virtualDirectives[virtualName] = getTextColorAlpha(newTextRule.directives, textColor, dynamicVars)
cache[lowerLevelSelector].textDefined[selector] = textColor earlyLowerLevelRule.virtualDirectives = virtualDirectives
cache[lowerLevelSelector].virtualDirectives = virtualDirectives
const virtualDirectives = {} // Debug: lets you see what it think background color should be
virtualDirectives[virtualName] = getTextColorAlpha(newTextRule, lowerLevelBackground, textColor)
// lastRule.computed = lastRule.computed || {}
const directives = { const directives = {
textColor, textColor,
background: convert(cache[lowerLevelSelector].background).hex,
...inheritedTextOpacity ...inheritedTextOpacity
} }
// Debug: lets you see what it think background color should be
// directives.background = convert(cache[lowerLevelSelector].background).hex
addRule({ addRule({
parent, parent,
virtual: true, virtual: true,
@ -445,15 +461,16 @@ export const init = (extraRuleset, palette) => {
const inheritSelector = ruleToSelector({ ...inheritRule, parent }, true) const inheritSelector = ruleToSelector({ ...inheritRule, parent }, true)
const inheritedBackground = cache[inheritSelector].background const inheritedBackground = cache[inheritSelector].background
const lowerLevelSelector = selector.split(/ /g).slice(0, -1).join(' ')
// TODO: DEFAULT TEXT COLOR // TODO: DEFAULT TEXT COLOR
const bg = cache[lowerLevelSelector]?.background || convert('#FFFFFF').rgb const lowerLevelComputedBackground = computed[lowerLevelSelector]?.background || convert('#FFFFFF').rgb
const rgb = convert(findColor(computedDirectives.background, inheritedBackground, cache[lowerLevelSelector].background)).rgb dynamicVars.inheritedBackground = inheritedBackground
const rgb = convert(findColor(computedDirectives.background, dynamicVars)).rgb
if (!cache[selector].background) { if (!cache[selector].background) {
const blend = computedDirectives.opacity < 1 ? alphaBlend(rgb, computedDirectives.opacity, bg) : rgb const blend = computedDirectives.opacity < 1 ? alphaBlend(rgb, computedDirectives.opacity, lowerLevelComputedBackground) : rgb
cache[selector].background = blend cache[selector].background = blend
computed[selector].background = rgb computed[selector].background = rgb
@ -479,7 +496,7 @@ export const init = (extraRuleset, palette) => {
return { return {
raw: rules, raw: rules,
css: rules.map(rule => { css: rules.map(rule => {
// if (rule.virtual) return '' if (rule.virtual) return ''
let selector = ruleToSelector(rule).replace(/\/\*.*\*\//g, '') let selector = ruleToSelector(rule).replace(/\/\*.*\*\//g, '')
if (!selector) { if (!selector) {