biome format --write
This commit is contained in:
parent
8372348148
commit
9262e803ec
415 changed files with 54076 additions and 17419 deletions
|
|
@ -2,7 +2,8 @@ import { convert } from 'chromatism'
|
|||
|
||||
import { hex2rgb, rgba2css } from '../color_convert/color_convert.js'
|
||||
|
||||
export const getCssColorString = (color, alpha = 1) => rgba2css({ ...convert(color).rgb, a: alpha })
|
||||
export const getCssColorString = (color, alpha = 1) =>
|
||||
rgba2css({ ...convert(color).rgb, a: alpha })
|
||||
|
||||
export const getCssShadow = (input, usesDropShadow) => {
|
||||
if (input.length === 0) {
|
||||
|
|
@ -10,16 +11,17 @@ export const getCssShadow = (input, usesDropShadow) => {
|
|||
}
|
||||
|
||||
return input
|
||||
.filter(_ => usesDropShadow ? _.inset : _)
|
||||
.map((shad) => [
|
||||
shad.x,
|
||||
shad.y,
|
||||
shad.blur,
|
||||
shad.spread
|
||||
].map(_ => _ + 'px ').concat([
|
||||
getCssColorString(shad.color, shad.alpha),
|
||||
shad.inset ? 'inset' : ''
|
||||
]).join(' ')).join(', ')
|
||||
.filter((_) => (usesDropShadow ? _.inset : _))
|
||||
.map((shad) =>
|
||||
[shad.x, shad.y, shad.blur, shad.spread]
|
||||
.map((_) => _ + 'px ')
|
||||
.concat([
|
||||
getCssColorString(shad.color, shad.alpha),
|
||||
shad.inset ? 'inset' : '',
|
||||
])
|
||||
.join(' '),
|
||||
)
|
||||
.join(', ')
|
||||
}
|
||||
|
||||
export const getCssShadowFilter = (input) => {
|
||||
|
|
@ -27,124 +29,156 @@ export const getCssShadowFilter = (input) => {
|
|||
return 'none'
|
||||
}
|
||||
|
||||
return input
|
||||
// drop-shadow doesn't support inset or spread
|
||||
.filter((shad) => !shad.inset && Number(shad.spread) === 0)
|
||||
.map((shad) => [
|
||||
shad.x,
|
||||
shad.y,
|
||||
// drop-shadow's blur is twice as strong compared to box-shadow
|
||||
shad.blur / 2
|
||||
].map(_ => _ + 'px').concat([
|
||||
getCssColorString(shad.color, shad.alpha)
|
||||
]).join(' '))
|
||||
.map(_ => `drop-shadow(${_})`)
|
||||
.join(' ')
|
||||
return (
|
||||
input
|
||||
// drop-shadow doesn't support inset or spread
|
||||
.filter((shad) => !shad.inset && Number(shad.spread) === 0)
|
||||
.map((shad) =>
|
||||
[
|
||||
shad.x,
|
||||
shad.y,
|
||||
// drop-shadow's blur is twice as strong compared to box-shadow
|
||||
shad.blur / 2,
|
||||
]
|
||||
.map((_) => _ + 'px')
|
||||
.concat([getCssColorString(shad.color, shad.alpha)])
|
||||
.join(' '),
|
||||
)
|
||||
.map((_) => `drop-shadow(${_})`)
|
||||
.join(' ')
|
||||
)
|
||||
}
|
||||
|
||||
// `debug` changes what backgrounds are used to "stacked" solid colors so you can see
|
||||
// what theme engine "thinks" is actual background color is for purposes of text color
|
||||
// generation and for when --stacked variable is used
|
||||
export const getCssRules = (rules, debug) => rules.map(rule => {
|
||||
let selector = rule.selector
|
||||
if (!selector) {
|
||||
selector = 'html'
|
||||
}
|
||||
const header = selector + ' {'
|
||||
const footer = '}'
|
||||
|
||||
const virtualDirectives = Object.entries(rule.virtualDirectives || {}).map(([k, v]) => {
|
||||
return ' ' + k + ': ' + v
|
||||
}).join(';\n')
|
||||
|
||||
const directives = Object.entries(rule.directives).map(([k, v]) => {
|
||||
switch (k) {
|
||||
case 'roundness': {
|
||||
return ' ' + [
|
||||
'--roundness: ' + v + 'px'
|
||||
].join(';\n ')
|
||||
export const getCssRules = (rules, debug) =>
|
||||
rules
|
||||
.map((rule) => {
|
||||
let selector = rule.selector
|
||||
if (!selector) {
|
||||
selector = 'html'
|
||||
}
|
||||
case 'shadow': {
|
||||
if (!rule.dynamicVars.shadow) {
|
||||
return ''
|
||||
}
|
||||
return ' ' + [
|
||||
'--shadow: ' + getCssShadow(rule.dynamicVars.shadow),
|
||||
'--shadowFilter: ' + getCssShadowFilter(rule.dynamicVars.shadow),
|
||||
'--shadowInset: ' + getCssShadow(rule.dynamicVars.shadow, true)
|
||||
].join(';\n ')
|
||||
}
|
||||
case 'background': {
|
||||
if (debug) {
|
||||
return `
|
||||
const header = selector + ' {'
|
||||
const footer = '}'
|
||||
|
||||
const virtualDirectives = Object.entries(rule.virtualDirectives || {})
|
||||
.map(([k, v]) => {
|
||||
return ' ' + k + ': ' + v
|
||||
})
|
||||
.join(';\n')
|
||||
|
||||
const directives = Object.entries(rule.directives)
|
||||
.map(([k, v]) => {
|
||||
switch (k) {
|
||||
case 'roundness': {
|
||||
return ' ' + ['--roundness: ' + v + 'px'].join(';\n ')
|
||||
}
|
||||
case 'shadow': {
|
||||
if (!rule.dynamicVars.shadow) {
|
||||
return ''
|
||||
}
|
||||
return (
|
||||
' ' +
|
||||
[
|
||||
'--shadow: ' + getCssShadow(rule.dynamicVars.shadow),
|
||||
'--shadowFilter: ' +
|
||||
getCssShadowFilter(rule.dynamicVars.shadow),
|
||||
'--shadowInset: ' +
|
||||
getCssShadow(rule.dynamicVars.shadow, true),
|
||||
].join(';\n ')
|
||||
)
|
||||
}
|
||||
case 'background': {
|
||||
if (debug) {
|
||||
return `
|
||||
--background: ${getCssColorString(rule.dynamicVars.stacked)};
|
||||
background-color: ${getCssColorString(rule.dynamicVars.stacked)};
|
||||
`
|
||||
}
|
||||
if (v === 'transparent') {
|
||||
if (rule.component === 'Root') return null
|
||||
return [
|
||||
rule.directives.backgroundNoCssColor !== 'yes' ? ('background-color: ' + v) : '',
|
||||
' --background: ' + v
|
||||
].filter(x => x).join(';\n')
|
||||
}
|
||||
const color = getCssColorString(rule.dynamicVars.background, rule.directives.opacity)
|
||||
const cssDirectives = ['--background: ' + color]
|
||||
if (rule.directives.backgroundNoCssColor !== 'yes') {
|
||||
cssDirectives.push('background-color: ' + color)
|
||||
}
|
||||
return cssDirectives.filter(x => x).join(';\n')
|
||||
}
|
||||
case 'blur': {
|
||||
const cssDirectives = []
|
||||
if (rule.directives.opacity < 1) {
|
||||
cssDirectives.push(`--backdrop-filter: blur(${v}) `)
|
||||
if (rule.directives.backgroundNoCssColor !== 'yes') {
|
||||
cssDirectives.push(`backdrop-filter: blur(${v}) `)
|
||||
}
|
||||
}
|
||||
return cssDirectives.join(';\n')
|
||||
}
|
||||
case 'font': {
|
||||
return 'font-family: ' + v
|
||||
}
|
||||
case 'textColor': {
|
||||
if (rule.directives.textNoCssColor === 'yes') { return '' }
|
||||
return 'color: ' + v
|
||||
}
|
||||
default:
|
||||
if (k.startsWith('--')) {
|
||||
const [type, value] = v.split('|').map(x => x.trim())
|
||||
switch (type) {
|
||||
case 'color': {
|
||||
const color = rule.dynamicVars[k]
|
||||
if (typeof color === 'string') {
|
||||
return k + ': ' + rgba2css(hex2rgb(color))
|
||||
} else {
|
||||
return k + ': ' + rgba2css(color)
|
||||
}
|
||||
if (v === 'transparent') {
|
||||
if (rule.component === 'Root') return null
|
||||
return [
|
||||
rule.directives.backgroundNoCssColor !== 'yes'
|
||||
? 'background-color: ' + v
|
||||
: '',
|
||||
' --background: ' + v,
|
||||
]
|
||||
.filter((x) => x)
|
||||
.join(';\n')
|
||||
}
|
||||
const color = getCssColorString(
|
||||
rule.dynamicVars.background,
|
||||
rule.directives.opacity,
|
||||
)
|
||||
const cssDirectives = ['--background: ' + color]
|
||||
if (rule.directives.backgroundNoCssColor !== 'yes') {
|
||||
cssDirectives.push('background-color: ' + color)
|
||||
}
|
||||
return cssDirectives.filter((x) => x).join(';\n')
|
||||
}
|
||||
case 'blur': {
|
||||
const cssDirectives = []
|
||||
if (rule.directives.opacity < 1) {
|
||||
cssDirectives.push(`--backdrop-filter: blur(${v}) `)
|
||||
if (rule.directives.backgroundNoCssColor !== 'yes') {
|
||||
cssDirectives.push(`backdrop-filter: blur(${v}) `)
|
||||
}
|
||||
}
|
||||
return cssDirectives.join(';\n')
|
||||
}
|
||||
case 'font': {
|
||||
return 'font-family: ' + v
|
||||
}
|
||||
case 'textColor': {
|
||||
if (rule.directives.textNoCssColor === 'yes') {
|
||||
return ''
|
||||
}
|
||||
return 'color: ' + v
|
||||
}
|
||||
case 'generic':
|
||||
return k + ': ' + value
|
||||
default:
|
||||
if (k.startsWith('--')) {
|
||||
const [type, value] = v.split('|').map((x) => x.trim())
|
||||
switch (type) {
|
||||
case 'color': {
|
||||
const color = rule.dynamicVars[k]
|
||||
if (typeof color === 'string') {
|
||||
return k + ': ' + rgba2css(hex2rgb(color))
|
||||
} else {
|
||||
return k + ': ' + rgba2css(color)
|
||||
}
|
||||
}
|
||||
case 'generic':
|
||||
return k + ': ' + value
|
||||
default:
|
||||
return null
|
||||
}
|
||||
}
|
||||
return null
|
||||
}
|
||||
}
|
||||
return null
|
||||
}
|
||||
}).filter(x => x).map(x => ' ' + x + ';').join('\n')
|
||||
})
|
||||
.filter((x) => x)
|
||||
.map((x) => ' ' + x + ';')
|
||||
.join('\n')
|
||||
|
||||
return [
|
||||
header,
|
||||
directives,
|
||||
(rule.component === 'Text' && rule.state.indexOf('faint') < 0 && rule.directives.textNoCssColor !== 'yes') ? ' color: var(--text);' : '',
|
||||
virtualDirectives,
|
||||
footer
|
||||
].filter(x => x).join('\n')
|
||||
}).filter(x => x)
|
||||
return [
|
||||
header,
|
||||
directives,
|
||||
rule.component === 'Text' &&
|
||||
rule.state.indexOf('faint') < 0 &&
|
||||
rule.directives.textNoCssColor !== 'yes'
|
||||
? ' color: var(--text);'
|
||||
: '',
|
||||
virtualDirectives,
|
||||
footer,
|
||||
]
|
||||
.filter((x) => x)
|
||||
.join('\n')
|
||||
})
|
||||
.filter((x) => x)
|
||||
|
||||
export const getScopedVersion = (rules, newScope) => {
|
||||
return rules.map(x => {
|
||||
return rules.map((x) => {
|
||||
if (x.startsWith('html')) {
|
||||
return x.replace('html', newScope)
|
||||
} else if (x.startsWith('#content')) {
|
||||
|
|
|
|||
|
|
@ -1,7 +1,17 @@
|
|||
import { flattenDeep } from 'lodash'
|
||||
|
||||
export const deserializeShadow = string => {
|
||||
const modes = ['_full', 'inset', 'x', 'y', 'blur', 'spread', 'color', 'alpha', 'name']
|
||||
export const deserializeShadow = (string) => {
|
||||
const modes = [
|
||||
'_full',
|
||||
'inset',
|
||||
'x',
|
||||
'y',
|
||||
'blur',
|
||||
'spread',
|
||||
'color',
|
||||
'alpha',
|
||||
'name',
|
||||
]
|
||||
const regexPrep = [
|
||||
// inset keyword (optional)
|
||||
'^',
|
||||
|
|
@ -20,7 +30,7 @@ export const deserializeShadow = string => {
|
|||
'(?:\\s+\\/\\s+([0-9]+(?:\\.[0-9]+)?)\\s*)?',
|
||||
// name
|
||||
'(?:\\s+#(\\w+)\\s*)?',
|
||||
'$'
|
||||
'$',
|
||||
].join('')
|
||||
const regex = new RegExp(regexPrep, 'gis') // global, (stable) indices, single-string
|
||||
const result = regex.exec(string)
|
||||
|
|
@ -32,20 +42,26 @@ export const deserializeShadow = string => {
|
|||
}
|
||||
} else {
|
||||
const numeric = new Set(['x', 'y', 'blur', 'spread', 'alpha'])
|
||||
const { x, y, blur, spread, alpha, inset, color, name } = Object.fromEntries(modes.map((mode, i) => {
|
||||
if (numeric.has(mode)) {
|
||||
const number = Number(result[i])
|
||||
if (Number.isNaN(number)) {
|
||||
if (mode === 'alpha') return [mode, 1]
|
||||
return [mode, 0]
|
||||
}
|
||||
return [mode, number]
|
||||
} else if (mode === 'inset') {
|
||||
return [mode, !!result[i]]
|
||||
} else {
|
||||
return [mode, result[i]]
|
||||
}
|
||||
}).filter(([, v]) => v !== false).slice(1))
|
||||
const { x, y, blur, spread, alpha, inset, color, name } =
|
||||
Object.fromEntries(
|
||||
modes
|
||||
.map((mode, i) => {
|
||||
if (numeric.has(mode)) {
|
||||
const number = Number(result[i])
|
||||
if (Number.isNaN(number)) {
|
||||
if (mode === 'alpha') return [mode, 1]
|
||||
return [mode, 0]
|
||||
}
|
||||
return [mode, number]
|
||||
} else if (mode === 'inset') {
|
||||
return [mode, !!result[i]]
|
||||
} else {
|
||||
return [mode, result[i]]
|
||||
}
|
||||
})
|
||||
.filter(([, v]) => v !== false)
|
||||
.slice(1),
|
||||
)
|
||||
|
||||
return { x, y, blur, spread, color, alpha, inset, name }
|
||||
}
|
||||
|
|
@ -94,77 +110,87 @@ const parseIss = (input) => {
|
|||
}
|
||||
export const deserialize = (input) => {
|
||||
const ast = parseIss(input)
|
||||
const finalResult = ast.filter(i => i.selector != null).map(item => {
|
||||
const { selector, content } = item
|
||||
let stateCount = 0
|
||||
const selectors = selector.split(/,/g)
|
||||
const result = selectors.map(selector => {
|
||||
const output = { component: '' }
|
||||
let currentDepth = null
|
||||
const finalResult = ast
|
||||
.filter((i) => i.selector != null)
|
||||
.map((item) => {
|
||||
const { selector, content } = item
|
||||
let stateCount = 0
|
||||
const selectors = selector.split(/,/g)
|
||||
const result = selectors.map((selector) => {
|
||||
const output = { component: '' }
|
||||
let currentDepth = null
|
||||
|
||||
selector.split(/ /g).reverse().forEach((fragment, index, arr) => {
|
||||
const fragmentObject = { component: '' }
|
||||
selector
|
||||
.split(/ /g)
|
||||
.reverse()
|
||||
.forEach((fragment, index, arr) => {
|
||||
const fragmentObject = { component: '' }
|
||||
|
||||
let mode = 'component'
|
||||
for (let i = 0; i < fragment.length; i++) {
|
||||
const char = fragment[i]
|
||||
switch (char) {
|
||||
case '.': {
|
||||
mode = 'variant'
|
||||
fragmentObject.variant = ''
|
||||
break
|
||||
}
|
||||
case ':': {
|
||||
mode = 'state'
|
||||
fragmentObject.state = fragmentObject.state || []
|
||||
stateCount++
|
||||
break
|
||||
}
|
||||
default: {
|
||||
if (mode === 'state') {
|
||||
const currentState = fragmentObject.state[stateCount - 1]
|
||||
if (currentState == null) {
|
||||
fragmentObject.state.push('')
|
||||
let mode = 'component'
|
||||
for (let i = 0; i < fragment.length; i++) {
|
||||
const char = fragment[i]
|
||||
switch (char) {
|
||||
case '.': {
|
||||
mode = 'variant'
|
||||
fragmentObject.variant = ''
|
||||
break
|
||||
}
|
||||
case ':': {
|
||||
mode = 'state'
|
||||
fragmentObject.state = fragmentObject.state || []
|
||||
stateCount++
|
||||
break
|
||||
}
|
||||
default: {
|
||||
if (mode === 'state') {
|
||||
const currentState = fragmentObject.state[stateCount - 1]
|
||||
if (currentState == null) {
|
||||
fragmentObject.state.push('')
|
||||
}
|
||||
fragmentObject.state[stateCount - 1] += char
|
||||
} else {
|
||||
fragmentObject[mode] += char
|
||||
}
|
||||
}
|
||||
fragmentObject.state[stateCount - 1] += char
|
||||
} else {
|
||||
fragmentObject[mode] += char
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (currentDepth !== null) {
|
||||
currentDepth.parent = { ...fragmentObject }
|
||||
currentDepth = currentDepth.parent
|
||||
} else {
|
||||
Object.keys(fragmentObject).forEach(key => {
|
||||
output[key] = fragmentObject[key]
|
||||
if (currentDepth !== null) {
|
||||
currentDepth.parent = { ...fragmentObject }
|
||||
currentDepth = currentDepth.parent
|
||||
} else {
|
||||
Object.keys(fragmentObject).forEach((key) => {
|
||||
output[key] = fragmentObject[key]
|
||||
})
|
||||
if (index !== arr.length - 1) {
|
||||
output.parent = { component: '' }
|
||||
}
|
||||
currentDepth = output
|
||||
}
|
||||
})
|
||||
if (index !== (arr.length - 1)) {
|
||||
output.parent = { component: '' }
|
||||
}
|
||||
currentDepth = output
|
||||
}
|
||||
|
||||
output.directives = Object.fromEntries(
|
||||
content.map((d) => {
|
||||
const [property, value] = d.split(':')
|
||||
let realValue = (value || '').trim()
|
||||
if (property === 'shadow') {
|
||||
if (realValue === 'none') {
|
||||
realValue = []
|
||||
} else {
|
||||
realValue = value
|
||||
.split(',')
|
||||
.map((v) => deserializeShadow(v.trim()))
|
||||
}
|
||||
}
|
||||
if (!Number.isNaN(Number(value))) {
|
||||
realValue = Number(value)
|
||||
}
|
||||
return [property, realValue]
|
||||
}),
|
||||
)
|
||||
|
||||
return output
|
||||
})
|
||||
|
||||
output.directives = Object.fromEntries(content.map(d => {
|
||||
const [property, value] = d.split(':')
|
||||
let realValue = (value || '').trim()
|
||||
if (property === 'shadow') {
|
||||
if (realValue === 'none') {
|
||||
realValue = []
|
||||
} else {
|
||||
realValue = value.split(',').map(v => deserializeShadow(v.trim()))
|
||||
}
|
||||
} if (!Number.isNaN(Number(value))) {
|
||||
realValue = Number(value)
|
||||
}
|
||||
return [property, realValue]
|
||||
}))
|
||||
|
||||
return output
|
||||
return result
|
||||
})
|
||||
return result
|
||||
})
|
||||
return flattenDeep(finalResult)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,40 +14,54 @@ export const serializeShadow = (s) => {
|
|||
}
|
||||
|
||||
export const serialize = (ruleset) => {
|
||||
return ruleset.map((rule) => {
|
||||
if (Object.keys(rule.directives || {}).length === 0) return false
|
||||
return ruleset
|
||||
.map((rule) => {
|
||||
if (Object.keys(rule.directives || {}).length === 0) return false
|
||||
|
||||
const header = unroll(rule).reverse().map(rule => {
|
||||
const { component } = rule
|
||||
const newVariant = (rule.variant == null || rule.variant === 'normal') ? '' : ('.' + rule.variant)
|
||||
const newState = (rule.state || []).filter(st => st !== 'normal')
|
||||
const header = unroll(rule)
|
||||
.reverse()
|
||||
.map((rule) => {
|
||||
const { component } = rule
|
||||
const newVariant =
|
||||
rule.variant == null || rule.variant === 'normal'
|
||||
? ''
|
||||
: '.' + rule.variant
|
||||
const newState = (rule.state || []).filter((st) => st !== 'normal')
|
||||
|
||||
return `${component}${newVariant}${newState.map(st => ':' + st).join('')}`
|
||||
}).join(' ')
|
||||
return `${component}${newVariant}${newState.map((st) => ':' + st).join('')}`
|
||||
})
|
||||
.join(' ')
|
||||
|
||||
const content = Object.entries(rule.directives).map(([directive, value]) => {
|
||||
if (directive.startsWith('--')) {
|
||||
const [valType, newValue] = value.split('|') // only first one! intentional!
|
||||
switch (valType) {
|
||||
case 'shadow':
|
||||
return ` ${directive}: ${valType.trim()} | ${newValue.map(serializeShadow).map(s => s.trim()).join(', ')}`
|
||||
default:
|
||||
return ` ${directive}: ${valType.trim()} | ${newValue.trim()}`
|
||||
}
|
||||
} else {
|
||||
switch (directive) {
|
||||
case 'shadow':
|
||||
if (value.length > 0) {
|
||||
return ` ${directive}: ${value.map(serializeShadow).join(', ')}`
|
||||
} else {
|
||||
return ` ${directive}: none`
|
||||
const content = Object.entries(rule.directives).map(
|
||||
([directive, value]) => {
|
||||
if (directive.startsWith('--')) {
|
||||
const [valType, newValue] = value.split('|') // only first one! intentional!
|
||||
switch (valType) {
|
||||
case 'shadow':
|
||||
return ` ${directive}: ${valType.trim()} | ${newValue
|
||||
.map(serializeShadow)
|
||||
.map((s) => s.trim())
|
||||
.join(', ')}`
|
||||
default:
|
||||
return ` ${directive}: ${valType.trim()} | ${newValue.trim()}`
|
||||
}
|
||||
default:
|
||||
return ` ${directive}: ${value}`
|
||||
}
|
||||
}
|
||||
})
|
||||
} else {
|
||||
switch (directive) {
|
||||
case 'shadow':
|
||||
if (value.length > 0) {
|
||||
return ` ${directive}: ${value.map(serializeShadow).join(', ')}`
|
||||
} else {
|
||||
return ` ${directive}: none`
|
||||
}
|
||||
default:
|
||||
return ` ${directive}: ${value}`
|
||||
}
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
return `${header} {\n${content.join(';\n')}\n}`
|
||||
}).filter(x => x).join('\n\n')
|
||||
return `${header} {\n${content.join(';\n')}\n}`
|
||||
})
|
||||
.filter((x) => x)
|
||||
.join('\n\n')
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,18 +15,18 @@ export const unroll = (item) => {
|
|||
// This gives you an array of arrays of all possible unique (i.e. order-insensitive) combinations
|
||||
// Can only accept primitives. Duplicates are not supported and can cause unexpected behavior
|
||||
export const getAllPossibleCombinations = (array) => {
|
||||
const combos = [array.map(x => [x])]
|
||||
const combos = [array.map((x) => [x])]
|
||||
for (let comboSize = 2; comboSize <= array.length; comboSize++) {
|
||||
const previous = combos[combos.length - 1]
|
||||
const newCombos = previous.map(self => {
|
||||
const newCombos = previous.map((self) => {
|
||||
const selfSet = new Set()
|
||||
self.forEach(x => selfSet.add(x))
|
||||
const nonSelf = array.filter(x => !selfSet.has(x))
|
||||
return nonSelf.map(x => [...self, x])
|
||||
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], [])
|
||||
const uniqueComboStrings = new Set()
|
||||
const uniqueCombos = flatCombos.map(sortBy).filter(x => {
|
||||
const uniqueCombos = flatCombos.map(sortBy).filter((x) => {
|
||||
if (uniqueComboStrings.has(x.join())) {
|
||||
return false
|
||||
} else {
|
||||
|
|
@ -56,75 +56,98 @@ export const getAllPossibleCombinations = (array) => {
|
|||
*
|
||||
* @returns {String} CSS selector (or path)
|
||||
*/
|
||||
export const genericRuleToSelector = components => (rule, ignoreOutOfTreeSelector, liteMode, children) => {
|
||||
const isParent = !!children
|
||||
if (!rule && !isParent) return null
|
||||
const component = components[rule.component]
|
||||
const { states = {}, variants = {}, outOfTreeSelector } = component
|
||||
export const genericRuleToSelector =
|
||||
(components) => (rule, ignoreOutOfTreeSelector, liteMode, children) => {
|
||||
const isParent = !!children
|
||||
if (!rule && !isParent) return null
|
||||
const component = components[rule.component]
|
||||
const { states = {}, variants = {}, outOfTreeSelector } = component
|
||||
|
||||
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 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()
|
||||
}
|
||||
})
|
||||
|
||||
const applicableVariantName = (rule.variant || 'normal')
|
||||
let variantSelectors = null
|
||||
if (applicableVariantName !== 'normal') {
|
||||
variantSelectors = variants[applicableVariantName]
|
||||
} else {
|
||||
variantSelectors = variants?.normal ?? ''
|
||||
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'
|
||||
let variantSelectors = null
|
||||
if (applicableVariantName !== 'normal') {
|
||||
variantSelectors = variants[applicableVariantName]
|
||||
} else {
|
||||
variantSelectors = variants?.normal ?? ''
|
||||
}
|
||||
variantSelectors = Array.isArray(variantSelectors)
|
||||
? variantSelectors
|
||||
: [variantSelectors]
|
||||
if (ignoreOutOfTreeSelector || liteMode)
|
||||
variantSelectors = [variantSelectors[0]]
|
||||
|
||||
const applicableStates = (rule.state || []).filter((x) => x !== 'normal')
|
||||
// const applicableStates = (rule.state || [])
|
||||
const statesSelectors = applicableStates.map((state) => {
|
||||
const selector = states[state] || ''
|
||||
let arraySelector = Array.isArray(selector) ? selector : [selector]
|
||||
if (ignoreOutOfTreeSelector || liteMode)
|
||||
arraySelector = [arraySelector[0]]
|
||||
arraySelector
|
||||
.sort((a) => {
|
||||
if (a.startsWith(':')) return 1
|
||||
if (/^[a-z]/.exec(a)) return -1
|
||||
else return 0
|
||||
})
|
||||
.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) {
|
||||
return genericRuleToSelector(components)(
|
||||
rule.parent,
|
||||
ignoreOutOfTreeSelector,
|
||||
liteMode,
|
||||
selectors,
|
||||
)
|
||||
}
|
||||
|
||||
return selectors.join(', ').trim()
|
||||
}
|
||||
variantSelectors = Array.isArray(variantSelectors) ? variantSelectors : [variantSelectors]
|
||||
if (ignoreOutOfTreeSelector || liteMode) variantSelectors = [variantSelectors[0]]
|
||||
|
||||
const applicableStates = (rule.state || []).filter(x => x !== 'normal')
|
||||
// const applicableStates = (rule.state || [])
|
||||
const statesSelectors = applicableStates.map(state => {
|
||||
const selector = states[state] || ''
|
||||
let arraySelector = Array.isArray(selector) ? selector : [selector]
|
||||
if (ignoreOutOfTreeSelector || liteMode) arraySelector = [arraySelector[0]]
|
||||
arraySelector
|
||||
.sort((a) => {
|
||||
if (a.startsWith(':')) return 1
|
||||
if (/^[a-z]/.exec(a)) return -1
|
||||
else return 0
|
||||
})
|
||||
.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) {
|
||||
return genericRuleToSelector(components)(rule.parent, ignoreOutOfTreeSelector, liteMode, selectors)
|
||||
}
|
||||
|
||||
return selectors.join(', ').trim()
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if combination matches
|
||||
|
|
@ -151,8 +174,8 @@ export const combinationsMatch = (criteria, subject, strict) => {
|
|||
const criteriaStatesSet = new Set(criteria.state)
|
||||
|
||||
const setsAreEqual =
|
||||
[...criteriaStatesSet].every(state => subjectStatesSet.has(state)) &&
|
||||
[...subjectStatesSet].every(state => criteriaStatesSet.has(state))
|
||||
[...criteriaStatesSet].every((state) => subjectStatesSet.has(state)) &&
|
||||
[...subjectStatesSet].every((state) => criteriaStatesSet.has(state))
|
||||
|
||||
if (!setsAreEqual) return false
|
||||
}
|
||||
|
|
@ -168,7 +191,7 @@ export const combinationsMatch = (criteria, subject, strict) => {
|
|||
*
|
||||
* @return function that returns true/false if subject matches
|
||||
*/
|
||||
export const findRules = (criteria, strict) => subject => {
|
||||
export const findRules = (criteria, strict) => (subject) => {
|
||||
// If we searching for "general" rules - ignore "specific" ones
|
||||
if (criteria.parent === null && !!subject.parent) return false
|
||||
if (!combinationsMatch(criteria, subject, strict)) return false
|
||||
|
|
@ -186,14 +209,15 @@ export const findRules = (criteria, strict) => subject => {
|
|||
const criteriaParent = pathCriteria[i]
|
||||
const subjectParent = pathSubject[i]
|
||||
if (!subjectParent) return true
|
||||
if (!combinationsMatch(criteriaParent, subjectParent, strict)) return false
|
||||
if (!combinationsMatch(criteriaParent, subjectParent, strict))
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// Pre-fills 'normal' state/variant if missing
|
||||
export const normalizeCombination = rule => {
|
||||
export const normalizeCombination = (rule) => {
|
||||
rule.variant = rule.variant ?? 'normal'
|
||||
rule.state = [...new Set(['normal', ...(rule.state || [])])]
|
||||
}
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ export const LAYERS = {
|
|||
alertPanel: 'panel',
|
||||
poll: 'bg',
|
||||
chatBg: 'underlay',
|
||||
chatMessage: 'chatBg'
|
||||
chatMessage: 'chatBg',
|
||||
}
|
||||
|
||||
/* By default opacity slots have 1 as default opacity
|
||||
|
|
@ -37,7 +37,7 @@ export const DEFAULT_OPACITY = {
|
|||
input: 0.5,
|
||||
faint: 0.5,
|
||||
underlay: 0.15,
|
||||
alertPopup: 0.95
|
||||
alertPopup: 0.95,
|
||||
}
|
||||
|
||||
/** SUBJECT TO CHANGE IN THE FUTURE, this is all beta
|
||||
|
|
@ -82,45 +82,45 @@ export const SLOT_INHERITANCE = {
|
|||
bg: {
|
||||
depends: [],
|
||||
opacity: 'bg',
|
||||
priority: 1
|
||||
priority: 1,
|
||||
},
|
||||
wallpaper: {
|
||||
depends: ['bg'],
|
||||
color: (mod, bg) => brightness(-2 * mod, bg).rgb
|
||||
color: (mod, bg) => brightness(-2 * mod, bg).rgb,
|
||||
},
|
||||
fg: {
|
||||
depends: [],
|
||||
priority: 1
|
||||
priority: 1,
|
||||
},
|
||||
text: {
|
||||
depends: [],
|
||||
layer: 'bg',
|
||||
opacity: null,
|
||||
priority: 1
|
||||
priority: 1,
|
||||
},
|
||||
underlay: {
|
||||
default: '#000000',
|
||||
opacity: 'underlay'
|
||||
opacity: 'underlay',
|
||||
},
|
||||
link: {
|
||||
depends: ['accent'],
|
||||
priority: 1
|
||||
priority: 1,
|
||||
},
|
||||
accent: {
|
||||
depends: ['link'],
|
||||
priority: 1
|
||||
priority: 1,
|
||||
},
|
||||
faint: {
|
||||
depends: ['text'],
|
||||
opacity: 'faint'
|
||||
opacity: 'faint',
|
||||
},
|
||||
faintLink: {
|
||||
depends: ['link'],
|
||||
opacity: 'faint'
|
||||
opacity: 'faint',
|
||||
},
|
||||
postFaintLink: {
|
||||
depends: ['postLink'],
|
||||
opacity: 'faint'
|
||||
opacity: 'faint',
|
||||
},
|
||||
|
||||
cBlue: '#0000ff',
|
||||
|
|
@ -133,101 +133,101 @@ export const SLOT_INHERITANCE = {
|
|||
color: (mod, bg) => ({
|
||||
r: Math.floor(bg.r * 0.53),
|
||||
g: Math.floor(bg.g * 0.56),
|
||||
b: Math.floor(bg.b * 0.59)
|
||||
})
|
||||
b: Math.floor(bg.b * 0.59),
|
||||
}),
|
||||
},
|
||||
profileTint: {
|
||||
depends: ['bg'],
|
||||
layer: 'profileTint',
|
||||
opacity: 'profileTint'
|
||||
opacity: 'profileTint',
|
||||
},
|
||||
|
||||
highlight: {
|
||||
depends: ['bg'],
|
||||
color: (mod, bg) => brightness(5 * mod, bg).rgb
|
||||
color: (mod, bg) => brightness(5 * mod, bg).rgb,
|
||||
},
|
||||
highlightLightText: {
|
||||
depends: ['lightText'],
|
||||
layer: 'highlight',
|
||||
textColor: true
|
||||
textColor: true,
|
||||
},
|
||||
highlightPostLink: {
|
||||
depends: ['postLink'],
|
||||
layer: 'highlight',
|
||||
textColor: 'preserve'
|
||||
textColor: 'preserve',
|
||||
},
|
||||
highlightFaintText: {
|
||||
depends: ['faint'],
|
||||
layer: 'highlight',
|
||||
textColor: true
|
||||
textColor: true,
|
||||
},
|
||||
highlightFaintLink: {
|
||||
depends: ['faintLink'],
|
||||
layer: 'highlight',
|
||||
textColor: 'preserve'
|
||||
textColor: 'preserve',
|
||||
},
|
||||
highlightPostFaintLink: {
|
||||
depends: ['postFaintLink'],
|
||||
layer: 'highlight',
|
||||
textColor: 'preserve'
|
||||
textColor: 'preserve',
|
||||
},
|
||||
highlightText: {
|
||||
depends: ['text'],
|
||||
layer: 'highlight',
|
||||
textColor: true
|
||||
textColor: true,
|
||||
},
|
||||
highlightLink: {
|
||||
depends: ['link'],
|
||||
layer: 'highlight',
|
||||
textColor: 'preserve'
|
||||
textColor: 'preserve',
|
||||
},
|
||||
highlightIcon: {
|
||||
depends: ['highlight', 'highlightText'],
|
||||
color: (mod, bg, text) => mixrgb(bg, text)
|
||||
color: (mod, bg, text) => mixrgb(bg, text),
|
||||
},
|
||||
|
||||
popover: {
|
||||
depends: ['bg'],
|
||||
opacity: 'popover'
|
||||
opacity: 'popover',
|
||||
},
|
||||
popoverLightText: {
|
||||
depends: ['lightText'],
|
||||
layer: 'popover',
|
||||
textColor: true
|
||||
textColor: true,
|
||||
},
|
||||
popoverPostLink: {
|
||||
depends: ['postLink'],
|
||||
layer: 'popover',
|
||||
textColor: 'preserve'
|
||||
textColor: 'preserve',
|
||||
},
|
||||
popoverFaintText: {
|
||||
depends: ['faint'],
|
||||
layer: 'popover',
|
||||
textColor: true
|
||||
textColor: true,
|
||||
},
|
||||
popoverFaintLink: {
|
||||
depends: ['faintLink'],
|
||||
layer: 'popover',
|
||||
textColor: 'preserve'
|
||||
textColor: 'preserve',
|
||||
},
|
||||
popoverPostFaintLink: {
|
||||
depends: ['postFaintLink'],
|
||||
layer: 'popover',
|
||||
textColor: 'preserve'
|
||||
textColor: 'preserve',
|
||||
},
|
||||
popoverText: {
|
||||
depends: ['text'],
|
||||
layer: 'popover',
|
||||
textColor: true
|
||||
textColor: true,
|
||||
},
|
||||
popoverLink: {
|
||||
depends: ['link'],
|
||||
layer: 'popover',
|
||||
textColor: 'preserve'
|
||||
textColor: 'preserve',
|
||||
},
|
||||
popoverIcon: {
|
||||
depends: ['popover', 'popoverText'],
|
||||
color: (mod, bg, text) => mixrgb(bg, text)
|
||||
color: (mod, bg, text) => mixrgb(bg, text),
|
||||
},
|
||||
|
||||
selectedPost: '--highlight',
|
||||
|
|
@ -235,201 +235,201 @@ export const SLOT_INHERITANCE = {
|
|||
depends: ['highlightFaintText'],
|
||||
layer: 'highlight',
|
||||
variant: 'selectedPost',
|
||||
textColor: true
|
||||
textColor: true,
|
||||
},
|
||||
selectedPostLightText: {
|
||||
depends: ['highlightLightText'],
|
||||
layer: 'highlight',
|
||||
variant: 'selectedPost',
|
||||
textColor: true
|
||||
textColor: true,
|
||||
},
|
||||
selectedPostPostLink: {
|
||||
depends: ['highlightPostLink'],
|
||||
layer: 'highlight',
|
||||
variant: 'selectedPost',
|
||||
textColor: 'preserve'
|
||||
textColor: 'preserve',
|
||||
},
|
||||
selectedPostFaintLink: {
|
||||
depends: ['highlightFaintLink'],
|
||||
layer: 'highlight',
|
||||
variant: 'selectedPost',
|
||||
textColor: 'preserve'
|
||||
textColor: 'preserve',
|
||||
},
|
||||
selectedPostText: {
|
||||
depends: ['highlightText'],
|
||||
layer: 'highlight',
|
||||
variant: 'selectedPost',
|
||||
textColor: true
|
||||
textColor: true,
|
||||
},
|
||||
selectedPostLink: {
|
||||
depends: ['highlightLink'],
|
||||
layer: 'highlight',
|
||||
variant: 'selectedPost',
|
||||
textColor: 'preserve'
|
||||
textColor: 'preserve',
|
||||
},
|
||||
selectedPostIcon: {
|
||||
depends: ['selectedPost', 'selectedPostText'],
|
||||
color: (mod, bg, text) => mixrgb(bg, text)
|
||||
color: (mod, bg, text) => mixrgb(bg, text),
|
||||
},
|
||||
|
||||
selectedMenu: {
|
||||
depends: ['bg'],
|
||||
color: (mod, bg) => brightness(5 * mod, bg).rgb
|
||||
color: (mod, bg) => brightness(5 * mod, bg).rgb,
|
||||
},
|
||||
selectedMenuLightText: {
|
||||
depends: ['highlightLightText'],
|
||||
layer: 'selectedMenu',
|
||||
variant: 'selectedMenu',
|
||||
textColor: true
|
||||
textColor: true,
|
||||
},
|
||||
selectedMenuFaintText: {
|
||||
depends: ['highlightFaintText'],
|
||||
layer: 'selectedMenu',
|
||||
variant: 'selectedMenu',
|
||||
textColor: true
|
||||
textColor: true,
|
||||
},
|
||||
selectedMenuFaintLink: {
|
||||
depends: ['highlightFaintLink'],
|
||||
layer: 'selectedMenu',
|
||||
variant: 'selectedMenu',
|
||||
textColor: 'preserve'
|
||||
textColor: 'preserve',
|
||||
},
|
||||
selectedMenuText: {
|
||||
depends: ['highlightText'],
|
||||
layer: 'selectedMenu',
|
||||
variant: 'selectedMenu',
|
||||
textColor: true
|
||||
textColor: true,
|
||||
},
|
||||
selectedMenuLink: {
|
||||
depends: ['highlightLink'],
|
||||
layer: 'selectedMenu',
|
||||
variant: 'selectedMenu',
|
||||
textColor: 'preserve'
|
||||
textColor: 'preserve',
|
||||
},
|
||||
selectedMenuIcon: {
|
||||
depends: ['selectedMenu', 'selectedMenuText'],
|
||||
color: (mod, bg, text) => mixrgb(bg, text)
|
||||
color: (mod, bg, text) => mixrgb(bg, text),
|
||||
},
|
||||
|
||||
selectedMenuPopover: {
|
||||
depends: ['popover'],
|
||||
color: (mod, bg) => brightness(5 * mod, bg).rgb
|
||||
color: (mod, bg) => brightness(5 * mod, bg).rgb,
|
||||
},
|
||||
selectedMenuPopoverLightText: {
|
||||
depends: ['selectedMenuLightText'],
|
||||
layer: 'selectedMenuPopover',
|
||||
variant: 'selectedMenuPopover',
|
||||
textColor: true
|
||||
textColor: true,
|
||||
},
|
||||
selectedMenuPopoverFaintText: {
|
||||
depends: ['selectedMenuFaintText'],
|
||||
layer: 'selectedMenuPopover',
|
||||
variant: 'selectedMenuPopover',
|
||||
textColor: true
|
||||
textColor: true,
|
||||
},
|
||||
selectedMenuPopoverFaintLink: {
|
||||
depends: ['selectedMenuFaintLink'],
|
||||
layer: 'selectedMenuPopover',
|
||||
variant: 'selectedMenuPopover',
|
||||
textColor: 'preserve'
|
||||
textColor: 'preserve',
|
||||
},
|
||||
selectedMenuPopoverText: {
|
||||
depends: ['selectedMenuText'],
|
||||
layer: 'selectedMenuPopover',
|
||||
variant: 'selectedMenuPopover',
|
||||
textColor: true
|
||||
textColor: true,
|
||||
},
|
||||
selectedMenuPopoverLink: {
|
||||
depends: ['selectedMenuLink'],
|
||||
layer: 'selectedMenuPopover',
|
||||
variant: 'selectedMenuPopover',
|
||||
textColor: 'preserve'
|
||||
textColor: 'preserve',
|
||||
},
|
||||
selectedMenuPopoverIcon: {
|
||||
depends: ['selectedMenuPopover', 'selectedMenuText'],
|
||||
color: (mod, bg, text) => mixrgb(bg, text)
|
||||
color: (mod, bg, text) => mixrgb(bg, text),
|
||||
},
|
||||
|
||||
lightText: {
|
||||
depends: ['text'],
|
||||
layer: 'bg',
|
||||
textColor: 'preserve',
|
||||
color: (mod, text) => brightness(20 * mod, text).rgb
|
||||
color: (mod, text) => brightness(20 * mod, text).rgb,
|
||||
},
|
||||
|
||||
postLink: {
|
||||
depends: ['link'],
|
||||
layer: 'bg',
|
||||
textColor: 'preserve'
|
||||
textColor: 'preserve',
|
||||
},
|
||||
|
||||
postGreentext: {
|
||||
depends: ['cGreen'],
|
||||
layer: 'bg',
|
||||
textColor: 'preserve'
|
||||
textColor: 'preserve',
|
||||
},
|
||||
|
||||
postCyantext: {
|
||||
depends: ['cBlue'],
|
||||
layer: 'bg',
|
||||
textColor: 'preserve'
|
||||
textColor: 'preserve',
|
||||
},
|
||||
|
||||
border: {
|
||||
depends: ['fg'],
|
||||
opacity: 'border',
|
||||
color: (mod, fg) => brightness(2 * mod, fg).rgb
|
||||
color: (mod, fg) => brightness(2 * mod, fg).rgb,
|
||||
},
|
||||
|
||||
poll: {
|
||||
depends: ['accent', 'bg'],
|
||||
copacity: 'poll',
|
||||
color: (mod, accent, bg) => alphaBlend(accent, 0.4, bg)
|
||||
color: (mod, accent, bg) => alphaBlend(accent, 0.4, bg),
|
||||
},
|
||||
pollText: {
|
||||
depends: ['text'],
|
||||
layer: 'poll',
|
||||
textColor: true
|
||||
textColor: true,
|
||||
},
|
||||
|
||||
icon: {
|
||||
depends: ['bg', 'text'],
|
||||
inheritsOpacity: false,
|
||||
color: (mod, bg, text) => mixrgb(bg, text)
|
||||
color: (mod, bg, text) => mixrgb(bg, text),
|
||||
},
|
||||
|
||||
// Foreground
|
||||
fgText: {
|
||||
depends: ['text'],
|
||||
layer: 'fg',
|
||||
textColor: true
|
||||
textColor: true,
|
||||
},
|
||||
fgLink: {
|
||||
depends: ['link'],
|
||||
layer: 'fg',
|
||||
textColor: 'preserve'
|
||||
textColor: 'preserve',
|
||||
},
|
||||
|
||||
// Panel header
|
||||
panel: {
|
||||
depends: ['fg'],
|
||||
opacity: 'panel'
|
||||
opacity: 'panel',
|
||||
},
|
||||
panelText: {
|
||||
depends: ['text'],
|
||||
layer: 'panel',
|
||||
textColor: true
|
||||
textColor: true,
|
||||
},
|
||||
panelFaint: {
|
||||
depends: ['fgText'],
|
||||
layer: 'panel',
|
||||
opacity: 'faint',
|
||||
textColor: true
|
||||
textColor: true,
|
||||
},
|
||||
panelLink: {
|
||||
depends: ['fgLink'],
|
||||
layer: 'panel',
|
||||
textColor: 'preserve'
|
||||
textColor: 'preserve',
|
||||
},
|
||||
|
||||
// Top bar
|
||||
|
|
@ -437,268 +437,268 @@ export const SLOT_INHERITANCE = {
|
|||
topBarText: {
|
||||
depends: ['fgText'],
|
||||
layer: 'topBar',
|
||||
textColor: true
|
||||
textColor: true,
|
||||
},
|
||||
topBarLink: {
|
||||
depends: ['fgLink'],
|
||||
layer: 'topBar',
|
||||
textColor: 'preserve'
|
||||
textColor: 'preserve',
|
||||
},
|
||||
|
||||
// Tabs
|
||||
tab: {
|
||||
depends: ['btn']
|
||||
depends: ['btn'],
|
||||
},
|
||||
tabText: {
|
||||
depends: ['btnText'],
|
||||
layer: 'btn',
|
||||
textColor: true
|
||||
textColor: true,
|
||||
},
|
||||
tabActiveText: {
|
||||
depends: ['text'],
|
||||
layer: 'bg',
|
||||
textColor: true
|
||||
textColor: true,
|
||||
},
|
||||
|
||||
// Buttons
|
||||
btn: {
|
||||
depends: ['fg'],
|
||||
variant: 'btn',
|
||||
opacity: 'btn'
|
||||
opacity: 'btn',
|
||||
},
|
||||
btnText: {
|
||||
depends: ['fgText'],
|
||||
layer: 'btn',
|
||||
textColor: true
|
||||
textColor: true,
|
||||
},
|
||||
btnPanelText: {
|
||||
depends: ['btnText'],
|
||||
layer: 'btnPanel',
|
||||
variant: 'btn',
|
||||
textColor: true
|
||||
textColor: true,
|
||||
},
|
||||
btnTopBarText: {
|
||||
depends: ['btnText'],
|
||||
layer: 'btnTopBar',
|
||||
variant: 'btn',
|
||||
textColor: true
|
||||
textColor: true,
|
||||
},
|
||||
|
||||
// Buttons: pressed
|
||||
btnPressed: {
|
||||
depends: ['btn'],
|
||||
layer: 'btn'
|
||||
layer: 'btn',
|
||||
},
|
||||
btnPressedText: {
|
||||
depends: ['btnText'],
|
||||
layer: 'btn',
|
||||
variant: 'btnPressed',
|
||||
textColor: true
|
||||
textColor: true,
|
||||
},
|
||||
btnPressedPanel: {
|
||||
depends: ['btnPressed'],
|
||||
layer: 'btn'
|
||||
layer: 'btn',
|
||||
},
|
||||
btnPressedPanelText: {
|
||||
depends: ['btnPanelText'],
|
||||
layer: 'btnPanel',
|
||||
variant: 'btnPressed',
|
||||
textColor: true
|
||||
textColor: true,
|
||||
},
|
||||
btnPressedTopBar: {
|
||||
depends: ['btnPressed'],
|
||||
layer: 'btn'
|
||||
layer: 'btn',
|
||||
},
|
||||
btnPressedTopBarText: {
|
||||
depends: ['btnTopBarText'],
|
||||
layer: 'btnTopBar',
|
||||
variant: 'btnPressed',
|
||||
textColor: true
|
||||
textColor: true,
|
||||
},
|
||||
|
||||
// Buttons: toggled
|
||||
btnToggled: {
|
||||
depends: ['btn'],
|
||||
layer: 'btn',
|
||||
color: (mod, btn) => brightness(mod * 20, btn).rgb
|
||||
color: (mod, btn) => brightness(mod * 20, btn).rgb,
|
||||
},
|
||||
btnToggledText: {
|
||||
depends: ['btnText'],
|
||||
layer: 'btn',
|
||||
variant: 'btnToggled',
|
||||
textColor: true
|
||||
textColor: true,
|
||||
},
|
||||
btnToggledPanelText: {
|
||||
depends: ['btnPanelText'],
|
||||
layer: 'btnPanel',
|
||||
variant: 'btnToggled',
|
||||
textColor: true
|
||||
textColor: true,
|
||||
},
|
||||
btnToggledTopBarText: {
|
||||
depends: ['btnTopBarText'],
|
||||
layer: 'btnTopBar',
|
||||
variant: 'btnToggled',
|
||||
textColor: true
|
||||
textColor: true,
|
||||
},
|
||||
|
||||
// Buttons: disabled
|
||||
btnDisabled: {
|
||||
depends: ['btn', 'bg'],
|
||||
color: (mod, btn, bg) => alphaBlend(btn, 0.25, bg)
|
||||
color: (mod, btn, bg) => alphaBlend(btn, 0.25, bg),
|
||||
},
|
||||
btnDisabledText: {
|
||||
depends: ['btnText', 'btnDisabled'],
|
||||
layer: 'btn',
|
||||
variant: 'btnDisabled',
|
||||
color: (mod, text, btn) => alphaBlend(text, 0.25, btn)
|
||||
color: (mod, text, btn) => alphaBlend(text, 0.25, btn),
|
||||
},
|
||||
btnDisabledPanelText: {
|
||||
depends: ['btnPanelText', 'btnDisabled'],
|
||||
layer: 'btnPanel',
|
||||
variant: 'btnDisabled',
|
||||
color: (mod, text, btn) => alphaBlend(text, 0.25, btn)
|
||||
color: (mod, text, btn) => alphaBlend(text, 0.25, btn),
|
||||
},
|
||||
btnDisabledTopBarText: {
|
||||
depends: ['btnTopBarText', 'btnDisabled'],
|
||||
layer: 'btnTopBar',
|
||||
variant: 'btnDisabled',
|
||||
color: (mod, text, btn) => alphaBlend(text, 0.25, btn)
|
||||
color: (mod, text, btn) => alphaBlend(text, 0.25, btn),
|
||||
},
|
||||
|
||||
// Input fields
|
||||
input: {
|
||||
depends: ['fg'],
|
||||
opacity: 'input'
|
||||
opacity: 'input',
|
||||
},
|
||||
inputText: {
|
||||
depends: ['text'],
|
||||
layer: 'input',
|
||||
textColor: true
|
||||
textColor: true,
|
||||
},
|
||||
inputPanelText: {
|
||||
depends: ['panelText'],
|
||||
layer: 'inputPanel',
|
||||
variant: 'input',
|
||||
textColor: true
|
||||
textColor: true,
|
||||
},
|
||||
inputTopbarText: {
|
||||
depends: ['topBarText'],
|
||||
layer: 'inputTopBar',
|
||||
variant: 'input',
|
||||
textColor: true
|
||||
textColor: true,
|
||||
},
|
||||
|
||||
alertError: {
|
||||
depends: ['cRed'],
|
||||
opacity: 'alert'
|
||||
opacity: 'alert',
|
||||
},
|
||||
alertErrorText: {
|
||||
depends: ['text'],
|
||||
layer: 'alert',
|
||||
variant: 'alertError',
|
||||
textColor: true
|
||||
textColor: true,
|
||||
},
|
||||
alertErrorPanelText: {
|
||||
depends: ['panelText'],
|
||||
layer: 'alertPanel',
|
||||
variant: 'alertError',
|
||||
textColor: true
|
||||
textColor: true,
|
||||
},
|
||||
|
||||
alertWarning: {
|
||||
depends: ['cOrange'],
|
||||
opacity: 'alert'
|
||||
opacity: 'alert',
|
||||
},
|
||||
alertWarningText: {
|
||||
depends: ['text'],
|
||||
layer: 'alert',
|
||||
variant: 'alertWarning',
|
||||
textColor: true
|
||||
textColor: true,
|
||||
},
|
||||
alertWarningPanelText: {
|
||||
depends: ['panelText'],
|
||||
layer: 'alertPanel',
|
||||
variant: 'alertWarning',
|
||||
textColor: true
|
||||
textColor: true,
|
||||
},
|
||||
|
||||
alertSuccess: {
|
||||
depends: ['cGreen'],
|
||||
opacity: 'alert'
|
||||
opacity: 'alert',
|
||||
},
|
||||
alertSuccessText: {
|
||||
depends: ['text'],
|
||||
layer: 'alert',
|
||||
variant: 'alertSuccess',
|
||||
textColor: true
|
||||
textColor: true,
|
||||
},
|
||||
alertSuccessPanelText: {
|
||||
depends: ['panelText'],
|
||||
layer: 'alertPanel',
|
||||
variant: 'alertSuccess',
|
||||
textColor: true
|
||||
textColor: true,
|
||||
},
|
||||
|
||||
alertNeutral: {
|
||||
depends: ['text'],
|
||||
opacity: 'alert'
|
||||
opacity: 'alert',
|
||||
},
|
||||
alertNeutralText: {
|
||||
depends: ['text'],
|
||||
layer: 'alert',
|
||||
variant: 'alertNeutral',
|
||||
color: (mod, text) => invertLightness(text).rgb,
|
||||
textColor: true
|
||||
textColor: true,
|
||||
},
|
||||
alertNeutralPanelText: {
|
||||
depends: ['panelText'],
|
||||
layer: 'alertPanel',
|
||||
variant: 'alertNeutral',
|
||||
textColor: true
|
||||
textColor: true,
|
||||
},
|
||||
|
||||
alertPopupError: {
|
||||
depends: ['alertError'],
|
||||
opacity: 'alertPopup'
|
||||
opacity: 'alertPopup',
|
||||
},
|
||||
alertPopupErrorText: {
|
||||
depends: ['alertErrorText'],
|
||||
layer: 'popover',
|
||||
variant: 'alertPopupError',
|
||||
textColor: true
|
||||
textColor: true,
|
||||
},
|
||||
|
||||
alertPopupWarning: {
|
||||
depends: ['alertWarning'],
|
||||
opacity: 'alertPopup'
|
||||
opacity: 'alertPopup',
|
||||
},
|
||||
alertPopupWarningText: {
|
||||
depends: ['alertWarningText'],
|
||||
layer: 'popover',
|
||||
variant: 'alertPopupWarning',
|
||||
textColor: true
|
||||
textColor: true,
|
||||
},
|
||||
|
||||
alertPopupSuccess: {
|
||||
depends: ['alertSuccess'],
|
||||
opacity: 'alertPopup'
|
||||
opacity: 'alertPopup',
|
||||
},
|
||||
alertPopupSuccessText: {
|
||||
depends: ['alertSuccessText'],
|
||||
layer: 'popover',
|
||||
variant: 'alertPopupSuccess',
|
||||
textColor: true
|
||||
textColor: true,
|
||||
},
|
||||
|
||||
alertPopupNeutral: {
|
||||
depends: ['alertNeutral'],
|
||||
opacity: 'alertPopup'
|
||||
opacity: 'alertPopup',
|
||||
},
|
||||
alertPopupNeutralText: {
|
||||
depends: ['alertNeutralText'],
|
||||
layer: 'popover',
|
||||
variant: 'alertPopupNeutral',
|
||||
textColor: true
|
||||
textColor: true,
|
||||
},
|
||||
|
||||
badgeNotification: '--cRed',
|
||||
|
|
@ -706,7 +706,7 @@ export const SLOT_INHERITANCE = {
|
|||
depends: ['text', 'badgeNotification'],
|
||||
layer: 'badge',
|
||||
variant: 'badgeNotification',
|
||||
textColor: 'bw'
|
||||
textColor: 'bw',
|
||||
},
|
||||
|
||||
badgeNeutral: '--cGreen',
|
||||
|
|
@ -714,59 +714,59 @@ export const SLOT_INHERITANCE = {
|
|||
depends: ['text', 'badgeNeutral'],
|
||||
layer: 'badge',
|
||||
variant: 'badgeNeutral',
|
||||
textColor: 'bw'
|
||||
textColor: 'bw',
|
||||
},
|
||||
|
||||
chatBg: {
|
||||
depends: ['bg']
|
||||
depends: ['bg'],
|
||||
},
|
||||
|
||||
chatMessageIncomingBg: {
|
||||
depends: ['chatBg']
|
||||
depends: ['chatBg'],
|
||||
},
|
||||
|
||||
chatMessageIncomingText: {
|
||||
depends: ['text'],
|
||||
layer: 'chatMessage',
|
||||
variant: 'chatMessageIncomingBg',
|
||||
textColor: true
|
||||
textColor: true,
|
||||
},
|
||||
|
||||
chatMessageIncomingLink: {
|
||||
depends: ['link'],
|
||||
layer: 'chatMessage',
|
||||
variant: 'chatMessageIncomingBg',
|
||||
textColor: 'preserve'
|
||||
textColor: 'preserve',
|
||||
},
|
||||
|
||||
chatMessageIncomingBorder: {
|
||||
depends: ['border'],
|
||||
opacity: 'border',
|
||||
color: (mod, border) => brightness(2 * mod, border).rgb
|
||||
color: (mod, border) => brightness(2 * mod, border).rgb,
|
||||
},
|
||||
|
||||
chatMessageOutgoingBg: {
|
||||
depends: ['chatMessageIncomingBg'],
|
||||
color: (mod, chatMessage) => brightness(5 * mod, chatMessage).rgb
|
||||
color: (mod, chatMessage) => brightness(5 * mod, chatMessage).rgb,
|
||||
},
|
||||
|
||||
chatMessageOutgoingText: {
|
||||
depends: ['text'],
|
||||
layer: 'chatMessage',
|
||||
variant: 'chatMessageOutgoingBg',
|
||||
textColor: true
|
||||
textColor: true,
|
||||
},
|
||||
|
||||
chatMessageOutgoingLink: {
|
||||
depends: ['link'],
|
||||
layer: 'chatMessage',
|
||||
variant: 'chatMessageOutgoingBg',
|
||||
textColor: 'preserve'
|
||||
textColor: 'preserve',
|
||||
},
|
||||
|
||||
chatMessageOutgoingBorder: {
|
||||
depends: ['chatMessageOutgoingBg'],
|
||||
opacity: 'border',
|
||||
color: (mod, border) => brightness(2 * mod, border).rgb
|
||||
}
|
||||
color: (mod, border) => brightness(2 * mod, border).rgb,
|
||||
},
|
||||
}
|
||||
|
|
|
|||
|
|
@ -173,5 +173,5 @@ export default [
|
|||
'chatMessageOutgoingBg',
|
||||
'chatMessageOutgoingText',
|
||||
'chatMessageOutgoingLink',
|
||||
'chatMessageOutgoingBorder'
|
||||
'chatMessageOutgoingBorder',
|
||||
]
|
||||
|
|
|
|||
|
|
@ -14,15 +14,10 @@ export const basePaletteKeys = new Set([
|
|||
'cGreen',
|
||||
'cOrange',
|
||||
|
||||
'wallpaper'
|
||||
'wallpaper',
|
||||
])
|
||||
|
||||
export const fontsKeys = new Set([
|
||||
'interface',
|
||||
'input',
|
||||
'post',
|
||||
'postCode'
|
||||
])
|
||||
export const fontsKeys = new Set(['interface', 'input', 'post', 'postCode'])
|
||||
|
||||
export const opacityKeys = new Set([
|
||||
'alert',
|
||||
|
|
@ -35,7 +30,7 @@ export const opacityKeys = new Set([
|
|||
'panel',
|
||||
'popover',
|
||||
'profileTint',
|
||||
'underlay'
|
||||
'underlay',
|
||||
])
|
||||
|
||||
export const shadowsKeys = new Set([
|
||||
|
|
@ -48,7 +43,7 @@ export const shadowsKeys = new Set([
|
|||
'button',
|
||||
'buttonHover',
|
||||
'buttonPressed',
|
||||
'input'
|
||||
'input',
|
||||
])
|
||||
|
||||
export const radiiKeys = new Set([
|
||||
|
|
@ -60,14 +55,11 @@ export const radiiKeys = new Set([
|
|||
'avatarAlt',
|
||||
'tooltip',
|
||||
'attachment',
|
||||
'chatMessage'
|
||||
'chatMessage',
|
||||
])
|
||||
|
||||
// Keys that are not available in editor and never meant to be edited
|
||||
export const hiddenKeys = new Set([
|
||||
'profileBg',
|
||||
'profileTint'
|
||||
])
|
||||
export const hiddenKeys = new Set(['profileBg', 'profileTint'])
|
||||
|
||||
export const extendedBasePrefixes = [
|
||||
'border',
|
||||
|
|
@ -93,33 +85,30 @@ export const extendedBasePrefixes = [
|
|||
'poll',
|
||||
|
||||
'chatBg',
|
||||
'chatMessage'
|
||||
'chatMessage',
|
||||
]
|
||||
export const nonComponentPrefixes = new Set([
|
||||
'border',
|
||||
'icon',
|
||||
'highlight',
|
||||
'lightText',
|
||||
'chatBg'
|
||||
'chatBg',
|
||||
])
|
||||
|
||||
export const extendedBaseKeys = Object.fromEntries(
|
||||
extendedBasePrefixes.map(prefix => [
|
||||
extendedBasePrefixes.map((prefix) => [
|
||||
prefix,
|
||||
allKeys.filter(k => {
|
||||
allKeys.filter((k) => {
|
||||
if (prefix === 'alert') {
|
||||
return k.startsWith(prefix) && !k.startsWith('alertPopup')
|
||||
}
|
||||
return k.startsWith(prefix)
|
||||
})
|
||||
])
|
||||
}),
|
||||
]),
|
||||
)
|
||||
|
||||
// Keysets that are only really used intermideately, i.e. to generate other colors
|
||||
export const temporary = new Set([
|
||||
'',
|
||||
'highlight'
|
||||
])
|
||||
export const temporary = new Set(['', 'highlight'])
|
||||
|
||||
export const temporaryColors = {}
|
||||
|
||||
|
|
@ -128,16 +117,18 @@ export const convertTheme2To3 = (data) => {
|
|||
data.colors.link = data.colors.link || data.colors.accent
|
||||
const generateRoot = () => {
|
||||
const directives = {}
|
||||
basePaletteKeys.forEach(key => { directives['--' + key] = 'color | ' + convert(data.colors[key]).hex })
|
||||
basePaletteKeys.forEach((key) => {
|
||||
directives['--' + key] = 'color | ' + convert(data.colors[key]).hex
|
||||
})
|
||||
return {
|
||||
component: 'Root',
|
||||
directives
|
||||
directives,
|
||||
}
|
||||
}
|
||||
|
||||
const convertOpacity = () => {
|
||||
const newRules = []
|
||||
Object.keys(data.opacity || {}).forEach(key => {
|
||||
Object.keys(data.opacity || {}).forEach((key) => {
|
||||
if (!opacityKeys.has(key) || data.opacity[key] === undefined) return null
|
||||
const originalOpacity = data.opacity[key]
|
||||
const rule = { source: '2to3' }
|
||||
|
|
@ -201,7 +192,12 @@ export const convertTheme2To3 = (data) => {
|
|||
if (rule.component === 'Button') {
|
||||
newRules.push({ ...rule, component: 'ScrollbarElement' })
|
||||
newRules.push({ ...rule, component: 'Tab' })
|
||||
newRules.push({ ...rule, component: 'Tab', state: ['active'], directives: { opacity: 0 } })
|
||||
newRules.push({
|
||||
...rule,
|
||||
component: 'Tab',
|
||||
state: ['active'],
|
||||
directives: { opacity: 0 },
|
||||
})
|
||||
}
|
||||
if (rule.component === 'Panel') {
|
||||
newRules.push({ ...rule, component: 'Post' })
|
||||
|
|
@ -212,7 +208,7 @@ export const convertTheme2To3 = (data) => {
|
|||
|
||||
const convertRadii = () => {
|
||||
const newRules = []
|
||||
Object.keys(data.radii || {}).forEach(key => {
|
||||
Object.keys(data.radii || {}).forEach((key) => {
|
||||
if (!radiiKeys.has(key) || data.radii[key] === undefined) return null
|
||||
const originalRadius = data.radii[key]
|
||||
const rule = { source: '2to3' }
|
||||
|
|
@ -249,7 +245,7 @@ export const convertTheme2To3 = (data) => {
|
|||
break
|
||||
}
|
||||
rule.directives = {
|
||||
roundness: originalRadius
|
||||
roundness: originalRadius,
|
||||
}
|
||||
newRules.push(rule)
|
||||
if (rule.component === 'Button') {
|
||||
|
|
@ -262,7 +258,7 @@ export const convertTheme2To3 = (data) => {
|
|||
|
||||
const convertFonts = () => {
|
||||
const newRules = []
|
||||
Object.keys(data.fonts || {}).forEach(key => {
|
||||
Object.keys(data.fonts || {}).forEach((key) => {
|
||||
if (!fontsKeys.has(key)) return
|
||||
if (!data.fonts[key]) return
|
||||
const originalFont = data.fonts[key].family
|
||||
|
|
@ -297,7 +293,7 @@ export const convertTheme2To3 = (data) => {
|
|||
}
|
||||
const convertShadows = () => {
|
||||
const newRules = []
|
||||
Object.keys(data.shadows || {}).forEach(key => {
|
||||
Object.keys(data.shadows || {}).forEach((key) => {
|
||||
if (!shadowsKeys.has(key)) return
|
||||
const originalShadow = data.shadows[key]
|
||||
const rule = { source: '2to3' }
|
||||
|
|
@ -338,11 +334,15 @@ export const convertTheme2To3 = (data) => {
|
|||
break
|
||||
}
|
||||
rule.directives = {
|
||||
shadow: originalShadow
|
||||
shadow: originalShadow,
|
||||
}
|
||||
newRules.push(rule)
|
||||
if (key === 'topBar') {
|
||||
newRules.push({ ...rule, component: 'PanelHeader', parent: { component: 'MobileDrawer' } })
|
||||
newRules.push({
|
||||
...rule,
|
||||
component: 'PanelHeader',
|
||||
parent: { component: 'MobileDrawer' },
|
||||
})
|
||||
}
|
||||
if (key === 'avatarStatus') {
|
||||
newRules.push({ ...rule, parent: { component: 'Notification' } })
|
||||
|
|
@ -363,169 +363,211 @@ export const convertTheme2To3 = (data) => {
|
|||
return newRules
|
||||
}
|
||||
|
||||
const extendedRules = Object.entries(extendedBaseKeys).map(([prefix, keys]) => {
|
||||
if (nonComponentPrefixes.has(prefix)) return null
|
||||
const rule = { source: '2to3' }
|
||||
if (prefix === 'alertPopup') {
|
||||
rule.component = 'Alert'
|
||||
rule.parent = { component: 'Popover' }
|
||||
} else if (prefix === 'selectedPost') {
|
||||
rule.component = 'Post'
|
||||
rule.state = ['selected']
|
||||
} else if (prefix === 'selectedMenu') {
|
||||
rule.component = 'MenuItem'
|
||||
rule.state = ['hover']
|
||||
} else if (prefix === 'chatMessageIncoming') {
|
||||
rule.component = 'ChatMessage'
|
||||
} else if (prefix === 'chatMessageOutgoing') {
|
||||
rule.component = 'ChatMessage'
|
||||
rule.variant = 'outgoing'
|
||||
} else if (prefix === 'panel') {
|
||||
rule.component = 'PanelHeader'
|
||||
} else if (prefix === 'topBar') {
|
||||
rule.component = 'TopBar'
|
||||
} else if (prefix === 'chatMessage') {
|
||||
rule.component = 'ChatMessage'
|
||||
} else if (prefix === 'poll') {
|
||||
rule.component = 'PollGraph'
|
||||
} else if (prefix === 'btn') {
|
||||
rule.component = 'Button'
|
||||
} else {
|
||||
rule.component = prefix[0].toUpperCase() + prefix.slice(1).toLowerCase()
|
||||
}
|
||||
return keys.map((key) => {
|
||||
if (!data.colors[key]) return null
|
||||
const leftoverKey = key.replace(prefix, '')
|
||||
const parts = (leftoverKey || 'Bg').match(/[A-Z][a-z]*/g)
|
||||
const last = parts.slice(-1)[0]
|
||||
let newRule = { source: '2to3', directives: {} }
|
||||
let variantArray = []
|
||||
|
||||
switch (last) {
|
||||
case 'Text':
|
||||
case 'Faint': // typo
|
||||
case 'Link':
|
||||
case 'Icon':
|
||||
case 'Greentext':
|
||||
case 'Cyantext':
|
||||
case 'Border':
|
||||
newRule.parent = rule
|
||||
newRule.directives.textColor = data.colors[key]
|
||||
variantArray = parts.slice(0, -1)
|
||||
break
|
||||
default:
|
||||
newRule = { ...rule, directives: {} }
|
||||
newRule.directives.background = data.colors[key]
|
||||
variantArray = parts
|
||||
break
|
||||
const extendedRules = Object.entries(extendedBaseKeys).map(
|
||||
([prefix, keys]) => {
|
||||
if (nonComponentPrefixes.has(prefix)) return null
|
||||
const rule = { source: '2to3' }
|
||||
if (prefix === 'alertPopup') {
|
||||
rule.component = 'Alert'
|
||||
rule.parent = { component: 'Popover' }
|
||||
} else if (prefix === 'selectedPost') {
|
||||
rule.component = 'Post'
|
||||
rule.state = ['selected']
|
||||
} else if (prefix === 'selectedMenu') {
|
||||
rule.component = 'MenuItem'
|
||||
rule.state = ['hover']
|
||||
} else if (prefix === 'chatMessageIncoming') {
|
||||
rule.component = 'ChatMessage'
|
||||
} else if (prefix === 'chatMessageOutgoing') {
|
||||
rule.component = 'ChatMessage'
|
||||
rule.variant = 'outgoing'
|
||||
} else if (prefix === 'panel') {
|
||||
rule.component = 'PanelHeader'
|
||||
} else if (prefix === 'topBar') {
|
||||
rule.component = 'TopBar'
|
||||
} else if (prefix === 'chatMessage') {
|
||||
rule.component = 'ChatMessage'
|
||||
} else if (prefix === 'poll') {
|
||||
rule.component = 'PollGraph'
|
||||
} else if (prefix === 'btn') {
|
||||
rule.component = 'Button'
|
||||
} else {
|
||||
rule.component = prefix[0].toUpperCase() + prefix.slice(1).toLowerCase()
|
||||
}
|
||||
return keys.map((key) => {
|
||||
if (!data.colors[key]) return null
|
||||
const leftoverKey = key.replace(prefix, '')
|
||||
const parts = (leftoverKey || 'Bg').match(/[A-Z][a-z]*/g)
|
||||
const last = parts.slice(-1)[0]
|
||||
let newRule = { source: '2to3', directives: {} }
|
||||
let variantArray = []
|
||||
|
||||
if (last === 'Text' || last === 'Link') {
|
||||
const secondLast = parts.slice(-2)[0]
|
||||
if (secondLast === 'Light') {
|
||||
return null // unsupported
|
||||
} else if (secondLast === 'Faint') {
|
||||
newRule.state = ['faint']
|
||||
variantArray = parts.slice(0, -2)
|
||||
switch (last) {
|
||||
case 'Text':
|
||||
case 'Faint': // typo
|
||||
case 'Link':
|
||||
case 'Icon':
|
||||
case 'Greentext':
|
||||
case 'Cyantext':
|
||||
case 'Border':
|
||||
newRule.parent = rule
|
||||
newRule.directives.textColor = data.colors[key]
|
||||
variantArray = parts.slice(0, -1)
|
||||
break
|
||||
default:
|
||||
newRule = { ...rule, directives: {} }
|
||||
newRule.directives.background = data.colors[key]
|
||||
variantArray = parts
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
switch (last) {
|
||||
case 'Text':
|
||||
case 'Link':
|
||||
case 'Icon':
|
||||
case 'Border':
|
||||
newRule.component = last
|
||||
break
|
||||
case 'Greentext':
|
||||
case 'Cyantext':
|
||||
newRule.component = 'FunText'
|
||||
newRule.variant = last.toLowerCase()
|
||||
break
|
||||
case 'Faint':
|
||||
newRule.component = 'Text'
|
||||
newRule.state = ['faint']
|
||||
break
|
||||
}
|
||||
|
||||
variantArray = variantArray.filter(x => x !== 'Bg')
|
||||
|
||||
if (last === 'Link' && prefix === 'selectedPost') {
|
||||
// selectedPost has typo - duplicate 'Post'
|
||||
variantArray = variantArray.filter(x => x !== 'Post')
|
||||
}
|
||||
|
||||
if (prefix === 'popover' && variantArray[0] === 'Post') {
|
||||
newRule.component = 'Post'
|
||||
newRule.parent = { source: '2to3hack', component: 'Popover' }
|
||||
variantArray = variantArray.filter(x => x !== 'Post')
|
||||
}
|
||||
|
||||
if (prefix === 'selectedMenu' && variantArray[0] === 'Popover') {
|
||||
newRule.parent = { source: '2to3hack', component: 'Popover' }
|
||||
variantArray = variantArray.filter(x => x !== 'Popover')
|
||||
}
|
||||
|
||||
switch (prefix) {
|
||||
case 'btn':
|
||||
case 'input':
|
||||
case 'alert': {
|
||||
const hasPanel = variantArray.find(x => x === 'Panel')
|
||||
if (hasPanel) {
|
||||
newRule.parent = { source: '2to3hack', component: 'PanelHeader', parent: newRule.parent }
|
||||
variantArray = variantArray.filter(x => x !== 'Panel')
|
||||
if (last === 'Text' || last === 'Link') {
|
||||
const secondLast = parts.slice(-2)[0]
|
||||
if (secondLast === 'Light') {
|
||||
return null // unsupported
|
||||
} else if (secondLast === 'Faint') {
|
||||
newRule.state = ['faint']
|
||||
variantArray = parts.slice(0, -2)
|
||||
}
|
||||
const hasTop = variantArray.find(x => x === 'Top') // TopBar
|
||||
if (hasTop) {
|
||||
newRule.parent = { source: '2to3hack', component: 'TopBar', parent: newRule.parent }
|
||||
variantArray = variantArray.filter(x => x !== 'Top' && x !== 'Bar')
|
||||
}
|
||||
|
||||
switch (last) {
|
||||
case 'Text':
|
||||
case 'Link':
|
||||
case 'Icon':
|
||||
case 'Border':
|
||||
newRule.component = last
|
||||
break
|
||||
case 'Greentext':
|
||||
case 'Cyantext':
|
||||
newRule.component = 'FunText'
|
||||
newRule.variant = last.toLowerCase()
|
||||
break
|
||||
case 'Faint':
|
||||
newRule.component = 'Text'
|
||||
newRule.state = ['faint']
|
||||
break
|
||||
}
|
||||
|
||||
variantArray = variantArray.filter((x) => x !== 'Bg')
|
||||
|
||||
if (last === 'Link' && prefix === 'selectedPost') {
|
||||
// selectedPost has typo - duplicate 'Post'
|
||||
variantArray = variantArray.filter((x) => x !== 'Post')
|
||||
}
|
||||
|
||||
if (prefix === 'popover' && variantArray[0] === 'Post') {
|
||||
newRule.component = 'Post'
|
||||
newRule.parent = { source: '2to3hack', component: 'Popover' }
|
||||
variantArray = variantArray.filter((x) => x !== 'Post')
|
||||
}
|
||||
|
||||
if (prefix === 'selectedMenu' && variantArray[0] === 'Popover') {
|
||||
newRule.parent = { source: '2to3hack', component: 'Popover' }
|
||||
variantArray = variantArray.filter((x) => x !== 'Popover')
|
||||
}
|
||||
|
||||
switch (prefix) {
|
||||
case 'btn':
|
||||
case 'input':
|
||||
case 'alert': {
|
||||
const hasPanel = variantArray.find((x) => x === 'Panel')
|
||||
if (hasPanel) {
|
||||
newRule.parent = {
|
||||
source: '2to3hack',
|
||||
component: 'PanelHeader',
|
||||
parent: newRule.parent,
|
||||
}
|
||||
variantArray = variantArray.filter((x) => x !== 'Panel')
|
||||
}
|
||||
const hasTop = variantArray.find((x) => x === 'Top') // TopBar
|
||||
if (hasTop) {
|
||||
newRule.parent = {
|
||||
source: '2to3hack',
|
||||
component: 'TopBar',
|
||||
parent: newRule.parent,
|
||||
}
|
||||
variantArray = variantArray.filter(
|
||||
(x) => x !== 'Top' && x !== 'Bar',
|
||||
)
|
||||
}
|
||||
break
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if (variantArray.length > 0) {
|
||||
if (prefix === 'btn') {
|
||||
newRule.state = variantArray.map(x => x.toLowerCase())
|
||||
} else {
|
||||
newRule.variant = variantArray[0].toLowerCase()
|
||||
if (variantArray.length > 0) {
|
||||
if (prefix === 'btn') {
|
||||
newRule.state = variantArray.map((x) => x.toLowerCase())
|
||||
} else {
|
||||
newRule.variant = variantArray[0].toLowerCase()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (newRule.component === 'Panel') {
|
||||
return [newRule, { ...newRule, component: 'MobileDrawer' }]
|
||||
} else if (newRule.component === 'Button') {
|
||||
const rules = [
|
||||
newRule,
|
||||
{ ...newRule, component: 'Tab' },
|
||||
{ ...newRule, component: 'ScrollbarElement' }
|
||||
]
|
||||
if (newRule.state?.indexOf('toggled') >= 0) {
|
||||
rules.push({ ...newRule, state: [...newRule.state, 'focused'] })
|
||||
rules.push({ ...newRule, state: [...newRule.state, 'hover'] })
|
||||
rules.push({ ...newRule, state: [...newRule.state, 'hover', 'focused'] })
|
||||
}
|
||||
if (newRule.state?.indexOf('hover') >= 0) {
|
||||
rules.push({ ...newRule, state: [...newRule.state, 'focused'] })
|
||||
}
|
||||
return rules
|
||||
} else if (newRule.component === 'Badge') {
|
||||
if (newRule.variant === 'notification') {
|
||||
return [newRule, { component: 'Root', directives: { '--badgeNotification': 'color | ' + newRule.directives.background } }]
|
||||
} else if (newRule.variant === 'neutral') {
|
||||
return [{ ...newRule, variant: 'normal' }]
|
||||
if (newRule.component === 'Panel') {
|
||||
return [newRule, { ...newRule, component: 'MobileDrawer' }]
|
||||
} else if (newRule.component === 'Button') {
|
||||
const rules = [
|
||||
newRule,
|
||||
{ ...newRule, component: 'Tab' },
|
||||
{ ...newRule, component: 'ScrollbarElement' },
|
||||
]
|
||||
if (newRule.state?.indexOf('toggled') >= 0) {
|
||||
rules.push({ ...newRule, state: [...newRule.state, 'focused'] })
|
||||
rules.push({ ...newRule, state: [...newRule.state, 'hover'] })
|
||||
rules.push({
|
||||
...newRule,
|
||||
state: [...newRule.state, 'hover', 'focused'],
|
||||
})
|
||||
}
|
||||
if (newRule.state?.indexOf('hover') >= 0) {
|
||||
rules.push({ ...newRule, state: [...newRule.state, 'focused'] })
|
||||
}
|
||||
return rules
|
||||
} else if (newRule.component === 'Badge') {
|
||||
if (newRule.variant === 'notification') {
|
||||
return [
|
||||
newRule,
|
||||
{
|
||||
component: 'Root',
|
||||
directives: {
|
||||
'--badgeNotification':
|
||||
'color | ' + newRule.directives.background,
|
||||
},
|
||||
},
|
||||
]
|
||||
} else if (newRule.variant === 'neutral') {
|
||||
return [{ ...newRule, variant: 'normal' }]
|
||||
} else {
|
||||
return [newRule]
|
||||
}
|
||||
} else if (newRule.component === 'TopBar') {
|
||||
return [
|
||||
newRule,
|
||||
{
|
||||
...newRule,
|
||||
parent: { component: 'MobileDrawer' },
|
||||
component: 'PanelHeader',
|
||||
},
|
||||
]
|
||||
} else {
|
||||
return [newRule]
|
||||
}
|
||||
} else if (newRule.component === 'TopBar') {
|
||||
return [newRule, { ...newRule, parent: { component: 'MobileDrawer' }, component: 'PanelHeader' }]
|
||||
} else {
|
||||
return [newRule]
|
||||
}
|
||||
})
|
||||
})
|
||||
})
|
||||
},
|
||||
)
|
||||
|
||||
const flatExtRules = extendedRules.filter(x => x).reduce((acc, x) => [...acc, ...x], []).filter(x => x).reduce((acc, x) => [...acc, ...x], [])
|
||||
const flatExtRules = extendedRules
|
||||
.filter((x) => x)
|
||||
.reduce((acc, x) => [...acc, ...x], [])
|
||||
.filter((x) => x)
|
||||
.reduce((acc, x) => [...acc, ...x], [])
|
||||
|
||||
return [generateRoot(), ...convertShadows(), ...convertRadii(), ...convertOpacity(), ...convertFonts(), ...flatExtRules]
|
||||
return [
|
||||
generateRoot(),
|
||||
...convertShadows(),
|
||||
...convertRadii(),
|
||||
...convertOpacity(),
|
||||
...convertFonts(),
|
||||
...flatExtRules,
|
||||
]
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,13 +1,28 @@
|
|||
import { convert, brightness } from 'chromatism'
|
||||
import { alphaBlend, arithmeticBlend, getTextColor, relativeLuminance } from '../color_convert/color_convert.js'
|
||||
import {
|
||||
alphaBlend,
|
||||
arithmeticBlend,
|
||||
getTextColor,
|
||||
relativeLuminance,
|
||||
} from '../color_convert/color_convert.js'
|
||||
|
||||
export const process = (text, functions, { findColor, findShadow }, { dynamicVars, staticVars }) => {
|
||||
const { funcName, argsString } = /\$(?<funcName>\w+)\((?<argsString>[#a-zA-Z0-9-+,.'"\s]*)\)/.exec(text).groups
|
||||
const args = argsString.split(/ /g).map(a => a.trim())
|
||||
export const process = (
|
||||
text,
|
||||
functions,
|
||||
{ findColor, findShadow },
|
||||
{ dynamicVars, staticVars },
|
||||
) => {
|
||||
const { funcName, argsString } =
|
||||
/\$(?<funcName>\w+)\((?<argsString>[#a-zA-Z0-9-+,.'"\s]*)\)/.exec(
|
||||
text,
|
||||
).groups
|
||||
const args = argsString.split(/ /g).map((a) => a.trim())
|
||||
|
||||
const func = functions[funcName]
|
||||
if (args.length < func.argsNeeded) {
|
||||
throw new Error(`$${funcName} requires at least ${func.argsNeeded} arguments, but ${args.length} were provided`)
|
||||
throw new Error(
|
||||
`$${funcName} requires at least ${func.argsNeeded} arguments, but ${args.length} were provided`,
|
||||
)
|
||||
}
|
||||
return func.exec(args, { findColor, findShadow }, { dynamicVars, staticVars })
|
||||
}
|
||||
|
|
@ -15,53 +30,57 @@ export const process = (text, functions, { findColor, findShadow }, { dynamicVar
|
|||
export const colorFunctions = {
|
||||
alpha: {
|
||||
argsNeeded: 2,
|
||||
documentation: 'Changes alpha value of the color only to be used for CSS variables',
|
||||
args: [
|
||||
'color: source color used',
|
||||
'amount: alpha value'
|
||||
],
|
||||
documentation:
|
||||
'Changes alpha value of the color only to be used for CSS variables',
|
||||
args: ['color: source color used', 'amount: alpha value'],
|
||||
exec: (args, { findColor }, { dynamicVars, staticVars }) => {
|
||||
const [color, amountArg] = args
|
||||
|
||||
const colorArg = convert(findColor(color, { dynamicVars, staticVars })).rgb
|
||||
const colorArg = convert(
|
||||
findColor(color, { dynamicVars, staticVars }),
|
||||
).rgb
|
||||
const amount = Number(amountArg)
|
||||
return { ...colorArg, a: amount }
|
||||
}
|
||||
},
|
||||
},
|
||||
brightness: {
|
||||
argsNeeded: 2,
|
||||
document: 'Changes brightness/lightness of color in HSL colorspace',
|
||||
args: [
|
||||
'color: source color used',
|
||||
'amount: lightness value'
|
||||
],
|
||||
args: ['color: source color used', 'amount: lightness value'],
|
||||
exec: (args, { findColor }, { dynamicVars, staticVars }) => {
|
||||
const [color, amountArg] = args
|
||||
|
||||
const colorArg = convert(findColor(color, { dynamicVars, staticVars })).hsl
|
||||
const colorArg = convert(
|
||||
findColor(color, { dynamicVars, staticVars }),
|
||||
).hsl
|
||||
colorArg.l += Number(amountArg)
|
||||
return { ...convert(colorArg).rgb }
|
||||
}
|
||||
},
|
||||
},
|
||||
textColor: {
|
||||
argsNeeded: 2,
|
||||
documentation: 'Get text color with adequate contrast for given background and intended text color. Same function is used internally',
|
||||
documentation:
|
||||
'Get text color with adequate contrast for given background and intended text color. Same function is used internally',
|
||||
args: [
|
||||
'background: color of backdrop where text will be shown',
|
||||
'foreground: intended text color',
|
||||
`[preserve]: (optional) intended color preservation:
|
||||
'preserve' - try to preserve the color
|
||||
'no-preserve' - if can't get adequate color - fall back to black or white
|
||||
'no-auto' - don't do anything (useless as a color function)`
|
||||
'no-auto' - don't do anything (useless as a color function)`,
|
||||
],
|
||||
exec: (args, { findColor }, { dynamicVars, staticVars }) => {
|
||||
const [backgroundArg, foregroundArg, preserve = 'preserve'] = args
|
||||
|
||||
const background = convert(findColor(backgroundArg, { dynamicVars, staticVars })).rgb
|
||||
const foreground = convert(findColor(foregroundArg, { dynamicVars, staticVars })).rgb
|
||||
const background = convert(
|
||||
findColor(backgroundArg, { dynamicVars, staticVars }),
|
||||
).rgb
|
||||
const foreground = convert(
|
||||
findColor(foregroundArg, { dynamicVars, staticVars }),
|
||||
).rgb
|
||||
|
||||
return getTextColor(background, foreground, preserve === 'preserve')
|
||||
}
|
||||
},
|
||||
},
|
||||
blend: {
|
||||
argsNeeded: 3,
|
||||
|
|
@ -69,17 +88,21 @@ export const colorFunctions = {
|
|||
args: [
|
||||
'background: bottom layer color',
|
||||
'amount: opacity of top layer',
|
||||
'foreground: upper layer color'
|
||||
'foreground: upper layer color',
|
||||
],
|
||||
exec: (args, { findColor }, { dynamicVars, staticVars }) => {
|
||||
const [backgroundArg, amountArg, foregroundArg] = args
|
||||
|
||||
const background = convert(findColor(backgroundArg, { dynamicVars, staticVars })).rgb
|
||||
const foreground = convert(findColor(foregroundArg, { dynamicVars, staticVars })).rgb
|
||||
const background = convert(
|
||||
findColor(backgroundArg, { dynamicVars, staticVars }),
|
||||
).rgb
|
||||
const foreground = convert(
|
||||
findColor(foregroundArg, { dynamicVars, staticVars }),
|
||||
).rgb
|
||||
const amount = Number(amountArg)
|
||||
|
||||
return alphaBlend(background, amount, foreground)
|
||||
}
|
||||
},
|
||||
},
|
||||
shift: {
|
||||
argsNeeded: 2,
|
||||
|
|
@ -87,66 +110,72 @@ export const colorFunctions = {
|
|||
args: [
|
||||
'origin: base color',
|
||||
'value: shift value',
|
||||
'operator: math operator to use (+ or -)'
|
||||
'operator: math operator to use (+ or -)',
|
||||
],
|
||||
exec: (args, { findColor }, { dynamicVars, staticVars }) => {
|
||||
const [originArg, valueArg, operatorArg] = args
|
||||
|
||||
const origin = convert(findColor(originArg, { dynamicVars, staticVars })).rgb
|
||||
const value = convert(findColor(valueArg, { dynamicVars, staticVars })).rgb
|
||||
const origin = convert(
|
||||
findColor(originArg, { dynamicVars, staticVars }),
|
||||
).rgb
|
||||
const value = convert(
|
||||
findColor(valueArg, { dynamicVars, staticVars }),
|
||||
).rgb
|
||||
|
||||
return arithmeticBlend(origin, value, operatorArg)
|
||||
}
|
||||
},
|
||||
},
|
||||
boost: {
|
||||
argsNeeded: 2,
|
||||
documentation: 'If given color is dark makes it darker, if color is light - makes it lighter',
|
||||
args: [
|
||||
'color: source color',
|
||||
'amount: how much darken/brighten the color'
|
||||
],
|
||||
documentation:
|
||||
'If given color is dark makes it darker, if color is light - makes it lighter',
|
||||
args: ['color: source color', 'amount: how much darken/brighten the color'],
|
||||
exec: (args, { findColor }, { dynamicVars, staticVars }) => {
|
||||
const [colorArg, amountArg] = args
|
||||
|
||||
const color = convert(findColor(colorArg, { dynamicVars, staticVars })).rgb
|
||||
const color = convert(
|
||||
findColor(colorArg, { dynamicVars, staticVars }),
|
||||
).rgb
|
||||
const amount = Number(amountArg)
|
||||
|
||||
const isLight = relativeLuminance(color) < 0.5
|
||||
const mod = isLight ? -1 : 1
|
||||
return brightness(amount * mod, color).rgb
|
||||
}
|
||||
},
|
||||
},
|
||||
mod: {
|
||||
argsNeeded: 2,
|
||||
documentation: 'Old function that increases or decreases brightness depending if background color is dark or light. Advised against using it as it might give unexpected results.',
|
||||
args: [
|
||||
'color: source color',
|
||||
'amount: how much darken/brighten the color'
|
||||
],
|
||||
documentation:
|
||||
'Old function that increases or decreases brightness depending if background color is dark or light. Advised against using it as it might give unexpected results.',
|
||||
args: ['color: source color', 'amount: how much darken/brighten the color'],
|
||||
exec: (args, { findColor }, { dynamicVars, staticVars }) => {
|
||||
const [colorArg, amountArg] = args
|
||||
|
||||
const color = convert(findColor(colorArg, { dynamicVars, staticVars })).rgb
|
||||
const color = convert(
|
||||
findColor(colorArg, { dynamicVars, staticVars }),
|
||||
).rgb
|
||||
const amount = Number(amountArg)
|
||||
|
||||
const effectiveBackground = dynamicVars.lowerLevelBackground ?? color
|
||||
const isLightOnDark = relativeLuminance(convert(effectiveBackground).rgb) < 0.5
|
||||
const isLightOnDark =
|
||||
relativeLuminance(convert(effectiveBackground).rgb) < 0.5
|
||||
const mod = isLightOnDark ? 1 : -1
|
||||
return brightness(amount * mod, color).rgb
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
export const shadowFunctions = {
|
||||
borderSide: {
|
||||
argsNeeded: 3,
|
||||
documentation: 'Simulate a border on a side with a shadow, best works on inset border',
|
||||
documentation:
|
||||
'Simulate a border on a side with a shadow, best works on inset border',
|
||||
args: [
|
||||
'color: border color',
|
||||
'side: string indicating on which side border should be, takes either one word or two words joined by dash (i.e. "left" or "bottom-right")',
|
||||
'width: border width (thickness)',
|
||||
'[alpha]: (Optional) border opacity, defaults to 1 (fully opaque)',
|
||||
'[inset]: (Optional) whether border should be on the inside or outside, defaults to inside'
|
||||
'[inset]: (Optional) whether border should be on the inside or outside, defaults to inside',
|
||||
],
|
||||
exec: (args) => {
|
||||
const [color, side, alpha = '1', widthArg = '1', inset = 'inset'] = args
|
||||
|
|
@ -161,7 +190,7 @@ export const shadowFunctions = {
|
|||
spread: 0,
|
||||
color,
|
||||
alpha: Number(alpha),
|
||||
inset: isInset
|
||||
inset: isInset,
|
||||
}
|
||||
|
||||
side.split('-').forEach((position) => {
|
||||
|
|
@ -181,6 +210,6 @@ export const shadowFunctions = {
|
|||
}
|
||||
})
|
||||
return [targetShadow]
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,12 @@
|
|||
import { convert, brightness, contrastRatio } from 'chromatism'
|
||||
import { rgb2hex, rgba2css, alphaBlendLayers, getTextColor, relativeLuminance, getCssColor } from '../color_convert/color_convert.js'
|
||||
import {
|
||||
rgb2hex,
|
||||
rgba2css,
|
||||
alphaBlendLayers,
|
||||
getTextColor,
|
||||
relativeLuminance,
|
||||
getCssColor,
|
||||
} from '../color_convert/color_convert.js'
|
||||
import { LAYERS, DEFAULT_OPACITY, SLOT_INHERITANCE } from './pleromafe.js'
|
||||
|
||||
/*
|
||||
|
|
@ -48,15 +55,17 @@ export const getLayersArray = (layer, data = LAYERS) => {
|
|||
return array
|
||||
}
|
||||
|
||||
export const getLayers = (layer, variant = layer, opacitySlot, colors, opacity) => {
|
||||
return getLayersArray(layer).map((currentLayer) => ([
|
||||
currentLayer === layer
|
||||
? colors[variant]
|
||||
: colors[currentLayer],
|
||||
currentLayer === layer
|
||||
? opacity[opacitySlot] || 1
|
||||
: opacity[currentLayer]
|
||||
]))
|
||||
export const getLayers = (
|
||||
layer,
|
||||
variant = layer,
|
||||
opacitySlot,
|
||||
colors,
|
||||
opacity,
|
||||
) => {
|
||||
return getLayersArray(layer).map((currentLayer) => [
|
||||
currentLayer === layer ? colors[variant] : colors[currentLayer],
|
||||
currentLayer === layer ? opacity[opacitySlot] || 1 : opacity[currentLayer],
|
||||
])
|
||||
}
|
||||
|
||||
const getDependencies = (key, inheritance) => {
|
||||
|
|
@ -67,11 +76,9 @@ const getDependencies = (key, inheritance) => {
|
|||
if (data === null) return []
|
||||
const { depends, layer, variant } = data
|
||||
const layerDeps = layer
|
||||
? getLayersArray(layer).map(currentLayer => {
|
||||
return currentLayer === layer
|
||||
? variant || layer
|
||||
: currentLayer
|
||||
})
|
||||
? getLayersArray(layer).map((currentLayer) => {
|
||||
return currentLayer === layer ? variant || layer : currentLayer
|
||||
})
|
||||
: []
|
||||
if (Array.isArray(depends)) {
|
||||
return [...depends, ...layerDeps]
|
||||
|
|
@ -93,7 +100,7 @@ const getDependencies = (key, inheritance) => {
|
|||
*/
|
||||
export const topoSort = (
|
||||
inheritance = SLOT_INHERITANCE,
|
||||
getDeps = getDependencies
|
||||
getDeps = getDependencies,
|
||||
) => {
|
||||
// This is an implementation of https://en.wikipedia.org/wiki/Tarjan%27s_strongly_connected_components_algorithm
|
||||
|
||||
|
|
@ -130,22 +137,25 @@ export const topoSort = (
|
|||
|
||||
// The index thing is to make sorting stable on browsers
|
||||
// where Array.sort() isn't stable
|
||||
return output.map((data, index) => ({ data, index })).sort(({ data: a, index: ai }, { data: b, index: bi }) => {
|
||||
const depsA = getDeps(a, inheritance).length
|
||||
const depsB = getDeps(b, inheritance).length
|
||||
return output
|
||||
.map((data, index) => ({ data, index }))
|
||||
.sort(({ data: a, index: ai }, { data: b, index: bi }) => {
|
||||
const depsA = getDeps(a, inheritance).length
|
||||
const depsB = getDeps(b, inheritance).length
|
||||
|
||||
if (depsA === depsB || (depsB !== 0 && depsA !== 0)) return ai - bi
|
||||
if (depsA === 0 && depsB !== 0) return -1
|
||||
if (depsB === 0 && depsA !== 0) return 1
|
||||
return 0 // failsafe, shouldn't happen?
|
||||
}).map(({ data }) => data)
|
||||
if (depsA === depsB || (depsB !== 0 && depsA !== 0)) return ai - bi
|
||||
if (depsA === 0 && depsB !== 0) return -1
|
||||
if (depsB === 0 && depsA !== 0) return 1
|
||||
return 0 // failsafe, shouldn't happen?
|
||||
})
|
||||
.map(({ data }) => data)
|
||||
}
|
||||
|
||||
const expandSlotValue = (value) => {
|
||||
if (typeof value === 'object') return value
|
||||
return {
|
||||
depends: value.startsWith('--') ? [value.substring(2)] : [],
|
||||
default: value.startsWith('#') ? value : undefined
|
||||
default: value.startsWith('#') ? value : undefined,
|
||||
}
|
||||
}
|
||||
/**
|
||||
|
|
@ -156,7 +166,7 @@ const expandSlotValue = (value) => {
|
|||
export const getOpacitySlot = (
|
||||
k,
|
||||
inheritance = SLOT_INHERITANCE,
|
||||
getDeps = getDependencies
|
||||
getDeps = getDependencies,
|
||||
) => {
|
||||
const value = expandSlotValue(inheritance[k])
|
||||
if (value.opacity === null) return
|
||||
|
|
@ -189,7 +199,7 @@ export const getOpacitySlot = (
|
|||
export const getLayerSlot = (
|
||||
k,
|
||||
inheritance = SLOT_INHERITANCE,
|
||||
getDeps = getDependencies
|
||||
getDeps = getDependencies,
|
||||
) => {
|
||||
const value = expandSlotValue(inheritance[k])
|
||||
if (LAYERS[k]) return k
|
||||
|
|
@ -218,8 +228,11 @@ export const getLayerSlot = (
|
|||
*/
|
||||
export const SLOT_ORDERED = topoSort(
|
||||
Object.entries(SLOT_INHERITANCE)
|
||||
.sort(([, aV], [, bV]) => ((aV && aV.priority) || 0) - ((bV && bV.priority) || 0))
|
||||
.reduce((acc, [k, v]) => ({ ...acc, [k]: v }), {})
|
||||
.sort(
|
||||
([, aV], [, bV]) =>
|
||||
((aV && aV.priority) || 0) - ((bV && bV.priority) || 0),
|
||||
)
|
||||
.reduce((acc, [k, v]) => ({ ...acc, [k]: v }), {}),
|
||||
)
|
||||
|
||||
/**
|
||||
|
|
@ -233,8 +246,11 @@ export const OPACITIES = Object.entries(SLOT_INHERITANCE).reduce((acc, [k]) => {
|
|||
...acc,
|
||||
[opacity]: {
|
||||
defaultValue: DEFAULT_OPACITY[opacity] || 1,
|
||||
affectedSlots: [...((acc[opacity] && acc[opacity].affectedSlots) || []), k]
|
||||
}
|
||||
affectedSlots: [
|
||||
...((acc[opacity] && acc[opacity].affectedSlots) || []),
|
||||
k,
|
||||
],
|
||||
},
|
||||
}
|
||||
} else {
|
||||
return acc
|
||||
|
|
@ -245,10 +261,11 @@ export const OPACITIES = Object.entries(SLOT_INHERITANCE).reduce((acc, [k]) => {
|
|||
* Handle dynamic color
|
||||
*/
|
||||
export const computeDynamicColor = (sourceColor, getColor, mod) => {
|
||||
if (typeof sourceColor !== 'string' || !sourceColor.startsWith('--')) return sourceColor
|
||||
if (typeof sourceColor !== 'string' || !sourceColor.startsWith('--'))
|
||||
return sourceColor
|
||||
let targetColor = null
|
||||
// Color references other color
|
||||
const [variable, modifier] = sourceColor.split(/,/g).map(str => str.trim())
|
||||
const [variable, modifier] = sourceColor.split(/,/g).map((str) => str.trim())
|
||||
const variableSlot = variable.substring(2)
|
||||
targetColor = getColor(variableSlot)
|
||||
if (modifier) {
|
||||
|
|
@ -261,151 +278,167 @@ export const computeDynamicColor = (sourceColor, getColor, mod) => {
|
|||
* THE function you want to use. Takes provided colors and opacities
|
||||
* value and uses inheritance data to figure out color needed for the slot.
|
||||
*/
|
||||
export const getColors = (sourceColors, sourceOpacity) => SLOT_ORDERED.reduce(({ colors, opacity }, key) => {
|
||||
const sourceColor = sourceColors[key]
|
||||
const value = expandSlotValue(SLOT_INHERITANCE[key])
|
||||
const deps = getDependencies(key, SLOT_INHERITANCE)
|
||||
const isTextColor = !!value.textColor
|
||||
const variant = value.variant || value.layer
|
||||
export const getColors = (sourceColors, sourceOpacity) =>
|
||||
SLOT_ORDERED.reduce(
|
||||
({ colors, opacity }, key) => {
|
||||
const sourceColor = sourceColors[key]
|
||||
const value = expandSlotValue(SLOT_INHERITANCE[key])
|
||||
const deps = getDependencies(key, SLOT_INHERITANCE)
|
||||
const isTextColor = !!value.textColor
|
||||
const variant = value.variant || value.layer
|
||||
|
||||
let backgroundColor = null
|
||||
let backgroundColor = null
|
||||
|
||||
if (isTextColor) {
|
||||
backgroundColor = alphaBlendLayers(
|
||||
{ ...(colors[deps[0]] || convert(sourceColors[key] || '#FF00FF').rgb) },
|
||||
getLayers(
|
||||
getLayerSlot(key) || 'bg',
|
||||
variant || 'bg',
|
||||
getOpacitySlot(variant),
|
||||
colors,
|
||||
opacity
|
||||
)
|
||||
)
|
||||
} else if (variant && variant !== key) {
|
||||
backgroundColor = colors[variant] || convert(sourceColors[variant]).rgb
|
||||
} else {
|
||||
backgroundColor = colors.bg || convert(sourceColors.bg)
|
||||
}
|
||||
|
||||
const isLightOnDark = relativeLuminance(backgroundColor) < 0.5
|
||||
const mod = isLightOnDark ? 1 : -1
|
||||
|
||||
let outputColor = null
|
||||
if (sourceColor) {
|
||||
// Color is defined in source color
|
||||
let targetColor = sourceColor
|
||||
if (targetColor === 'transparent') {
|
||||
// We take only layers below current one
|
||||
const layers = getLayers(
|
||||
getLayerSlot(key),
|
||||
key,
|
||||
getOpacitySlot(key) || key,
|
||||
colors,
|
||||
opacity
|
||||
).slice(0, -1)
|
||||
targetColor = {
|
||||
...alphaBlendLayers(
|
||||
convert('#FF00FF').rgb,
|
||||
layers
|
||||
),
|
||||
a: 0
|
||||
}
|
||||
} else if (typeof sourceColor === 'string' && sourceColor.startsWith('--')) {
|
||||
targetColor = computeDynamicColor(
|
||||
sourceColor,
|
||||
variableSlot => colors[variableSlot] || sourceColors[variableSlot],
|
||||
mod
|
||||
)
|
||||
} else if (typeof sourceColor === 'string' && sourceColor.startsWith('#')) {
|
||||
targetColor = convert(targetColor).rgb
|
||||
}
|
||||
outputColor = { ...targetColor }
|
||||
} else if (value.default) {
|
||||
// same as above except in object form
|
||||
outputColor = convert(value.default).rgb
|
||||
} else {
|
||||
// calculate color
|
||||
const defaultColorFunc = (mod, dep) => ({ ...dep })
|
||||
const colorFunc = value.color || defaultColorFunc
|
||||
|
||||
if (value.textColor) {
|
||||
if (value.textColor === 'bw') {
|
||||
outputColor = contrastRatio(backgroundColor).rgb
|
||||
} else {
|
||||
let color = { ...colors[deps[0]] }
|
||||
if (value.color) {
|
||||
color = colorFunc(mod, ...deps.map((dep) => ({ ...colors[dep] })))
|
||||
}
|
||||
outputColor = getTextColor(
|
||||
backgroundColor,
|
||||
{ ...color },
|
||||
value.textColor === 'preserve'
|
||||
if (isTextColor) {
|
||||
backgroundColor = alphaBlendLayers(
|
||||
{
|
||||
...(colors[deps[0]] || convert(sourceColors[key] || '#FF00FF').rgb),
|
||||
},
|
||||
getLayers(
|
||||
getLayerSlot(key) || 'bg',
|
||||
variant || 'bg',
|
||||
getOpacitySlot(variant),
|
||||
colors,
|
||||
opacity,
|
||||
),
|
||||
)
|
||||
} else if (variant && variant !== key) {
|
||||
backgroundColor = colors[variant] || convert(sourceColors[variant]).rgb
|
||||
} else {
|
||||
backgroundColor = colors.bg || convert(sourceColors.bg)
|
||||
}
|
||||
} else {
|
||||
// background color case
|
||||
outputColor = colorFunc(
|
||||
mod,
|
||||
...deps.map((dep) => ({ ...colors[dep] }))
|
||||
)
|
||||
}
|
||||
}
|
||||
if (!outputColor) {
|
||||
throw new Error('Couldn\'t generate color for ' + key)
|
||||
}
|
||||
|
||||
const opacitySlot = value.opacity || getOpacitySlot(key)
|
||||
const ownOpacitySlot = value.opacity
|
||||
const isLightOnDark = relativeLuminance(backgroundColor) < 0.5
|
||||
const mod = isLightOnDark ? 1 : -1
|
||||
|
||||
if (ownOpacitySlot === null) {
|
||||
outputColor.a = 1
|
||||
} else if (sourceColor === 'transparent') {
|
||||
outputColor.a = 0
|
||||
} else {
|
||||
const opacityOverriden = ownOpacitySlot && sourceOpacity[opacitySlot] !== undefined
|
||||
let outputColor = null
|
||||
if (sourceColor) {
|
||||
// Color is defined in source color
|
||||
let targetColor = sourceColor
|
||||
if (targetColor === 'transparent') {
|
||||
// We take only layers below current one
|
||||
const layers = getLayers(
|
||||
getLayerSlot(key),
|
||||
key,
|
||||
getOpacitySlot(key) || key,
|
||||
colors,
|
||||
opacity,
|
||||
).slice(0, -1)
|
||||
targetColor = {
|
||||
...alphaBlendLayers(convert('#FF00FF').rgb, layers),
|
||||
a: 0,
|
||||
}
|
||||
} else if (
|
||||
typeof sourceColor === 'string' &&
|
||||
sourceColor.startsWith('--')
|
||||
) {
|
||||
targetColor = computeDynamicColor(
|
||||
sourceColor,
|
||||
(variableSlot) =>
|
||||
colors[variableSlot] || sourceColors[variableSlot],
|
||||
mod,
|
||||
)
|
||||
} else if (
|
||||
typeof sourceColor === 'string' &&
|
||||
sourceColor.startsWith('#')
|
||||
) {
|
||||
targetColor = convert(targetColor).rgb
|
||||
}
|
||||
outputColor = { ...targetColor }
|
||||
} else if (value.default) {
|
||||
// same as above except in object form
|
||||
outputColor = convert(value.default).rgb
|
||||
} else {
|
||||
// calculate color
|
||||
const defaultColorFunc = (mod, dep) => ({ ...dep })
|
||||
const colorFunc = value.color || defaultColorFunc
|
||||
|
||||
const dependencySlot = deps[0]
|
||||
const dependencyColor = dependencySlot && colors[dependencySlot]
|
||||
if (value.textColor) {
|
||||
if (value.textColor === 'bw') {
|
||||
outputColor = contrastRatio(backgroundColor).rgb
|
||||
} else {
|
||||
let color = { ...colors[deps[0]] }
|
||||
if (value.color) {
|
||||
color = colorFunc(mod, ...deps.map((dep) => ({ ...colors[dep] })))
|
||||
}
|
||||
outputColor = getTextColor(
|
||||
backgroundColor,
|
||||
{ ...color },
|
||||
value.textColor === 'preserve',
|
||||
)
|
||||
}
|
||||
} else {
|
||||
// background color case
|
||||
outputColor = colorFunc(
|
||||
mod,
|
||||
...deps.map((dep) => ({ ...colors[dep] })),
|
||||
)
|
||||
}
|
||||
}
|
||||
if (!outputColor) {
|
||||
throw new Error("Couldn't generate color for " + key)
|
||||
}
|
||||
|
||||
if (!ownOpacitySlot && dependencyColor && !value.textColor && ownOpacitySlot !== null) {
|
||||
// Inheriting color from dependency (weird, i know)
|
||||
// except if it's a text color or opacity slot is set to 'null'
|
||||
outputColor.a = dependencyColor.a
|
||||
} else if (!dependencyColor && !opacitySlot) {
|
||||
// Remove any alpha channel if no dependency and no opacitySlot found
|
||||
delete outputColor.a
|
||||
} else {
|
||||
// Otherwise try to assign opacity
|
||||
if (dependencyColor && dependencyColor.a === 0) {
|
||||
// transparent dependency shall make dependents transparent too
|
||||
const opacitySlot = value.opacity || getOpacitySlot(key)
|
||||
const ownOpacitySlot = value.opacity
|
||||
|
||||
if (ownOpacitySlot === null) {
|
||||
outputColor.a = 1
|
||||
} else if (sourceColor === 'transparent') {
|
||||
outputColor.a = 0
|
||||
} else {
|
||||
// Otherwise check if opacity is overriden and use that or default value instead
|
||||
outputColor.a = Number(
|
||||
opacityOverriden
|
||||
? sourceOpacity[opacitySlot]
|
||||
: (OPACITIES[opacitySlot] || {}).defaultValue
|
||||
)
|
||||
const opacityOverriden =
|
||||
ownOpacitySlot && sourceOpacity[opacitySlot] !== undefined
|
||||
|
||||
const dependencySlot = deps[0]
|
||||
const dependencyColor = dependencySlot && colors[dependencySlot]
|
||||
|
||||
if (
|
||||
!ownOpacitySlot &&
|
||||
dependencyColor &&
|
||||
!value.textColor &&
|
||||
ownOpacitySlot !== null
|
||||
) {
|
||||
// Inheriting color from dependency (weird, i know)
|
||||
// except if it's a text color or opacity slot is set to 'null'
|
||||
outputColor.a = dependencyColor.a
|
||||
} else if (!dependencyColor && !opacitySlot) {
|
||||
// Remove any alpha channel if no dependency and no opacitySlot found
|
||||
delete outputColor.a
|
||||
} else {
|
||||
// Otherwise try to assign opacity
|
||||
if (dependencyColor && dependencyColor.a === 0) {
|
||||
// transparent dependency shall make dependents transparent too
|
||||
outputColor.a = 0
|
||||
} else {
|
||||
// Otherwise check if opacity is overriden and use that or default value instead
|
||||
outputColor.a = Number(
|
||||
opacityOverriden
|
||||
? sourceOpacity[opacitySlot]
|
||||
: (OPACITIES[opacitySlot] || {}).defaultValue,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (Number.isNaN(outputColor.a) || outputColor.a === undefined) {
|
||||
outputColor.a = 1
|
||||
}
|
||||
if (Number.isNaN(outputColor.a) || outputColor.a === undefined) {
|
||||
outputColor.a = 1
|
||||
}
|
||||
|
||||
if (opacitySlot) {
|
||||
return {
|
||||
colors: { ...colors, [key]: outputColor },
|
||||
opacity: { ...opacity, [opacitySlot]: outputColor.a }
|
||||
}
|
||||
} else {
|
||||
return {
|
||||
colors: { ...colors, [key]: outputColor },
|
||||
opacity
|
||||
}
|
||||
}
|
||||
}, { colors: {}, opacity: {} })
|
||||
if (opacitySlot) {
|
||||
return {
|
||||
colors: { ...colors, [key]: outputColor },
|
||||
opacity: { ...opacity, [opacitySlot]: outputColor.a },
|
||||
}
|
||||
} else {
|
||||
return {
|
||||
colors: { ...colors, [key]: outputColor },
|
||||
opacity,
|
||||
}
|
||||
}
|
||||
},
|
||||
{ colors: {}, opacity: {} },
|
||||
)
|
||||
|
||||
export const composePreset = (colors, radii, shadows, fonts) => {
|
||||
return {
|
||||
|
|
@ -413,14 +446,14 @@ export const composePreset = (colors, radii, shadows, fonts) => {
|
|||
...shadows.rules,
|
||||
...colors.rules,
|
||||
...radii.rules,
|
||||
...fonts.rules
|
||||
...fonts.rules,
|
||||
},
|
||||
theme: {
|
||||
...shadows.theme,
|
||||
...colors.theme,
|
||||
...radii.theme,
|
||||
...fonts.theme
|
||||
}
|
||||
...fonts.theme,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -430,7 +463,7 @@ export const generatePreset = (input) => {
|
|||
colors,
|
||||
generateRadii(input),
|
||||
generateShadows(input, colors.theme.colors, colors.mod),
|
||||
generateFonts(input)
|
||||
generateFonts(input),
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -440,16 +473,17 @@ export const getCssShadow = (input, usesDropShadow) => {
|
|||
}
|
||||
|
||||
return input
|
||||
.filter(_ => usesDropShadow ? _.inset : _)
|
||||
.map((shad) => [
|
||||
shad.x,
|
||||
shad.y,
|
||||
shad.blur,
|
||||
shad.spread
|
||||
].map(_ => _ + 'px').concat([
|
||||
getCssColor(shad.color, shad.alpha),
|
||||
shad.inset ? 'inset' : ''
|
||||
]).join(' ')).join(', ')
|
||||
.filter((_) => (usesDropShadow ? _.inset : _))
|
||||
.map((shad) =>
|
||||
[shad.x, shad.y, shad.blur, shad.spread]
|
||||
.map((_) => _ + 'px')
|
||||
.concat([
|
||||
getCssColor(shad.color, shad.alpha),
|
||||
shad.inset ? 'inset' : '',
|
||||
])
|
||||
.join(' '),
|
||||
)
|
||||
.join(', ')
|
||||
}
|
||||
|
||||
export const getCssShadowFilter = (input) => {
|
||||
|
|
@ -457,19 +491,24 @@ export const getCssShadowFilter = (input) => {
|
|||
return 'none'
|
||||
}
|
||||
|
||||
return input
|
||||
// drop-shadow doesn't support inset or spread
|
||||
.filter((shad) => !shad.inset && Number(shad.spread) === 0)
|
||||
.map((shad) => [
|
||||
shad.x,
|
||||
shad.y,
|
||||
// drop-shadow's blur is twice as strong compared to box-shadow
|
||||
shad.blur / 2
|
||||
].map(_ => _ + 'px').concat([
|
||||
getCssColor(shad.color, shad.alpha)
|
||||
]).join(' '))
|
||||
.map(_ => `drop-shadow(${_})`)
|
||||
.join(' ')
|
||||
return (
|
||||
input
|
||||
// drop-shadow doesn't support inset or spread
|
||||
.filter((shad) => !shad.inset && Number(shad.spread) === 0)
|
||||
.map((shad) =>
|
||||
[
|
||||
shad.x,
|
||||
shad.y,
|
||||
// drop-shadow's blur is twice as strong compared to box-shadow
|
||||
shad.blur / 2,
|
||||
]
|
||||
.map((_) => _ + 'px')
|
||||
.concat([getCssColor(shad.color, shad.alpha)])
|
||||
.join(' '),
|
||||
)
|
||||
.map((_) => `drop-shadow(${_})`)
|
||||
.join(' ')
|
||||
)
|
||||
}
|
||||
|
||||
export const generateColors = (themeData) => {
|
||||
|
|
@ -479,24 +518,26 @@ export const generateColors = (themeData) => {
|
|||
|
||||
const { colors, opacity } = getColors(sourceColors, themeData.opacity || {})
|
||||
|
||||
const htmlColors = Object.entries(colors)
|
||||
.reduce((acc, [k, v]) => {
|
||||
const htmlColors = Object.entries(colors).reduce(
|
||||
(acc, [k, v]) => {
|
||||
if (!v) return acc
|
||||
acc.solid[k] = rgb2hex(v)
|
||||
acc.complete[k] = typeof v.a === 'undefined' ? rgb2hex(v) : rgba2css(v)
|
||||
return acc
|
||||
}, { complete: {}, solid: {} })
|
||||
},
|
||||
{ complete: {}, solid: {} },
|
||||
)
|
||||
return {
|
||||
rules: {
|
||||
colors: Object.entries(htmlColors.complete)
|
||||
.filter(([, v]) => v)
|
||||
.map(([k, v]) => `--${k}: ${v}`)
|
||||
.join(';')
|
||||
.join(';'),
|
||||
},
|
||||
theme: {
|
||||
colors: htmlColors.solid,
|
||||
opacity
|
||||
}
|
||||
opacity,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -504,68 +545,85 @@ export const generateRadii = (input) => {
|
|||
let inputRadii = input.radii || {}
|
||||
// v1 -> v2
|
||||
if (typeof input.btnRadius !== 'undefined') {
|
||||
inputRadii = Object
|
||||
.entries(input)
|
||||
inputRadii = Object.entries(input)
|
||||
.filter(([k]) => k.endsWith('Radius'))
|
||||
.reduce((acc, e) => { acc[e[0].split('Radius')[0]] = e[1]; return acc }, {})
|
||||
.reduce((acc, e) => {
|
||||
acc[e[0].split('Radius')[0]] = e[1]
|
||||
return acc
|
||||
}, {})
|
||||
}
|
||||
const radii = Object.entries(inputRadii).filter(([, v]) => v).reduce((acc, [k, v]) => {
|
||||
acc[k] = v
|
||||
return acc
|
||||
}, {
|
||||
btn: 4,
|
||||
input: 4,
|
||||
checkbox: 2,
|
||||
panel: 10,
|
||||
avatar: 5,
|
||||
avatarAlt: 50,
|
||||
tooltip: 2,
|
||||
attachment: 5,
|
||||
chatMessage: inputRadii.panel
|
||||
})
|
||||
const radii = Object.entries(inputRadii)
|
||||
.filter(([, v]) => v)
|
||||
.reduce(
|
||||
(acc, [k, v]) => {
|
||||
acc[k] = v
|
||||
return acc
|
||||
},
|
||||
{
|
||||
btn: 4,
|
||||
input: 4,
|
||||
checkbox: 2,
|
||||
panel: 10,
|
||||
avatar: 5,
|
||||
avatarAlt: 50,
|
||||
tooltip: 2,
|
||||
attachment: 5,
|
||||
chatMessage: inputRadii.panel,
|
||||
},
|
||||
)
|
||||
|
||||
return {
|
||||
rules: {
|
||||
radii: Object.entries(radii).filter(([, v]) => v).map(([k, v]) => `--${k}Radius: ${v}px`).join(';')
|
||||
radii: Object.entries(radii)
|
||||
.filter(([, v]) => v)
|
||||
.map(([k, v]) => `--${k}Radius: ${v}px`)
|
||||
.join(';'),
|
||||
},
|
||||
theme: {
|
||||
radii
|
||||
}
|
||||
radii,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
export const generateFonts = (input) => {
|
||||
const fonts = Object.entries(input.fonts || {}).filter(([, v]) => v).reduce((acc, [k, v]) => {
|
||||
acc[k] = Object.entries(v).filter(([, v]) => v).reduce((acc, [k, v]) => {
|
||||
acc[k] = v
|
||||
return acc
|
||||
}, acc[k])
|
||||
return acc
|
||||
}, {
|
||||
interface: {
|
||||
family: 'sans-serif'
|
||||
},
|
||||
input: {
|
||||
family: 'inherit'
|
||||
},
|
||||
post: {
|
||||
family: 'inherit'
|
||||
},
|
||||
postCode: {
|
||||
family: 'monospace'
|
||||
}
|
||||
})
|
||||
const fonts = Object.entries(input.fonts || {})
|
||||
.filter(([, v]) => v)
|
||||
.reduce(
|
||||
(acc, [k, v]) => {
|
||||
acc[k] = Object.entries(v)
|
||||
.filter(([, v]) => v)
|
||||
.reduce((acc, [k, v]) => {
|
||||
acc[k] = v
|
||||
return acc
|
||||
}, acc[k])
|
||||
return acc
|
||||
},
|
||||
{
|
||||
interface: {
|
||||
family: 'sans-serif',
|
||||
},
|
||||
input: {
|
||||
family: 'inherit',
|
||||
},
|
||||
post: {
|
||||
family: 'inherit',
|
||||
},
|
||||
postCode: {
|
||||
family: 'monospace',
|
||||
},
|
||||
},
|
||||
)
|
||||
|
||||
return {
|
||||
rules: {
|
||||
fonts: Object
|
||||
.entries(fonts)
|
||||
fonts: Object.entries(fonts)
|
||||
.filter(([, v]) => v)
|
||||
.map(([k, v]) => `--${k}Font: ${v.family}`).join(';')
|
||||
.map(([k, v]) => `--${k}Font: ${v.family}`)
|
||||
.join(';'),
|
||||
},
|
||||
theme: {
|
||||
fonts
|
||||
}
|
||||
fonts,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -576,7 +634,7 @@ const border = (top, shadow) => ({
|
|||
spread: 0,
|
||||
color: shadow ? '#000000' : '#FFFFFF',
|
||||
alpha: 0.2,
|
||||
inset: true
|
||||
inset: true,
|
||||
})
|
||||
const buttonInsetFakeBorders = [border(true, false), border(false, true)]
|
||||
const inputInsetFakeBorders = [border(true, true), border(false, false)]
|
||||
|
|
@ -586,63 +644,77 @@ const hoverGlow = {
|
|||
blur: 4,
|
||||
spread: 0,
|
||||
color: '--faint',
|
||||
alpha: 1
|
||||
alpha: 1,
|
||||
}
|
||||
|
||||
export const DEFAULT_SHADOWS = {
|
||||
panel: [{
|
||||
x: 1,
|
||||
y: 1,
|
||||
blur: 4,
|
||||
spread: 0,
|
||||
color: '#000000',
|
||||
alpha: 0.6
|
||||
}],
|
||||
topBar: [{
|
||||
x: 0,
|
||||
y: 0,
|
||||
blur: 4,
|
||||
spread: 0,
|
||||
color: '#000000',
|
||||
alpha: 0.6
|
||||
}],
|
||||
popup: [{
|
||||
x: 2,
|
||||
y: 2,
|
||||
blur: 3,
|
||||
spread: 0,
|
||||
color: '#000000',
|
||||
alpha: 0.5
|
||||
}],
|
||||
avatar: [{
|
||||
x: 0,
|
||||
y: 1,
|
||||
blur: 8,
|
||||
spread: 0,
|
||||
color: '#000000',
|
||||
alpha: 0.7
|
||||
}],
|
||||
panel: [
|
||||
{
|
||||
x: 1,
|
||||
y: 1,
|
||||
blur: 4,
|
||||
spread: 0,
|
||||
color: '#000000',
|
||||
alpha: 0.6,
|
||||
},
|
||||
],
|
||||
topBar: [
|
||||
{
|
||||
x: 0,
|
||||
y: 0,
|
||||
blur: 4,
|
||||
spread: 0,
|
||||
color: '#000000',
|
||||
alpha: 0.6,
|
||||
},
|
||||
],
|
||||
popup: [
|
||||
{
|
||||
x: 2,
|
||||
y: 2,
|
||||
blur: 3,
|
||||
spread: 0,
|
||||
color: '#000000',
|
||||
alpha: 0.5,
|
||||
},
|
||||
],
|
||||
avatar: [
|
||||
{
|
||||
x: 0,
|
||||
y: 1,
|
||||
blur: 8,
|
||||
spread: 0,
|
||||
color: '#000000',
|
||||
alpha: 0.7,
|
||||
},
|
||||
],
|
||||
avatarStatus: [],
|
||||
panelHeader: [],
|
||||
button: [{
|
||||
x: 0,
|
||||
y: 0,
|
||||
blur: 2,
|
||||
spread: 0,
|
||||
color: '#000000',
|
||||
alpha: 1
|
||||
}, ...buttonInsetFakeBorders],
|
||||
button: [
|
||||
{
|
||||
x: 0,
|
||||
y: 0,
|
||||
blur: 2,
|
||||
spread: 0,
|
||||
color: '#000000',
|
||||
alpha: 1,
|
||||
},
|
||||
...buttonInsetFakeBorders,
|
||||
],
|
||||
buttonHover: [hoverGlow, ...buttonInsetFakeBorders],
|
||||
buttonPressed: [hoverGlow, ...inputInsetFakeBorders],
|
||||
input: [...inputInsetFakeBorders, {
|
||||
x: 0,
|
||||
y: 0,
|
||||
blur: 2,
|
||||
inset: true,
|
||||
spread: 0,
|
||||
color: '#000000',
|
||||
alpha: 1
|
||||
}]
|
||||
input: [
|
||||
...inputInsetFakeBorders,
|
||||
{
|
||||
x: 0,
|
||||
y: 0,
|
||||
blur: 2,
|
||||
inset: true,
|
||||
spread: 0,
|
||||
color: '#000000',
|
||||
alpha: 1,
|
||||
},
|
||||
],
|
||||
}
|
||||
export const generateShadows = (input, colors) => {
|
||||
// TODO this is a small hack for `mod` to work with shadows
|
||||
|
|
@ -654,58 +726,65 @@ export const generateShadows = (input, colors) => {
|
|||
popup: 'popover',
|
||||
avatar: 'bg',
|
||||
panelHeader: 'panel',
|
||||
input: 'input'
|
||||
input: 'input',
|
||||
}
|
||||
|
||||
const cleanInputShadows = Object.fromEntries(
|
||||
Object.entries(input.shadows || {})
|
||||
.map(([name, shadowSlot]) => [
|
||||
name,
|
||||
// defaulting color to black to avoid potential problems
|
||||
shadowSlot.map(shadowDef => ({ color: '#000000', ...shadowDef }))
|
||||
])
|
||||
Object.entries(input.shadows || {}).map(([name, shadowSlot]) => [
|
||||
name,
|
||||
// defaulting color to black to avoid potential problems
|
||||
shadowSlot.map((shadowDef) => ({ color: '#000000', ...shadowDef })),
|
||||
]),
|
||||
)
|
||||
const inputShadows = cleanInputShadows && !input.themeEngineVersion
|
||||
? shadows2to3(cleanInputShadows, input.opacity)
|
||||
: cleanInputShadows || {}
|
||||
const inputShadows =
|
||||
cleanInputShadows && !input.themeEngineVersion
|
||||
? shadows2to3(cleanInputShadows, input.opacity)
|
||||
: cleanInputShadows || {}
|
||||
const shadows = Object.entries({
|
||||
...DEFAULT_SHADOWS,
|
||||
...inputShadows
|
||||
...inputShadows,
|
||||
}).reduce((shadowsAcc, [slotName, shadowDefs]) => {
|
||||
const slotFirstWord = slotName.replace(/[A-Z].*$/, '')
|
||||
const colorSlotName = hackContextDict[slotFirstWord]
|
||||
const isLightOnDark = relativeLuminance(convert(colors[colorSlotName]).rgb) < 0.5
|
||||
const isLightOnDark =
|
||||
relativeLuminance(convert(colors[colorSlotName]).rgb) < 0.5
|
||||
const mod = isLightOnDark ? 1 : -1
|
||||
const newShadow = shadowDefs.reduce((shadowAcc, def) => [
|
||||
...shadowAcc,
|
||||
{
|
||||
...def,
|
||||
color: rgb2hex(computeDynamicColor(
|
||||
def.color,
|
||||
(variableSlot) => convert(colors[variableSlot]).rgb,
|
||||
mod
|
||||
))
|
||||
}
|
||||
], [])
|
||||
const newShadow = shadowDefs.reduce(
|
||||
(shadowAcc, def) => [
|
||||
...shadowAcc,
|
||||
{
|
||||
...def,
|
||||
color: rgb2hex(
|
||||
computeDynamicColor(
|
||||
def.color,
|
||||
(variableSlot) => convert(colors[variableSlot]).rgb,
|
||||
mod,
|
||||
),
|
||||
),
|
||||
},
|
||||
],
|
||||
[],
|
||||
)
|
||||
return { ...shadowsAcc, [slotName]: newShadow }
|
||||
}, {})
|
||||
|
||||
return {
|
||||
rules: {
|
||||
shadows: Object
|
||||
.entries(shadows)
|
||||
// TODO for v2.2: if shadow doesn't have non-inset shadows with spread > 0 - optionally
|
||||
// convert all non-inset shadows into filter: drop-shadow() to boost performance
|
||||
.map(([k, v]) => [
|
||||
`--${k}Shadow: ${getCssShadow(v)}`,
|
||||
`--${k}ShadowFilter: ${getCssShadowFilter(v)}`,
|
||||
`--${k}ShadowInset: ${getCssShadow(v, true)}`
|
||||
].join(';'))
|
||||
.join(';')
|
||||
shadows: Object.entries(shadows)
|
||||
// TODO for v2.2: if shadow doesn't have non-inset shadows with spread > 0 - optionally
|
||||
// convert all non-inset shadows into filter: drop-shadow() to boost performance
|
||||
.map(([k, v]) =>
|
||||
[
|
||||
`--${k}Shadow: ${getCssShadow(v)}`,
|
||||
`--${k}ShadowFilter: ${getCssShadowFilter(v)}`,
|
||||
`--${k}ShadowInset: ${getCssShadow(v, true)}`,
|
||||
].join(';'),
|
||||
)
|
||||
.join(';'),
|
||||
},
|
||||
theme: {
|
||||
shadows
|
||||
}
|
||||
shadows,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -715,18 +794,25 @@ export const generateShadows = (input, colors) => {
|
|||
* Back in v2 shadows allowed you to use dynamic colors however those used pure CSS3 variables
|
||||
*/
|
||||
export const shadows2to3 = (shadows, opacity) => {
|
||||
return Object.entries(shadows).reduce((shadowsAcc, [slotName, shadowDefs]) => {
|
||||
const isDynamic = ({ color = '#000000' }) => color.startsWith('--')
|
||||
const getOpacity = ({ color }) => opacity[getOpacitySlot(color.substring(2).split(',')[0])]
|
||||
const newShadow = shadowDefs.reduce((shadowAcc, def) => [
|
||||
...shadowAcc,
|
||||
{
|
||||
...def,
|
||||
alpha: isDynamic(def) ? getOpacity(def) || 1 : def.alpha
|
||||
}
|
||||
], [])
|
||||
return { ...shadowsAcc, [slotName]: newShadow }
|
||||
}, {})
|
||||
return Object.entries(shadows).reduce(
|
||||
(shadowsAcc, [slotName, shadowDefs]) => {
|
||||
const isDynamic = ({ color = '#000000' }) => color.startsWith('--')
|
||||
const getOpacity = ({ color }) =>
|
||||
opacity[getOpacitySlot(color.substring(2).split(',')[0])]
|
||||
const newShadow = shadowDefs.reduce(
|
||||
(shadowAcc, def) => [
|
||||
...shadowAcc,
|
||||
{
|
||||
...def,
|
||||
alpha: isDynamic(def) ? getOpacity(def) || 1 : def.alpha,
|
||||
},
|
||||
],
|
||||
[],
|
||||
)
|
||||
return { ...shadowsAcc, [slotName]: newShadow }
|
||||
},
|
||||
{},
|
||||
)
|
||||
}
|
||||
|
||||
export const colors2to3 = (colors) => {
|
||||
|
|
@ -738,12 +824,13 @@ export const colors2to3 = (colors) => {
|
|||
case 'btnText':
|
||||
return {
|
||||
...acc,
|
||||
...btnPositions
|
||||
.reduce(
|
||||
(statePositionAcc, position) =>
|
||||
({ ...statePositionAcc, ['btn' + position + 'Text']: color })
|
||||
, {}
|
||||
)
|
||||
...btnPositions.reduce(
|
||||
(statePositionAcc, position) => ({
|
||||
...statePositionAcc,
|
||||
['btn' + position + 'Text']: color,
|
||||
}),
|
||||
{},
|
||||
),
|
||||
}
|
||||
default:
|
||||
return { ...acc, [slotName]: color }
|
||||
|
|
|
|||
|
|
@ -6,13 +6,13 @@ import {
|
|||
getTextColor,
|
||||
rgba2css,
|
||||
mixrgb,
|
||||
relativeLuminance
|
||||
relativeLuminance,
|
||||
} from '../color_convert/color_convert.js'
|
||||
|
||||
import {
|
||||
colorFunctions,
|
||||
shadowFunctions,
|
||||
process
|
||||
process,
|
||||
} from './theme3_slot_functions.js'
|
||||
|
||||
import {
|
||||
|
|
@ -20,7 +20,7 @@ import {
|
|||
getAllPossibleCombinations,
|
||||
genericRuleToSelector,
|
||||
normalizeCombination,
|
||||
findRules
|
||||
findRules,
|
||||
} from './iss_utils.js'
|
||||
import { deserializeShadow } from './iss_deserializer.js'
|
||||
|
||||
|
|
@ -36,15 +36,20 @@ const components = {
|
|||
Panel: null,
|
||||
Chat: null,
|
||||
ChatMessage: null,
|
||||
Button: null
|
||||
Button: null,
|
||||
}
|
||||
|
||||
export const findShadow = (shadows, { dynamicVars, staticVars }) => {
|
||||
return (shadows || []).map(shadow => {
|
||||
return (shadows || []).map((shadow) => {
|
||||
let targetShadow
|
||||
if (typeof shadow === 'string') {
|
||||
if (shadow.startsWith('$')) {
|
||||
targetShadow = process(shadow, shadowFunctions, { findColor, findShadow }, { dynamicVars, staticVars })
|
||||
targetShadow = process(
|
||||
shadow,
|
||||
shadowFunctions,
|
||||
{ findColor, findShadow },
|
||||
{ dynamicVars, staticVars },
|
||||
)
|
||||
} else if (shadow.startsWith('--')) {
|
||||
// modifiers are completely unsupported here
|
||||
const variableSlot = shadow.substring(2)
|
||||
|
|
@ -56,21 +61,27 @@ export const findShadow = (shadows, { dynamicVars, staticVars }) => {
|
|||
targetShadow = shadow
|
||||
}
|
||||
|
||||
const shadowArray = Array.isArray(targetShadow) ? targetShadow : [targetShadow]
|
||||
return shadowArray.map(s => ({
|
||||
const shadowArray = Array.isArray(targetShadow)
|
||||
? targetShadow
|
||||
: [targetShadow]
|
||||
return shadowArray.map((s) => ({
|
||||
...s,
|
||||
color: findColor(s.color, { dynamicVars, staticVars })
|
||||
color: findColor(s.color, { dynamicVars, staticVars }),
|
||||
}))
|
||||
})
|
||||
}
|
||||
|
||||
export const findColor = (color, { dynamicVars, staticVars }) => {
|
||||
try {
|
||||
if (typeof color !== 'string' || (!color.startsWith('--') && !color.startsWith('$'))) return color
|
||||
if (
|
||||
typeof color !== 'string' ||
|
||||
(!color.startsWith('--') && !color.startsWith('$'))
|
||||
)
|
||||
return color
|
||||
let targetColor = null
|
||||
if (color.startsWith('--')) {
|
||||
// Modifier support is pretty much for v2 themes only
|
||||
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)
|
||||
if (variableSlot === 'stack') {
|
||||
const { r, g, b } = dynamicVars.stacked
|
||||
|
|
@ -81,7 +92,9 @@ export const findColor = (color, { dynamicVars, staticVars }) => {
|
|||
targetColor = { r, g, b }
|
||||
} else {
|
||||
const virtualSlot = variableSlot.replace(/^parent/, '')
|
||||
targetColor = convert(dynamicVars.lowerLevelVirtualDirectivesRaw[virtualSlot]).rgb
|
||||
targetColor = convert(
|
||||
dynamicVars.lowerLevelVirtualDirectivesRaw[virtualSlot],
|
||||
).rgb
|
||||
}
|
||||
} else {
|
||||
const staticVar = staticVars[variableSlot]
|
||||
|
|
@ -98,18 +111,32 @@ ${JSON.stringify(dynamicVars, null, 2)}`)
|
|||
}
|
||||
|
||||
if (modifier) {
|
||||
const effectiveBackground = dynamicVars.lowerLevelBackground ?? targetColor
|
||||
const isLightOnDark = relativeLuminance(convert(effectiveBackground).rgb) < 0.5
|
||||
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
|
||||
targetColor = brightness(
|
||||
Number.parseFloat(modifier) * mod,
|
||||
targetColor,
|
||||
).rgb
|
||||
}
|
||||
}
|
||||
|
||||
if (color.startsWith('$')) {
|
||||
try {
|
||||
targetColor = process(color, colorFunctions, { findColor }, { dynamicVars, staticVars })
|
||||
targetColor = process(
|
||||
color,
|
||||
colorFunctions,
|
||||
{ findColor },
|
||||
{ dynamicVars, staticVars },
|
||||
)
|
||||
} catch (e) {
|
||||
console.error('Failure executing color function', e ,'\n Function: ' + color)
|
||||
console.error(
|
||||
'Failure executing color function',
|
||||
e,
|
||||
'\n Function: ' + color,
|
||||
)
|
||||
targetColor = '#FF00FF'
|
||||
}
|
||||
}
|
||||
|
|
@ -124,10 +151,17 @@ ${JSON.stringify(dynamicVars, null, 2)}\nError: ${e}`)
|
|||
}
|
||||
}
|
||||
|
||||
const getTextColorAlpha = (directives, intendedTextColor, dynamicVars, staticVars) => {
|
||||
const getTextColorAlpha = (
|
||||
directives,
|
||||
intendedTextColor,
|
||||
dynamicVars,
|
||||
staticVars,
|
||||
) => {
|
||||
const opacity = directives.textOpacity
|
||||
const backgroundColor = convert(dynamicVars.lowerLevelBackground).rgb
|
||||
const textColor = convert(findColor(intendedTextColor, { dynamicVars, staticVars })).rgb
|
||||
const textColor = convert(
|
||||
findColor(intendedTextColor, { dynamicVars, staticVars }),
|
||||
).rgb
|
||||
if (opacity === null || opacity === undefined || opacity >= 1) {
|
||||
return convert(textColor).hex
|
||||
}
|
||||
|
|
@ -148,26 +182,29 @@ const getTextColorAlpha = (directives, intendedTextColor, dynamicVars, staticVar
|
|||
// Loading all style.js[on] files dynamically
|
||||
const componentsContext = import.meta.glob(
|
||||
['/src/**/*.style.js', '/src/**/*.style.json'],
|
||||
{ eager: true }
|
||||
{ eager: true },
|
||||
)
|
||||
Object.keys(componentsContext).forEach(key => {
|
||||
Object.keys(componentsContext).forEach((key) => {
|
||||
const component = componentsContext[key].default
|
||||
if (components[component.name] != null) {
|
||||
console.warn(`Component in file ${key} is trying to override existing component ${component.name}! You have collisions/duplicates!`)
|
||||
console.warn(
|
||||
`Component in file ${key} is trying to override existing component ${component.name}! You have collisions/duplicates!`,
|
||||
)
|
||||
}
|
||||
components[component.name] = component
|
||||
})
|
||||
|
||||
Object.keys(components).forEach(key => {
|
||||
Object.keys(components).forEach((key) => {
|
||||
if (key === 'Root') return
|
||||
components.Root.validInnerComponents = components.Root.validInnerComponents || []
|
||||
components.Root.validInnerComponents =
|
||||
components.Root.validInnerComponents || []
|
||||
components.Root.validInnerComponents.push(key)
|
||||
})
|
||||
|
||||
Object.keys(components).forEach(key => {
|
||||
Object.keys(components).forEach((key) => {
|
||||
const component = components[key]
|
||||
const { validInnerComponents = [] } = component
|
||||
validInnerComponents.forEach(inner => {
|
||||
validInnerComponents.forEach((inner) => {
|
||||
const child = components[inner]
|
||||
component.possibleChildren = component.possibleChildren || []
|
||||
component.possibleChildren.push(child)
|
||||
|
|
@ -176,7 +213,6 @@ Object.keys(components).forEach(key => {
|
|||
})
|
||||
})
|
||||
|
||||
|
||||
const engineChecksum = sum(components)
|
||||
|
||||
const ruleToSelector = genericRuleToSelector(components)
|
||||
|
|
@ -206,7 +242,7 @@ export const init = ({
|
|||
liteMode = false,
|
||||
editMode = false,
|
||||
onlyNormalState = false,
|
||||
initialStaticVars = {}
|
||||
initialStaticVars = {},
|
||||
}) => {
|
||||
const rootComponentName = 'Root'
|
||||
if (!inputRuleset) throw new Error('Ruleset is null or undefined!')
|
||||
|
|
@ -216,10 +252,16 @@ export const init = ({
|
|||
|
||||
const rulesetUnsorted = [
|
||||
...Object.values(components)
|
||||
.map(c => (c.defaultRules || []).map(r => ({ source: 'Built-in', component: c.name, ...r })))
|
||||
.map((c) =>
|
||||
(c.defaultRules || []).map((r) => ({
|
||||
source: 'Built-in',
|
||||
component: c.name,
|
||||
...r,
|
||||
})),
|
||||
)
|
||||
.reduce((acc, arr) => [...acc, ...arr], []),
|
||||
...inputRuleset
|
||||
].map(rule => {
|
||||
...inputRuleset,
|
||||
].map((rule) => {
|
||||
normalizeCombination(rule)
|
||||
let currentParent = rule.parent
|
||||
while (currentParent) {
|
||||
|
|
@ -245,8 +287,8 @@ export const init = ({
|
|||
aScore += a.variant !== 'normal' ? 100 : 0
|
||||
bScore += b.variant !== 'normal' ? 100 : 0
|
||||
|
||||
aScore += a.state.filter(x => x !== 'normal').length * 1000
|
||||
bScore += b.state.filter(x => x !== 'normal').length * 1000
|
||||
aScore += a.state.filter((x) => x !== 'normal').length * 1000
|
||||
bScore += b.state.filter((x) => x !== 'normal').length * 1000
|
||||
|
||||
aScore += a.component === 'Text' ? 1 : 0
|
||||
bScore += b.component === 'Text' ? 1 : 0
|
||||
|
|
@ -263,24 +305,44 @@ export const init = ({
|
|||
.map(({ data }) => data)
|
||||
|
||||
if (!ultimateBackgroundColor) {
|
||||
console.warn('No ultimate background color provided, falling back to panel color')
|
||||
const rootRule = ruleset.findLast((x) => (x.component === 'Root' && x.directives?.['--bg']))
|
||||
console.warn(
|
||||
'No ultimate background color provided, falling back to panel color',
|
||||
)
|
||||
const rootRule = ruleset.findLast(
|
||||
(x) => x.component === 'Root' && x.directives?.['--bg'],
|
||||
)
|
||||
ultimateBackgroundColor = rootRule.directives['--bg'].split('|')[1].trim()
|
||||
}
|
||||
|
||||
const virtualComponents = new Set(Object.values(components).filter(c => c.virtual).map(c => c.name))
|
||||
const transparentComponents = new Set(Object.values(components).filter(c => c.transparent).map(c => c.name))
|
||||
const nonEditableComponents = new Set(Object.values(components).filter(c => c.notEditable).map(c => c.name))
|
||||
const virtualComponents = new Set(
|
||||
Object.values(components)
|
||||
.filter((c) => c.virtual)
|
||||
.map((c) => c.name),
|
||||
)
|
||||
const transparentComponents = new Set(
|
||||
Object.values(components)
|
||||
.filter((c) => c.transparent)
|
||||
.map((c) => c.name),
|
||||
)
|
||||
const nonEditableComponents = new Set(
|
||||
Object.values(components)
|
||||
.filter((c) => c.notEditable)
|
||||
.map((c) => c.name),
|
||||
)
|
||||
const extraCompileComponents = new Set([])
|
||||
|
||||
Object.values(components).forEach(component => {
|
||||
const relevantRules = ruleset.filter(r => r.component === component.name)
|
||||
const backgrounds = relevantRules.map(r => r.directives.background).filter(x => x)
|
||||
const opacities = relevantRules.map(r => r.directives.opacity).filter(x => x)
|
||||
Object.values(components).forEach((component) => {
|
||||
const relevantRules = ruleset.filter((r) => r.component === component.name)
|
||||
const backgrounds = relevantRules
|
||||
.map((r) => r.directives.background)
|
||||
.filter((x) => x)
|
||||
const opacities = relevantRules
|
||||
.map((r) => r.directives.opacity)
|
||||
.filter((x) => x)
|
||||
if (
|
||||
backgrounds.some(x => x.match(/--parent/)) ||
|
||||
opacities.some(x => x != null && x < 1))
|
||||
{
|
||||
backgrounds.some((x) => x.match(/--parent/)) ||
|
||||
opacities.some((x) => x != null && x < 1)
|
||||
) {
|
||||
extraCompileComponents.add(component.name)
|
||||
}
|
||||
})
|
||||
|
|
@ -299,25 +361,26 @@ export const init = ({
|
|||
// FIXME hack for editor until it supports handling component backgrounds
|
||||
lowerLevelBackground = '#00FFFF'
|
||||
}
|
||||
const lowerLevelVirtualDirectives = computed[lowerLevelSelector]?.virtualDirectives
|
||||
const lowerLevelVirtualDirectivesRaw = computed[lowerLevelSelector]?.virtualDirectivesRaw
|
||||
const lowerLevelVirtualDirectives =
|
||||
computed[lowerLevelSelector]?.virtualDirectives
|
||||
const lowerLevelVirtualDirectivesRaw =
|
||||
computed[lowerLevelSelector]?.virtualDirectivesRaw
|
||||
|
||||
const dynamicVars = computed[selector] || {
|
||||
lowerLevelSelector,
|
||||
lowerLevelBackground,
|
||||
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 computedDirectives = existingRules
|
||||
.map((r) => r.directives)
|
||||
.reduce((acc, directives) => ({ ...acc, ...directives }), {})
|
||||
const computedRule = {
|
||||
...combination,
|
||||
directives: computedDirectives
|
||||
directives: computedDirectives,
|
||||
}
|
||||
|
||||
computed[selector] = computed[selector] || {}
|
||||
|
|
@ -327,7 +390,8 @@ export const init = ({
|
|||
// avoid putting more stuff into actual CSS
|
||||
computed[selector].virtualDirectives = {}
|
||||
// but still be able to access it i.e. from --parent
|
||||
computed[selector].virtualDirectivesRaw = computed[lowerLevelSelector]?.virtualDirectivesRaw || {}
|
||||
computed[selector].virtualDirectivesRaw =
|
||||
computed[lowerLevelSelector]?.virtualDirectivesRaw || {}
|
||||
|
||||
if (virtualComponents.has(combination.component)) {
|
||||
const virtualName = [
|
||||
|
|
@ -335,22 +399,37 @@ export const init = ({
|
|||
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())
|
||||
: 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 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
|
||||
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 = {
|
||||
|
|
@ -360,26 +439,37 @@ export const init = ({
|
|||
textColor: inheritedTextColor,
|
||||
textAuto: inheritedTextAuto ?? 'preserve',
|
||||
textOpacity: inheritedTextOpacity,
|
||||
textOpacityMode: inheritedTextOpacityMode
|
||||
}
|
||||
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 || {}) }
|
||||
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)
|
||||
virtualDirectives[virtualName] = getTextColorAlpha(
|
||||
newTextRule.directives,
|
||||
textColor,
|
||||
dynamicVars,
|
||||
)
|
||||
virtualDirectivesRaw[virtualName] = textColor
|
||||
|
||||
computed[lowerLevelSelector].virtualDirectives = virtualDirectives
|
||||
|
|
@ -391,13 +481,14 @@ export const init = ({
|
|||
...combination,
|
||||
directives: {},
|
||||
virtualDirectives,
|
||||
virtualDirectivesRaw
|
||||
virtualDirectivesRaw,
|
||||
}
|
||||
} else {
|
||||
computed[selector] = computed[selector] || {}
|
||||
|
||||
// TODO: DEFAULT TEXT COLOR
|
||||
const lowerLevelStackedBackground = stacked[lowerLevelSelector] || convert(ultimateBackgroundColor).rgb
|
||||
const lowerLevelStackedBackground =
|
||||
stacked[lowerLevelSelector] || convert(ultimateBackgroundColor).rgb
|
||||
|
||||
if (computedDirectives.background) {
|
||||
let inheritRule = null
|
||||
|
|
@ -405,27 +496,37 @@ export const init = ({
|
|||
findRules({
|
||||
component: combination.component,
|
||||
variant: combination.variant,
|
||||
parent: combination.parent
|
||||
})
|
||||
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 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 inheritSelector = ruleToSelector(
|
||||
{ ...inheritRule, parent: combination.parent },
|
||||
true,
|
||||
)
|
||||
const inheritedBackground = computed[inheritSelector].background
|
||||
|
||||
dynamicVars.inheritedBackground = inheritedBackground
|
||||
|
||||
const rgb = convert(findColor(computedDirectives.background, { dynamicVars, staticVars })).rgb
|
||||
const rgb = convert(
|
||||
findColor(computedDirectives.background, {
|
||||
dynamicVars,
|
||||
staticVars,
|
||||
}),
|
||||
).rgb
|
||||
|
||||
if (!stacked[selector]) {
|
||||
let blend
|
||||
|
|
@ -435,31 +536,48 @@ export const init = ({
|
|||
} else if (alpha <= 0) {
|
||||
blend = lowerLevelStackedBackground
|
||||
} else {
|
||||
blend = alphaBlend(rgb, computedDirectives.opacity, lowerLevelStackedBackground)
|
||||
blend = alphaBlend(
|
||||
rgb,
|
||||
computedDirectives.opacity,
|
||||
lowerLevelStackedBackground,
|
||||
)
|
||||
}
|
||||
stacked[selector] = blend
|
||||
computed[selector].background = { ...rgb, a: computedDirectives.opacity ?? 1 }
|
||||
computed[selector].background = {
|
||||
...rgb,
|
||||
a: computedDirectives.opacity ?? 1,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (computedDirectives.shadow) {
|
||||
dynamicVars.shadow = flattenDeep(findShadow(flattenDeep(computedDirectives.shadow), { dynamicVars, staticVars }))
|
||||
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 }
|
||||
computed[selector].background = {
|
||||
...lowerLevelStackedBackground,
|
||||
a: 0,
|
||||
}
|
||||
}
|
||||
|
||||
dynamicVars.stacked = stacked[selector]
|
||||
dynamicVars.background = computed[selector].background
|
||||
|
||||
const dynamicSlots = Object.entries(computedDirectives).filter(([k]) => k.startsWith('--'))
|
||||
const dynamicSlots = Object.entries(computedDirectives).filter(([k]) =>
|
||||
k.startsWith('--'),
|
||||
)
|
||||
|
||||
dynamicSlots.forEach(([k, v]) => {
|
||||
const [type, value] = v.split('|').map(x => x.trim()) // woah, Extreme!
|
||||
const [type, value] = v.split('|').map((x) => x.trim()) // woah, Extreme!
|
||||
switch (type) {
|
||||
case 'color': {
|
||||
const color = findColor(value, { dynamicVars, staticVars })
|
||||
|
|
@ -470,7 +588,10 @@ export const init = ({
|
|||
break
|
||||
}
|
||||
case 'shadow': {
|
||||
const shadow = value.split(/,/g).map(s => s.trim()).filter(x => x)
|
||||
const shadow = value
|
||||
.split(/,/g)
|
||||
.map((s) => s.trim())
|
||||
.filter((x) => x)
|
||||
dynamicVars[k] = shadow
|
||||
if (combination.component === rootComponentName) {
|
||||
staticVars[k.substring(2)] = shadow
|
||||
|
|
@ -491,81 +612,97 @@ export const init = ({
|
|||
dynamicVars,
|
||||
selector: cssSelector,
|
||||
...combination,
|
||||
directives: computedDirectives
|
||||
directives: computedDirectives,
|
||||
}
|
||||
|
||||
return rule
|
||||
}
|
||||
} catch (e) {
|
||||
const { component, variant, state } = combination
|
||||
throw new Error(`Error processing combination ${component}.${variant}:${state.join(':')}: ${e}`)
|
||||
throw new Error(
|
||||
`Error processing combination ${component}.${variant}:${state.join(':')}: ${e}`,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
const processInnerComponent = (component, parent) => {
|
||||
const combinations = []
|
||||
const {
|
||||
states: originalStates = {},
|
||||
variants: originalVariants = {}
|
||||
} = component
|
||||
const { states: originalStates = {}, variants: originalVariants = {} } =
|
||||
component
|
||||
|
||||
let validInnerComponents
|
||||
if (editMode) {
|
||||
const temp = (component.validInnerComponentsLite || component.validInnerComponents || [])
|
||||
validInnerComponents = temp
|
||||
.filter(c => virtualComponents.has(c) && !nonEditableComponents.has(c))
|
||||
const temp =
|
||||
component.validInnerComponentsLite ||
|
||||
component.validInnerComponents ||
|
||||
[]
|
||||
validInnerComponents = temp.filter(
|
||||
(c) => virtualComponents.has(c) && !nonEditableComponents.has(c),
|
||||
)
|
||||
} else if (liteMode) {
|
||||
validInnerComponents = (component.validInnerComponentsLite || component.validInnerComponents || [])
|
||||
} else if (component.name === 'Root' || component.states != null || component.background?.includes('--parent')) {
|
||||
validInnerComponents =
|
||||
component.validInnerComponentsLite ||
|
||||
component.validInnerComponents ||
|
||||
[]
|
||||
} else if (
|
||||
component.name === 'Root' ||
|
||||
component.states != null ||
|
||||
component.background?.includes('--parent')
|
||||
) {
|
||||
validInnerComponents = component.validInnerComponents || []
|
||||
} else {
|
||||
validInnerComponents = component
|
||||
.validInnerComponents
|
||||
?.filter(
|
||||
c => virtualComponents.has(c)
|
||||
|| transparentComponents.has(c)
|
||||
|| extraCompileComponents.has(c)
|
||||
)
|
||||
|| []
|
||||
validInnerComponents =
|
||||
component.validInnerComponents?.filter(
|
||||
(c) =>
|
||||
virtualComponents.has(c) ||
|
||||
transparentComponents.has(c) ||
|
||||
extraCompileComponents.has(c),
|
||||
) || []
|
||||
}
|
||||
|
||||
// Normalizing states and variants to always include "normal"
|
||||
const states = { normal: '', ...originalStates }
|
||||
const variants = { normal: '', ...originalVariants }
|
||||
const innerComponents = (validInnerComponents).map(name => {
|
||||
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!`)
|
||||
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 = (onlyNormalState && !virtualComponents.has(component.name))
|
||||
? [
|
||||
['normal']
|
||||
]
|
||||
: [
|
||||
['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 ||
|
||||
const permutationStateKeys = Object.keys(states).filter(
|
||||
(s) => s !== 'normal',
|
||||
)
|
||||
const stateCombinations =
|
||||
onlyNormalState && !virtualComponents.has(component.name)
|
||||
? [['normal']]
|
||||
: [
|
||||
['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
|
||||
})
|
||||
]
|
||||
)
|
||||
}
|
||||
return true
|
||||
}),
|
||||
]
|
||||
|
||||
const stateVariantCombination = Object.keys(variants).map(variant => {
|
||||
return stateCombinations.map(state => ({ variant, state }))
|
||||
}).reduce((acc, x) => [...acc, ...x], [])
|
||||
const stateVariantCombination = Object.keys(variants)
|
||||
.map((variant) => {
|
||||
return stateCombinations.map((state) => ({ variant, state }))
|
||||
})
|
||||
.reduce((acc, x) => [...acc, ...x], [])
|
||||
|
||||
stateVariantCombination.forEach(combination => {
|
||||
stateVariantCombination.forEach((combination) => {
|
||||
combination.component = component.name
|
||||
combination.lazy = component.lazy || parent?.lazy
|
||||
combination.parent = parent
|
||||
|
|
@ -576,16 +713,16 @@ export const init = ({
|
|||
if (
|
||||
!liteMode &&
|
||||
parent?.component !== 'Root' &&
|
||||
!virtualComponents.has(component.name) &&
|
||||
!transparentComponents.has(component.name) &&
|
||||
extraCompileComponents.has(component.name)
|
||||
!virtualComponents.has(component.name) &&
|
||||
!transparentComponents.has(component.name) &&
|
||||
extraCompileComponents.has(component.name)
|
||||
) {
|
||||
combination.lazy = true
|
||||
}
|
||||
|
||||
combinations.push(combination)
|
||||
|
||||
innerComponents.forEach(innerComponent => {
|
||||
innerComponents.forEach((innerComponent) => {
|
||||
combinations.push(...processInnerComponent(innerComponent, combination))
|
||||
})
|
||||
})
|
||||
|
|
@ -594,19 +731,23 @@ export const init = ({
|
|||
}
|
||||
|
||||
const t0 = performance.now()
|
||||
const combinations = processInnerComponent(components[rootComponentName] ?? components.Root)
|
||||
const combinations = processInnerComponent(
|
||||
components[rootComponentName] ?? components.Root,
|
||||
)
|
||||
const t1 = performance.now()
|
||||
if (debug) {
|
||||
console.debug('Tree traveral took ' + (t1 - t0) + ' ms')
|
||||
}
|
||||
|
||||
const result = combinations.map((combination) => {
|
||||
if (combination.lazy) {
|
||||
return async () => processCombination(combination)
|
||||
} else {
|
||||
return processCombination(combination)
|
||||
}
|
||||
}).filter(x => x)
|
||||
const result = combinations
|
||||
.map((combination) => {
|
||||
if (combination.lazy) {
|
||||
return async () => processCombination(combination)
|
||||
} else {
|
||||
return processCombination(combination)
|
||||
}
|
||||
})
|
||||
.filter((x) => x)
|
||||
const t2 = performance.now()
|
||||
if (debug) {
|
||||
console.debug('Eager processing took ' + (t2 - t1) + ' ms')
|
||||
|
|
@ -616,7 +757,7 @@ export const init = ({
|
|||
const eager = []
|
||||
const lazy = []
|
||||
|
||||
result.forEach(x => {
|
||||
result.forEach((x) => {
|
||||
if (typeof x === 'function') {
|
||||
lazy.push(x)
|
||||
} else {
|
||||
|
|
@ -629,6 +770,6 @@ export const init = ({
|
|||
eager,
|
||||
staticVars,
|
||||
engineChecksum,
|
||||
themeChecksum: sum([lazy, eager])
|
||||
themeChecksum: sum([lazy, eager]),
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue