diff options
| author | Henry Jameson <me@hjkos.com> | 2024-07-17 17:19:57 +0300 |
|---|---|---|
| committer | Henry Jameson <me@hjkos.com> | 2024-07-17 17:19:57 +0300 |
| commit | 40c9163d215b5ac7b69437f3585586b2174211ca (patch) | |
| tree | 44555e278889ce5b91adb8a8feec36241cb4bbad /src/services/theme_data | |
| parent | 9d76fcc425abe1236304c270765ffdb4e6d0ba6e (diff) | |
optimizations, WIP theme selector
Diffstat (limited to 'src/services/theme_data')
| -rw-r--r-- | src/services/theme_data/css_utils.js | 12 | ||||
| -rw-r--r-- | src/services/theme_data/iss_utils.js | 39 | ||||
| -rw-r--r-- | src/services/theme_data/theme_data_3.service.js | 70 |
3 files changed, 101 insertions, 20 deletions
diff --git a/src/services/theme_data/css_utils.js b/src/services/theme_data/css_utils.js index 8423e8ac..9bce4834 100644 --- a/src/services/theme_data/css_utils.js +++ b/src/services/theme_data/css_utils.js @@ -159,3 +159,15 @@ export const getCssRules = (rules, debug) => rules.map(rule => { footer ].join('\n') }).filter(x => x) + +export const getScopedVersion = (rules, newScope) => { + return rules.map(x => { + if (x.startsWith('html')) { + return x.replace('html', newScope) + } else if (x.startsWith('#content')) { + return x.replace('#content', newScope) + } else { + return newScope + ' > ' + x + } + }) +} diff --git a/src/services/theme_data/iss_utils.js b/src/services/theme_data/iss_utils.js index 83ca8242..75f8dd93 100644 --- a/src/services/theme_data/iss_utils.js +++ b/src/services/theme_data/iss_utils.js @@ -39,7 +39,23 @@ export const getAllPossibleCombinations = (array) => { return combos.reduce((acc, x) => [...acc, ...x], []) } -// Converts rule, parents and their criteria into a CSS (or path if ignoreOutOfTreeSelector == true) selector +/** + * Converts rule, parents and their criteria into a CSS (or path if ignoreOutOfTreeSelector == true) + * selector. + * + * "path" here refers to "fake" selector that cannot be actually used in UI but is used for internal + * purposes + * + * @param {Object} components - object containing all components definitions + * + * @returns {Function} + * @param {Object} rule - rule in question to convert to CSS selector + * @param {boolean} ignoreOutOfTreeSelector - wthether to ignore aformentioned field in + * component definition and use selector + * @param {boolean} isParent - (mostly) internal argument used when recursing + * + * @returns {String} CSS selector (or path) + */ export const genericRuleToSelector = components => (rule, ignoreOutOfTreeSelector, isParent) => { if (!rule && !isParent) return null const component = components[rule.component] @@ -79,6 +95,17 @@ export const genericRuleToSelector = components => (rule, ignoreOutOfTreeSelecto return selectors.trim() } +/** + * Check if combination matches + * + * @param {Object} criteria - criteria to match against + * @param {Object} subject - rule/combination to check match + * @param {boolean} strict - strict checking: + * By default every variant and state inherits from "normal" state/variant + * so when checking if combination matches, it WILL match against "normal" + * state/variant. In strict mode inheritance is ignored an "normal" does + * not match + */ export const combinationsMatch = (criteria, subject, strict) => { if (criteria.component !== subject.component) return false @@ -101,6 +128,15 @@ export const combinationsMatch = (criteria, subject, strict) => { return true } +/** + * Search for rule that matches `criteria` in set of rules + * meant to be used in a ruleset.filter() function + * + * @param {Object} criteria - criteria to search for + * @param {boolean} strict - whether search strictly or not (see combinationsMatch) + * + * @return function that returns true/false if subject matches + */ export const findRules = (criteria, strict) => subject => { // If we searching for "general" rules - ignore "specific" ones if (criteria.parent === null && !!subject.parent) return false @@ -125,6 +161,7 @@ export const findRules = (criteria, strict) => subject => { return true } +// Pre-fills 'normal' state/variant if missing export const normalizeCombination = rule => { rule.variant = rule.variant ?? 'normal' rule.state = [...new Set(['normal', ...(rule.state || [])])] diff --git a/src/services/theme_data/theme_data_3.service.js b/src/services/theme_data/theme_data_3.service.js index e802a893..cf58da11 100644 --- a/src/services/theme_data/theme_data_3.service.js +++ b/src/services/theme_data/theme_data_3.service.js @@ -149,11 +149,30 @@ const ruleToSelector = genericRuleToSelector(components) export const getEngineChecksum = () => engineChecksum +/** + * Initializes and compiles the theme according to the ruleset + * + * @param {Object[]} inputRuleset - set of rules to compile theme against. Acts as an override to + * component default rulesets + * @param {string} ultimateBackgroundColor - Color that will be the "final" background for + * calculating contrast ratios and making text automatically accessible. Really used for cases when + * stuff is transparent. + * @param {boolean} debug - print out debug information in console, mostly just performance stuff + * @param {boolean} liteMode - use validInnerComponentsLite instead of validInnerComponents, meant to + * generatate theme previews and such that need to be compiled faster and don't require a lot of other + * components present in "normal" mode + * @param {boolean} onlyNormalState - only use components 'normal' states, meant for generating theme + * previews since states are the biggest factor for compilation time and are completely unnecessary + * when previewing multiple themes at same time + * @param {string} rootComponentName - [UNTESTED] which component to start from, meant for previewing a + * part of the theme (i.e. just the button) for themes 3 editor. + */ export const init = ({ inputRuleset, ultimateBackgroundColor, debug = false, liteMode = false, + onlyNormalState = false, rootComponentName = 'Root' }) => { if (!inputRuleset) throw new Error('Ruleset is null or undefined!') @@ -402,11 +421,16 @@ export const init = ({ const processInnerComponent = (component, parent) => { const combinations = [] const { - validInnerComponents = [], states: originalStates = {}, variants: originalVariants = {} } = component + const validInnerComponents = ( + liteMode + ? (component.validInnerComponentsLite || component.validInnerComponents) + : component.validInnerComponents + ) || [] + // Normalizing states and variants to always include "normal" const states = { normal: '', ...originalStates } const variants = { normal: '', ...originalVariants } @@ -418,22 +442,26 @@ export const init = ({ // Optimization: we only really need combinations without "normal" because all states implicitly have it const permutationStateKeys = Object.keys(states).filter(s => s !== 'normal') - const stateCombinations = [ - ['normal'], - ...getAllPossibleCombinations(permutationStateKeys) - .map(combination => ['normal', ...combination]) - .filter(combo => { - // Optimization: filter out some hard-coded combinations that don't make sense - if (combo.indexOf('disabled') >= 0) { - return !( - combo.indexOf('hover') >= 0 || - combo.indexOf('focused') >= 0 || - combo.indexOf('pressed') >= 0 - ) - } - return true - }) - ] + const stateCombinations = onlyNormalState + ? [ + ['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 + }) + ] const stateVariantCombination = Object.keys(variants).map(variant => { return stateCombinations.map(state => ({ variant, state })) @@ -460,7 +488,9 @@ export const init = ({ const t0 = performance.now() const combinations = processInnerComponent(components[rootComponentName] ?? components.Root) const t1 = performance.now() - console.debug('Tree traveral took ' + (t1 - t0) + ' ms') + if (debug) { + console.debug('Tree traveral took ' + (t1 - t0) + ' ms') + } const result = combinations.map((combination) => { if (combination.lazy) { @@ -470,7 +500,9 @@ export const init = ({ } }).filter(x => x) const t2 = performance.now() - console.debug('Eager processing took ' + (t2 - t1) + ' ms') + if (debug) { + console.debug('Eager processing took ' + (t2 - t1) + ' ms') + } return { lazy: result.filter(x => typeof x === 'function'), |
