more optimizations, execution is now split into eager (for main UI) and

lazy (for modals, popovers etc) parts
This commit is contained in:
Henry Jameson 2024-02-15 20:20:27 +02:00
parent 34e4dd0a79
commit 96e3a1593a
13 changed files with 156 additions and 51 deletions

View File

@ -0,0 +1,42 @@
export default {
name: 'Alert',
selector: '.alert',
validInnerComponents: [
'Text',
'Icon',
'Link',
'Border'
],
variants: {
normal: '.neutral',
error: '.error',
warning: '.warning',
success: '.success'
},
defaultRules: [
{
directives: {
background: '--text',
opacity: 0.8
}
},
{
variant: 'error',
directives: {
background: '--cRed'
}
},
{
variant: 'warning',
directives: {
background: '--cOrange'
}
},
{
variant: 'success',
directives: {
background: '--cGreen'
}
}
]
}

View File

@ -0,0 +1,25 @@
export default {
name: 'Badge',
selector: '.badge',
validInnerComponents: [
'Text',
'Icon'
],
variants: {
normal: '.neutral',
notification: '.notification'
},
defaultRules: [
{
directives: {
background: '--cGreen'
}
},
{
variant: 'notification',
directives: {
background: '--cRed'
}
}
]
}

View File

@ -38,11 +38,11 @@ export default {
// All states inherit from "normal" state, there is no other inheirtance, i.e. hover+disabled only inherits from "normal", not from hover nor disabled. // All states inherit from "normal" state, there is no other inheirtance, i.e. hover+disabled only inherits from "normal", not from hover nor disabled.
// However, cascading still works, so resulting state will be result of merging of all relevant states/variants // However, cascading still works, so resulting state will be result of merging of all relevant states/variants
// normal: '' // normal state is implicitly added, it is always included // normal: '' // normal state is implicitly added, it is always included
disabled: ':disabled',
toggled: '.toggled', toggled: '.toggled',
pressed: ':active', pressed: ':active',
hover: ':hover', hover: ':hover',
focused: ':focus-within' focused: ':focus-within',
disabled: ':disabled'
}, },
// Variants are mutually exclusive, each component implicitly has "normal" variant, and all other variants inherit from it. // Variants are mutually exclusive, each component implicitly has "normal" variant, and all other variants inherit from it.
variants: { variants: {
@ -107,13 +107,6 @@ export default {
shadow: [...buttonInsetFakeBorders] shadow: [...buttonInsetFakeBorders]
} }
}, },
{
state: ['disabled', 'hover'],
directives: {
background: '$blend(--background, 0.25, --parent)',
shadow: [...buttonInsetFakeBorders]
}
},
{ {
component: 'Text', component: 'Text',
parent: { parent: {
@ -124,17 +117,6 @@ export default {
textOpacity: 0.25, textOpacity: 0.25,
textOpacityMode: 'blend' textOpacityMode: 'blend'
} }
},
{
component: 'Text',
parent: {
component: 'Button',
state: ['disabled', 'hover']
},
directives: {
textOpacity: 0.25,
textOpacityMode: 'blend'
}
} }
] ]
} }

View File

@ -10,7 +10,8 @@ export default {
}, },
validInnerComponents: [ validInnerComponents: [
'Text', 'Text',
'Icon' 'Icon',
'Badge'
], ],
defaultRules: [ defaultRules: [
{ {

View File

@ -5,7 +5,8 @@ export default {
'Text', 'Text',
'Icon', 'Icon',
'Input', 'Input',
'Border' 'Border',
'ButtonUnstyled'
], ],
states: { states: {
hover: ':hover', hover: ':hover',

View File

@ -1,6 +1,7 @@
export default { export default {
name: 'Modals', name: 'Modals',
selector: '.modal-view', selector: '.modal-view',
lazy: true,
validInnerComponents: [ validInnerComponents: [
'Panel' 'Panel'
], ],

View File

@ -11,7 +11,8 @@ export default {
'PanelHeader', 'PanelHeader',
'MenuItem', 'MenuItem',
'Post', 'Post',
'Notification' 'Notification',
'Alert'
], ],
defaultRules: [ defaultRules: [
{ {

View File

@ -6,7 +6,9 @@ export default {
'Link', 'Link',
'Icon', 'Icon',
'Button', 'Button',
'ButtonUnstyled' 'ButtonUnstyled',
'Badge',
'Alert'
], ],
defaultRules: [ defaultRules: [
{ {

View File

@ -1,8 +1,8 @@
export default { export default {
name: 'Popover', name: 'Popover',
selector: '.popover', selector: '.popover',
lazy: true,
variants: { variants: {
tooltip: '.tooltip',
modal: '.modal' modal: '.modal'
}, },
validInnerComponents: [ validInnerComponents: [

View File

@ -7,7 +7,8 @@ export default {
'Icon', 'Icon',
'Button', 'Button',
'ButtonUnstyled', 'ButtonUnstyled',
'Input' 'Input',
'Badge'
], ],
defaultRules: [ defaultRules: [
{ {

View File

@ -6,7 +6,8 @@ export default {
// we are searching for underlay specifically or for whatever is laid on top of it. // we are searching for underlay specifically or for whatever is laid on top of it.
outOfTreeSelector: '.underlay', outOfTreeSelector: '.underlay',
validInnerComponents: [ validInnerComponents: [
'Panel' 'Panel',
'Alert'
], ],
defaultRules: [ defaultRules: [
{ {

View File

@ -28,11 +28,17 @@ export const applyTheme = (input) => {
// styleSheet.insertRule(`:root { ${rules.colors} }`, 'index-max') // styleSheet.insertRule(`:root { ${rules.colors} }`, 'index-max')
// styleSheet.insertRule(`:root { ${rules.shadows} }`, 'index-max') // styleSheet.insertRule(`:root { ${rules.shadows} }`, 'index-max')
styleSheet.insertRule(`:root { ${rules.fonts} }`, 'index-max') styleSheet.insertRule(`:root { ${rules.fonts} }`, 'index-max')
themes3.css.forEach(rule => { themes3.css(themes3.eager).forEach(rule => {
console.log(rule)
styleSheet.insertRule(rule, 'index-max') styleSheet.insertRule(rule, 'index-max')
}) })
body.classList.remove('hidden') body.classList.remove('hidden')
themes3.lazy.then(lazyRules => {
themes3.css(lazyRules).forEach(rule => {
styleSheet.insertRule(rule, 'index-max')
})
const t3 = performance.now()
console.log('Themes 3 finalization took ' + (t3 - t2) + 'ms')
})
} }
const configColumns = ({ sidebarColumnWidth, contentColumnWidth, notifsColumnWidth, emojiReactionsScale }) => const configColumns = ({ sidebarColumnWidth, contentColumnWidth, notifsColumnWidth, emojiReactionsScale }) =>

View File

@ -27,6 +27,8 @@ import Post from 'src/components/post.style.js'
import Notification from 'src/components/notification.style.js' import Notification from 'src/components/notification.style.js'
import RichContent from 'src/components/rich_content.style.js' import RichContent from 'src/components/rich_content.style.js'
import Avatar from 'src/components/avatar.style.js' import Avatar from 'src/components/avatar.style.js'
import Badge from 'src/components/badge.style.js'
import Alert from 'src/components/alert.style.js'
const DEBUG = false const DEBUG = false
@ -50,7 +52,9 @@ const components = {
Post, Post,
Notification, Notification,
RichContent, RichContent,
Avatar Avatar,
Alert,
Badge
} }
// "Unrolls" a tree structure of item: { parent: { ...item2, parent: { ...item3, parent: {...} } }} // "Unrolls" a tree structure of item: { parent: { ...item2, parent: { ...item3, parent: {...} } }}
@ -59,9 +63,8 @@ const unroll = (item) => {
const out = [] const out = []
let currentParent = item let currentParent = item
while (currentParent) { while (currentParent) {
const { parent: newParent, ...rest } = currentParent out.push(currentParent)
out.push(rest) currentParent = currentParent.parent
currentParent = newParent
} }
return out return out
} }
@ -173,7 +176,8 @@ export const init = (extraRuleset, palette) => {
const stacked = {} const stacked = {}
const computed = {} const computed = {}
const rules = [] const eagerRules = []
const lazyRules = []
const normalizeCombination = rule => { const normalizeCombination = rule => {
rule.variant = rule.variant ?? 'normal' rule.variant = rule.variant ?? 'normal'
@ -211,10 +215,6 @@ export const init = (extraRuleset, palette) => {
const virtualComponents = new Set(Object.values(components).filter(c => c.virtual).map(c => c.name)) const virtualComponents = new Set(Object.values(components).filter(c => c.virtual).map(c => c.name))
const addRule = (rule) => {
rules.push(rule)
}
const findColor = (color, dynamicVars) => { const findColor = (color, dynamicVars) => {
if (typeof color !== 'string' || (!color.startsWith('--') && !color.startsWith('$'))) return color if (typeof color !== 'string' || (!color.startsWith('--') && !color.startsWith('$'))) return color
let targetColor = null let targetColor = null
@ -346,7 +346,14 @@ export const init = (extraRuleset, palette) => {
.join(' ') .join(' ')
} }
const processInnerComponent = (component, parent) => { let counter = 0
const promises = []
const processInnerComponent = (component, rules, parent) => {
const addRule = (rule) => {
rules.push(rule)
}
const parentSelector = ruleToSelector(parent, true)
// const parentList = parent ? unroll(parent).reverse().map(c => c.component) : [] // const parentList = parent ? unroll(parent).reverse().map(c => c.component) : []
// if (!component.virtual) { // if (!component.virtual) {
// const path = [...parentList, component.name].join(' > ') // const path = [...parentList, component.name].join(' > ')
@ -367,18 +374,36 @@ export const init = (extraRuleset, palette) => {
// Optimization: we only really need combinations without "normal" because all states implicitly have it // Optimization: we only really need combinations without "normal" because all states implicitly have it
const permutationStateKeys = Object.keys(states).filter(s => s !== 'normal') const permutationStateKeys = Object.keys(states).filter(s => s !== 'normal')
const stateCombinations = [['normal'], ...getAllPossibleCombinations(permutationStateKeys).map(combination => ['normal', ...combination])] const stateCombinations = [
['normal'],
...getAllPossibleCombinations(permutationStateKeys)
.map(combination => ['normal', ...combination])
.filter(combo => {
// Optimization: filter out some hard-coded combinations that don't make sense
if (combo.indexOf('disabled') >= 0) {
return !(
combo.indexOf('hover') >= 0 ||
combo.indexOf('focused') >= 0 ||
combo.indexOf('pressed') >= 0
)
}
return true
})
]
const stateVariantCombination = Object.keys(variants).map(variant => { const stateVariantCombination = Object.keys(variants).map(variant => {
return stateCombinations.map(state => ({ variant, state })) return stateCombinations.map(state => ({ variant, state }))
}).reduce((acc, x) => [...acc, ...x], []) }).reduce((acc, x) => [...acc, ...x], [])
stateVariantCombination.forEach(combination => { stateVariantCombination.forEach(combination => {
counter++
// const tt0 = performance.now() // const tt0 = performance.now()
const soloSelector = ruleToSelector({ component: component.name, ...combination }, true)
const selector = ruleToSelector({ component: component.name, ...combination, parent }, true)
const lowerLevelSelector = selector.split(/ /g).slice(0, -1).join(' ') combination.component = component.name
const soloSelector = ruleToSelector(combination, true)
const selector = [parentSelector, soloSelector].filter(x => x).join(' ')
const lowerLevelSelector = parentSelector
const lowerLevelBackground = computed[lowerLevelSelector]?.background const lowerLevelBackground = computed[lowerLevelSelector]?.background
const lowerLevelVirtualDirectives = computed[lowerLevelSelector]?.virtualDirectives const lowerLevelVirtualDirectives = computed[lowerLevelSelector]?.virtualDirectives
const lowerLevelVirtualDirectivesRaw = computed[lowerLevelSelector]?.virtualDirectivesRaw const lowerLevelVirtualDirectivesRaw = computed[lowerLevelSelector]?.virtualDirectivesRaw
@ -489,10 +514,7 @@ export const init = (extraRuleset, palette) => {
// TODO: DEFAULT TEXT COLOR // TODO: DEFAULT TEXT COLOR
const lowerLevelComputedBackground = computed[lowerLevelSelector]?.background || convert('#FFFFFF').rgb const lowerLevelComputedBackground = computed[lowerLevelSelector]?.background || convert('#FFFFFF').rgb
if ( if (computedDirectives.shadow != null || computedDirectives.roundness != null) {
computedDirectives.shadow != null ||
computedDirectives.roundness != null
) {
addRuleNeeded = true addRuleNeeded = true
} }
@ -549,7 +571,22 @@ export const init = (extraRuleset, palette) => {
} }
} }
innerComponents.forEach(innerComponent => processInnerComponent(innerComponent, { parent, component: name, ...combination })) innerComponents.forEach(innerComponent => {
if (innerComponent.lazy) {
promises.push(new Promise((resolve, reject) => {
setTimeout(() => {
try {
processInnerComponent(innerComponent, lazyRules, { parent, component: name, ...combination })
resolve()
} catch (e) {
reject(e)
}
}, 0)
}))
} else {
processInnerComponent(innerComponent, rules, { parent, component: name, ...combination })
}
})
// const tt1 = performance.now() // const tt1 = performance.now()
// if (!component.virtual) { // if (!component.virtual) {
// console.log('State-variant ' + combination.variant + ' : ' + combination.state.join('+') + ' procession time: ' + (tt1 - tt0) + 'ms') // console.log('State-variant ' + combination.variant + ' : ' + combination.state.join('+') + ' procession time: ' + (tt1 - tt0) + 'ms')
@ -563,11 +600,16 @@ export const init = (extraRuleset, palette) => {
// } // }
} }
processInnerComponent(components.Root) processInnerComponent(components.Root, eagerRules)
console.log('TOTAL COMBOS: ' + counter)
const lazyExec = Promise.all(promises).then(() => {
console.log('TOTAL COMBOS: ' + counter)
}).then(() => lazyRules)
return { return {
raw: rules, lazy: lazyExec,
css: rules.map(rule => { eager: eagerRules,
css: rules => rules.map(rule => {
let selector = rule.selector let selector = rule.selector
if (!selector) { if (!selector) {
selector = 'body' selector = 'body'